[[Page002]]
* Document class の作成 [#l96b3717]
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 += 20;
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 で固めて &ref("myapp03.tgz"); として添付しておく。
全ファイルを展開したフォルダで、
myapp03 build.bat
として実行すると、以下のような結果が得られるだろう。
#ref("myapp03.jpg");
なお、これは Windows-XP SP2 で実行した結果である。[[Page001]]に示してあるのは Windows 2000 SP4 での結果である。