Menggunakan Pesan dan Antrean Pesan
Contoh kode berikut menunjukkan cara melakukan tugas berikut yang terkait dengan pesan Windows dan antrean pesan.
Membuat Perulangan Pesan
Sistem tidak secara otomatis membuat antrean pesan untuk setiap utas. Sebaliknya, sistem membuat antrean pesan hanya untuk utas yang melakukan operasi yang memerlukan antrean pesan. Jika utas membuat satu atau beberapa jendela, perulangan pesan harus disediakan; perulangan pesan ini mengambil pesan dari antrean pesan utas dan mengirimkannya ke prosedur jendela yang sesuai.
Karena sistem mengarahkan pesan ke jendela individual dalam aplikasi, utas harus membuat setidaknya satu jendela sebelum memulai perulangan pesannya. Sebagian besar aplikasi berisi satu utas yang membuat jendela. Aplikasi umum mendaftarkan kelas jendela untuk jendela utamanya, membuat dan menunjukkan jendela utama, lalu memulai perulangan pesannya - semua dalam fungsi WinMain .
Anda membuat perulangan pesan dengan menggunakan fungsi GetMessage dan DispatchMessage . Jika aplikasi Anda harus mendapatkan input karakter dari pengguna, sertakan fungsi TranslateMessage dalam perulangan. TranslateMessage menerjemahkan pesan kunci virtual ke dalam pesan karakter. Contoh berikut menunjukkan perulangan pesan dalam fungsi WinMain dari aplikasi sederhana berbasis 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;
}
Contoh berikut menunjukkan perulangan pesan untuk utas yang menggunakan akselerator dan menampilkan kotak dialog tanpa mode. Ketika TranslateAccelerator atau IsDialogMessage mengembalikan TRUE (menunjukkan bahwa pesan telah diproses), TranslateMessage dan DispatchMessage tidak dipanggil. Alasan untuk ini adalah bahwa TranslateAccelerator dan IsDialogMessage melakukan semua penerjemahan dan pengiriman pesan yang diperlukan.
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);
}
}
}
Memeriksa Antrean Pesan
Terkadang, aplikasi perlu memeriksa konten antrean pesan utas dari luar perulangan pesan utas. Misalnya, jika prosedur jendela aplikasi melakukan operasi gambar yang panjang, Anda mungkin ingin pengguna dapat mengganggu operasi. Kecuali aplikasi Anda secara berkala memeriksa antrean pesan selama operasi untuk pesan mouse dan keyboard, aplikasi tidak akan merespons input pengguna sampai setelah operasi selesai. Alasan untuk ini adalah bahwa fungsi DispatchMessage dalam perulangan pesan utas tidak kembali sampai prosedur jendela selesai memproses pesan.
Anda dapat menggunakan fungsi PeekMessage untuk memeriksa antrean pesan selama operasi yang panjang. PeekMessage mirip dengan fungsi GetMessage ; keduanya memeriksa antrean pesan untuk pesan yang cocok dengan kriteria filter lalu salin pesan ke struktur MSG . Perbedaan utama antara kedua fungsi adalah bahwa GetMessage tidak kembali sampai pesan yang cocok dengan kriteria filter ditempatkan dalam antrean, sedangkan PeekMessage segera kembali terlepas dari apakah pesan berada dalam antrean.
Contoh berikut menunjukkan cara menggunakan PeekMessage untuk memeriksa antrean pesan untuk klik mouse dan input keyboard selama operasi yang panjang.
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;
}
}
}
Fungsi lain, termasuk GetQueueStatus dan GetInputState, juga memungkinkan Anda untuk memeriksa konten antrean pesan utas. GetQueueStatus mengembalikan array bendera yang menunjukkan jenis pesan dalam antrean; menggunakannya adalah cara tercepat untuk menemukan apakah antrean berisi pesan apa pun. GetInputState mengembalikan TRUE jika antrean berisi pesan mouse atau keyboard. Kedua fungsi ini dapat digunakan untuk menentukan apakah antrean berisi pesan yang perlu diproses.
Memposting Pesan
Anda dapat memposting pesan ke antrean pesan dengan menggunakan fungsi PostMessage . PostMessage menempatkan pesan di akhir antrean pesan utas dan segera kembali, tanpa menunggu utas memproses pesan. Parameter fungsi mencakup handel jendela, pengidentifikasi pesan, dan dua parameter pesan. Sistem menyalin parameter ini ke struktur MSG , mengisi waktu dan anggota pt struktur, dan menempatkan struktur dalam antrean pesan.
Sistem menggunakan handel jendela yang diteruskan dengan fungsi PostMessage untuk menentukan antrean pesan utas mana yang harus menerima pesan. Jika handel HWND_TOPMOST, sistem memposting pesan ke antrean pesan utas dari semua jendela tingkat atas.
Anda dapat menggunakan fungsi PostThreadMessage untuk memposting pesan ke antrean pesan utas tertentu. PostThreadMessage mirip dengan PostMessage, kecuali parameter pertama adalah pengidentifikasi utas daripada handel jendela. Anda dapat mengambil pengidentifikasi utas dengan memanggil fungsi GetCurrentThreadId .
Gunakan fungsi PostQuitMessage untuk keluar dari perulangan pesan. PostQuitMessage memposting pesan WM_QUIT ke utas yang sedang dijalankan. Perulangan pesan utas berakhir dan mengembalikan kontrol ke sistem saat menemukan pesan WM_QUIT . Aplikasi biasanya memanggil PostQuitMessage sebagai respons terhadap pesan WM_DESTROY , seperti yang ditunjukkan dalam contoh berikut.
case WM_DESTROY:
// Perform cleanup tasks.
PostQuitMessage(0);
break;
Mengirim Pesan
Fungsi SendMessage digunakan untuk mengirim pesan langsung ke prosedur jendela. SendMessage memanggil prosedur jendela dan menunggu prosedur tersebut memproses pesan dan mengembalikan hasilnya.
Pesan dapat dikirim ke jendela mana pun dalam sistem; semua yang diperlukan adalah handel jendela. Sistem menggunakan handel untuk menentukan prosedur jendela mana yang harus menerima pesan.
Sebelum memproses pesan yang mungkin telah dikirim dari utas lain, prosedur jendela harus terlebih dahulu memanggil fungsi InSendMessage . Jika fungsi ini mengembalikan TRUE, prosedur jendela harus memanggil ReplyMessage sebelum fungsi apa pun yang menyebabkan utas menghasilkan kontrol, seperti yang ditunjukkan dalam contoh berikut.
case WM_USER + 5:
if (InSendMessage())
ReplyMessage(TRUE);
DialogBox(hInst, "MyDialogBox", hwndMain, (DLGPROC) MyDlgProc);
break;
Sejumlah pesan dapat dikirim ke kontrol dalam kotak dialog. Pesan kontrol ini mengatur tampilan, perilaku, dan konten kontrol atau mengambil informasi tentang kontrol. Misalnya, pesan CB_ADDSTRING dapat menambahkan string ke kotak kombo, dan pesan BM_SETCHECK dapat mengatur status centang kotak centang atau tombol radio.
Gunakan fungsi SendDlgItemMessage untuk mengirim pesan ke kontrol, menentukan pengidentifikasi kontrol dan gagang jendela kotak dialog yang berisi kontrol. Contoh berikut, diambil dari prosedur kotak dialog, menyalin string dari kontrol edit kotak kombo ke dalam kotak daftarnya. Contohnya menggunakan SendDlgItemMessage untuk mengirim pesan CB_ADDSTRING ke kotak kombo.
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.
//
}