Tutorial 10: Dialog Box as Main Window

やっとGUIプログラム醍醐味であるダイアログボックスの説明ができるようになった。 このチュートリアル(と次のチュートリアル)で、ダイアログボックスの使い方を習得できるようになる。
 ソース1   リソーススクリプト1   実行結果1 
 ソース2   リソーススクリプト2   実行結果2 

Theory:

一つ前のチュートリアルででてきた例題で遊んでいれば、 TABキーでフォーカスを変更できないことに気づいたはずだ。 入力したいコントロールにフォーカスを変更するには、 そのコントロールをクリックしないといけないのだが、少々めんどくさい。 また、親ウィンドウの背景をいつもの白色から グレーに変更していたことに気づいたかもしれない。 これは、チャイコンと親ウィンドウのクライアントエリアとのつなぎ目をきれいにするためだ。 この問題を回避する方法もあるにはあるのだが、 簡単ではなく、全てのチャイコンをサブクラス化しなければならない。

このように不便なわけは、チャイコンはそもそも通常のウィンドウ上ではなく、 ダイアログボックス上で動作するように設計されているからだ。 ダイアログボックスのクライアントエリアは通常グレーなので、 ボタンのようなチャイコンのデフォルトカラーはグレーとなっており、 そのため、プログラマはなんの苦労もなしにそれらを融合させることができる。

詳細に入る前に、ダイアログボックスがどういうものかを説明しよう。 ダイアログボックスというのは、チャイコンと共同作業を行うように設計されたウィンドウに過ぎない。 Windowsは内部的な"ダイアログボックスマネージャ" を提供しており、 そのダイアログボックスマネージャはユーザがタブを押した時にフォーカスを移動したり、 エンターキーが押されたらデフォルトのプッシュボタンを押す、 などといったキーボード処理を行うことになっており、プログラマの手助けを担っている。 ダイアログボックスは主としてユーザからの入力を受け取ったり、 ユーザへ何らかの情報を通知するために使用される。 そのようなわけで、ダイアログボックスは入出力の「ブラックボックス」と考えるがえることができ、 つまり、ダイアログボックスを使用するために内部的にダイアログボックスがどのように処理を行っているかは知る必要がなく、 ダイアログボックスとの情報をやりとりする方法さえ知っておけばよい。 この考え方はオブジェクト指向の原則で、「隠蔽」と呼ばれている考え方だ。 もし、完全にブラックボックスとして設計されていれば、 そのブラックボックスがどのように処理を行うかについて、何の知識も無く使用することができる。 問題は、ブラックボックスが完全にブラックボックスとならなければならないことで、 実際には非常に難しいのである。 Win32APIもブラックボックスとして設計されている。

ちょっと横道にそれたので、軌道修正して本題に戻ることにする。 ダイアログボックスはプログラマの負担を軽減する働きがある。 通常、標準のウィンドウにチャイコンをのせると、サブクラス化しなければならないし、 キーボード処理も自分で書かなければならない。 しかし、ダイアログボックスに配置するときは、ダイアログボックスがプログラマの代わりにキーボード処理を行ってくれる。 なのでプログラマは、ダイアログボックスへの入力を取得する方法と、ダイアログボックスへどのようにコマンドを送信するか、 ということだけを知っておけばよいことになる。

ダイアログボックスはメニューとほとんど同じ方法でリソースとして定義する。 ダイアログボックスとそのコントロールの特徴を記述したダイアログボックステンプレートを書いて、 リソーススクリプトをコンパイルする。

全てのリソースは同じリソースファイルに書かなければならないことに注意しなければならない。 別にテキストエディタでもダイアログボックステンプレートを記述することはできるのだが、おすすめはしない。 リソースエディターを使用してビジュアルに作成したほうがよい。 ダイアログボックス上のチャイコンを手で編集するのは非常に骨の折れる作業だからだ。 いくつかの非常に優れたリソースエディタがあり、 たいていのメジャーなコンパイラ(IDE)には付属している。 それらのリソースエディタでリソーススクリプトを作成したら、 MFCに関数部分など、不必要なところは削除する。

