Uso di messaggi e code di messaggi

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

Creazione di un ciclo di messaggi

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 di 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 relativa finestra principale, crea e mostra la finestra principale e quindi avvia il ciclo di messaggi, tutto nella funzione WinMain .

Si crea un ciclo di messaggi usando le funzioni GetMessage e DispatchMessage . Se l'applicazione deve ottenere l'input di carattere dall'utente, includere la funzione TranslateMessage nel ciclo. TranslateMessage converte i messaggi di chiave virtuale in messaggi di carattere. Nell'esempio seguente viene illustrato il ciclo di messaggi nella funzione WinMain di un'applicazione basata su Windows semplice.

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 (che indica che il messaggio è stato elaborato), TranslateMessage e DispatchMessage non vengono chiamati. Il motivo è che TranslateAccelerator e IsDialogMessage eseguono tutte le operazioni necessarie di traduzione e invio di messaggi.

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

Esame di una coda di messaggi

In alcuni casi, un'applicazione deve esaminare il contenuto della coda del messaggio di un thread dall'esterno del ciclo di messaggi del thread. Ad esempio, se la routine della finestra di un'applicazione esegue un'operazione di disegno prolungata, è possibile che l'utente possa interrompere l'operazione. A meno che l'applicazione non esamini periodicamente la coda dei 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 del messaggio del thread non restituisce fino al termine dell'elaborazione di un messaggio.

È possibile usare la funzione PeekMessage per esaminare una coda di messaggi durante un'operazione prolungata. PeekMessage è simile alla funzione GetMessage ; sia controllare una coda di messaggi per un messaggio che corrisponda ai criteri di filtro e quindi copiare il messaggio in una struttura MSG . La differenza principale tra le due funzioni è che GetMessage non restituisce finché un messaggio corrispondente ai criteri di filtro viene inserito 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 prolungata.

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; l'uso è il modo più veloce per individuare se la coda contiene eventuali 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.

Registrazione di un messaggio

È possibile pubblicare un messaggio in una coda di messaggi usando la funzione PostMessage . PostMessage inserisce un messaggio alla fine della coda del messaggio di un thread e restituisce immediatamente, senza attendere che il thread elabora 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 il tempo e i membri pt della struttura e inserisce la struttura nella coda dei messaggi.

Il sistema usa l'handle della 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 thread di tutte le finestre di primo livello.

È possibile usare la funzione PostThreadMessage per pubblicare un messaggio a 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 pubblica il messaggio di WM_QUIT al thread attualmente in esecuzione. Il ciclo del messaggio del thread termina e restituisce il controllo al sistema quando rileva il messaggio di WM_QUIT . Un'applicazione chiama in genere PostQuitMessage in risposta al messaggio di 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 procedura di finestra. SendMessage chiama una routine di finestra e attende che tale procedura elabora 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 procedura finestra deve ricevere il messaggio.

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

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

Un numero di messaggi può essere inviato 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 controllo di una casella di controllo o 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 procedura della finestra di dialogo, viene copiata una stringa dal controllo di modifica di una casella combinata nella casella di riepilogo. Nell'esempio viene usato SendDlgItemMessage per inviare un messaggio di 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. 
    // 
 
}