admin管理员组

文章数量:1621906

原地址: http://blog.csdn/flowshell/article/details/6010860

   Windows系统里,一个窗口的属性分两个地方存放:一部分放在窗口类里头,如上所述的在注册窗口时指定另一部分放在Windows Object本身,如:窗口的尺寸,窗口的位置(XY轴),窗口的Z轴顺序,窗口的状态(ACTIVEMINIMIZEDMAXMIZEDRESTORED…),和其他窗口的关系(父窗口,子窗口),窗口是否可以接收键盘或鼠标消息,等等。
  
为了表达所有这些窗口的共性,MFC设计了一个窗口基类CWnd。有一点非常重要,那就是CWnd提供了一个标准而通用的MFC窗口过程,MFC下所有的窗口都使用这个窗口过程。至于通用的窗口过程却能为各个窗口实现不同的操作,那就是MFC消息映射机制的奥秘和作用了。这些,将在后面有关章节详细论述。
    CWnd
提供了一系列成员函数,或者是对Win32相关函数的封装,或者是CWnd新设计的一些函数。这些函数大致如下。

1)窗口创建函数
这里主要讨论函数CreateCreateEx
它们封装了Win32窗口创建函数::CreateWindowExCreate的原型如下:
BOOL CWnd::Create(LPCTSTR lpszClassName,LPCTSTR lpszWindowName, DWORD dwStyle,
const RECT& rect,CWnd* pParentWnd, UINT nID,CCreateContext* pContext)
Create
是一个虚拟函数,用来创建子窗口(不能创建桌面窗口和POP UP窗口)。CWnd的基类可以覆盖该函数,例如边框窗口类等覆盖了该函数以实现边框窗口的创建,视类则使用它来创建视窗口。
Create
调用了成员函数CreateExCWnd::CreateEx的原型如下:
BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,LPCTSTRlpszWindowName, DWORD dwStyle,
int x, int y, int nWidth, int nHeight,HWND hWndParent, HMENU nIDorHMenu, LPVOIDlpParam)
CreateEx
11个参数,它将调用::CreateWindowEx完成窗口的创建,这11个参数对应地传递给::CreateWindowEx。参数指定了窗口扩展风格、窗口类、窗口名、窗口大小和位置、父窗口句柄、窗口菜单和窗口创建参数。
CreateEx函数中还有一个虚函数PreCreateWindow
窗口创建时发送WM_CREATE消息,消息参数lParam指向一个CreateStruct结构的变量,该结构有11个域,其描述见后面4.4.1节对窗口过程的分析,Windows使用和CreateEx参数一样的内容填充该变量。

2)窗口创建过程

      MFC中窗口创建主要涉及三个重要的函数,分别是CWnd::CreateEx(或者CWnd::Create)、AfxHookWindowCreateAfxCbtFilterHook函数,首先是大概介绍下MFC的窗口创建过程,当CWnd::CreateEx被调用时,CWnd::CreateEx在调用API函数::CreateWindowEx创建窗口前会通过调用AfxHookWindowCreate安装一个名为_AfxCbtFilterHook的线程钩子,并将需要创建的窗口的CWnd指针保存到线程状态结构中,在API函数::CreateWindowEx真正创建窗口前AfxCbtFilterHook会被调用,AfxCbtFilterHook会执行子类化操作,把要创建的窗口的窗口过程子类化为线程状态结构中的窗口过程(AfxGetModuleState()->m_pfnAfxWndProc),即MFC的标准窗口过程,这样MFC的窗口(CWnd及其派生类)都可以通过消息映射机制接收和响应包括从创建开始的各种各样的消息。

1.CreateEx函数

[cpp] view plaincopy

1.  BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,  

2.         LPCTSTR lpszWindowName, DWORD dwStyle,  

3.         int x, int y, int nWidth, int nHeight,  

4.         HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam)  

