使用窗口过程

本部分介绍如何执行与窗口过程关联的以下任务。

设计窗口过程

以下示例演示典型窗口过程的结构。 窗口过程在 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); 
}