メッセージとメッセージ キューの使用
次のコード例は、Windows メッセージとメッセージ キューに関連付けられている次のタスクを実行する方法を示しています。
メッセージ ループの作成
システムは、スレッドごとにメッセージ キューを自動的に作成しません。 代わりに、システムは、メッセージ キューを必要とする操作を実行するスレッドに対してのみメッセージ キューを作成します。 スレッドが 1 つ以上のウィンドウを作成する場合は、メッセージ ループを指定する必要があります。このメッセージ ループは、スレッドのメッセージ キューからメッセージを取得し、適切なウィンドウ プロシージャにディスパッチします。
システムはアプリケーション内の個々のウィンドウにメッセージを転送するため、スレッドはメッセージ ループを開始する前に少なくとも 1 つのウィンドウを作成する必要があります。 ほとんどのアプリケーションには、ウィンドウを作成する単一のスレッドが含まれています。 一般的なアプリケーションでは、ウィンドウ クラスをメイン ウィンドウに登録し、メイン ウィンドウを作成して表示した後、メッセージ ループを開始します 。すべて WinMain 関数で開始されます。
メッセージ ループを作成するには、 GetMessage 関数と DispatchMessage 関数を使用します。 アプリケーションでユーザーから文字入力を取得する必要がある場合は、 ループに TranslateMessage 関数を含めます。 TranslateMessage は、仮想キー メッセージを文字メッセージに変換します。 次の例は、単純な Windows ベースのアプリケーションの WinMain 関数のメッセージ ループを示しています。
HINSTANCE hinst;
HWND hwndMain;
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow)
{
MSG msg;
BOOL bRet;
WNDCLASS wc;
UNREFERENCED_PARAMETER(lpszCmdLine);
// Register the window class for the main window.
if (!hPrevInstance)
{
wc.style = 0;
wc.lpfnWndProc = (WNDPROC) WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon((HINSTANCE) NULL,
IDI_APPLICATION);
wc.hCursor = LoadCursor((HINSTANCE) NULL,
IDC_ARROW);
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = "MainMenu";
wc.lpszClassName = "MainWndClass";
if (!RegisterClass(&wc))
return FALSE;
}
hinst = hInstance; // save instance handle
// Create the main window.
hwndMain = CreateWindow("MainWndClass", "Sample",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, (HWND) NULL,
(HMENU) NULL, hinst, (LPVOID) NULL);
// If the main window cannot be created, terminate
// the application.
if (!hwndMain)
return FALSE;
// Show the window and paint its contents.
ShowWindow(hwndMain, nCmdShow);
UpdateWindow(hwndMain);
// Start the message loop.
while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
// Return the exit code to the system.
return msg.wParam;
}
次の例は、アクセラレータを使用し、モードレス ダイアログ ボックスを表示するスレッドのメッセージ ループを示しています。 TranslateAccelerator または IsDialogMessage が TRUE を返す場合 (メッセージが処理されたことを示します)、TranslateMessage と DispatchMessage は呼び出されません。 その理由は、 TranslateAccelerator と IsDialogMessage が 必要なすべてのメッセージの翻訳とディスパッチを実行するためです。
HWND hwndMain;
HWND hwndDlgModeless = NULL;
MSG msg;
BOOL bRet;
HACCEL haccel;
//
// Perform initialization and create a main window.
//
while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
if (hwndDlgModeless == (HWND) NULL ||
!IsDialogMessage(hwndDlgModeless, &msg) &&
!TranslateAccelerator(hwndMain, haccel,
&msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
メッセージ キューの確認
場合によっては、アプリケーションはスレッドのメッセージ ループの外部からスレッドのメッセージ キューの内容を調べる必要があります。 たとえば、アプリケーションのウィンドウ プロシージャが長い描画操作を実行する場合、ユーザーが操作を中断できるようにする場合があります。 アプリケーションは、操作中にマウスおよびキーボード メッセージのメッセージ キューを定期的に検査しない限り、操作が完了するまでユーザー入力に応答しません。 その理由は、スレッドのメッセージ ループ内の DispatchMessage 関数は、ウィンドウ プロシージャがメッセージの処理を完了するまで戻らないということです。
PeekMessage 関数を使用すると、長い操作中にメッセージ キューを調べることができます。 PeekMessage は GetMessage 関数に似ています。両方チェックフィルター条件に一致するメッセージのメッセージ キューを作成し、メッセージを MSG 構造体にコピーします。 2 つの関数のメイン違いは、フィルター条件に一致するメッセージがキューに配置されるまで GetMessage が返されないのに対し、PeekMessage はメッセージがキューにあるかどうかに関係なくすぐに返される点です。
次の例は、 PeekMessage を使用して、長い操作中にマウス クリックとキーボード入力のメッセージ キューを調べる方法を示しています。
HWND hwnd;
BOOL fDone;
MSG msg;
// Begin the operation and continue until it is complete
// or until the user clicks the mouse or presses a key.
fDone = FALSE;
while (!fDone)
{
fDone = DoLengthyOperation(); // application-defined function
// Remove any messages that may be in the queue. If the
// queue contains any mouse or keyboard
// messages, end the operation.
while (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE))
{
switch(msg.message)
{
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_KEYDOWN:
//
// Perform any required cleanup.
//
fDone = TRUE;
}
}
}
GetQueueStatus や GetInputState などの他の関数を使用すると、スレッドのメッセージ キューの内容を調べることもできます。 GetQueueStatus は、キュー内のメッセージの種類を示すフラグの配列を返します。を使用すると、キューにメッセージが含まれているかどうかを検出する最速の方法です。 キューにマウスまたはキーボード メッセージが含まれている場合、GetInputState は TRUE を返します。 これらの両方の関数を使用して、処理する必要があるメッセージがキューに含まれているかどうかを判断できます。
メッセージの投稿
PostMessage 関数を使用して、メッセージ キューにメッセージを投稿できます。 PostMessage は 、スレッドのメッセージ キューの末尾にメッセージを配置し、スレッドがメッセージを処理するのを待たずに、すぐにを返します。 関数のパラメーターには、ウィンドウ ハンドル、メッセージ識別子、および 2 つのメッセージ パラメーターが含まれます。 システムは、これらのパラメーターを MSG 構造体にコピーし、構造体の time メンバーと pt メンバーを入力して、構造体をメッセージ 待ち行列に入れます。
システムは 、PostMessage 関数で渡されたウィンドウ ハンドルを使用して、メッセージを受信するスレッド メッセージ キューを決定します。 ハンドルが HWND_TOPMOST場合、システムはすべての最上位ウィンドウのスレッド メッセージ キューにメッセージをポストします。
PostThreadMessage 関数を使用して、特定のスレッド メッセージ キューにメッセージを投稿できます。 PostThreadMessage は PostMessage に似ていますが、最初のパラメーターはウィンドウ ハンドルではなくスレッド識別子です。 スレッド識別子を取得するには、 GetCurrentThreadId 関数を呼び出します。
メッセージ ループを終了するには、 PostQuitMessage 関数を使用します。 PostQuitMessage は 、 現在 実行中のスレッドにWM_QUIT メッセージを投稿します。 スレッドのメッセージ ループは終了し、 WM_QUIT メッセージが検出されたときにシステムに制御を返します。 次の例に示すように、アプリケーションは通常、WM_DESTROY メッセージに応答して PostQuitMessage を呼び出します。
case WM_DESTROY:
// Perform cleanup tasks.
PostQuitMessage(0);
break;
メッセージの送信
SendMessage 関数は、ウィンドウ プロシージャにメッセージを直接送信するために使用されます。 SendMessage は ウィンドウ プロシージャを呼び出し、そのプロシージャがメッセージを処理して結果を返すのを待ちます。
メッセージは、システム内の任意のウィンドウに送信できます。必要なのはウィンドウ ハンドルです。 システムは、ハンドルを使用して、メッセージを受信するウィンドウ プロシージャを決定します。
別のスレッドから送信された可能性のあるメッセージを処理する前に、ウィンドウ プロシージャで最初に InSendMessage 関数を呼び出す必要があります。 この関数が TRUE を返す場合、次の例に示すように、ウィンドウ プロシージャは、スレッドが制御を生成する関数の前に ReplyMessage を呼び出す必要があります。
case WM_USER + 5:
if (InSendMessage())
ReplyMessage(TRUE);
DialogBox(hInst, "MyDialogBox", hwndMain, (DLGPROC) MyDlgProc);
break;
ダイアログ ボックス内のコントロールには、多数のメッセージを送信できます。 これらのコントロール メッセージは、コントロールの外観、動作、コンテンツを設定したり、コントロールに関する情報を取得したりします。 たとえば、CB_ADDSTRING メッセージはコンボ ボックスに文字列を追加でき、BM_SETCHECK メッセージはチェック ボックスまたはラジオ ボタンのチェック状態を設定できます。
SendDlgItemMessage 関数を使用して、コントロールの識別子と、コントロールを含むダイアログ ボックス ウィンドウのハンドルを指定して、コントロールにメッセージを送信します。 次の例は、ダイアログ ボックス プロシージャから取得し、コンボ ボックスの編集コントロールからリスト ボックスに文字列をコピーします。 この例では 、SendDlgItemMessage を使用して 、CB_ADDSTRING メッセージをコンボ ボックスに送信します。
HWND hwndCombo;
int cTxtLen;
PSTR pszMem;
switch (uMsg)
{
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDD_ADDCBITEM:
// Get the handle of the combo box and the
// length of the string in the edit control
// of the combo box.
hwndCombo = GetDlgItem(hwndDlg, IDD_COMBO);
cTxtLen = GetWindowTextLength(hwndCombo);
// Allocate memory for the string and copy
// the string into the memory.
pszMem = (PSTR) VirtualAlloc((LPVOID) NULL,
(DWORD) (cTxtLen + 1), MEM_COMMIT,
PAGE_READWRITE);
GetWindowText(hwndCombo, pszMem,
cTxtLen + 1);
// Add the string to the list box of the
// combo box and remove the string from the
// edit control of the combo box.
if (pszMem != NULL)
{
SendDlgItemMessage(hwndDlg, IDD_COMBO,
CB_ADDSTRING, 0,
(DWORD) ((LPSTR) pszMem));
SetWindowText(hwndCombo, (LPSTR) NULL);
}
// Free the memory and return.
VirtualFree(pszMem, 0, MEM_RELEASE);
return TRUE;
//
// Process other dialog box commands.
//
}
//
// Process other dialog box messages.
//
}