Uso di messaggi e code di messaggi

Gli esempi di codice seguenti illustrano come eseguire le attività seguenti associate a Windows messaggi e code di messaggi.

Creazione di un messaggio Loop

Il sistema non crea automaticamente una coda di messaggi per ogni thread. Il sistema crea invece una coda di messaggi solo per i thread che eseguono operazioni che richiedono una coda di messaggi. Se il thread crea una o più finestre, è necessario specificare un ciclo di messaggi; questo ciclo di messaggi recupera i messaggi dalla coda dei messaggi del thread e li invia alle procedure della finestra appropriate.

Poiché il sistema indirizza i messaggi a singole finestre in un'applicazione, un thread deve creare almeno una finestra prima di avviare il ciclo di messaggi. La maggior parte delle applicazioni contiene un singolo thread che crea finestre. Un'applicazione tipica registra la classe finestra per la finestra principale, crea e mostra la finestra principale e quindi avvia il ciclo di messaggi, tutto nella funzione WinMain .

Per creare un ciclo di messaggi, usare le funzioni GetMessage e DispatchMessage . Se l'applicazione deve ottenere l'input di caratteri dall'utente, includere la funzione TranslateMessage nel ciclo. TranslateMessage converte i messaggi di chiave virtuale in messaggi di carattere. L'esempio seguente mostra il ciclo di messaggi nella funzione WinMain di una semplice applicazione basata su 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; 
} 

L'esempio seguente mostra un ciclo di messaggi per un thread che usa acceleratori e visualizza una finestra di dialogo senza modalità. Quando TranslateAccelerator o IsDialogMessage restituisce TRUE (a indicare che il messaggio è stato elaborato), TranslateMessage e DispatchMessage non vengono chiamati. Il motivo è che TranslateAccelerator e IsDialogMessage eseguono tutte le operazioni di traduzione e invio dei messaggi necessarie.

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

Analisi di una coda di messaggi

In alcuni casi, un'applicazione deve esaminare il contenuto della coda di messaggi di un thread dall'esterno del ciclo di messaggi del thread. Ad esempio, se la routine finestra di un'applicazione esegue un'operazione di disegno lunga, è possibile che l'utente possa interrompere l'operazione. A meno che l'applicazione non esamini periodicamente la coda di messaggi durante l'operazione per i messaggi del mouse e della tastiera, non risponderà all'input dell'utente fino al completamento dell'operazione. Il motivo è che la funzione DispatchMessage nel ciclo di messaggi del thread non restituisce finché la routine della finestra non completa l'elaborazione di un messaggio.

È possibile usare la funzione PeekMessage per esaminare una coda di messaggi durante un'operazione lunga. PeekMessage è simile alla funzione GetMessage ; entrambe controllano una coda di messaggi per un messaggio che corrisponde ai criteri di filtro e quindi copiano il messaggio in una struttura MSG . La differenza principale tra le due funzioni è che GetMessage non restituisce finché non viene inserito un messaggio corrispondente ai criteri di filtro nella coda, mentre PeekMessage restituisce immediatamente indipendentemente dal fatto che un messaggio si trova nella coda.

Nell'esempio seguente viene illustrato come usare PeekMessage per esaminare una coda di messaggi per i clic del mouse e l'input della tastiera durante un'operazione lunga.

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

Altre funzioni, tra cui GetQueueStatus e GetInputState, consentono anche di esaminare il contenuto della coda di messaggi di un thread. GetQueueStatus restituisce una matrice di flag che indica i tipi di messaggi nella coda; è il modo più rapido per determinare se la coda contiene messaggi. GetInputState restituisce TRUE se la coda contiene messaggi del mouse o della tastiera. Entrambe queste funzioni possono essere usate per determinare se la coda contiene messaggi che devono essere elaborati.

Pubblicazione di un messaggio

È possibile pubblicare un messaggio in una coda di messaggi usando la funzione PostMessage . PostMessage inserisce un messaggio alla fine della coda di messaggi di un thread e restituisce immediatamente, senza attendere che il thread elabori il messaggio. I parametri della funzione includono un handle di finestra, un identificatore di messaggio e due parametri del messaggio. Il sistema copia questi parametri in una struttura MSG , riempie i membri time e pt della struttura e inserisce la struttura nella coda dei messaggi.

Il sistema usa l'handle di finestra passato con la funzione PostMessage per determinare quale coda di messaggi thread deve ricevere il messaggio. Se l'handle è HWND_TOPMOST, il sistema invia il messaggio alle code di messaggi del thread di tutte le finestre di primo livello.

È possibile usare la funzione PostThreadMessage per pubblicare un messaggio in una coda di messaggi di thread specifica. PostThreadMessage è simile a PostMessage, ad eccezione del primo parametro è un identificatore di thread anziché un handle di finestra. È possibile recuperare l'identificatore del thread chiamando la funzione GetCurrentThreadId .

Usare la funzione PostQuitMessage per uscire da un ciclo di messaggi. PostQuitMessage invia il messaggio WM_QUIT al thread attualmente in esecuzione. Il ciclo di messaggi del thread termina e restituisce il controllo al sistema quando rileva il messaggio WM_QUIT . Un'applicazione in genere chiama PostQuitMessage in risposta al messaggio WM_DESTROY , come illustrato nell'esempio seguente.

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

Invio di un messaggio

La funzione SendMessage viene usata per inviare un messaggio direttamente a una routine finestra. SendMessage chiama una routine finestra e attende che tale routine elabori il messaggio e restituisca un risultato.

Un messaggio può essere inviato a qualsiasi finestra del sistema; tutto ciò che è necessario è un handle di finestra. Il sistema usa l'handle per determinare quale routine della finestra deve ricevere il messaggio.

Prima di elaborare un messaggio che potrebbe essere stato inviato da un altro thread, una routine window deve prima chiamare la funzione InSendMessage . Se questa funzione restituisce TRUE, la routine della finestra deve chiamare ReplyMessage prima di qualsiasi funzione che causa la restituzione del controllo del thread, come illustrato nell'esempio seguente.

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

È possibile inviare diversi messaggi ai controlli in una finestra di dialogo. Questi messaggi di controllo impostano l'aspetto, il comportamento e il contenuto dei controlli o recuperano informazioni sui controlli. Ad esempio, il messaggio CB_ADDSTRING può aggiungere una stringa a una casella combinata e il messaggio BM_SETCHECK può impostare lo stato di spunta di una casella di controllo o di un pulsante di opzione.

Utilizzare la funzione SendDlgItemMessage per inviare un messaggio a un controllo, specificando l'identificatore del controllo e l'handle della finestra di dialogo contenente il controllo . Nell'esempio seguente, tratto da una routine della finestra di dialogo, viene copiata una stringa dal controllo di modifica di una casella combinata nella relativa casella di riepilogo. Nell'esempio viene utilizzato SendDlgItemMessage per inviare un messaggio CB_ADDSTRING alla casella combinata.

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