Usando mensagens e filas de mensagens
Os exemplos de código a seguir demonstram como executar as tarefas a seguir associadas a mensagens do Windows e filas de mensagens.
- Criando um loop de mensagem
- Examinando uma fila de mensagens
- Postando uma mensagem
- Enviando uma mensagem
Criando um loop de mensagem
O sistema não cria automaticamente uma fila de mensagens para cada thread. Em vez disso, o sistema cria uma fila de mensagens somente para threads que executam operações que exigem uma fila de mensagens. Se o thread criar uma ou mais janelas, um loop de mensagem deverá ser fornecido; esse loop de mensagem recupera mensagens da fila de mensagens do thread e as envia para os procedimentos de janela apropriados.
Como o sistema direciona mensagens para janelas individuais em um aplicativo, um thread deve criar pelo menos uma janela antes de iniciar seu loop de mensagem. A maioria dos aplicativos contém um único thread que cria janelas. Um aplicativo típico registra a classe de janela para sua janela main, cria e mostra a janela main e inicia o loop de mensagem , tudo na função WinMain.
Você cria um loop de mensagem usando as funções GetMessage e DispatchMessage . Se o aplicativo precisar obter a entrada de caractere do usuário, inclua a função TranslateMessage no loop. TranslateMessage converte mensagens de chave virtual em mensagens de caractere. O exemplo a seguir mostra o loop de mensagem na função WinMain de um aplicativo simples baseado no 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;
}
O exemplo a seguir mostra um loop de mensagem para um thread que usa aceleradores e exibe uma caixa de diálogo de modelagem. Quando TranslateAccelerator ou IsDialogMessage retorna TRUE (indicando que a mensagem foi processada), TranslateMessage e DispatchMessage não são chamados . O motivo disso é que TranslateAccelerator e IsDialogMessage executam toda a tradução e expedição necessárias de mensagens.
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);
}
}
}
Examinando uma fila de mensagens
Ocasionalmente, um aplicativo precisa examinar o conteúdo da fila de mensagens de um thread de fora do loop de mensagem do thread. Por exemplo, se o procedimento de janela de um aplicativo executar uma longa operação de desenho, talvez você queira que o usuário possa interromper a operação. A menos que seu aplicativo examine periodicamente a fila de mensagens durante a operação para mensagens de mouse e teclado, ele não responderá à entrada do usuário até que a operação seja concluída. O motivo disso é que a função DispatchMessage no loop de mensagem do thread não retorna até que o procedimento de janela termine de processar uma mensagem.
Você pode usar a função PeekMessage para examinar uma fila de mensagens durante uma longa operação. PeekMessage é semelhante à função GetMessage; ambos marcar uma fila de mensagens para uma mensagem que corresponda aos critérios de filtro e, em seguida, copie a mensagem para uma estrutura MSG. A main diferença entre as duas funções é que GetMessage não retorna até que uma mensagem correspondente aos critérios de filtro seja colocada na fila, enquanto PeekMessage retorna imediatamente, independentemente de uma mensagem estar na fila.
O exemplo a seguir mostra como usar PeekMessage para examinar uma fila de mensagens para cliques de mouse e entrada de teclado durante uma longa operação.
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;
}
}
}
Outras funções, incluindo GetQueueStatus e GetInputState, também permitem examinar o conteúdo da fila de mensagens de um thread. GetQueueStatus retorna uma matriz de sinalizadores que indica os tipos de mensagens na fila; usá-la é a maneira mais rápida de descobrir se a fila contém mensagens. GetInputState retornará TRUE se a fila contiver mensagens de mouse ou teclado. Ambas as funções podem ser usadas para determinar se a fila contém mensagens que precisam ser processadas.
Postando uma mensagem
Você pode postar uma mensagem em uma fila de mensagens usando a função PostMessage . PostMessage coloca uma mensagem no final da fila de mensagens de um thread e retorna imediatamente, sem esperar que o thread processe a mensagem. Os parâmetros da função incluem um identificador de janela, um identificador de mensagem e dois parâmetros de mensagem. O sistema copia esses parâmetros para uma estrutura MSG , preenche a hora e os membros pt da estrutura e coloca a estrutura na fila de mensagens.
O sistema usa o identificador de janela passado com a função PostMessage para determinar qual fila de mensagens de thread deve receber a mensagem. Se o identificador for HWND_TOPMOST, o sistema postará a mensagem nas filas de mensagens de thread de todas as janelas de nível superior.
Você pode usar a função PostThreadMessage para postar uma mensagem em uma fila de mensagens de thread específica. PostThreadMessage é semelhante ao PostMessage, exceto que o primeiro parâmetro é um identificador de thread em vez de um identificador de janela. Você pode recuperar o identificador de thread chamando a função GetCurrentThreadId .
Use a função PostQuitMessage para sair de um loop de mensagem. PostQuitMessage posta a mensagem WM_QUIT no thread em execução no momento. O loop de mensagem do thread termina e retorna o controle para o sistema quando encontra a mensagem WM_QUIT . Um aplicativo geralmente chama PostQuitMessage em resposta à mensagem WM_DESTROY , conforme mostrado no exemplo a seguir.
case WM_DESTROY:
// Perform cleanup tasks.
PostQuitMessage(0);
break;
Enviando uma mensagem
A função SendMessage é usada para enviar uma mensagem diretamente para um procedimento de janela. SendMessage chama um procedimento de janela e aguarda que esse procedimento processe a mensagem e retorne um resultado.
Uma mensagem pode ser enviada para qualquer janela no sistema; tudo o que é necessário é um identificador de janela. O sistema usa o identificador para determinar qual procedimento de janela deve receber a mensagem.
Antes de processar uma mensagem que pode ter sido enviada de outro thread, um procedimento de janela deve primeiro chamar a função InSendMessage . Se essa função retornar TRUE, o procedimento de janela deverá chamar ReplyMessage antes de qualquer função que faça com que o thread gere controle, conforme mostrado no exemplo a seguir.
case WM_USER + 5:
if (InSendMessage())
ReplyMessage(TRUE);
DialogBox(hInst, "MyDialogBox", hwndMain, (DLGPROC) MyDlgProc);
break;
Várias mensagens podem ser enviadas para controles em uma caixa de diálogo. Essas mensagens de controle definem a aparência, o comportamento e o conteúdo dos controles ou recuperam informações sobre controles. Por exemplo, a mensagem CB_ADDSTRING pode adicionar uma cadeia de caracteres a uma caixa de combinação e a mensagem BM_SETCHECK pode definir o estado marcar de uma caixa de marcar ou botão de opção.
Use a função SendDlgItemMessage para enviar uma mensagem a um controle, especificando o identificador do controle e o identificador da janela da caixa de diálogo que contém o controle. O exemplo a seguir, obtido de um procedimento de caixa de diálogo, copia uma cadeia de caracteres do controle de edição de uma caixa de combinação em sua caixa de listagem. O exemplo usa SendDlgItemMessage para enviar uma mensagem CB_ADDSTRING para a caixa de combinação.
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.
//
}