ダイアログボックスには2つの主なタイプがある。 モーダルモーダレスだ。 モーダレスダイアログボックスは他のウィンドウにフォーカスを移すことを許可するものだ。 MS-Word(やIE)の検索コマンドの際に出てくるダイアログボックスをイメージするとわかりやすい。 一方、モーダルダイアログボックスには2つのサブタイプがある。 アプリケーションモーダルシステムモーダルだ。 アプリケーションモーダルダイアログボックスは、同一アプリケーションで表示されるウィンドウ間で、 フォーカスの移動ができないものだ。しかし、他のアプリケーションのウィンドウにはフォーカスが移動できる。 システムモーダルダイアログボックスはそのダイアログボックスに応答しなければ他のウィンドウに一切フォーカスを移動できない。 モーダレスダイアログボックスはCreateDialogParam関数をCALLすることによって作成できる。 対して、モーダルダイアログボックスはDialogBoxParam関数をCALLすることになっている。 アプリケーションモーダルダイアログとシステムモーダルダイアログとの唯一の違いは、 DS_SYSMODALスタイルが指定されるかどうかである。 DS_SYSMODALが指定されれば、そのダイアログボックスはシステムモーダルとなる。

SendDlgItemMessage関数を使用することにより、ダイアログボックス上のチャイコンと情報のやりとりが可能になる。 この関数の構文は以下のようになっている。

SendDlgItemMessage proto hwndDlg  : DWORD,\
                         idControl: DWORD,\
                         uMsg     : DWORD,\
                         wParam   : DWORD,\
                         lParam   : DWORD

このAPI関数はチャイコンと非常に相性が良い。例えば、エディットコントロールから文字列を取得したければ以下のようにできる。

call SendDlgItemMessage, hDlg, ID_EDITBOX, WM_GETTEXT, 256, ADDR text_buffer

どのメッセージを送信すればよいかは、Win32APIリファレンスを参照せよ。
Windowsは get や set といった制御方法を指定したAPI関数も用意している。 例えば、GetDlgItemTextやCheckDlgButtonといった関数だ。 これらの関数はプログラマには非常に便利なもので、 メッセージごとに意味の異なるwParamやlParamをわざわざ調べなくてもよくなるのだ。 なので、コードのメンテナンスを容易にすることになるので、 積極的にこのような制御API関数を使用するようにしよう。 SendDlgItemMessage関数を使用するのは、そういった制御API関数が利用できないメッセージを扱うときだけにしよう。

Windowsダイアログボックスマネージャは、ダイアログボックスプロシージャと呼ばれる特殊化したコールバック関数にメッセージを送信するようになっている。 そのダイアログボックスプロシージャのフォーマットは以下のようになっている。

DlgProc proto hDlg  : DWORD,\
              iMsg  : DWORD,\
              wParam: DWORD,\
              lParam: DWORD

ダイアログボックスプロシージャはウィンドウプロシージャに非常によく似ており、 違うところは、ウィンドウプロシージャが戻り値がLRESULTだったのに対し、 ダイアログボックスプロシージャはTRUE/FALSEとなっている。 Windows内部で処理をおこなっているダイアログボックスマネージャが、 ダイアログボックスにとっての本当のウィンドウプロシージャだ。 そのダイアログボックスマネージャが、登録したダイアログボックスプロシージャをメッセージを付けてCALLするのである。

一般的な方法をおおざっぱに説明すると次のようになる。
作成するダイアログボックスプロシージャはメッセージを処理するのなら、eaxレジスタにTRUEをSETしてリターンしなければならない。 もし、そのメッセージを処理しないのなら、eaxレジスタにFALSEをSETする。 ダイアログボックスプロシージャは処理しないメッセージのときはDefWindowProc関数というようなことをしてはいけない。 というのは、ダイアログボックスプロシージャは本当のウィンドウプロシージャではないからだ。

ダイアログボックスの主な2つの使い方を紹介する。 アプリケーションのメインウィンドウとして使用することもできるし、 ユーザとの情報のやりとりとして使用することもできる。 このチュートリアルでは、前者(メインウィンドウとして使用する)のプログラムを調査することにするのだが、 これにも2つの解釈の仕方がある。

  1. RegisterClassEx関数で登録したクラステンプレートとしてダイアログボックスを使用できる。 この場合、ダイアログボックスは単なる標準のウィンドウと同じように動作する。 つまり、ウィンドウクラスのメンバー lpfnWndProc で指定されるウィンドウプロシージャ経由でメッセージが送られてくる。 ダイアログボックスプロシージャでないのである。 この方法で便利なところは、チャイコンを作成しなくていいところで、Windowsが作成してくれるのである。 しかも、TABキーなどのキーボード処理もWindowsが担ってくれる。 さらに、ウィンドウクラス構造体にウィンドウのアイコンやカーソルを指定することもできる。
  2. 親ウィンドウを一切作らずに、ダイアログボックスだけを作成する。 この方法ではメッセージループを作成しなくてもよくなる。 なぜなら、メッセージが直接ダイアログボックスプロシージャに送られてくるからである。 しかも、ウィンドウクラスを登録する必要もないのである。

