Document class の作成 †Microsoft の software development kit (SDK)などでは、昔から Document-View model での実装を想定した作りになっていた。ここでは、それに習って document と document を表示するための window class を制作しよう。 まずは plain text document を考える。CMyPlain class としよう。なお、今回から、多数の class を作成する可能性があるので、名前の衝突を防ぐために、namespace を導入する。 // myplain.h #ifndef MYPLAIN_H_INCLUDED #define MYPLAIN_H_INCLUDED #include <windows.h> #include <tchar.h> #include <vector> namespace mylib { class CMyPlain { public: CMyPlain(); CMyPlain(const TCHAR* pszFileName); virtual ~CMyPlain(); virtual int Draw(HDC hdc, int x, int y, PAINTSTRUCT& ps); private: std::vector<std::vector<TCHAR> > m_linebuf; }; } // namespace mylib #endif // MYPLAIN_H_INCLUDED CMyPlain の本体 // myplain.cpp #include "myplain.h" namespace mylib { CMyPlain::CMyPlain() { } CMyPlain::CMyPlain(const TCHAR* pszFileName) { HANDLE hFile = CreateFile(pszFileName, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, 0); if(hFile == INVALID_HANDLE_VALUE) return; DWORD nRead = 0; unsigned char buf[2]; while(1) { std::vector<TCHAR> linebuf; while(ReadFile(hFile, buf, 1, &nRead, 0)) { if((nRead == 0) || (buf[0] == '\n')) { m_linebuf.push_back(linebuf); break; } if(buf[0] > ' ') linebuf.push_back((TCHAR)buf[0]); else linebuf.push_back((TCHAR)' '); } if(nRead == 0) { CloseHandle(hFile); return; } } CloseHandle(hFile); } CMyPlain::~CMyPlain() { } int CMyPlain::Draw(HDC hdc, int x, int y, PAINTSTRUCT& ps) { TEXTMETRIC tm; GetTextMetrics(hdc, &tm); int ystep = (int)(tm.tmHeight + tm.tmExternalLeading); std::vector<std::vector<TCHAR> >::iterator p = m_linebuf.begin(); while(p != m_linebuf.end() ) { if(p->size() > 0) { std::vector<TCHAR>::iterator q = p->begin(); TextOut(hdc, x, y, &*q, p->size()); } p++; y += ystep; } return 0; } } // namespace mylib 当然ながら、CMyApp も変更される。 // myapp.h #ifndef MYAPP_H_INCLUDED #define MYAPP_H_INCLUDED #include "myplain.h" namespace mylib { class CMyApp { private: static const int MAX_LOAD_STRING = 256; static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); public: CMyApp(HINSTANCE hInstance, HINSTANCE hPrevInstance); ~CMyApp(); int Run(int nCmdShow); public: HINSTANCE m_hInstance; CMyPlain m_doc; private: BOOL ParseCommandLine(); private: std::vector<TCHAR> m_DocName; TCHAR m_szWinClass[(MAX_LOAD_STRING+1)]; TCHAR m_szAppTitle[(MAX_LOAD_STRING+1)]; }; } // namespace mylib #endif // MYAPP_H_INCLUDED CMyApp の本体。 #include <windows.h> #include <tchar.h> #include "resource.h" #include "myapp.h" extern mylib::CMyApp* g_pApp; namespace mylib { CMyApp::CMyApp(HINSTANCE hInstance, HINSTANCE hPrevInstance) { // Save the instance handle m_hInstance = hInstance; // Load the strings int nChar; nChar = LoadString(hInstance, IDS_MY_WINCLASS, m_szWinClass, MAX_LOAD_STRING); m_szWinClass[nChar] = 0; nChar = LoadString(hInstance, IDS_MY_APPTITLE, m_szAppTitle, MAX_LOAD_STRING); m_szAppTitle[nChar] = 0; // Register the Window Class if(hPrevInstance == 0) { WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(0, IDI_APPLICATION); wcex.hCursor = LoadCursor(0, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = 0; wcex.lpszClassName = m_szWinClass; wcex.hIconSm = LoadIcon(0, IDI_APPLICATION); RegisterClassEx(&wcex); } ParseCommandLine(); } CMyApp::~CMyApp() { } LRESULT CALLBACK CMyApp::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch(message) { case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); g_pApp->m_doc.Draw(hdc, 0, 0, ps); EndPaint(hWnd, &ps); break; } case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } int CMyApp::Run(int nCmdShow) { // Create Window HWND hWnd = CreateWindow(m_szWinClass, m_szAppTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 0, 0, m_hInstance, 0); if(!hWnd) return 0; // Create the document if(m_DocName.size() > 0) { std::vector<TCHAR>::const_iterator p = m_DocName.begin(); m_doc = CMyPlain(&*p); } ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); // Enter Message Loop MSG msg; while(GetMessage(&msg, 0, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (int)msg.wParam; } BOOL CMyApp::ParseCommandLine() { const TCHAR* pCmdLine = GetCommandLine(); if(pCmdLine == 0) return FALSE; // First argument (executable name) const TCHAR* p = pCmdLine; while(*p <= (TCHAR)' ') { if(*p == 0) return FALSE; ++p; } TCHAR q = ' '; if((*p == '"') || (*p == '\'')) { q = *p; ++p; } while(*p != q) { if(*p == 0) return FALSE; ++p; } ++p; // Second argument while(*p <= (TCHAR)' ') { if(*p == 0) return FALSE; ++p; } if((*p == '"') || (*p == '\'')) { q = *p; ++p; } std::vector<TCHAR> arg; while(*p != q) { if(*p == 0) { arg.push_back((TCHAR)0); m_DocName = arg; return TRUE; } arg.push_back(*p); ++p; } arg.push_back((TCHAR)0); m_DocName = arg; return TRUE; } } // namespace mylib Main program も若干修正を受ける。myapp03.cpp としよう。CMyApp の object(ただ一つしかない)への pointer を global にして、各 module から参照できるようにしておく。 #include <windows.h> #include <tchar.h> #include "myapp.h" mylib::CMyApp* g_pApp; int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { using namespace mylib; // Create my application CMyApp app(hInstance, hPrevInstance); g_pApp = &app; return app.Run(nCmdShow); } 残りの resource.h と myapp.rc には変更は無い。前頁同様、全ファイル一式を tar と gzip で固めて 全ファイルを展開したフォルダで、 myapp03 build.bat として実行すると、以下のような結果が得られるだろう。 なお、これは Windows-XP SP2 で実行した結果である。Page001に示してあるのは Windows 2000 SP4 での結果である。 |