使用窗口过程
本部分介绍如何执行与窗口过程关联的以下任务。
设计窗口过程
以下示例演示典型窗口过程的结构。 窗口过程在 switch 语句中使用 message 参数,以及由单独的 case 语句处理的各个消息。 请注意,每个事例返回每条消息的特定值。 对于它不处理的消息,窗口过程调用 DefWindowProc 函数。
LRESULT CALLBACK MainWndProc(
HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam) // second message parameter
{
switch (uMsg)
{
case WM_CREATE:
// Initialize the window.
return 0;
case WM_PAINT:
// Paint the window's client area.
return 0;
case WM_SIZE:
// Set the size and position of the window.
return 0;
case WM_DESTROY:
// Clean up window-specific data objects.
return 0;
//
// Process other messages.
//
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
WM_NCCREATE消息在创建窗口后发送,但如果应用程序通过返回 FALSE 响应此消息,CreateWindowEx 函数将失败。 创建窗口后,将发送 WM_CREATE 消息。
当窗口即将被销毁时,将发送 WM_DESTROY 消息。 DestroyWindow 函数负责销毁正在销毁的窗口的任何子窗口。 WM_NCDESTROY消息在窗口销毁前发送。
至少,窗口过程应处理 WM_PAINT 消息以绘制自身。 通常,它还应处理鼠标和键盘消息。 查阅各个消息的说明,以确定窗口过程是否应处理它们。
应用程序可以在处理消息过程中调用 DefWindowProc 函数。 在这种情况下,应用程序可以在将消息传递到 DefWindowProc 之前修改消息参数,也可以在执行自己的操作后继续执行默认处理。
对话框过程接收 WM_INITDIALOG 消息而不是 WM_CREATE 消息,并且不会将未处理的消息传递给 DefDlgProc 函数。 否则,对话框过程与窗口过程完全相同。
将 Window 过程与 Window 类相关联
注册该类时,将窗口过程与窗口类相关联。 必须使用有关 类的信息填充 WNDCLASS 结构,并且 lpfnWndProc 成员必须指定窗口过程的地址。 若要注册类,请将 WNDCLASS 结构的地址传递给 RegisterClass 函数。 注册窗口类后,窗口过程将自动与使用该类创建的每个新窗口相关联。
以下示例演示如何将上一示例中的窗口过程与窗口类相关联。
int APIENTRY WinMain(
HINSTANCE hinstance, // handle to current instance
HINSTANCE hinstPrev, // handle to previous instance
LPSTR lpCmdLine, // address of command-line string
int nCmdShow) // show-window type
{
WNDCLASS wc;
// Register the main window class.
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = (WNDPROC) MainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hinstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = "MainMenu";
wc.lpszClassName = "MainWindowClass";
if (!RegisterClass(&wc))
return FALSE;
//
// Process other messages.
//
}
对窗口进行子类化
若要对窗口实例进行子类化,请调用 SetWindowLong 函数,并指定窗口的句柄以子类化GWL_WNDPROC标志和指向子类过程的指针。 SetWindowLong 返回指向原始窗口过程的指针;使用此指针将消息传递给原始过程。 子类窗口过程必须使用 CallWindowProc 函数调用原始窗口过程。
注意
若要编写与 32 位和 64 位版本的 Windows 兼容的代码,请使用 SetWindowLongPtr 函数。
以下示例演示如何在对话框中对编辑控件的实例进行子类化。 子类窗口过程使编辑控件能够接收所有键盘输入,包括 ENTER 和 TAB 键,只要控件具有输入焦点。
WNDPROC wpOrigEditProc;
LRESULT APIENTRY EditBoxProc(
HWND hwndDlg,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
HWND hwndEdit;
switch(uMsg)
{
case WM_INITDIALOG:
// Retrieve the handle to the edit control.
hwndEdit = GetDlgItem(hwndDlg, ID_EDIT);
// Subclass the edit control.
wpOrigEditProc = (WNDPROC) SetWindowLong(hwndEdit,
GWL_WNDPROC, (LONG) EditSubclassProc);
//
// Continue the initialization procedure.
//
return TRUE;
case WM_DESTROY:
// Remove the subclass from the edit control.
SetWindowLong(hwndEdit, GWL_WNDPROC,
(LONG) wpOrigEditProc);
//
// Continue the cleanup procedure.
//
break;
}
return FALSE;
UNREFERENCED_PARAMETER(lParam);
}
// Subclass procedure
LRESULT APIENTRY EditSubclassProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
if (uMsg == WM_GETDLGCODE)
return DLGC_WANTALLKEYS;
return CallWindowProc(wpOrigEditProc, hwnd, uMsg,
wParam, lParam);
}