このチュートリアルは非常に長いものとなっている。1番目の方法を説明してから2番目の説明を続けることにする。

Examples:(part 1)


dialog.asm (part 1)

.386
.model flat,stdcall
option casemap:none
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib

.data
ClassName db "DLGCLASS",0
MenuName db "MyMenu",0
DlgName db "MyDialog",0
AppName db "Our First Dialog Box",0
TestString db "Wow! I'm in an edit box now",0

.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
buffer db 512 dup(?)

.const
IDC_EDIT       equ 3000
IDC_BUTTON     equ 3001
IDC_EXIT       equ 3002
IDM_GETTEXT    equ 32000
IDM_CLEAR      equ 32001
IDM_EXIT       equ 32002

.code
start:
   invoke GetModuleHandle, NULL
   mov   hInstance,eax
   invoke GetCommandLine
    mov CommandLine,eax
   invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
   invoke ExitProcess,eax

WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
   LOCAL wc:WNDCLASSEX
   LOCAL msg:MSG
   LOCAL hDlg:HWND
   mov  wc.cbSize,SIZEOF WNDCLASSEX
   mov  wc.style, CS_HREDRAW or CS_VREDRAW
   mov  wc.lpfnWndProc, OFFSET WndProc
   mov  wc.cbClsExtra,NULL
   mov  wc.cbWndExtra,DLGWINDOWEXTRA
   push hInst
   pop  wc.hInstance
   mov  wc.hbrBackground,COLOR_BTNFACE+1
   mov  wc.lpszMenuName,OFFSET MenuName
   mov  wc.lpszClassName,OFFSET ClassName
   invoke LoadIcon,NULL,IDI_APPLICATION
   mov  wc.hIcon,eax
   mov  wc.hIconSm,eax
   invoke LoadCursor,NULL,IDC_ARROW
   mov  wc.hCursor,eax
   invoke RegisterClassEx, addr wc
   invoke CreateDialogParam,hInstance,ADDR DlgName,NULL,NULL,NULL
   mov  hDlg,eax
   invoke ShowWindow, hDlg,SW_SHOWNORMAL
   invoke UpdateWindow, hDlg
   invoke GetDlgItem,hDlg,IDC_EDIT
   invoke SetFocus,eax
   .WHILE TRUE
       invoke GetMessage, ADDR msg,NULL,0,0
       .BREAK .IF (!eax)
      invoke IsDialogMessage, hDlg, ADDR msg
       .IF eax ==FALSE
           invoke TranslateMessage, ADDR msg
           invoke DispatchMessage, ADDR msg
       .ENDIF
   .ENDW
   mov    eax,msg.wParam
   ret
WinMain endp

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
   .IF uMsg==WM_DESTROY
       invoke PostQuitMessage,NULL
   .ELSEIF uMsg==WM_COMMAND
       mov eax,wParam
       .IF lParam==0
           .IF ax==IDM_GETTEXT
               invoke GetDlgItemText,hWnd,IDC_EDIT,ADDR buffer,512
               invoke MessageBox,NULL,ADDR buffer,ADDR AppName,MB_OK
           .ELSEIF ax==IDM_CLEAR
               invoke SetDlgItemText,hWnd,IDC_EDIT,NULL
           .ELSE
               invoke DestroyWindow,hWnd
           .ENDIF
       .ELSE
           mov edx,wParam
           shr edx,16
           .IF dx==BN_CLICKED
               .IF ax==IDC_BUTTON
                   invoke SetDlgItemText,hWnd,IDC_EDIT,ADDR TestString
               .ELSEIF ax==IDC_EXIT
                   invoke SendMessage,hWnd,WM_COMMAND,IDM_EXIT,0
               .ENDIF
           .ENDIF
       .ENDIF
   .ELSE
       invoke DefWindowProc,hWnd,uMsg,wParam,lParam
       ret
   .ENDIF
   xor   eax,eax
   ret
WndProc endp
end start


