Verwenden von Nachrichten und Nachrichtenwarteschlangen

Die folgenden Codebeispiele veranschaulichen, wie die folgenden Aufgaben ausgeführt werden, die Windows-Nachrichten und -Nachrichtenwarteschlangen zugeordnet sind.

Erstellen einer Nachrichtenschleife

Das System erstellt nicht automatisch eine Nachrichtenwarteschlange für jeden Thread. Stattdessen erstellt das System eine Nachrichtenwarteschlange nur für Threads, die Vorgänge ausführen, die eine Nachrichtenwarteschlange erfordern. Wenn der Thread ein oder mehrere Fenster erstellt, muss eine Nachrichtenschleife bereitgestellt werden. Diese Nachrichtenschleife ruft Nachrichten aus der Nachrichtenwarteschlange des Threads ab und sendet sie an die entsprechenden Fensterprozeduren.

Da das System Nachrichten an einzelne Fenster in einer Anwendung weiter leitet, muss ein Thread mindestens ein Fenster erstellen, bevor die Nachrichtenschleife gestartet wird. Die meisten Anwendungen enthalten einen einzelnen Thread, der Fenster erstellt. Eine typische Anwendung registriert die Fensterklasse für ihr Standard Fenster, erstellt und zeigt das Standard-Fensters an und startet dann die Nachrichtenschleife – alles in der WinMain-Funktion.

Sie erstellen eine Nachrichtenschleife, indem Sie die Funktionen GetMessage und DispatchMessage verwenden. Wenn Ihre Anwendung Zeicheneingaben vom Benutzer erhalten muss, schließen Sie die TranslateMessage-Funktion in die Schleife ein. TranslateMessage übersetzt Nachrichten mit virtuellen Schlüsseln in Zeichennachrichten. Das folgende Beispiel zeigt die Nachrichtenschleife in der WinMain-Funktion einer einfachen Windows-basierten Anwendung.

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; 
} 

Das folgende Beispiel zeigt eine Nachrichtenschleife für einen Thread, der Beschleuniger verwendet und ein modusloses Dialogfeld anzeigt. Wenn TranslateAccelerator oder IsDialogMessageTRUE zurückgibt (was angibt, dass die Nachricht verarbeitet wurde), werden TranslateMessage und DispatchMessage nicht aufgerufen. Der Grund dafür ist, dass TranslateAccelerator und IsDialogMessage alle erforderlichen Übersetzungen und Das Senden von Nachrichten ausführen.

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); 
        }
    } 
} 

Untersuchen einer Nachrichtenwarteschlange

Gelegentlich muss eine Anwendung den Inhalt der Nachrichtenwarteschlange eines Threads außerhalb der Nachrichtenschleife des Threads untersuchen. Wenn beispielsweise die Fensterprozedur einer Anwendung einen langwierigen Zeichnungsvorgang ausführt, soll der Benutzer den Vorgang unterbrechen können. Sofern Ihre Anwendung die Nachrichtenwarteschlange während des Vorgangs nicht regelmäßig auf Maus- und Tastaturnachrichten untersucht, reagiert sie erst nach Abschluss des Vorgangs auf Benutzereingaben. Der Grund dafür ist, dass die DispatchMessage-Funktion in der Nachrichtenschleife des Threads erst zurückgegeben wird, wenn die Fensterprozedur die Verarbeitung einer Nachricht abgeschlossen hat.

Sie können die PeekMessage-Funktion verwenden, um eine Nachrichtenwarteschlange während eines langwierigen Vorgangs zu untersuchen. PeekMessage ähnelt der GetMessage-Funktion . Beide überprüfen eine Nachrichtenwarteschlange auf eine Nachricht, die den Filterkriterien entspricht, und kopieren die Nachricht dann in eine MSG-Struktur . Der Standard Unterschied zwischen den beiden Funktionen besteht darin, dass GetMessage erst zurückgegeben wird, wenn eine Nachricht, die den Filterkriterien entspricht, in der Warteschlange platziert wird, während PeekMessage unabhängig davon, ob sich eine Nachricht in der Warteschlange befindet, sofort zurückgibt.

Das folgende Beispiel zeigt, wie Sie peekMessage verwenden, um eine Nachrichtenwarteschlange für Mausklicks und Tastatureingaben während eines langwierigen Vorgangs zu untersuchen.

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; 
        } 
    } 
} 

Mit anderen Funktionen, einschließlich GetQueueStatus und GetInputState, können Sie auch den Inhalt der Nachrichtenwarteschlange eines Threads untersuchen. GetQueueStatus gibt ein Array von Flags zurück, das die Nachrichtentypen in der Warteschlange angibt. Die Verwendung ist der schnellste Weg, um zu ermitteln, ob die Warteschlange Nachrichten enthält. GetInputState gibt TRUE zurück, wenn die Warteschlange Maus- oder Tastaturmeldungen enthält. Mit beiden Funktionen kann ermittelt werden, ob die Warteschlange Nachrichten enthält, die verarbeitet werden müssen.