5.  {  

6.      CREATESTRUCT cs;  

7.      cs.dwExStyle = dwExStyle;cs.lpszClass = lpszClassName;  

8.      cs.lpszName = lpszWindowName;cs.style = dwStyle;  

9.      cs.x = x;cs.y = y;cs.cx = nWidth;cs.cy = nHeight;  

10.     cs.hwndParent = hWndParent;cs.hMenu = nIDorHMenu;  

11.     cs.hInstance = AfxGetInstanceHandle();  

12.     cs.lpCreateParams = lpParam;  

13.     //允许应用程序修改窗口创建的参数。  

14.     if (!PreCreateWindow(cs))  

15.     {  

16.         PostNcDestroy();  

17.         return FALSE;  

18.     }  

19.     //Hook窗口的创建过程:主要是给当前现成安装一个名为_AfxCbtFilterHook的线程钩子,并讲需要创建的窗口的CWnd指针保存到线程状态结构(_AFX_THREAD_STATE)中。  

20.     AfxHookWindowCreate(this);  

21.     //开始创建窗口,在该函数正真开始创建窗口之前,AfxCbtFilterHook会被调用,AfxCbtFilterHook会执行子类化操作,把要创建的窗口的窗口过程子类化为线程状态结构中的窗口过程  

22.     //(AfxGetModuleState()->m_pfnAfxWndProc),这样MFC的窗口(CWnd及其派生类)都可以接收和响应包括从创建开始的各种各样的消息  

23.     HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass,  

24.                   cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,  

25.                   cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);  

26.     #ifdef _DEBUG  

27.     if (hWnd == NULL)  

28.     {  

29.         TRACE1("Warning: Window creation failed: GetLastError returns 0x%8.8X/n",  

30.         GetLastError());  

31.     }  

32.     #endif  

33.     //解除创建窗口的Hook  

34.     if (!AfxUnhookWindowCreate())  

35.         PostNcDestroy();  

36.     if (hWnd == NULL)  

37.         return FALSE;  

38.     ASSERT(hWnd == m_hWnd); // should have been set in send msg hook  

39.     return TRUE;  

40. }  

2.AfxHookWindowCreate
AfxHookWindowCreate
主要是Hook窗口的创建过程:主要是给当前现成安装一个名为_AfxCbtFilterHook的线程钩子,并讲需要创建的窗口的CWnd指针保存到线程状态结构

[cpp] view plaincopy

1.  void AFXAPI AfxHookWindowCreate(CWnd* pWnd)  

2.  {  

3.        //获取线程状态  

4.      _AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();  

5.      //如果线程状态里的m_pWndInit成员与pWnd相等表明给窗口正在创建中,直接返回  

6.      if (pThreadState->m_pWndInit == pWnd)  

7.          return;  

8.      if (pThreadState->m_hHookOldCbtFilter == NULL)  

9.      {  

10.         //给本线程安装一个名为AfxCbtFilterHook的钩子,AfxCbtFilterHook会在::CreateWindowEx真正开始创建窗口前被调用  

11.         pThreadState->m_hHookOldCbtFilter = ::SetWindowsHookEx(WH_CBT,_AfxCbtFilterHook, NULL, ::GetCurrentThreadId());  

12.         if (pThreadState->m_hHookOldCbtFilter == NULL)  

13.                AfxThrowMemoryException();  

14.     }  

15.     ASSERT(pThreadState->m_hHookOldCbtFilter != NULL);  

16.     ASSERT(pWnd != NULL);  

17.     ASSERT(pWnd->m_hWnd == NULL);   // only do once  

18.     ASSERT(pThreadState->m_pWndInit == NULL);   // hook not already in progress  

19.     //把需要Hook创建的窗口指针保存到线程状态中,AfxCbtFilterHook被调用时会用到。  

20.     pThreadState->m_pWndInit = pWnd;  

21. }  

3.AfxCbtFilterHook
AfxCbtFilterHook
会执行子类化操作,把要创建的窗口的窗口过程子类化为线程状态结构中的窗口过程 (AfxGetModuleState()->m_pfnAfxWndProc),即MFC的标准窗口过程,这样MFC的窗口(CWnd及其派生类)都可以接收和响应包括从创建开始的各种各样的消息

