VC++中窗口创建的流程

来源:互联网 发布:数学编程 编辑:程序博客网 时间:2024/05/17 22:38
 一、什么是窗口类 
  在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函数中可以对窗口进行操作。
*******************************一、什么是窗口类 
  在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函数中可以对窗口进行操作。
*******************************