dialog.rc (part 1)

#include "resource.h"

#define IDC_EDIT                                      3000
#define IDC_BUTTON                               3001
#define IDC_EXIT                                      3002

#define IDM_GETTEXT                            32000
#define IDM_CLEAR                                 32001
#define IDM_EXIT                                     32003
 

MyDialog DIALOG 10, 10, 205, 60
STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX |
WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK
CAPTION "Our First Dialog Box"
CLASS "DLGCLASS"
BEGIN
   EDITTEXT        IDC_EDIT,  15,17,111,13, ES_AUTOHSCROLL | ES_LEFT
   DEFPUSHBUTTON  "Say Hello", IDC_BUTTON,   141,10,52,13
   PUSHBUTTON     "E&xit", IDC_EXIT, 141,26,52,13, WS_GROUP
END
 

MyMenu MENU
BEGIN
   POPUP "Test Controls"
   BEGIN
       MENUITEM "Get Text", IDM_GETTEXT
       MENUITEM "Clear Text", IDM_CLEAR
       MENUITEM "", , 0x0800 /*MFT_SEPARATOR*/
       MENUITEM "E&xit", IDM_EXIT
   END
END

Analysis:(part 1)

では、まずは最初の例の解説を始めよう。
この例では、どうやってダイアログテンプレートをウィンドウクラスとして登録し、 そのクラスから「ウィンドウ」をどうやって作成するかを紹介している。 チャイコンを作成する必要が無いので、単純なプログラムである。

MyDialog DIALOG 10, 10, 205, 60

ここでは、「DIALOG」キーワードの前にある「MyDialog」というダイアログ名を宣言している。 続く数字はダイアログボックスの大きさなどを表す数字なのだが、順番に、X座標、Y座標、幅、高さ、となっている(単位はピクセルではない)。

STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX |
WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK

ダイアログボックスのスタイルを宣言している。

CAPTION "Our First Dialog Box"

これはダイアログボックスのタイトルバーに表示される文字列だ。

CLASS "DLGCLASS"

この行は非常に重要で、この「CLASS」キーワードにより、 ダイアログボックステンプレートをウィンドウクラスとして使用できるようになる。 その「CLASS」キーワードに続く文字列が「ウィンドウクラス名」となる。

BEGIN
   EDITTEXT        IDC_EDIT,  15,17,111,13, ES_AUTOHSCROLL | ES_LEFT
   DEFPUSHBUTTON  "Say Hello", IDC_BUTTON,   141,10,52,13
   PUSHBUTTON     "E&xit", IDC_EXIT, 141,26,52,13
END

上のBEGIN−ENDブロックで、ダイアログボックス上のチャイコンを定義している。 一般に以下のような構文となっている。

control-type "text"  ,controlID, x, y, width, height [,styles]

control-type はりソースファイルのコンパイラが使用する定数なので、 それぞれのコンパイラに付属しているマニュアルを調べないといけない。
では、ソースコードへと移ろう。面白いところはウィンドウクラス構造体だ。

mov  wc.cbWndExtra,DLGWINDOWEXTRA
mov  wc.lpszClassName,OFFSET ClassName

通常、このメンバーはNULLのままなのだが、ダイアログボックステンプレートをウィンドウクラスとして登録するときは、 このメンバーをDLGWINDOWEXTRAにセットしなければならない。 このクラス名はリソーススクリプトで使用する「CLASS」キーワードの次にくる文字列と同一のものにしなければならないことに気をつけよう。 残りのメンバはいつもどおりにSETしよう。ウィンドウクラス構造体の設定が終わったら、RegisterClassEx関数で登録する。 見たことがあるでしょう?そう、通常のウィンドウクラスを登録するのと同じだ。

invoke CreateDialogParam,hInstance,ADDR DlgName,NULL,NULL,NULL

「ウィンドウクラス」を登録し終えた後は、ダイアログボックスを作成する。 この例では、CreateDialogParam関数を使用して、モーダレスダイアログボックスとして作成している。 この関数は5つの引数をとることになっているが、1つ目と2つ目だけをSETすればよく、 インスタンスハンドルとダイアログボックステンプレート名へのポインタだ。 クラス名へのポインタではないことに注意せよ。

この時点で、Windowsはダイアログボックスとチャイコンを作成する。 ここでもいつもと同じように、WM_CREATEメッセージをウィンドウプロシージャが受け取ることだろう。