[cpp] view plaincopy

1.  LRESULT CALLBACK_AfxCbtFilterHook(int code, WPARAM wParam, LPARAM lParam)  

2.  {  

3.      _AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();  

4.      //不是HCBT_CREATEWND,传递给下一个HOOK  

5.      if (code != HCBT_CREATEWND)  

6.      {  

7.          return CallNextHookEx(pThreadState->m_hHookOldCbtFilter, code,wParam, lParam);  

8.      }  

9.      ASSERT(lParam != NULL);  

10.     LPCREATESTRUCT lpcs = ((LPCBT_CREATEWND)lParam)->lpcs;  

11.     ASSERT(lpcs != NULL);  

12.     //获取要创建的窗口  

13.     CWnd* pWndInit = pThreadState->m_pWndInit;  

14.     BOOL bContextIsDLL = afxContextIsDLL;  

15.     if (pWndInit != NULL || (!(lpcs->style & WS_CHILD) && !bContextIsDLL))  

16.     {  

17.         // Note: special check to avoid subclassing the IME window  

18.         if (_afxDBCS)  

19.         {  

20.             // check for cheap CS_IME style first...  

21.             if (GetClassLong((HWND)wParam, GCL_STYLE) & CS_IME)  

22.             goto lCallNextHook;  

23.             //获取窗口类  

24.             // get class name of the window that is being created  

25.             LPCTSTR pszClassName;  

26.             TCHAR szClassName[_countof("ime")+1];  

27.             if (HIWORD(lpcs->lpszClass))  

28.             {  

29.                  pszClassName = lpcs->lpszClass;  

30.             }  

31.             else  

32.             {  

33.             szClassName[0] = '/0';  

34.             GlobalGetAtomName((ATOM)lpcs->lpszClass, szClassName, _countof(szClassName));  

35.             pszClassName = szClassName;  

36.             }  

37.             // a little more expensive to test this way, but necessary...  

38.             if (lstrcmpi(pszClassName, _T("ime")) == 0)  

39.             goto lCallNextHook;  

40.         }  

41.         ASSERT(wParam != NULL); // should be non-NULL HWND  

42.         //获取即将创建的窗口句柄  

43.         HWND hWnd = (HWND)wParam;  

44.         WNDPROC oldWndProc;  

45.         if (pWndInit != NULL)  

46.         {  

47.             #ifdef _AFXDLL  

48.             AFX_MANAGE_STATE(pWndInit->m_pModuleState);  

49.             #endif  

50.             //确保窗口句柄未与窗口(CWnd)关联  

51.             // the window should not be in the permanent map at this time  

52.             ASSERT(CWnd::FromHandlePermanent(hWnd) == NULL);  

53.             //使窗口句柄与窗口(CWnd)关联,  

54.             pWndInit->Attach(hWnd);  

55.             //CWnd子类可以重载这个函数以在子类化进行前做一些处理  

56.             pWndInit->PreSubclassWindow();  

57.             //获取窗口的原始窗口过程,该窗口过程保存在CWnd:: m_pfnSuper里,用CWnd::GetSuperWndProcAddr()函数可以获取,该函数为虚函数,可以重载把原来的窗口过程替换。  

58.             WNDPROC *pOldWndProc = pWndInit->GetSuperWndProcAddr();  

59.             ASSERT(pOldWndProc != NULL);  

60.             #ifndef _AFX_NO_CTL3D_SUPPORT  

61.             _AFX_CTL3D_STATE* pCtl3dState;  

62.             DWORD dwFlags;  

63.             if (!afxData.bWin4 && !bContextIsDLL &&(pCtl3dState = _afxCtl3dState.GetDataNA()) != NULL &&  

64.             pCtl3dState->m_pfnSubclassDlgEx != NULL &&(dwFlags = AfxCallWndProc(pWndInit, hWnd, WM_QUERY3DCONTROLS)) != 0)  

65.             {  

66.                 // was the class registered with AfxWndProc?  

67.                 WNDPROC afxWndProc = AfxGetAfxWndProc();  

68.                 BOOL bAfxWndProc = ((WNDPROC)GetWindowLong(hWnd, GWL_WNDPROC) == afxWndProc);  

69.                 pCtl3dState->m_pfnSubclassDlgEx(hWnd, dwFlags);  

70.                 // subclass the window if not already wired to AfxWndProc  

71.                if (!bAfxWndProc)  

72.                {  

73.                    // subclass the window with standard AfxWndProc  

74.                   oldWndProc = (WNDPROC)SetWindowLong(hWnd, GWL_WNDPROC,(DWORD)afxWndProc);  

75.                   ASSERT(oldWndProc != NULL);  

76.                   *pOldWndProc = oldWndProc;  

77.                 }  

78.             }  

79.             else  

80.             #endif  

81.             {  

82.                     // 通过AfxGetAfxWndProc()获取MFC的标准窗口过程,该窗口过程保存在线程状态的m_pfnAfxWndProc成员中  

83.                     // subclass the window with standard AfxWndProc  

84.                     WNDPROC afxWndProc = AfxGetAfxWndProc();  

85.                     //子类化窗口,即把要创建的窗口的窗口过程设为标准的MFC窗口过程,这样标准MFC窗口过程就会通过MFC的消息映射给窗口(CWnd)发送各种消息了。  

86.                     oldWndProc = (WNDPROC)SetWindowLong(hWnd, GWL_WNDPROC, (DWORD)afxWndProc);  

87.                     ASSERT(oldWndProc != NULL);  

88.                     //保存子类化前的窗口过程,保存在CWnd:: m_pfnSuper成员中  

89.                     if (oldWndProc != afxWndProc)  

90.                       *pOldWndProc = oldWndProc;  

91.             }  

92.            //结束窗口创建的Hook  

93.            pThreadState->m_pWndInit = NULL;  

94.         }  

95.         else  

96.         {  

97.                ASSERT(!bContextIsDLL);   // should never get here  

98.                // subclass the window with the proc which does gray backgrounds  

99.                oldWndProc = (WNDPROC)GetWindowLong(hWnd, GWL_WNDPROC);  

100.                if (oldWndProc != NULL && GetProp(hWnd, _afxOldWndProc) == NULL)  

101.                {  

102.                      SetProp(hWnd, _afxOldWndProc, oldWndProc);  

103.                      if ((WNDPROC)GetProp(hWnd, _afxOldWndProc) == oldWndProc)  

104.                      {  

105.                            GlobalAddAtom(_afxOldWndProc);  

106.                            SetWindowLong(hWnd, GWL_WNDPROC,(DWORD)(pThreadState->m_bDlgCreate ? _AfxGrayBackgroundWndProc :  

107.                                _AfxActivationWndProc));  

108.                            ASSERT(oldWndProc != NULL);  

109.                       }  

110.                 }  

111.         }  

112.     }  

113.     lCallNextHook:LRESULT lResult = CallNextHookEx(pThreadState->m_hHookOldCbtFilter, code, wParam, lParam);  

114.     #ifndef _AFXDLL  

115.     if (bContextIsDLL)  

116.     {  

117.         ::UnhookWindowsHookEx(pThreadState->m_hHookOldCbtFilter);  

118.         pThreadState->m_hHookOldCbtFilter = NULL;  

119.     }  

120.     #endif  

121.     return lResult;  

122. }  

 

