`

VC++窗口的创建过程

阅读更多

在Windows中运行的程序,大多数都有一个或几个可以看得见的窗口,而在这些窗口被创建起来之前,操作系统怎么知道该怎样创建该窗口,以及用户操作该窗口的各种消息交给谁处理呢?所以VC在调用Windows的API(CreateWindow或者CreateWindowEx)创建窗口之前,要求程序员必须定义一个窗口类(不是传统C++意义上的类)来规定所创建该窗口所需要的各种信息,主要包括:窗口的消息处理函数、窗口的风格、图标、 鼠标、菜单等。其定义如下:

typedef struct tagWNDCLASSA(注:该结构为ANSII版本)
{
UINT    style ;
WNDPROC    lpfnWndProc ;
int    cbClsExtra ;
int    cbWndExtra ;
HINSTANCE  hInstance ;
HICON     hIcon ;
HCURSOR   hCursor ;
HBRUSH    hbrBackground ;
LPCSTR  lpszMenuName ;
LPCSTR  lpszClassName ;
}WNDCLASSA, * PWNDCLASSA, NEAR * NPWNDCLASSA, FAR * LPWNDCLASSA ;

style 表示该类窗口的风格,如style = CS_VREDRAW|CS_HREDRAW表示窗口在运动或者调整大小时需要重画,关于其它风格可在 MSDN中查到。
lpfnWndProc为一指针,指向用户定义的该窗口的消息处理函数。
cbClsExtra 用于在窗口类结构中保留一定空间,用于存在自己需要的某些信息。
cbWndExtra用于在Windows内部保存的窗口结构中保留一定空间。
hInstance 表示创建该窗口的程序的运行实体代号(WinMain的参数之一)。
hIcon、hCursor、hbrBackground、lpszMenuName分别表示该窗口的图标、鼠标形状、背景色以及菜单。
lpszClassName表示该窗口类别的名称,即标识该窗口类的标志。
  从上面可以看出一个窗口类就对应一个WNDCLASSA结构(这里以ANSII为例),当程序员将该结构按自己要求填写完成后,就可以调用RegisterClass(或RegisterClassEx)函数将该类注册,这样以后凡是要创建该窗口,只需要以该类名(lpszClassName中指定)为参数调用CreateWindow,你看多方便呀,真是一举多得啊!
    总结:但窗口结构注册(调用RegisterClass(或RegisterClassEx)函数)后,以后凡是要创建该窗口,只需要以该类名(lpszClassName中指定)为参数调用CreateWindow。
二、传统SDK中的窗口类
  既然我们知道了什么是窗口类,那我们就将它放到一个传统的SDK程序中,看看是怎样运行的。 #include <windows.h>

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
          PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT ("HelloWin") ;
WNDCLAS wndclass ;

wndclass.style  = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra   = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance   = hInstance ;
wndclass.hIcon  = LoadIcon (NULL, IDI_APPLICATION) ;
  wndclass.hCursor  = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
  wndclass.lpszMenuNam = NULL ;
wndclass.lpszClassName = szAppName ;

RegisterClass (&wndclass);

hwnd = CreateWindow( szAppName, // window class name
TEXT ("The Hello Program"), // window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // initial x position
CW_USEDEFAULT, // initial y position
CW_USEDEFAULT, // initial x size
CW_USEDEFAULT, // initial y size
NULL, // parent window handle
  NULL,     // window menu handle
  hInstance,   // program instance handle
  NULL) ;   // creation parameters
  
ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;
  
while (GetMessage (&msg, NULL, 0, 0))
   {
TranslateMessage (&msg) ;
  DispatchMessage (&msg) ;
    }
return msg.wParam ;
}

  这是一个标准的Windows程序代码,程序被启动后,填写一个窗口类,然后调用RegisterClass将该类注册,接着创建该窗口,最后显示窗口和进入消息循环。

三、MFC中的窗口类
  当你看到这里,也许你可能会感到奇怪:我在用MFC向导做程序时,并没有进行什么窗口类的填写和注册吗?是的,你没有,但是向导帮你做了。在展示向导是怎么做的之前,请让我先介绍一下预先知识。
  在MFC系统中定义了五个默认的窗口类(这里不包括AFX_WNDCOMMCTLS_REG),分别定义在AFXIMPL.h中:   #define AFX_WND_REG          (0x0001)
  #define AFX_WNDCONTROLBAR_REG    (0x0002)
  #define AFX_WNDMDIFRAME_REG      (0x0004)
  #define AFX_WNDFRAMEORVIEW_REG    (0x0008)
  #define AFX_WNDDOLECONTROL_REG    (0x0020)

在WINCORE.cpp定义了这些窗口类对应的字符串名称:  const TCHAR _afxWnd[] = AFX_WND;
  const TCHAR _afxWndControlBar[] = AFX_WNDCONTROLBAR;
  const TCHAR _afxWndMDIFrame[] = AFX_WNDMDIFRAME;
  const TCHAR _afxWndFrameOrView[] = AFX_WNDFRAMEORVIEW;
  const TCHAR _afxWndOleControl[] = AFX_WNDOLERONTROL;

在AFXIMPL.h中定义了五个AFX_XXX对应的字符串:  #define AFX_WND         AFX_WNDCLASS("WND")
  #define AFX_WNDCONTROLBAR  AFX_WNDCLASS("ControlBar")
  #define AFX_WNDMDIFRAME   AFX_WNDCLASS("MDIFrame")
  #define AFX_WNDFRAMEORVIEW AFX_WNDCLASS("FrameOrView")
  #define AFX_WNDOLECONTROL  AFX_WNDCLASS("OleControl")

  看到这里也许有些心急了,其实上面一堆代码只是定义了五个默认窗口类的字符串名称和二进制名称,具体注册行为在全局函数AfxDeferRegisterClass中: #define AfxDeferRegisterClass(fClass) \
((afxRegisteredClasses & fClass) ? TRUE:AfxEndDeferRegisterClass(fClass)
  #define afxRegisteredClasses AfxGetModuleState()->m_fRegisteredClasses

  BOOL AFXAPI AfxEndDeferRegisterClass(short fClass)
  {
    WNDCLASS wndCls;
    wndCls.lpfnWndProc = DefWindowProc;
    if(fClass & AFX_WND_REG)
    {
      wndCls.lpszClassName=_afxWnd;
      AfxRegisterClass(&wndCls);
    }else if(fClass & AFX_WNDOLECONTROL_REG)
    {
      wndCls.lpszClassName=_afxWndOleControl;
      AfxRegisterClass(&wndCls);
    }else if(fClass & AFX_WNDCONTROLBAR_REG)
    {
      wndCls.lpszClassName=_afxWndControlBar;
      AfxRegisterClass(&wndCls);
    }else if(fClass & AFX_WNDMDIFRAME_REG)
    {
      RegisterWithIcon(&wndCls,_afxWndMDIFrame,AFX_IDI_MDIFRAME);
    }else if(fClass & AFX_WNDFRAMEORVIEW_REG)
    {
  RegisterWithIcon(&wndCls,_afxWndFrameOrView,AFX_IDI_STD_FRAME);
    }else if(fClass & AFX_WNDCOMMCTLS_REG)
    {
      InitCommonControls();
    }
  }
从以上例子可以看出, AfxDeferRegisterClass函数用if/else结构实现各种不同窗口的注册,所所以MFC函数窗口注册的时候调用AfxDeferRegisterClass函数就可以了。
  从上面的代码可以看出,AfxDeferRegisterClass函数首先判断该窗口类是否注册,如已注册则直接返回,否则调用AfxEndDeferRegisterClass进行注册,即注册要求的默认窗口类。其中RegisterWithIcon和InitCommonControls最终也是转化为调用AfxRegisterClass,而AfxRegisterClass函数调用RegisterClass进行注册,啊,终于看到SDK中的RegisterClass了,看到它总有一种亲切感!
  有了上面的知识,我们就可以很容易摸清MFC是怎样注册窗口类的了!我们知道Windows上所有看得见的东西,在MFC中都是继承于CWnd类的,而CWnd类创建窗口的成员函数是Create和CreateEx,由于Create最终是调用CreateEx,所以我们只需要看CreateEx函数就行了: create()-->CreateEx()??CREATESTRUCT
??  PreCreateWindow(cs);
|?? AfxDeferRegisterClass(AFX_WND_REG)
                          ?? CreateWindowEx()

 BOOL CWnd::CreateEx(DWORD dwExStyle, LPCSTSTR lpszClassName,
            …… LPVOID lpParam)
  {
   CREATESTRUCT cs;
   cs.dwExStyle = dwExStyle;
   … …
   cs.lpCreateParams = lpParam;

   PreCreateWindow(cs);
   AfxHookWindowCreate(this);
   HWND hWnd=::CreateWindowEx(cs.dwStyle,cs.lpszClass,…,cs.lpCreateParams);
   ……
  }

  啊,一看到CreateWindowEx,亲切感又来了,这不是和SDK中的CreateWindow一样嘛,是创建窗口!既然这样,那么注册窗口肯定在该函数之前,会是谁呢?如果你做过一点MFC程序,你就会对将眼光停留PreCreateWindow上。对!就是它了。
  PreCreateWindow函数是CWnd类的一个虚拟函数,提供程序设置待创建窗口的属性(包括窗口类),这样继承于CWnd的类都可以按照自己的要求在PreCreateWindow中设置自己的属性了,而且我们看到MFC也是这样做的: BOOL CWnd::PreCreateWindow(CREATESTRUCT &cs)
{
   if(cs.lpszClass = = NULL)
   {
     AfxDeferRegisterClass(AFX_WND_REG);
     cs.lpszClass = _afxWnd;
   }
   return TRUE;
}

BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT &cs)
{
   if(cs.lpszClass = = NULL)
   {
     AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG);
    cs.lpszClass = _afxWndFrameOrView;
   }
   return TRUE;
}

BOOL CMDIFrameWnd::PreCreateWindow(CREATESTRUCT &cs)
{
  if(cs.lpszClass = = NULL)
  {
   AfxDeferRegisterClass(AFX_WNDMDIFRAME_REG);
   cs.lpszClass = _afxWndMDIFrame;
  }
}

BOOL CMDIChildWnd::PreCreateWindow(CREATESTRUCT &cs)
{
   return CFrameWnd::PreCreateWindow(cs);
}

BOOL CView::PreCreateWindow(CREATESTRUCT &cs)
{
   if(cs.lpszClass = = NULL)
   {
     AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG);
     cs.lpszClass = _afxWndFrameOrView;
   }
}

就是通过继承的方法,MFC实现常用类的窗口注册(代码并不完全,是从MFC中抽取对我们有意义的一部分代码)。

四、在MFC中注册自己的窗口类
  在MFC中创建一个窗口,就必须是继承于CWnd类的,这样你的CMyWnd类自然就有了PreCreateWindow方法。你想注册有自己个性的窗口类,那么就在该函数中进行吧。也就是在PreCreateWindow函数中注册自己的窗口类,然后将窗口类的类名以及待创建窗口的其它属性(见CREATESTRUCT结构)填写cs,然后返回系统,供系统创建你的窗口。

用SDK建立类的过程:
填写一个窗口类,然后调用RegisterClass将该类注册,接着创建该窗口,最后显示窗口和进入消息循环。
用MFC建立窗口的过程:
我们知道Windows上所有看得见的东西,在MFC中都是继承于CWnd类的,而CWnd类创建窗口的成员函数是Create和CreateEx,由于Create最终是调用CreateEx,所以我们只需要看CreateEx函数就行了:
create()-->CreateEx()??CREATESTRUCT
??PreCreateWindow(cs);
|?? AfxDeferRegisterClass(AFX_WND_REG)
                 ?? AfxHookWindowCreate(this); //为窗口关联一个消息处理函数WndProc()
                 ?? CreateWindowEx()
**********************************************************************************************
CWnd::CreateEX中HOOK函数作用

VC   2009-08-26 20:25   阅读9   评论0   字号: 大大  中中  小小 用最基本的一句话概述,钩子函数起了很大作用。故事是这样的,有些漫长,也需要些耐心。

MFC中消息分为3类:

 1. WM_COMMAND:所有的UI组件和加速键都会产生这种消息,所有派生于CCmdTarget的类都有能力处理该消息

 2. 标准消息:除WM_COMMAND之外的WM_xx消息都是标准消息,派生于CWnd的类都有能力处理该消息

 3. 控件通知消息:用于子窗口控件向父窗口发送的消息

在MFC的消息映射表的建立中,通过一组宏,你就可以让自己的类先于父类处理某些Windows消息,这种行为很像虚函数,只是我们重载的内容不是虚函数,而是消息。

推动消息的泵

第一阶段 窗口过程
在产生一个窗口的时候,会调用CFrameWnd::Create,所有的故事也都从这里展开。下面的代码为了简洁,去掉了不相关的代码
  BOOL CFrameWnd::Create(…)   {
     //  …
       if  ( ! CreateEx(…))   {
         //  …
     }
     //  …
 }
BOOL CWnd::CreateEx(…)   {
     //  …
     AfxHookWindowCreate( this );
    HWND hWnd  =  ::CreateWindowEx(…);
     //  …
 }

  void  AFXAPI AfxHookWindowCreate(CWnd *  pWnd)   {
     //  …
       if  (pThreadState -> m_hHookOldCbtFilter  ==  NULL)   {
        pThreadState -> m_hHookOldCbtFilter  =  ::SetWindowsHookEx(WH_CBT,
        _AfxCbtFilterHook, NULL, ::GetCurrentThreadId());
         //  …
     }
     //  …
     pThreadState -> m_pWndInit  =  pWnd;
}

这样,通过AfxHookWindowCreate,在当前线程中安装了一个钩子,用来拦截和窗口相关的事件,每当:
1. 另一个窗口成为active;
2. 产生或摧毁一个窗口
3. Minimize或maximize一个窗口;
4. 移动或缩放一个窗口;
5. 完成一个来自系统菜单的命令;
6. 从系统队列中取出一个消息;
时,都会先调用_AfxCbtFilterHook(即每当有一个可能引发消息发生的事件的时候都会调用_AfxCbtFilterHook,然后这个函数对这些消息进行过滤,能够处理的就交给AfxGetAfxWndProc,不能处理的就交给全局的DefWndProc()函数),接下来看看钩子函数作了什么:
 LRESULT CALLBACK
_AfxCbtFilterHook( int  code, WPARAM wParam, LPARAM lParam)   {
     //  …
     WNDPROC afxWndProc  =  AfxGetAfxWndProc();
    oldWndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC,(DWORD_PTR)afxWndProc);
     //  …
 }
 WNDPROC AFXAPI AfxGetAfxWndProc()   {
     //  …
     return   & AfxWndProc;
}

这样,_AfxCbtFilterHook的工作总结起来就是通过窗口子类化,把新建的窗口的窗口过程设置成AfxWndProc。
到这里,我们终于找到了窗口过程。
结论
CFrameWnd::Create创建窗口调用CWnd::CreateEx
CWnd::CreateEx调用AfxHookWindowCreate准备为窗口设置钩子
AfxHookWindowCreate调用::SetWindowHookEx为窗口设置了一个WH_CBT类型的钩子来过滤消息,并把过滤函数设置成_AfxCbtFilterHook
_AfxCbtFilterHook通过窗口子类化设置窗口的窗口过程为AfxWndProc
这样,通过::DispatchMessage发送给窗口的消息就会源源不断地送到AfxWndProc中来,可以想到,AfxWndProc利用MFC的消息映射表,分门别类的对消息进行分流。

即每当有一个可能引发消息发生的事件的时候都会调用_AfxCbtFilterHook,然后这个函数对这些消息进行过滤,能够处理的就交给AfxGetAfxWndProc,不能处理的就交给全局的DefWndProc()函数
OnNcCreate,当CWnd对象第一次被创建时,框架在WM_CREATE消息之前调用这个成员函数。可以修改CREATESTRUCT结构,PreCreateWindow也是可以修改CREATESTRUCT
  结构,他们有什么区别?  
  PreCreateWindow用的比较多,OnNcCreate都用在什么地方??

OnNcCreate是响应WM_NCCREATE,   当窗口开始时先创建客户区,所以先发送WM_NCCREATE消息,   当非客户区都创建好了,再发送WM_CREATE,去创建窗口客户区,  
  The   WM_NCCREATE   message   is   sent   prior   to   the   WM_CREATE   message   when   a   window   is   first   created.    
   
  意思是说,WM_NCCREATE比WM_CREATE先发给窗口程序,在窗口一创建的时候  
  就是说:  
   
  0.   call   CreateWindow/CreateWindowEx开始  
  0.5   PreCreateWindow   <---   HOOK(窗口句柄无效)  
  1.   窗口创建  
  2.   WM_NCCREATE   (窗口句柄有效)  
  3.   WM_CREATE   (窗口句柄有效)  
  4.   call   CreateWindow/CreateWindowEx结束  
   
  这些很容易验证
[转]CWnd中PreCreateWindow、PreSubclassWindow、SubclassWindow的区别
Posted on 2009-01-15 16:35 天之骄子 阅读(503) 评论(0)  编辑 收藏 引用 
MFC(VC6.0)的CWnd及其子类中,有如下三个函数:
 class CWnd : public CCmdTarget
 {
public:
        virtual BOOL PreCreateWindow(CREATESTRUCT& cs);                             virtual void PreSubclassWindow();
   BOOL SubclassWindow(HWND hWnd);
      
};
  让人很不容易区分,不知道它们究竟干了些什么,在什么情况下要改写哪个函数?
  想知道改写函数?让我先告诉你哪个不能改写,那就是SubclassWindow。Scott Meyers的杰作<<Effective C++>>的第36条是这样的Differentiate between inheritance of interface and inheritance of implementation. 看了后你马上就知道,父类中的非虚拟函数是设计成不被子类改写的。根据有无virtual关键字,我们在排除了SubclassWindow后,也就知道PreCreateWindow和PreSubClassWindow是被设计成可改写的。接着的问题便是该在什么时候该写了。要知道什么时候该写,必须知道函数是在什么时候被调用,还有执行函数的想要达到的目的。我们先看看对这三个函数,MSDN给的解释:
  PreCreateWindow:
  Called by the framework before the creation of the Windows window
  attached to this CWnd object.
  (译:在窗口被创建并attach到this指针所指的CWnd对象之前,被framework调用)
  PreSubclassWindow:
  This member function is called by the framework to allow other necessary
  subclassing to occur before the window is subclassed.
  (译:在window被subclassed之前被framework调用,用来允许其它必要的subclassing发生)
虽然我已有译文,但还是让我对CWnd的attach和窗口的subclass作简单的解释吧!要理解attach,我们必须要知道一个C++的CWnd对象和窗口(window)的区别:window就是实在的窗口,而CWnd就是MFC用类对window所进行C++封装。attach,就是把窗口附加到CWnd对象上操作。附加(attach)完成后,CWnd对象才和窗口发生了联系。窗口的subclass是指修改窗口过程的操作,而不是面向对象中的派生子类。
  好了,PreCreateWindow由framework在窗口创建前被调用,函数名也说明了这一点,Pre应该是previous的缩写,PreSubclassWindow由framework在subclass窗口前调用。 这段话说了等于没说,你可能还是不知道,什么时候该改写哪个函数。罗罗嗦嗦的作者,还是用代码说话吧!源码之前,了无秘密(候捷语)。我们就看看MFC中的这三个函数都是这样实现的吧!
 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)
                       {
     // allow modification of several common create parameters
     CREATESTRUCT cs;
     cs.dwExStyle = dwExStyle;
     cs.lpszClass = lpszClassName;
     cs.lpszName = lpszWindowName;
     cs.style = dwStyle;
     cs.x = x;
     cs.y = y;
     cs.cx = nWidth;
     cs.cy = nHeight;
     cs.hwndParent = hWndParent;
     cs.hMenu = nIDorHMenu;
     cs.hInstance = AfxGetInstanceHandle();
     cs.lpCreateParams = lpParam;
    
     if (!PreCreateWindow(cs))
       {
         PostNcDestroy();
         return FALSE;
     }
    
     AfxHookWindowCreate(this);
     HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass,
         cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,
         cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);
        
         return TRUE;
 }
 
 // for child windows
BOOL CWnd::PreCreateWindow(CREATESTRUCT& cs)
{
     if (cs.lpszClass == NULL)
    {
         // make sure the default window class is registered
         VERIFY(AfxDeferRegisterClass(AFX_WND_REG));
        
         // no WNDCLASS provided - use child window default
         ASSERT(cs.style & WS_CHILD);
         cs.lpszClass = _afxWnd;
     }
     return TRUE;
 }
  CWnd::CreateEx先设定cs(CREATESTRUCT),在调用真正的窗口创建函数::CreateWindowEx之前,调用了CWnd::PreCreateWindow函数,并把参数cs以引用的方式传递了进去。而CWnd的PreCreateWindow函数也只是给cs.lpszClass赋值而已。毕竟,窗口创建函数CWnd::CreateEx的诸多参数中,并没有哪个指定了所要创建窗口的窗口类,而这又是不可缺少的(请参考<<windows程序设计>>第三章)。所以当你需要修改窗口的大小、风格、窗口所属的窗口类等cs成员变量时,要改写PreCreateWindow函数。
 // From VS Install PathVC98MFCSRCWINCORE.CPP
 BOOL CWnd::SubclassWindow(HWND hWnd)
{
     if (!Attach(hWnd))
         return FALSE;
    
     // allow any other subclassing to occur
     PreSubclassWindow();
    
     // now hook into the AFX WndProc
     WNDPROC* lplpfn = GetSuperWndProcAddr();
     WNDPROC oldWndProc = (WNDPROC)::SetWindowLong(hWnd, GWL_WNDPROC,
         (DWORD)AfxGetAfxWndProc());
     ASSERT(oldWndProc != (WNDPROC)AfxGetAfxWndProc());
    
     if (*lplpfn == NULL)
         *lplpfn = oldWndProc;   // the first control of that type created
 #ifdef _DEBUG
     else if (*lplpfn != oldWndProc)
{
         
             ::SetWindowLong(hWnd, GWL_WNDPROC, (DWORD)oldWndProc);
     }
 #endif
    
     return TRUE;
 }
 
 void CWnd::PreSubclassWindow()
{
     // no default processing
 }
  CWnd::SubclassWindow先调用函数Attach(hWnd)让CWnd对象和hWnd所指的窗口发生关联。接着在用::SetWindowLong修改窗口过程(subclass)前,调用了PreSubclassWindow。CWnd::PreSubclassWindow则是什么都没有做。
  在CWnd的实现中,除了CWnd::SubclassWindow会调用PreSubclassWindow外,还有一处。上面所列函数CreateEx的代码,其中调用了一个AfxHookWindowCreate函数,见下面代码:
 // From VS Install PathVC98MFCSRCWINCORE.CPP
 BOOL CWnd::CreateEx( )
   {
     // allow modification of several common create parameters
     
        
         if (!PreCreateWindow(cs))
               {
             PostNcDestroy();
             return FALSE;
         }
        
         AfxHookWindowCreate(this);
         HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass,
             cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,
             cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);
        
         
             return TRUE;
 }
  接着察看AfxHookWindowCreate的代码:
 
 // From VS Install PathVC98MFCSRCWINCORE.CPP
 void AFXAPI AfxHookWindowCreate(CWnd* pWnd)
   {
     
        
         if (pThreadState->m_hHookOldCbtFilter == NULL)
               {
             pThreadState->m_hHookOldCbtFilter = ::SetWindowsHookEx(WH_CBT,
                 _AfxCbtFilterHook, NULL, ::GetCurrentThreadId());
             if (pThreadState->m_hHookOldCbtFilter == NULL)
                 AfxThrowMemoryException();
         }
         
 }
 
  其主要作用的::SetWindowsHookEx函数用于设置一个挂钩函数(Hook函数)_AfxCbtFilterHook,每当Windows产生一个窗口时(还有许多其它类似,请参考<<深入浅出MFC>>第9章,563页),就会调用你设定的Hook函数。
  这样设定完成后,回到CWnd::CreateEx函数中,执行::CreateWindowEx进行窗口创建,窗口一产生,就会调用上面设定的Hook函数_AfxCbtFilterHook。而正是在_AfxCbtFilterHook中对函数PreSubclassWindow进行了第二次调用。见如下代码:
 // From VS Install PathVC98MFCSRCWINCORE.CPP
  /**//////////////////////////////////////////////////////////////////////////////
 // Window creation hooks
 
 LRESULT CALLBACK
 _AfxCbtFilterHook(int code, WPARAM wParam, LPARAM lParam)
   {
            
         
         // connect the HWND to pWndInit
         pWndInit->Attach(hWnd);
     // allow other subclassing to occur first
     pWndInit->PreSubclassWindow();
     
           {
         // subclass the window with standard AfxWndProc
         oldWndProc = (WNDPROC)SetWindowLong(hWnd, GWL_WNDPROC, (DWORD)afxWndProc);
         ASSERT(oldWndProc != NULL);
         *pOldWndProc = oldWndProc;
     }
     
 }
  也在调用函数SetWindowLong进行窗口subclass前调用了PreSubclassWindow.
通常情况下窗口是由用户创建的
CWnd::Create(..)
●在此流程中,MFC提供一个机会"PreCreateWindow()供用户在创建前作点手脚

而对于对话框等,窗口是通过subclass方式交给用户的
系统读入对话框模板,建立其中各个子窗口

然后将各子窗口的 消息处理函数替换成 对应的C++对象 的消息处理函数 (Subclass:子类化,或"接管") ,然后,这个子窗口就会按类中定义的方式来动作了。

在此过程中,调用的是CWnd:SubclassWindow( HWND hWnd );
●在此流程中,MFC提供一个机会"PreSubclassWindow" 供用户在关联前作点手脚

具体来说,如果你定义一个窗口(如CButton派生类CMyButton),然后使用对话框数据交换将一个按钮与自己的派生类对象关联,这时候,一些"建立前"的处理就应该写在"PreSubclassWindow"中。

如果你用的不是"对话框数据关联",而是在OnInitDialg中自己创建m_mybtn.Create(...)
这时候,一些"建立前"的处理就应该写在
"PreCreateWindow"中。
这里“建立前”的处理包括像那些处理,跟PreCreateWindows()做的一些窗口初始化的工作有什么不同?
PreCreateWindows函数中没有窗口可以用——还没有创建
PreSubclassWindow函数中可以对窗口进行操作。
******************************
这些在窗口创建之初就加入了钩子,能否截获这些钩子。

------------------以下内容是对上面内容的具体解释,两部分必须结合着看--------------------------------------

MFC的窗口类(如CWnd)与窗口过程。
Windows是基于事件机制的,任何窗口都能发送和处理消息,每一个窗口都对应着自己的消息处理函数,即通常所说的窗口过程(WindowProc)。窗口过程通常是在WNDCLASSEX的lpfnWndProc变量中指定的,然后调用RegisterClassEx注册窗口类,lpfnWndProc要求是全局的或是类的静态成员,而MFC的窗口和类对象是一一对应的,在类中定义的窗口过程(CWnd::WindowProc)并非类的静态成员,那么窗口消息是怎样传给窗口对象的WindowProc函数去处理的呢?MFC中定义了一个全局的AfxWndProc函数,AfxWndProc是MFC中所有的窗口共用的窗口过程。这里要注意在AfxEndDeferRegisterClass中注册窗口类时并没有把AfxWndProc赋给lpfnWndProc,而是把DefWindowProc赋给了lpfnWndProc。真正把AfxWndProc指定为窗口过程的是在CWnd::CreateEx函数中,CWnd::CreateEx中先后调用了SetWindowsHookEx、CreateWindowEx和UnhookWindowsHookEx,SetWindowsHookEx安装了一个WH_CBT类型的钩子,在调用CreateWindowEx时(在CreateWindowEx返回之前)窗口会发送WM_CREATE、 WM_NCCREATE等消息,钩子过程CBTProc会在窗口消息WM_CREATE、 WM_NCCREATE等发送前被调用,并提前得到窗口的句柄值。钩子过程CBTProc的任务是把窗口句柄赋给窗口对象(CWnd::m_hWnd),并调用SetWindowLong把窗口过程替换成AfxWndProc(如是控件还要保留原窗口过程,用CallWindowProc进行默认处理)。在这有人可能会问,为什么不在AfxEndDeferRegisterClass中直接指定AfxWndProc呢?当然是有原因的:其一是控件的窗口过程必须用SetWindowLong来替换,其二是消息WM_CREATE、 WM_NCCREATE等是在CreateWindowEx返回前发送的,CWnd::WindowProc在处理这些消息时CWnd::m_hWnd必须是已经被初始化的,这个就是由前面的CBTProc完成的。
好现在我们只要关注AfxWndProc了。AfxWndProc是如何把消息分配给各个窗口对象的窗口过程的呢?在MFC中有一个全局的映射表(还没到消息映射,呵呵),这个表是窗口句柄到窗口对象的映射(即通过窗口句柄就能找到窗口对象的地址),找到了窗口对象就可以把消息处理的任务交给CWnd::WindowProc了(调用pWnd- >WindowProc)。

下面就是消息映射了
其实这就简单了,因为这时只需关注CWnd::WindowProc和消处理函数(如onCreate)了。在MFC中定义了几个宏:DECLARE_MESSAGE_MAP、BEGIN_MESSAGE_MAP、END_MESSAGE_MAP等,其实把这几个宏换回来就很好理解了。为了便于理解,我把这些宏简化一下:
//
typedef struct _MSGMAP_ENTRY
{
UINT nMessage; //消息
void (CWnd::*pfn)(); //消息处理函数据
}MSGMAP_ENTRY;

DECLARE_MESSAGE_MAP相当于
static MSGMAP_ENTRY _MessageEntry[]; //定义了一个映射表

BEGIN_MESSAGE_MAP、END_MESSAGE_MAP和两者之间的宏相当于
MSGMAP_ENTRY CWnd::_MessageEntry[] =
{
{WM_CREATE, &onCreate}, //第一个消息映射
{WM_CLOSE, &onClose}, //第二个消息映射
{0, 0} //消息映射结尾
};

CWnd::WindowProc之不过是在_MessageEntry[]查找有没有定义的消息,如有,则调用相应的处理函数,如没有则调用CWnd::DefWindowProc

还想提一下Delphi中的相关处理,Delphi是不是用了同样的方法呢?答案是否定的,Delphi用汇编语句把类的非静态成员函数的地址赋给lpfnWndProc,这个也很有意思,当然用C++也可这么做。

 

对于传递函做个解释如下:

AfxWndProc()     
该函数负责接收消息,找到消息所属的CWnd对象,然后调用AfxCallWndProc

AfxCallWndProc() 
该函数负责保存消息(保存的内容主要是消息标识符和消息参数)供应用程序以后使用,
然后调用WindowProc()函数

WindowProc()     
该函数负责发送消息到OnWndMsg()函数,如果未被处理,则调用DefWindowProc()函数

OnWndMsg()       
该函数的功能首先按字节对消息进行排序,
对于WM_COMMAND消息,调用OnCommand()消息响应函数,
对于WM_NOTIFY消息调用OnNotify()消息响应函数。
任何被遗漏的消息将是一个窗口消息。
OnWndMsg()函数搜索类的消息映像,以找到一个能处理任何窗口消息的处理函数。
如果OnWndMsg()函数不能找到这样的处理函数的话,则把消息返回到WindowProc()函数,
由它将消息发送给DefWindowProc()函数

OnCommand()      
该函数查看这是不是一个控件通知
(lParam参数不为NULL,如果lParam参数为空的话,说明该消息不是控件通知),
如果它是,OnCommand()函数会试图将消息映射到制造通知的控件;
如果他不是一个控件通知(或者如果控件拒绝映射的消息)OnCommand()就会调用OnCmdMsg()函数

OnNotify()也试图将消息映射到制造通知的控件;
如果映射不成功,OnNotify()就调用相同的OnCmdMsg()函数

OnCmdMsg()       
根据接收消息的类,
OnCmdMsg()函数将在一个称为命令传递(Command Routing)的过程中潜在的传递命令消息和控件通知。
例如:如果拥有该窗口的类是一个框架类,
则命令和通知消息也被传递到视图和文档类,并为该类寻找一个消息处理函数

 

分享到:
评论

相关推荐

    实训1vc++6.0创建窗口

    描述:本实训将指导大家如何使用 VC++6.0 创建一个简单的窗口程序,涵盖从项目创建到窗口显示的整个过程。 标签:VC++6.0 创建窗口 知识点: 1. 创建项目:在 VC++6.0 中,创建一个新的项目,选择 Win32...

    VC++窗口类创建

    "VC++窗口类创建"这个主题主要涉及如何使用Microsoft Visual C++来开发基于Windows的应用程序,尤其是通过WinMain函数和消息循环来实现窗口的初始化和交互。 首先,我们需要了解Windows程序的基本结构。Windows应用...

    vc++6.0创建窗口代码(win32 application)

    窗口创建后,我们需要进入消息循环,处理来自系统和用户的输入。`GetMessage()`函数用于获取消息,`TranslateMessage()`用于翻译消息,`DispatchMessage()`用于分发消息到对应的窗口过程。示例代码如下: ```cpp ...

    VC++6.0 创建不规则窗口

    本教程将针对初学者,详细讲解如何使用VC++6.0来创建一个不规则形状的窗口,这是一种在用户界面上实现独特视觉效果的技术。 首先,创建不规则窗口的关键在于自定义窗口过程(Window Procedure)。在Windows API中,...

    VC++ 窗口分割几个窗口

    总的来说,实现VC++窗口分割涉及MFC的MDI机制、CSplitterWnd控件的使用以及适当的事件处理。通过理解这些核心概念和技术,开发者可以构建出功能丰富的多窗口应用程序,满足用户对多任务处理的需求。在实际项目中,还...

    VC++窗口编写代码

    4. **窗口创建(CreateWindowEx)**:使用窗口类信息,通过`CreateWindowEx`函数创建窗口实例。例如: ```cpp HWND hWnd = CreateWindowEx( 0, // 窗口扩展样式 className, // 窗口类名 "我的窗口", // 窗口...

    VC++窗口制作与显示结果

    总之,VC++窗口制作涉及窗口类的设计、消息处理和用户交互的实现。通过阅读和分析提供的程序代码,你可以深入了解这个过程,并掌握在VC++环境下创建功能丰富的GUI应用程序的关键技能。同时,实践是学习的最佳方式,...

    VC++ 实现可任意停靠的吸附窗口

    在VC++编程中,创建一个可以任意停靠的吸附窗口是一项常见的需求,尤其在开发桌面应用软件时,如音乐播放器、系统监控工具等。这个技术可以让用户自定义界面布局,提升用户体验。以下是对实现这一功能的关键知识点的...

    VC++ 窗口任意部分完全透明

    总结一下,实现VC++窗口任意部分完全透明的关键步骤包括: 1. 设置窗口的WS_EX_LAYERED扩展样式。 2. 使用`SetLayeredWindowAttributes`设定透明度和颜色键。 3. 自定义窗口消息处理,特别是在处理WM_PAINT消息时,...

    VC++窗口切割

    至此,我们已经详细介绍了如何在VC++环境中创建和固定窗口切割的过程。这个过程涵盖了MFC的基本概念,如对话框、视图类、分栏窗口以及消息处理。理解并掌握这些知识,对于开发复杂的Windows应用程序是非常重要的。

    怎样在VC++中创建基于SDI多框架多视图

    在软件开发过程中,特别是在使用Visual C++(VC++)开发复杂的应用程序时,开发者经常会遇到这样的情况:需要在一个程序中集成多个具有不同功能模块的界面。这些模块可能涉及到的数据处理方式和流程差异较大。为了更...

    VC++ 窗口的出现和关闭不同的显示风格

    总的来说,理解和掌握VC++窗口的显示风格及其使用方法,对于开发出用户友好且功能丰富的应用程序至关重要。通过灵活应用这些技术,开发者能够创建出满足不同需求的窗口,提升应用程序的整体体验。

    VC++6.0 自学简单window窗口程序(窗口创建、消息处理)

    首先,让我们了解窗口创建的过程。在Windows编程中,窗口是用户界面的基本元素。创建一个窗口需要以下几个步骤: 1. **定义窗口类**:窗口类包含了关于窗口的各种属性,如窗口类名、图标、背景刷等。在VC++6.0中,...

    vc++实现非窗口类中使用定时器的方法

    创建定时器后,系统会在指定的时间间隔发送`WM_TIMER`消息到指定的窗口过程。 对于窗口类来说,使用定时器非常直观简单。通常情况下,程序员会利用`SetTimer()`函数设置一个定时器,并且通过重写`CWnd`类中的`...

    VC++6.0窗口绑架(强行改变父窗口)

    总结来说,"VC++6.0窗口绑架(强行改变父窗口)"是一个关于Windows编程中改变窗口父子关系的技术,它涉及到跨进程操作和`SetParent`函数的使用,可以在多文档界面或Win32 API中实现。在实际操作时,需要充分了解其潜在...

    VC++动态创建控件

    总结来说,VC++动态创建控件是一个涉及多个步骤的过程,包括使用API函数创建控件,关联消息处理函数以响应用户操作,调整控件属性以满足各种需求,以及在适当时候回收资源以保持程序效率和稳定性。理解并熟练掌握...

    VC++窗口任意拆分(文档讲解+源码)

    综上所述,学习并掌握CSplitterWnd类是提升VC++窗口应用开发能力的关键一步。通过实践和研究提供的“使用VC6_0实现窗口的任意分割”资料,开发者可以深入了解这一功能,并将其应用到自己的项目中,创建出更加灵活和...

    VC++获得主窗口指针的方法

    总的来说,VC++中获取主窗口指针的过程涉及窗口类的创建、注册、窗口的创建以及消息循环的运行。理解并熟练掌握这些步骤,对于进行Windows API编程和VC++应用开发至关重要。同时,主窗口句柄的管理也是确保程序正常...

    VC++动态切分窗口的源码实现

    在VC++中,窗口的创建、更新和销毁等操作都通过消息来驱动。当用户与窗口交互,如拖动分隔线,Windows会发送特定的消息到窗口过程函数(WndProc)。我们需要重载WndProc并处理这些消息,比如WM_NCLBUTTONDOWN、WM_...

    VC++自绘制窗口标题栏、窗口背景的类

    在VC++编程中,自定义窗口的外观是提高应用程序独特性和用户体验的重要手段。标题栏和窗口背景的自绘制能够使程序界面更加个性化和专业。本文将深入探讨如何使用VC++来实现这一功能,特别是如何自绘制窗口标题栏、...

Global site tag (gtag.js) - Google Analytics