窗口封装类与Windows窗口实例的关系-3、CWnd如何处理窗口消息

在窗口消息处理方面,CWnd使用了窗口子类化和消息映射机制,关于消息映射的知识将在第9章详述,下面着重阐述CWnd是如何应用子类化处理窗口消息的。其实,在6.2节的示例中,CBaseWnd已经使用了与CWnd类似的子类化方法处理窗口消息。成员函数CBaseWnd::SubWindowClass()将窗口过程子类化为CBaseWnd::MyBaseWndProc(),在这个窗口过程中调用CBaseWnd::WindowProc()处理窗口消息。原始的窗口过程存入成员m_Super WndProc中,在默认处理中调用。

CWnd在窗口建立之初,使用AfxWndProc()子类化窗口,在AfxWndProc()中同样调用CWnd::WindowProc()处理窗口消息。不同的是,对于具体的消息处理,CWnd::WindowProc()使用消息映射机制,而不是调用固定的虚拟函数。原始的窗口过程存储在CWnd::m_pfnSuper中,在默认的处理过程CWnd::DefWindowProc()中调用。

下面是与消息处理相关的几个CWnd成员函数:

//用于子类化的窗口过程

LRESULT CALLBACK

AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)

{

         // special message which identifies the window as using AfxWndProc

         if (nMsg == WM_QUERYAFXWNDPROC)

                  return 1;

         // all other messages route through message map

         //通过句柄取得CWnd对象指针

         CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);

         ASSERT(pWnd != NULL);

         ASSERT(pWnd->m_hWnd == hWnd);

         return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam);

}

LRESULT AFXAPI AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg,

         WPARAM wParam = 0, LPARAM lParam = 0)

{  ……

//调用CWnd的虚拟成员函数处理消息

         lResult = pWnd->WindowProc(nMsg, wParam, lParam);

         ……

return lResult;

}

//可在类向导中重载的虚拟函数

LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)

{

         // OnWndMsg函数处理消息映射,如果在映射中没有发现当前消息的处理函数,则返回false

         LRESULT lResult = 0;

         if (!OnWndMsg(message, wParam, lParam, &lResult))

         //如果当前消息没被映射处理,调用默认处理函数

         lResult = DefWindowProc(message, wParam, lParam);

         return lResult;

}

//默认的消息处理函数,同Default()

LRESULT CWnd::DefWindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam)

{

         if (m_pfnSuper != NULL)

         //调用原始的窗口过程

                  return::CallWindowProc(m_pfnSuper, m_hWnd, nMsg, wParam, lParam);

         WNDPROC pfnWndProc;

         if ((pfnWndProc = *GetSuperWndProcAddr()) == NULL)

                  //调用默认窗口处理过程

                  return::DefWindowProc(m_hWnd, nMsg, wParam, lParam);

         else

                  return::CallWindowProc(pfnWndProc, m_hWnd, nMsg, wParam, lParam);

}

除以上几个相关成员函数外,CWnd类还定义了两个公共成员,SubclassWindow()和UnsubclassWindow(),前者用于动态地关联并使用AfxWndProc()子类化Windows窗口,后者执行相反过程。这意味着,可以随时将一个使用WIN32 API创建的窗口,通过调用SubclassWindow()关联到CWnd实例上。因为同时执行了子类化操作,所以此时就如同使用CWnd的Create()函数创建了这个Windows窗口一样。函数的代码如下:

BOOL CWnd::SubclassWindow(HWND hWnd)

{

         //hWnd应该是一个没有与任何CWnd实例关联的Windows窗口

         if (!Attach(hWnd)) //先建立关联,再子类化

                  return FALSE;

         //在子类化前调用这个虚函数,给用户提供编程接口

         PreSubclassWindow();

WNDPROC* lplpfn = GetSuperWndProcAddr();/*取得原始窗口函数。如果当前类尚未子类化窗口,返回NULL*/

         //使用AfxWndProc()子类化该窗口

         WNDPROC oldWndProc = (WNDPROC)::SetWindowLong(hWnd, GWL_WNDPROC,

                  (DWORD)AfxGetAfxWndProc());//取得AfxWndProc()子类窗口函数

         ASSERT(oldWndProc != (WNDPROC)AfxGetAfxWndProc());

         if (*lplpfn == NULL)

                  *lplpfn = oldWndProc; //保存原始窗口函数,在默认处理时调用

         return TRUE;

}

HWND CWnd::UnsubclassWindow()

{

         ASSERT(::IsWindow(m_hWnd));

//得到原始窗口过程

         WNDPROC* lplpfn = GetSuperWndProcAddr();

         //恢复窗口过程

SetWindowLong(m_hWnd, GWL_WNDPROC, (LONG)*lplpfn);

         *lplpfn = NULL;

         //分离窗口对象和窗口句柄

         return Detach();

}

virtual void CWnd::PreSubclassWindow()

{//该虚拟函数在建立窗口时,子类化前被调用,在CWnd::SubclassWindow中也被调用

//它不执行任何操作,可以重载它,根据需要执行子类化。那样,子类化过程会成为原始的窗口过程,在消息默认处理时被调用

}

对窗口操作的封装是很好理解的,成员CWnd::m_hWnd存储映射的窗口句柄,不同的窗口操作成员函数一般封装同名的WIN32 API,函数通过m_hWnd调用同名API即可。下面列举几个相关的成员函数。

void CWnd::SetWindowText(LPCTSTR lpszString)

         { ASSERT(::IsWindow(m_hWnd)); ::SetWindowText(m_hWnd, lpszString); }

BOOL CWnd::IsIconic() const

         { ASSERT(::IsWindow(m_hWnd)); return ::IsIconic(m_hWnd); }

void CWnd::MoveWindow(int x, int y, int nWidth, int nHeight, BOOL bRepaint)

         { ASSERT(::IsWindow(m_hWnd)); ::MoveWindow(m_hWnd, x, y, nWidth, nHeight, bRepaint); }

BOOL CWnd::SetWindowPos(const CWnd* pWndInsertAfter, int x, int y, int cx, int cy, UINT nFlags)

         { ASSERT(::IsWindow(m_hWnd));

 return ::SetWindowPos(m_hWnd, pWndInsertAfter->GetSafeHwnd(),         x, y, cx, cy, nFlags); }

CDC* CWnd::GetWindowDC()

         { ASSERT(::IsWindow(m_hWnd)); return CDC::FromHandle(::GetWindowDC(m_hWnd)); }

int CWnd::ReleaseDC(CDC* pDC)

         { ASSERT(::IsWindow(m_hWnd)); return ::ReleaseDC(m_hWnd, pDC->m_hDC); }

void CWnd::UpdateWindow()

         { ASSERT(::IsWindow(m_hWnd)); ::UpdateWindow(m_hWnd); }

关于CWnd的子窗口管理部分,将在第7章阐述。

转载于:https://www.cnblogs.com/carekee/articles/2041423.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值