3)窗口销毁函数
例如:
DestroyWindow函数销毁窗口
PostNcDestroy( )
,销毁窗口后调用,虚拟函数

4)用于设定、获取、改变窗口属性的函数,例如:
SetWindowText(CStringtiltle) 设置窗口标题
GetWindowText()
得到窗口标题
SetIcon(HICON hIcon, BOOL bBigIcon)
;设置窗口像标
GetIcon( BOOL bBigIcon ) ;
得到窗口像标
GetDlgItem( int nID)
;得到窗口类指定ID的控制子窗口
GetDC();
得到窗口的设备上下文
SetMenu(CMenu *pMenu);
设置窗口菜单
GetMenu()
;得到窗口菜单

5)用于完成窗口动作的函数

用于更新窗口,滚动窗口,等等。一部分成员函数设计成或可重载(Overloaded)函数,或虚拟(Overridden)函数,或MFC消息处理函数。这些函数或者实现了一部分功能,或者仅仅是一个空函数。如:

* 有关消息发送的函数:
SendMessage( UINTmessage,WPARAM wParam = 0, LPARAM lParam = 0 ); 给窗口发送发送消息,立即调用方式
PostMessage(( UINT message,WPARAM wParam = 0, LPARAM lParam = 0 );
给窗口发送消息,放进消息队列

*
有关改变窗口状态的函数
MoveWindow( LPCRECTlpRect, BOOL bRepaint = TRUE );移动窗口到指定位置
ShowWindow(BOOL )
;显示窗口,使之可见或不可见

*
实现MFC消息处理机制的函数:
virtual LRESULTWindowProc( UINT message, WPARAM wParam, LPARAM lParam ); 窗口过程,虚拟函数
virtual BOOL OnCommand( WPARAM wParam, LPARAM lParam )
;处理命令消息
OnNotify              
处理通知消息
DefWindowProc         
消息的缺省处理
OnChildNotify         
消息反射处理

*
消息处理函数:
OnCreate(LPCREATESTRUCT lpCreateStruct );MFC窗口消息处理函数,窗口创建时由MFC框架调用
OnClose();MFC
窗口消息处理函数,窗口创建时由MFC框架调用

*
其他功能的函数
CWnd
的导出类是类型更具体、功能更完善的窗口类,它们继承了CWnd的属性和方法,并提供了新的成员函数(消息处理函数、虚拟函数、等等)。


1.
MFC下创建一个窗口对象

MFC下创建一个窗口对象分两步,首先创建MFC窗口对象,然后创建对应的Windows窗口。在内存使用上,MFC窗口对象可以在栈或者堆(使用new创建)中创建。具体表述如下:
*
创建MFC窗口对象。通过定义一个CWnd或其派生类的实例变量或者动态创建一个MFC窗口的实例,前者在栈空间创建一个MFC窗口对象,后者在堆空间创建一个MFC窗口对象。
*
调用相应的窗口创建函数,创建Windows窗口对象。

例如:在前面提到的AppWizard产生的源码中,有CMainFrame(派生于CMDIFrame(SDI)或者CMDIFrameWnd(MDI))类。它有两个成员变量定义如下:
CToolBar m_wndToolBar;
CStatusBar m_wndStatusBar;
当创建CMainFrame类对象时,上面两个MFC Object也被构造。
CMainFrame
还有一个成员函数
OnCreate
LPCREATESTRUCT lpCreateStruct)
它的实现包含如下一段代码,调用CToolBarCStatusBar的成员函数Create来创建上述两个MFC对象对应的工具栏HWND窗口和状态栏HWND窗口.

[cpp] view plaincopy

1.  int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)  

2.  {  

3.      …  

4.      if (!m_wndToolBar.Create(this) ||  

5.      !m_wndToolBar.LoadToolBar(IDR_MAINFRAME))  

6.      {  

7.          TRACE0("Failed to create toolbar/n");  

8.          return -1; // fail to create  

9.      }  

10.     if (!m_wndStatusBar.Create(this) ||  

11.     !m_wndStatusBar.SetIndicators(indicators,  

12.     sizeof(indicators)/sizeof(UINT)))  

13.     {  

14.         TRACE0("Failed to create status bar/n");  

15.         return -1; // fail to create  

16.     }  

17.     …  

18. }  

 

本文标签: 架构MFCCWnd