• 窗口过程函数WindowProc和DefWindowProc函数[转]


     
    Windows操作系统里,当窗口显示之后,它就可以接收到系统源源不断地发过来的消息,然后窗口就需要处理这些消息,因此就需要一个函数来处理这些消息。在API里定义了一个函数为回调函数,当系统需要向窗口发送消息时,就会调用窗口给出的回调函数WindowProc,如果WindowProc函数不处理这个消息,就可以把它转向DefWindowProc函数来处理,这是系统的默认消息处理函数。当你按下菜单,或者点击窗口时,窗口需要运行这个消息处理函数。
    函数WindowProc声明如下:
    Title
    LRESULT CALLBACK WindowProc(          HWND hwnd,
        UINT uMsg,
        WPARAM wParam,
        LPARAM lParam
    );
    hwnd是当前窗口的句柄。
    uMsg是系统发过来的消息。
    wParam是消息参数。
    lParam是消息参数。
     
    这个函数一定是静态函数,也就是全局函数,在编译时已经确定了地址。由于它需要设置在注册的窗口类型里,如下:
    Title
    #008 ATOM MyRegisterClass(HINSTANCE hInstance)
    #009 {
    #010  WNDCLASSEX wcex;
    #011 
    #012  wcex.cbSize = sizeof(WNDCLASSEX);
    #013 
    #014  wcex.style             = CS_HREDRAW | CS_VREDRAW;
    #015  wcex.lpfnWndProc = WndProc;
    15行就是设置窗口的消息处理函数。
     
    函数DefWindowProc声明如下:
    LRESULT DefWindowProc(          HWND hWnd,
        UINT Msg,
        WPARAM wParam,
        LPARAM lParam
    );
    这个函数参数跟上面那个函数是一样的。
    只不过,它是处理所有默认的消息。
     
    调用这两个函数的实例如下:
    Title
    #001 //
    #002 // 函数: WndProc(HWND, UINT, WPARAM, LPARAM)
    #003 //
    #004 // 目的处理主窗口的消息.
    #005 //
    #006 // 蔡军生 2007/07/12
    #007 //
    #008 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    #009 {
    #010  int wmId, wmEvent;
    #011  PAINTSTRUCT ps;
    #012  HDC hdc;
    #013 
    #014  switch (message)
    #015  {
    #016  case WM_COMMAND:
    #017         wmId    = LOWORD(wParam);
    #018         wmEvent = HIWORD(wParam);
    #019         // 菜单选项命令响应:
    #020         switch (wmId)
    #021         {
    #022         case IDM_ABOUT:
    #023               DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
    #024               break;
    #025         case IDM_EXIT:
    #026               DestroyWindow(hWnd);
    #027               break;
    #028         default:
    #029               return DefWindowProc(hWnd, message, wParam, lParam);
    #030         }
    #031         break;
    #032  case WM_PAINT:
    #033         hdc = BeginPaint(hWnd, &ps);
    #034         //
    #035         EndPaint(hWnd, &ps);
    #036         break;
    #037  case WM_DESTROY:
    #038         PostQuitMessage(0);
    #039         break;
      //这里我加一个消息WM_NOTIFY通知一个控件的父窗口:一个消息在这个控件上上产生了      
         case WM_NOTIFY:
                {
             //我们在这里接收到这个消息后只知道窗口中的控件发生了消息但是不知道是什么消息,所以我们要进一步判断,然后分别进行处理(有的时候需要判定是那个控件)
                   NM_HTMLVIEW* pnmHTMLView = (NM_HTMLVIEW*)lParam;
                   switch (pnmHTMLView->hdr.code)
                  {
                    case NM_HOTSPOT: //点击了链接
                    /////do some thing....
                    break;
                    case NM_TITLECHANGE://html标题变化了
                     /////do some thing....
                    break;
                     .
                     .
                     .
                  }   
                }
    #040  default:
    #041         return DefWindowProc(hWnd, message, wParam, lParam);
    #042  }
    #043  return 0;
    #044 }
    8行定义消息处理函数
    14行开始根据不同的消息作处理。
    29行和第41行都是调用DefWindowProc函数来处理未处理的消息。
     
    有了窗口消息处理函数,就可以响应不同的消息,实现各种各样的功能。
    ----------------------------------------------------------------------------------------------------------------------------------------------------------------
    对窗口函数更深刻的理解(转载的一篇文章)

    think window procedure
    作者:温昱

    1. 用Win32 API编程时,window procedure比较明显,那就是程序员自定义window procedure,但Win32提供了一个API函数DefWindowProc(),缺省的处理要交给它。

    int APIENTRY WinMain(HINSTANCE hInstance,
    HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)
    {
    WNDCLASSEX wcex;
    wcex.lpszClassName = "MyClass";
    wcex.lpfnWndProc = (WNDPROC)MyWndProc;
    ...
    RegisterClassEx(&wcex);
    HWND hWnd;
    hWnd = CreateWindow("MyClass", szTitle, WS_OVERLAPPEDWINDOW,
    CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
    if (!hWnd)
    return FALSE;
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
    while (GetMessage(&msg, NULL, 0, 0))
    {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
    }
    return msg.wParam;
    }
    LRESULT CALLBACK MyWndProc(HWND hWnd,
    UINT message, WPARAM wParam, LPARAM lParam)
    {
    switch (message)
    {
    ...
    default:
    return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
    }
    
    2. 用MFC,window procedure会复杂一些,先看静态的,就是MFC预注册过的那些类,一句话,MFC替你打点好了window procedure的事。

    2.1 最抽象的,MFC把window procedure封装了起来,程序员只需"programming by difference",你对哪个消息感兴趣,就建立哪个消息的响应函数。(当然还有虚函数override...)
    void CMyClass::OnLButtonDown(UINT nFlags, CPoint pt)
    {
    ...
    }
    
    2.2 往底层一点,我们可以说CWnd::WindowProc()是现在的window procedure,它是一个template method,被你"programming by difference"的消息,会被它交给CWnd::OnWndMsg()处理,缺省的,会被它交给CWnd::DefWindowProc()处理。当然,上面说的没有考虑多态的情况,其实CWnd::OnWndMsg()和CWnd::DefWindowProc()都是虚函数。我们也注意到CWnd::DefWindowProc()中调用了::DefWindowProc(),也就是Win32 API的DefWindowProc()。
    class CWnd : public CCmdTarget
    {
    ...
    protected:
    // for processing Windows messages
    virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
    virtual BOOL OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult);
    ...
    };
    ///template method
    LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
    {
    LRESULT lResult = 0;
    if (!OnWndMsg(message, wParam, lParam, &lResult))
    lResult = DefWindowProc(message, wParam, lParam);
    return lResult;
    }
    //primitive method
    LRESULT CWnd::DefWindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam)
    {
    if (m_pfnSuper != NULL)
    return ::CallWindowProc(m_pfnSuper, m_hWnd, nMsg, wParam, lParam);
    ...
    }
    2.3 往更底层,来看看MFC预注册的那些类,window procedure是谁。注意,Pre-Registers Window Classes没有什么神秘的,因为Window Classes就是一个struct,而当你想用某个Pre-Registers Window Classes时,无非是传一个parameter过去,某段程序一判断,给wc结构赋值,调用AfxRegisterClass( & wc),OK。哈哈,我看到了,用的还是Win32 API的::DefWindowProc()。
    BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,
    LPCTSTR lpszWindowName, DWORD dwStyle,
    int x, int y, int nWidth, int nHeight,
    HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam)
    {
    CREATESTRUCT cs;
    cs.lpszClass = lpszClassName;
    ...
    PreCreateWindow(cs); //########pass a cs with lpszClass null
    ...
    }
    BOOL CWnd::PreCreateWindow(CREATESTRUCT& cs) //########pass a cs with lpszClass NULL
    {
    if (cs.lpszClass == NULL) //########pass a cs with lpszClass NULL
    {
    // make sure the default window class is registered
    VERIFY(AfxDeferRegisterClass(AFX_WND_REG));//########pass a para AFX_WND_REG
    // no WNDCLASS provided - use child window default
    ASSERT(cs.style & WS_CHILD);
    cs.lpszClass = _afxWnd;
    }
    return TRUE;
    }
    #define AfxDeferRegisterClass(fClass) AfxEndDeferRegisterClass(fClass)
    BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister)//########pass a para AFX_WND_REG
    {
    ...
    // common initialization
    WNDCLASS wndcls;
    memset( & wndcls, 0, sizeof(WNDCLASS));
    wndcls.lpfnWndProc = DefWindowProc; //########## here,Win32 API ::DefWindowProc()
    wndcls.hInstance = AfxGetInstanceHandle();
    wndcls.hCursor = afxData.hcurArrow;
    ...
    if (fToRegister & AFX_WND_REG) //########pass a para AFX_WND_REG
    {
    wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
    wndcls.lpszClassName = _afxWnd; //########pass a para _afxWnd
    AfxRegisterClass( & wndcls);
    ...
    }
    ...
    }
    const TCHAR _afxWnd[] = AFX_WND;
    #define AFX_WND       AFX_WNDCLASS("Wnd")
    #define AFX_WNDCLASS(s) \
    _T("Afx") _T(s) _T("42") _STATIC_SUFFIX _UNICODE_SUFFIX _DEBUG_SUFFIX
    
    2.4 好,总结一下。
    class CWnd : public CCmdTarget
    {
    ...
    protected:
    virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
    virtual BOOL OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult);
    virtual LRESULT DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam);
    virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);
    virtual BOOL OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult);
    ...
    }
    LRESULT CALLBACK
    AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
    {
    CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);
    return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam);
    }
    LRESULT AFXAPI
    AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg,
    WPARAM wParam = 0, LPARAM lParam = 0)
    {
    LRESULT lResult;
    lResult = pWnd->WindowProc(nMsg, wParam, lParam);
    return lResult;
    }
    LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
    {
    LRESULT lResult = 0;
    if (!OnWndMsg(message, wParam, lParam, & lResult))
    lResult = DefWindowProc(message, wParam, lParam);
    return lResult;
    }
    BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
    {
    if (message == WM_COMMAND)
    OnCommand(wParam, lParam);
    else if (message == WM_NOTIFY)
    OnNotify(wParam, lParam, & lResult);
    else
    ... // msg map related
    }
    LRESULT CWnd::DefWindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam)
    {
    if (m_pfnSuper != NULL)
    return ::CallWindowProc(m_pfnSuper, m_hWnd, nMsg, wParam, lParam);
    }
    
  • 相关阅读:
    [转]AJAX POST请求中参数以form data和request payload形式在servlet中的获取方式
    C#文档注释
    VS 单元测试项目,测试的时候运行按钮不可用
    获取泛型类的Type
    Sql 复习(4)
    Sql 复习(3)
    sqlserver的soundex
    sqlserver的trim
    Sql 复习(2)
    糟糕的设计
  • 原文地址:https://www.cnblogs.com/fuyanwen/p/3131623.html
Copyright © 2020-2023  润新知