invoke GetDlgItem,hDlg,IDC_EDIT
invoke SetFocus,eax

ダイアログボックスが作成されたら、エディットコントロールにフォーカスを移すのだが、 そのフォーカスを移動するコードをWM_CREATEセクションで記述しようとすると、 まだその時にはチャイコンが作られていないので、GetDlgItem関数をCALLしても失敗してしまう。 なので、GetDlgItem関数を呼ぶのは、ダイアログボックスとそのチャイコンが作成されてからになるので、 UpdateWindow関数をCALLした後に上の2行を記述することにする。 GetDlgItem関数はコントロールIDを引数にとり、関連したコントロールウィンドウハンドルを返す関数で、 コントロールIDは知っているのだが、ウィンドウハンドルがわからない、という場合に使用する。

invoke IsDialogMessage, hDlg, ADDR msg
 .IF eax ==FALSE
     invoke TranslateMessage, ADDR msg
     invoke DispatchMessage, ADDR msg
 .ENDIF

プログラムがメッセージループに入り、Windowsからやってくるメッセージをディスパッチしたり、トランスレートする前に、 IsDialogMessage関数をCALLし、ダイアログボックスマネージャが作成したダイアログボックスのキーボード処理を行ってくれるように設定する。 この関数がTRUEを返せば、そのときのメッセージはダイアログボックスマネージャが対象としていたもので、 ダイアログボックスマネージャが処理したということになる。前章のチュートリアルとは違っていることに注意せよ。 ウィンドウプロシージャがエディットコントロールから文字列を取得するとき、 GetWindowText関数の代わりにGetDlgItemText関数をCALLする。 GetDlgItemText関数はウィンドウハンドルではなくコントロールIDを受け取ることになっており、 ダイアログボックスを使用する場合に非常に簡単になる。

Examples:(part 2)

では次に、メインウィンドウとして使用するダイアログボックスの2つ目のアプローチの解説を行う。 この例では、モーダルダイアログボックスアプリケーションを作成する。 メッセージループは探しても見つけられないだろう。なぜなら必要ないからである!


dialog.asm (part 2)

.386
.model flat,stdcall
option casemap:none

DlgProc proto :DWORD,:DWORD,:DWORD,:DWORD

include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib

.data
DlgName db "MyDialog",0
AppName db "Our Second Dialog Box",0
TestString db "Wow! I'm in an edit box now",0

.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
buffer db 512 dup(?)

.const
IDC_EDIT           equ 3000
IDC_BUTTON    equ 3001
IDC_EXIT           equ 3002
IDM_GETTEXT equ 32000
IDM_CLEAR      equ 32001
IDM_EXIT          equ 32002
 

.code
start:
   invoke GetModuleHandle, NULL
   mov   hInstance,eax
   invoke DialogBoxParam, hInstance, ADDR DlgName,NULL, addr DlgProc, NULL
   invoke ExitProcess,eax

DlgProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
   .IF uMsg==WM_INITDIALOG
       invoke GetDlgItem, hWnd,IDC_EDIT
       invoke SetFocus,eax
   .ELSEIF uMsg==WM_CLOSE
       invoke SendMessage,hWnd,WM_COMMAND,IDM_EXIT,0
   .ELSEIF uMsg==WM_COMMAND
       mov eax,wParam
       .IF lParam==0
           .IF ax==IDM_GETTEXT
               invoke GetDlgItemText,hWnd,IDC_EDIT,ADDR buffer,512
               invoke MessageBox,NULL,ADDR buffer,ADDR AppName,MB_OK
           .ELSEIF ax==IDM_CLEAR
               invoke SetDlgItemText,hWnd,IDC_EDIT,NULL
           .ELSEIF ax==IDM_EXIT
               invoke EndDialog, hWnd,NULL
           .ENDIF
       .ELSE
           mov edx,wParam
           shr edx,16
           .if dx==BN_CLICKED
               .IF ax==IDC_BUTTON
                   invoke SetDlgItemText,hWnd,IDC_EDIT,ADDR TestString
               .ELSEIF ax==IDC_EXIT
                   invoke SendMessage,hWnd,WM_COMMAND,IDM_EXIT,0
               .ENDIF
           .ENDIF
       .ENDIF
   .ELSE
       mov eax,FALSE
       ret
   .ENDIF
   mov eax,TRUE
   ret
DlgProc endp
end start


dialog.asm (part 2)