Posten einer Nachricht

Sie können eine Nachricht mit der PostMessage-Funktion in einer Nachrichtenwarteschlange posten. PostMessage platziert eine Nachricht am Ende der Nachrichtenwarteschlange eines Threads und gibt sofort zurück, ohne darauf zu warten, dass der Thread die Nachricht verarbeitet. Die Parameter der Funktion umfassen ein Fensterhandle, einen Nachrichtenbezeichner und zwei Nachrichtenparameter. Das System kopiert diese Parameter in eine MSG-Struktur , füllt die time - und pt-Member der Struktur aus und platziert die Struktur in der Nachrichtenwarteschlange.

Das System verwendet das Fensterhandle, das mit der PostMessage-Funktion übergeben wurde, um zu bestimmen, welche Threadnachrichtenwarteschlange die Nachricht empfangen soll. Wenn das Handle HWND_TOPMOST ist, sendet das System die Nachricht an die Threadnachrichtenwarteschlangen aller Fenster der obersten Ebene.

Sie können die PostThreadMessage-Funktion verwenden, um eine Nachricht in einer bestimmten Threadnachrichtenwarteschlange zu posten. PostThreadMessage ähnelt PostMessage, mit dem Unterschied, dass der erste Parameter ein Threadbezeichner und kein Fensterhandle ist. Sie können den Threadbezeichner abrufen, indem Sie die GetCurrentThreadId-Funktion aufrufen.

Verwenden Sie die PostQuitMessage-Funktion , um eine Nachrichtenschleife zu beenden. PostQuitMessage sendet die WM_QUIT Nachricht an den derzeit ausgeführten Thread. Die Nachrichtenschleife des Threads wird beendet und gibt die Steuerung an das System zurück, wenn die WM_QUIT Nachricht angezeigt wird. Eine Anwendung ruft normalerweise PostQuitMessage als Reaktion auf die WM_DESTROY-Nachricht auf, wie im folgenden Beispiel gezeigt.

case WM_DESTROY: 
 
    // Perform cleanup tasks. 
 
    PostQuitMessage(0); 
    break; 

Senden einer Nachricht

Die SendMessage-Funktion wird verwendet, um eine Nachricht direkt an eine Fensterprozedur zu senden. SendMessage ruft eine Fensterprozedur auf und wartet, bis diese Prozedur die Nachricht verarbeitet und ein Ergebnis zurückgibt.

Eine Nachricht kann an jedes Fenster im System gesendet werden. Alles, was erforderlich ist, ist ein Fensterhandle. Das System verwendet das Handle, um zu bestimmen, welche Fensterprozedur die Nachricht empfangen soll.

Vor der Verarbeitung einer Nachricht, die möglicherweise von einem anderen Thread gesendet wurde, sollte eine Fensterprozedur zuerst die Funktion InSendMessage aufrufen. Wenn diese Funktion TRUE zurückgibt, sollte die Fensterprozedur ReplyMessage vor jeder Funktion aufrufen, die bewirkt, dass der Thread ein Steuerelement liefert, wie im folgenden Beispiel gezeigt.

case WM_USER + 5: 
    if (InSendMessage()) 
        ReplyMessage(TRUE); 
 
    DialogBox(hInst, "MyDialogBox", hwndMain, (DLGPROC) MyDlgProc); 
    break; 

In einem Dialogfeld können mehrere Nachrichten an Steuerelemente gesendet werden. Diese Steuerelementmeldungen legen die Darstellung, das Verhalten und den Inhalt von Steuerelementen fest oder rufen Informationen zu Steuerelementen ab. Beispielsweise kann die CB_ADDSTRING Nachricht einem Kombinationsfeld eine Zeichenfolge hinzufügen, und die BM_SETCHECK Nachricht kann den Überprüfungsstatus eines Kontrollkästchens oder Optionsfelds festlegen.

Verwenden Sie die SendDlgItemMessage-Funktion , um eine Nachricht an ein Steuerelement zu senden. Geben Sie dabei den Bezeichner des Steuerelements und das Handle des Dialogfeldfensters an, das das Steuerelement enthält. Im folgenden Beispiel aus einer Dialogfeldprozedur wird eine Zeichenfolge aus dem Bearbeitungssteuerelement eines Kombinationsfelds in dessen Listenfeld kopiert. Im Beispiel wird SendDlgItemMessage verwendet, um eine CB_ADDSTRING Nachricht an das Kombinationsfeld zu senden.

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. 
    // 
 
}