今回は、ダイアログアプリではなく、通常のウィンドウアプリを作ることにします。 メニューがついたメインウィンドウがあるアプリです。
MyProjectフォルダを作成し、その中に、MsgCrackを使って、「MWindowBase.hpp」と 「targetver.h」「CMakeLists.txt」ファイルを作成します。
さらにMsgCrackで「MyProject.cpp (Window)」を選んで、「MyProject.cpp」ファイルを作成します。
次にRisohEditorを開きます。RisohEditorが空っぽの状態で、名前が1のアイコンを追加して下さい。
さらに、名前が1のメニューを追加して下さい。
さらに文字列テーブルを追加します。RisohEditorの「編集」メニューから「追加」→「文字列テーブルを追加」を選びます。
「リソースの追加」ダイアログが表示されます。そのままEnterキーを押して下さい。
RisohEditorが次のようになりますね。
文字列テーブルのテキストを次のように修正します。
LANGUAGE LANG_JAPANESE, SUBLANG_DEFAULT
STRINGTABLE
{
1, "私のウィンドウアプリ"
}
文字列テーブルでは、自由に日本語のテキストが使えます。 テキストを変更したら、変更を適用するためにコンパイルが必要です。 「コンパイル」ボタンを押して下さい。
MyProjectフォルダの中の「MyProject_res.rc」というファイル名でエクスポートして下さい。 「MyProject_res.rc」「resource.h」という2つのファイルができますね。
MSYS2で「cmake -G "MSYS Makefiles"」「make」を順番に実行してビルドしてみて下さい。
ビルド成功です。作成されたbuildフォルダの中にあるMyProject.exeをダブルクリックしてみて下さい。
メニューのついたメインウィンドウが表示されます。「File」→「Exit」を選ぶか、 ウィンドウ右上の「×」をクリックすると終了します。
では、ソースを見ることにしましょう。「MyProject.cpp」を開いてみて下さい。
...
virtual LPCTSTR GetWndClassNameDx() const
{
return TEXT("MZC4 MMainWnd Class");
}
virtual VOID ModifyWndClassDx(WNDCLASSEX& wcx)
{
wcx.lpszMenuName = MAKEINTRESOURCE(1);
wcx.hIcon = m_hIcon;
wcx.hIconSm = m_hIconSm;
}
...
MMainWndクラスがMyProjectのメインウィンドウを表しています。 GetWndClassNameDxメソッドは、ウィンドウクラス特有のウィンドウクラス名を指定します。 MMainWndのウィンドウクラス名は、"MZC4 MMainWnd Class"になっています。
EDITやSTATIC、BUTTONなどはウィンドウクラス名と呼ばれます。システムで定義されていないウィンドウを作成するには、 ウィンドウプロシージャなどの情報を持ったウィンドウクラスを登録しなければいけませんが、 MZC4では、MWindowBase::CreateWindowDxを実行するだけで、自動的に未定義のウィンドウクラスを登録することができます。
ModifyWndClassDxは、CreateWindowDxにおいてウィンドウクラスを登録するときに使われる情報を編集するためのメソッドです。 wcx.lpszMenuName = MAKEINTRESOURCE(1);により、1という名前のクラスメニューがMMainWndに追加されます。 これにより、リソースで定義された名前1のメニューが使われます。 MAKEINTRESOURCEマクロは、整数をリソース名にするためのマクロです。 アイコンのm_hIconとm_hIconSmも指定されています。
StartDxメソッドを見てみましょう。
...
BOOL StartDx(INT nCmdShow)
{
m_hIcon = LoadIconDx(1);
m_hIconSm = LoadSmallIconDx(1);
m_hAccel = ::LoadAccelerators(m_hInst, MAKEINTRESOURCE(1));
if (!CreateWindowDx(NULL, LoadStringDx(1)))
{
MsgBoxDx(TEXT("failure of CreateWindow"), NULL, MB_ICONERROR);
return FALSE;
}
::ShowWindow(*this, nCmdShow);
::UpdateWindow(*this);
return TRUE;
}
...
MZC4のLoadIconDxとLoadSmallIconDx関数により、アイコンを読み込んでいます。 LoadAccelerators APIはアクセスキーを読み込むためのリソースを読み込みます。 リソースにアクセスキーはないのでLoadAcceleratorsは失敗します。
次に、CreateWindowDxが呼ばれます。LoadStringDx(1)は、文字列リソースのIDが1の文字列を読み込みます。 これがウィンドウテキスト(キャプション)になり、タイトルバーに表示されます。 WS_VISIBLEを指定しないと、作成されただけでは表示されないので、ShowWindow APIとUpdateWindow APIを呼んでいます。 ここでは、TRUEは処理の成功を表しています。
MZC4のCreateWindowDx関数は、Win32のCreateWindow APIをラップした関数です。
参考資料:MSDNのCreateWindow
ウィンドウが作成されたときは、WM_CREATEメッセージが届きます。WM_CREATEメッセージが来ると、ウィンドウプロシージャは、OnCreateメソッドを呼びます。
参考資料:MSDNのWM_CREATE
RunDxメソッドは、メインループです。ここでMMainWndウィンドウに来るメッセージを待っています。
DestroyWindowが呼ばれると、ウィンドウは破壊されます。 ウィンドウが破壊されたときは、WM_DESTROYメッセージが届きます。WM_DESTROYメッセージが来ると、ウィンドウプロシージャは、OnDestroyメソッドを呼びます。 OnDestroyメソッドは、PostQuitMessage(0);を実行します。これは、メッセージループを終了させ、アプリを終了に導きます。 ダイアログアプリではない場合、PostQuitMessageを呼ばないと、メッセージループが終了しないことに注意して下さい。
参考資料: MSDNのDestroyWindow、 MSDNのWM_DESTROY、 MSDNのPostQuitMessage
少し、MMainWndのクライアント領域にお絵かきをしてみましょう。 クライアント領域は、タイトルバーや外枠以外の画面の中身の長方形の領域のことです。 タイトルバーや外枠などは、非クライアント領域と呼ばれます。
MsgCrackでWM_PAINTと入力して、出てきたテキストをコピーして、MMainWndの内部に貼り付けて下さい。 次のように書いて下さい。
void OnPaint(HWND hwnd)
{
RECT rc;
GetClientRect(hwnd, &rc);
PAINTSTRUCT ps;
if (HDC hDC = BeginPaint(hwnd, &ps))
{
if (HPEN hPen = CreatePen(PS_SOLID, 1, RGB(255, 0, 0)))
{
HGDIOBJ hPenOld = SelectObject(hDC, hPen);
MoveToEx(hDC, rc.left, rc.top, NULL);
LineTo(hDC, rc.right, rc.bottom);
MoveToEx(hDC, rc.right, rc.top, NULL);
LineTo(hDC, rc.left, rc.bottom);
SelectObject(hDC, hPenOld);
DeleteObject(hPen);
}
EndPaint(hwnd, &ps);
}
}
WindowProcDxにWM_PAINT/OnPaintを登録して下さい。
virtual LRESULT CALLBACK
WindowProcDx(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
HANDLE_MSG(hwnd, WM_CREATE, OnCreate);
HANDLE_MSG(hwnd, WM_COMMAND, OnCommand);
HANDLE_MSG(hwnd, WM_DESTROY, OnDestroy);
HANDLE_MSG(hwnd, WM_PAINT, OnPaint);
default:
return DefaultProcDx();
}
}
参考資料: MSDNのWM_PAINT
makeでビルドして実行してみましょう。
クライアント領域に、赤いバッテンが表示されましたね。
GetClientRect APIは、クライアント領域を取得する関数です。 RECT構造体は、長方形領域を格納する構造体です。 BeginPaint APIはWM_PAINTで描画を行う前に呼ぶ関数です。 EndPaint APIはWM_PAINTで描画を行った後に呼ぶ関数です。
参考資料: MSDNのGetClientRect、 MSDNのRECT、 MSDNのBeginPaint、 MSDNのEndPaint
HDCは、「デバイスコンテキスト」という、描画対象の装置を表すハンドルです。 HPENは線を引くための「ペン」のハンドルです。 CreatePen APIは文字通り、ペンを作成します。 SelectObject APIでペンを選択すると、そのペンで線を描画できます。 MoveToEx APIでペンの描画位置を動かして、LineTo APIで実際にペンで線を引きます。 DeleteObject APIは、ペンを破棄する関数です。
参考資料: MSDNのCreatePen、 MSDNのSelectObject、 MSDNのMoveToEx、 MSDNのLineTo、 MSDNのDeleteObject
ペンの太さを変えてみましょう。
if (HPEN hPen = CreatePen(PS_SOLID, 1, RGB(255, 0, 0)))
を
if (HPEN hPen = CreatePen(PS_SOLID, 8, RGB(255, 0, 0)))
に変更して、MyProject.exeを終了させた後、再ビルドして下さい。
太さが1ピクセルから8ピクセルに代わります。ピクセルというのは、ディスプレイ画面で最小の点を表示できる「画素」のことです。
次に、色を赤から青に変えてみましょう。
if (HPEN hPen = CreatePen(PS_SOLID, 8, RGB(255, 0, 0)))
を
if (HPEN hPen = CreatePen(PS_SOLID, 8, RGB(0, 0, 255)))
に変更します。
色の指定は、RGBマクロによって指定します。RGBとは「Red」「Green」「Blue」という3つの色成分のことです。 画面で表示される色は、0~255の3つのRGB成分によって指定できます。
参考資料: MSDNのRGB
ウィンドウのサイズを変えると、それに合わせてバッテンのサイズも変わります。
好奇心のある人は、MWindowBase.hppのCreateWindowDx関数の中身を見るといいでしょう。