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 での結果である。  |