メッセージとメッセージ キューの使用

次のコード例は、メッセージとメッセージ キューに関連付けられている次のタスクWindows実行する方法を示しています。

メッセージ Loopの作成

システムは、スレッドごとにメッセージ キューを自動的に作成しません。 代わりに、システムは、メッセージ キューを必要とする操作を実行するスレッドに対してのみメッセージ キューを作成します。 スレッドが 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 または IsDialogMessageTRUE を返す場合 (メッセージが処理されたことを示します)、TranslateMessageDispatchMessage は呼び出されません。 その理由は、 TranslateAcceleratorIsDialogMessage が必要なすべてのメッセージの翻訳とディスパッチを実行するためです。

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 関数を使用すると、長い操作中にメッセージ キューを調べることができます。 PeekMessageGetMessage 関数に似ています。両方とも、フィルター条件に一致するメッセージがないかメッセージ キューをチェックし、 メッセージを 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; 
        } 
    } 
} 

GetQueueStatusGetInputState などの他の関数では、スレッドのメッセージ キューの内容を調べることもできます。 GetQueueStatus は、キュー内のメッセージの種類を示すフラグの配列を返します。キューにメッセージが含まれているかどうかを検出する最も高速な方法です。 GetInputState は、キューにマウスまたはキーボード メッセージが含まれている場合に TRUE を 返します。 これらの両方の関数を使用して、処理する必要があるメッセージがキューに含まれているかどうかを判断できます。

メッセージの投稿

PostMessage 関数を使用してメッセージ キューにメッセージを投稿できます。 PostMessage は 、スレッドのメッセージ キューの末尾にメッセージを配置し、スレッドがメッセージを処理するのを待たずに直ちに返します。 関数のパラメーターには、ウィンドウ ハンドル、メッセージ識別子、および 2 つのメッセージ パラメーターが含まれます。 システムは、これらのパラメーターを MSG 構造体にコピーし、構造体の 時刻 メンバーと pt メンバーを入力し、その構造体をメッセージ・キューに入れます。

システムは 、PostMessage 関数で渡されたウィンドウ ハンドルを使用して、メッセージを受信するスレッド メッセージ キューを決定します。 ハンドルが HWND_TOPMOSTされている場合、システムはすべての最上位ウィンドウのスレッド メッセージ キューにメッセージを投稿します。

PostThreadMessage 関数を使用して、特定のスレッド メッセージ キューにメッセージを投稿できます。 PostThreadMessagePostMessage に似ていますが、最初のパラメーターはウィンドウ ハンドルではなくスレッド識別子です。 スレッド識別子を取得するには、 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. 
    // 
 
}