使用窗口过程

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

设计窗口过程

下面的示例显示了典型的窗口过程的结构。 窗口过程使用 switch 语句中的 message 参数,其中的各个消息由单独的 case 语句处理。 请注意,每个 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 _ 销毁 消息。 DestroyWindow函数负责销毁要销毁的窗口的任何子窗口。 在销毁窗口之前,只会发送 WM _ NCDESTROY 消息。

窗口过程至少应处理 WM _ 画图 消息才能自行绘制。 通常情况下,它还应处理鼠标和键盘消息。 请参阅各个消息的说明,确定窗口过程是否应处理它们。

在处理消息的过程中,应用程序可以调用 DefWindowProc 函数。 在这种情况下,应用程序可以在将消息传递给 DefWindowProc 之前修改消息参数,也可以在执行自己的操作后继续默认处理。

对话框过程会接收一条 wm _ INITDIALOG 消息,而不是使用 wm _ 创建 消息,并且不会将未处理的消息传递给 DefDlgProc 函数。 否则,对话框过程与窗口过程完全相同。

将窗口过程与窗口类相关联

注册类时,将窗口过程与窗口类相关联。 必须使用有关类的信息填充 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); 
}