Utilisation de messages et de files d’attente de messages

Les exemples de code suivants montrent comment effectuer les tâches suivantes associées aux messages Windows et aux files d’attente de messages.

Création d’une boucle de message

Le système ne crée pas automatiquement de file d’attente de messages pour chaque thread. Au lieu de cela, le système crée une file d’attente de messages uniquement pour les threads qui effectuent des opérations qui nécessitent une file d’attente de messages. Si le thread crée une ou plusieurs fenêtres, une boucle de message doit être fournie ; cette boucle de message récupère les messages de la file d’attente de messages du thread et les distribue aux procédures de fenêtre appropriées.

Étant donné que le système dirige les messages vers des fenêtres individuelles dans une application, un thread doit créer au moins une fenêtre avant de démarrer sa boucle de message. La plupart des applications contiennent un thread unique qui crée des fenêtres. Une application classique inscrit la classe window pour sa fenêtre de main, crée et affiche la fenêtre main, puis démarre sa boucle de message, le tout dans la fonction WinMain.

Vous créez une boucle de message à l’aide des fonctions GetMessage et DispatchMessage . Si votre application doit obtenir une entrée de caractère de l’utilisateur, incluez la fonction TranslateMessage dans la boucle. TranslateMessage traduit les messages à clé virtuelle en messages caractères. L’exemple suivant montre la boucle de message dans la fonction WinMain d’une application Windows simple.

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’exemple suivant montre une boucle de message pour un thread qui utilise des accélérateurs et affiche une boîte de dialogue sans mode. Lorsque TranslateAccelerator ou IsDialogMessage retourne TRUE (indiquant que le message a été traité), TranslateMessage et DispatchMessage ne sont pas appelés. La raison en est que TranslateAccelerator et IsDialogMessage effectuent toutes les traductions et distributions nécessaires des messages.

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

Examen d’une file d’attente de messages

Parfois, une application doit examiner le contenu de la file d’attente de messages d’un thread à partir de l’extérieur de la boucle de message du thread. Par exemple, si la procédure de fenêtre d’une application effectue une longue opération de dessin, vous pouvez souhaiter que l’utilisateur puisse interrompre l’opération. À moins que votre application examine régulièrement la file d’attente des messages pendant l’opération pour les messages de souris et de clavier, elle ne répond pas aux entrées utilisateur tant qu’une fois l’opération terminée. La raison en est que la fonction DispatchMessage dans la boucle de message du thread ne retourne pas tant que la procédure de fenêtre n’a pas terminé le traitement d’un message.

Vous pouvez utiliser la fonction PeekMessage pour examiner une file d’attente de messages pendant une opération de longue durée. PeekMessage est similaire à la fonction GetMessage ; les deux case activée une file d’attente de messages pour un message qui correspond aux critères de filtre, puis copient le message dans une structure MSG. La main différence entre les deux fonctions est que GetMessage ne retourne pas un message correspondant aux critères de filtre placé dans la file d’attente, tandis que PeekMessage retourne immédiatement, qu’un message se trouve ou non dans la file d’attente.

L’exemple suivant montre comment utiliser PeekMessage pour examiner une file d’attente de messages pour les clics de souris et l’entrée au clavier pendant une opération de longue durée.

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

D’autres fonctions, notamment GetQueueStatus et GetInputState, vous permettent également d’examiner le contenu de la file d’attente de messages d’un thread. GetQueueStatus retourne un tableau d’indicateurs qui indique les types de messages dans la file d’attente ; son utilisation est le moyen le plus rapide de déterminer si la file d’attente contient des messages. GetInputState retourne TRUE si la file d’attente contient des messages de souris ou de clavier. Ces deux fonctions peuvent être utilisées pour déterminer si la file d’attente contient des messages qui doivent être traités.

Publication d’un message

Vous pouvez publier un message dans une file d’attente de messages à l’aide de la fonction PostMessage . PostMessage place un message à la fin de la file d’attente de messages d’un thread et retourne immédiatement, sans attendre que le thread traite le message. Les paramètres de la fonction incluent un handle de fenêtre, un identificateur de message et deux paramètres de message. Le système copie ces paramètres dans une structure MSG , remplit les membres d’heure et de pt de la structure, et place la structure dans la file d’attente des messages.

Le système utilise le handle de fenêtre passé avec la fonction PostMessage pour déterminer quelle file d’attente de messages de thread doit recevoir le message. Si le handle est HWND_TOPMOST, le système publie le message dans les files d’attente de messages de thread de toutes les fenêtres de niveau supérieur.

Vous pouvez utiliser la fonction PostThreadMessage pour publier un message dans une file d’attente de messages de thread spécifique. PostThreadMessage est similaire à PostMessage, sauf que le premier paramètre est un identificateur de thread plutôt qu’un handle de fenêtre. Vous pouvez récupérer l’identificateur de thread en appelant la fonction GetCurrentThreadId .

Utilisez la fonction PostQuitMessage pour quitter une boucle de message. PostQuitMessage publie le message WM_QUIT dans le thread en cours d’exécution. La boucle de message du thread se termine et retourne le contrôle au système lorsqu’il rencontre le message WM_QUIT . Une application appelle généralement PostQuitMessage en réponse au message WM_DESTROY , comme illustré dans l’exemple suivant.

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

Envoi d’un message

La fonction SendMessage est utilisée pour envoyer un message directement à une procédure de fenêtre. SendMessage appelle une procédure de fenêtre et attend que cette procédure traite le message et retourne un résultat.

Un message peut être envoyé à n’importe quelle fenêtre du système ; tout ce qui est nécessaire est un handle de fenêtre. Le système utilise le handle pour déterminer quelle procédure de fenêtre doit recevoir le message.

Avant de traiter un message qui peut avoir été envoyé à partir d’un autre thread, une procédure de fenêtre doit d’abord appeler la fonction InSendMessage . Si cette fonction retourne TRUE, la procédure de fenêtre doit appeler ReplyMessage avant toute fonction qui entraîne le contrôle du thread, comme illustré dans l’exemple suivant.

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

Un certain nombre de messages peuvent être envoyés aux contrôles dans une boîte de dialogue. Ces messages de contrôle définissent l’apparence, le comportement et le contenu des contrôles ou récupèrent des informations sur les contrôles. Par exemple, le message CB_ADDSTRING peut ajouter une chaîne à une zone de liste déroulante, et le message BM_SETCHECK peut définir l’état case activée d’une zone de case activée ou d’une case d’option.

Utilisez la fonction SendDlgItemMessage pour envoyer un message à un contrôle, en spécifiant l’identificateur du contrôle et le handle de la fenêtre de boîte de dialogue qui contient le contrôle. L’exemple suivant, tiré d’une procédure de boîte de dialogue, copie une chaîne du contrôle d’édition d’une zone de liste modifiable dans sa zone de liste. L’exemple utilise SendDlgItemMessage pour envoyer un message CB_ADDSTRING à la zone de liste déroulante.

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