#include "resource.h"

#define IDC_EDIT                                      3000
#define IDC_BUTTON                               3001
#define IDC_EXIT                                      3002

#define IDR_MENU1                                 3003

#define IDM_GETTEXT                             32000
#define IDM_CLEAR                                  32001
#define IDM_EXIT                                      32003
 

MyDialog DIALOG 10, 10, 205, 60
STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX |
WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK
CAPTION "Our Second Dialog Box"
MENU IDR_MENU1
BEGIN
   EDITTEXT        IDC_EDIT,  15,17,111,13, ES_AUTOHSCROLL | ES_LEFT
   DEFPUSHBUTTON  "Say Hello", IDC_BUTTON,   141,10,52,13
   PUSHBUTTON     "E&xit", IDC_EXIT, 141,26,52,13
END
 

IDR_MENU1 MENU
BEGIN
   POPUP "Test Controls"
   BEGIN
       MENUITEM "Get Text", IDM_GETTEXT
       MENUITEM "Clear Text", IDM_CLEAR
       MENUITEM "", , 0x0800 /*MFT_SEPARATOR*/
       MENUITEM "E&xit", IDM_EXIT
   END
END

Analysis:(part 2)

DlgProc proto :DWORD,:DWORD,:DWORD,:DWORD

これにより、DlgProc関数のプロトタイプを宣言しているので、 このDlgProc関数へのポインタを以下のように使用することができる。

invoke DialogBoxParam, hInstance, ADDR DlgName,NULL, addr DlgProc, NULL

上のDialogBoxParam関数は5つの引数をとり、それらは
1 インスタンスハンドル
2 ダイアログボックステンプレート名
3 親ウィンドウハンドル
4 ダイアログボックスプロシージャのポインタ
5 そのダイアログボックス固有のデータ
となっている。DialogBoxParam関数はモーダルダイアログボックスを作成し、 ダイアログボックスが閉じられるまで制御を返さない。

.IF uMsg==WM_INITDIALOG
    invoke GetDlgItem, hWnd,IDC_EDIT
    invoke SetFocus,eax
.ELSEIF uMsg==WM_CLOSE
    invoke SendMessage,hWnd,WM_COMMAND,IDM_EXIT,0

ダイアログボックスプロシージャはウィンドウプロシージャによく似ているが、 WM_CREATEメッセージを受け取らないことが違っており、 一番最初に受け取るメッセージはWM_INITDIALOGとなっている。 通常、このWM_INITDIALOGセクションに初期化コードを書けることになっているが、 もし何らかの処理を行うのなら、TRUEをeaxレジスタにSETして関数をリターンしなければならない。

内部的なダイアログボックスマネージャは、作成したダイアログボックスがWM_CLOSEメッセージを受け取ったとき、 デフォルトではWM_DESTROYメッセージをあなたが作成したダイアログボックスプロシージャに送ることはしない。 なので、もしユーザが閉じるボタンを押したときに、何らかの処理を行いたければ、 WM_CLOSEメッセージに対して行わなければならない。 この例では、wParamにIDM_EXITをSETしつつ、WM_COMMANDメッセージを送信している。 これによって、ユーザがメニューアイテムの「Exit」をクリックしたことと同じことになる。 EndDialog関数はIDM_EXITに応答してCALLされる。

そのほかの部分のWM_COMMANDメッセージに対する処理は同じなので、省略する。

ダイアログボックスを閉じたければ、EndDialog関数をCALLするのが唯一の方法だ。 絶対にDestroyWindow関数をCALLしてはならない。 EndDialog関数はすぐにはウィンドウを閉じず、ダイアログボックスマネージャが管理するフラグをSETするだけで、 次の実行コードへと続くのである。

次に、リソースファイルを見ていこう。 注目すべき変更は、メニュー名として文字列を使用するのではなく、IDR_MENU1という値を使用している点だ。 DialogBoxParam関数で作成したダイアログボックスにメニューを付けたい場合は、このような方法となる。 ダイアログボックステンプレート中で、 メニューリソースIDの後に続く「MENU」キーワード(IDR_MENU1 MENU)を忘れてはならない。

このチュートリアルにおける2つの例において、後者の例ではアイコンが無いことにすぐに気づいたことだろう。 しかしながら、WM_INITDIALOGセクションにおいて、WM_SECTIONメッセージを送ることにより、アイコンをSETすることができる。


[戻る]