Udostępnij za pośrednictwem


Używanie komunikatów i kolejek wiadomości

W poniższych przykładach kodu pokazano, jak wykonywać następujące zadania skojarzone z komunikatami systemu Windows i kolejkami komunikatów.

Tworzenie pętli komunikatów

System nie tworzy automatycznie kolejki komunikatów dla każdego wątku. Zamiast tego system tworzy kolejkę komunikatów tylko dla wątków wykonujących operacje, które wymagają kolejki komunikatów. Jeśli wątek tworzy co najmniej jedno okno, należy podać pętlę komunikatu; Ta pętla komunikatów pobiera komunikaty z kolejki komunikatów wątku i wysyła je do odpowiednich procedur okna.

Ponieważ system kieruje komunikaty do poszczególnych okien w aplikacji, wątek musi utworzyć co najmniej jedno okno przed uruchomieniem pętli komunikatów. Większość aplikacji zawiera jeden wątek, który tworzy okna. Typowa aplikacja rejestruje klasę okna dla swojego głównego okna, tworzy i wyświetla główne okno, a następnie uruchamia pętlę komunikatów — wszystko w funkcji WinMain.

Pętla komunikatów jest tworzona przy użyciu funkcji GetMessage i DispatchMessage. Jeśli aplikacja musi uzyskać znakowe dane wejściowe od użytkownika, dołącz funkcję TranslateMessage w każdej iteracji pętli. TranslateMessage tłumaczy wiadomości dotyczące kluczy wirtualnych na wiadomości znakowe. W poniższym przykładzie przedstawiono pętlę komunikatów w funkcji WinMain prostej aplikacji opartej na systemie Windows.

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

W poniższym przykładzie przedstawiono pętlę komunikatów dla wątku, który używa akceleratorów i wyświetla niemodalne okno dialogowe. Gdy TranslateAccelerator lub IsDialogMessage zwraca true (wskazując, że komunikat został przetworzony), translateMessage i DispatchMessage nie są wywoływane. Powodem jest to, że TranslateAccelerator i IsDialogMessage wykonują wszystkie niezbędne tłumaczenia i wysyłanie komunikatów.

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

Badanie kolejki komunikatów

Czasami aplikacja musi zbadać zawartość kolejki komunikatów wątku spoza pętli komunikatów wątku. Jeśli na przykład procedura okna aplikacji wykonuje długotrwałą operację rysowania, możesz chcieć, aby użytkownik mógł przerwać operację. Jeżeli aplikacja nie sprawdza okresowo kolejki komunikatów dla komunikatów myszy i klawiatury podczas operacji, nie będzie odpowiadać na dane wejściowe użytkownika aż do zakończenia operacji. Przyczyną tego jest to, że funkcja DispatchMessage w pętli komunikatów wątku nie zwraca się, dopóki procedura okna nie zakończy przetwarzania komunikatu.

Za pomocą funkcji PeekMessage można zbadać kolejkę komunikatów podczas długotrwałej operacji. PeekMessage jest podobna do funkcji GetMessage; Sprawdź kolejkę komunikatów pod kątem komunikatu zgodnego z kryteriami filtrowania, a następnie skopiuj komunikat do struktury msG. Główną różnicą między dwiema funkcjami jest to, że getMessage nie zwraca, dopóki komunikat zgodny z kryteriami filtru nie zostanie umieszczony w kolejce, podczas gdy PeekMessage zwraca natychmiast, niezależnie od tego, czy komunikat znajduje się w kolejce.

W poniższym przykładzie pokazano, jak używać PeekMessage do badania kolejki komunikatów pod kątem kliknięć myszy i wprowadzania za pomocą klawiatury podczas długiej operacji.

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

Inne funkcje, w tym GetQueueStatus i GetInputState, umożliwiają również sprawdzenie zawartości kolejki komunikatów wątku. GetQueueStatus zwraca tablicę flag wskazującą typy komunikatów w kolejce; użycie go to najszybszy sposób wykrywania, czy kolejka zawiera jakiekolwiek komunikaty. GetInputState zwraca TRUE, jeśli kolejka zawiera komunikaty myszy lub klawiatury. Obie te funkcje mogą służyć do określenia, czy kolejka zawiera komunikaty, które należy przetworzyć.

Publikowanie wiadomości

Można wysłać komunikat do kolejki komunikatów, używając funkcji PostMessage. PostMessage umieszcza komunikat na końcu kolejki komunikatów wątku i zwraca natychmiast, nie czekając na przetworzenie komunikatu przez wątek. Parametry funkcji obejmują uchwyt okna, identyfikator komunikatu i dwa parametry komunikatu. System kopiuje te parametry do struktury msG, wypełnia czasu i pt składowych struktury i umieszcza strukturę w kolejce komunikatów.

System używa dojścia okna przekazanego z funkcji PostMessage w celu określenia, która kolejka komunikatów wątku powinna odbierać komunikat. Jeśli dojście jest HWND_TOPMOST, system wysyła komunikat do kolejek komunikatów wszystkich wątków okien najwyższego poziomu.

Możesz użyć funkcji PostThreadMessage, aby opublikować komunikat w określonej kolejce komunikatów wątku. PostThreadMessage jest podobna do PostMessage, z tym że pierwszy parametr jest identyfikatorem wątku, a nie uchwytem okna. Identyfikator wątku można pobrać, wywołując funkcję GetCurrentThreadId.

Użyj funkcji PostQuitMessage, aby zamknąć pętlę komunikatów. PostQuitMessage wysyła komunikat WM_QUIT do aktualnie wykonywanego wątku. Pętla komunikatów wątku kończy działanie i zwraca kontrolę do systemu, gdy napotka komunikat WM_QUIT. Aplikacja zwykle wywołuje PostQuitMessage w odpowiedzi na komunikat WM_DESTROY, jak pokazano w poniższym przykładzie.

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

Wysyłanie wiadomości

Funkcja SendMessage służy do wysyłania komunikatu bezpośrednio do procedury okna. SendMessage wywołuje procedurę okna, która czeka na przetworzenie komunikatu i zwrócenie wyniku.

Komunikat można wysłać do dowolnego okna w systemie; wszystko, co jest wymagane, to uchwyt okna. System używa uchwytu do określenia, która procedura okna powinna otrzymać komunikat.

Przed przetworzeniem komunikatu, który mógł zostać wysłany z innego wątku, procedura okna powinna najpierw wywołać funkcję InSendMessage. Jeśli ta funkcja zwróci true, procedura okna powinna wywołać ReplyMessage przed dowolną funkcją, która powoduje, że wątek zwróci kontrolę, jak pokazano w poniższym przykładzie.

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

Wiele komunikatów można wysyłać do kontrolek w oknie dialogowym. Te komunikaty sterujące ustawiają wygląd, zachowanie i zawartość kontrolek lub pobierają informacje o kontrolkach. Na przykład komunikat CB_ADDSTRING może dodać ciąg do pola kombi, a komunikat BM_SETCHECK może ustawić stan zaznaczenia pola wyboru lub przycisku radiowego.

Użyj funkcji SendDlgItemMessage, aby wysłać komunikat do kontrolki, określając identyfikator kontrolki i uchwyt okna dialogowego zawierającego kontrolkę. Poniższy przykład, pobrany z procedury okna dialogowego, kopiuje ciąg z kontrolki edycji pola kombi do pola listy. W przykładzie użyto SendDlgItemMessage do wysłania komunikatu CB_ADDSTRING do pola kombi.

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