第三课:MFC内部组织原理结构

申明:本文是我看了孙鑫老师的VC++视频教程后的总结,相当于学习笔记吧,纯粹是为了方便自己。同时也希望能对大家学习vc++有所帮助。至于文中的内容如果有什么错误或不           妥,欢迎指出。欢迎加我的QQ:124561376进行讨论,共同学习。

由于这是笔记,所以阅读起来可能有点凌乱,所以详细请看“孙鑫老师的VC++视频教程”,

1.  无论是用win32 SDK编写的应用程序还是MFC应用程序,程序的入口都是WinMain;在MFC中程序的入口函数为:_tWinMain,这个函数被微软隐藏在源文件文件:AppModule.cpp中。

  为了能找到MFC的运行流程的脉络,我们可以让程序处于调试模式,单步跟踪程序的运行状况。下面首先列举出将要跟踪的函数和类及其所在的源文件中。

        函数————————————————>所在源文件

        —————————————>

        AfxWinMain—————————————>WinMain.cpp

        CWinApp类—————————————>AppCode.cpp

        CreateEx——————————————>WinCode.cpp(调用API函数CreateWindowEx,进行窗口创建,他是CreateWindow的扩展函数,同是API函数)

        AfxEndDeferRegisterClass——————>WinCode.cpp(注册窗口类)

        AfxDeferRegisterClass————————>AfxIMPL.h(因为这个函数是一个宏,他是在这个头文件中定义的,他实际上就是AfxEndDeferRegisterClass)

        Create————————————————>WinFrm.cpp(调用CreateEx函数)

        CFrameWnd::PreCreateWindow————>WinFrm.cpp(是虚函数。窗体创建前,在子类的PreCreateWindow中可以修改窗体的样式。在窗口创建前做一些准备工作。)

        CFrameWnd::LoadFrame———————>WinFrm.cpp(调用Create函数,创建窗口)

 这些文件的位置在:VC++环境安装位置\Microsoft Visual Studio\VC98\MFC\SRC,进入后可点击搜索——>“文件包含一个词或一个词组”,输入函数类容....

2. 首先创建SDI程序(单文档程序)作为测试程序。每一个MFC程序都有且仅有一个全局变量theApp,他表示当前应用程序模块。

  整个MFC程序的执行顺序大致为:

  首先是全局构造

CObject构造函数à CCmdTarget àCWinThreadàCWinAppà theApp构造函数

然后进入WinMain函数

WinMainàAfxWinMainàAfxWinInitàtheApp.InitApplicationàtheApp.InitInstance

接着执行线程过程。

theApp.Run()

最后清理

AfxWinTerm

我们首先进入文件AppModule.cpp,找到_tWinMain,函数定义为:

代码
1 _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
2     LPTSTR lpCmdLine,  int  nCmdShow)
3 ExpandedBlockStart.gifContractedBlock.gif {
4    // call shared/exported WinMain
5    return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
6}

发现就一个函数:AfxWinMain,我们在进入这个函数。他的主要定义如下:

代码
 1 int  AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
 2     LPTSTR lpCmdLine,  int  nCmdShow)
 3 ExpandedBlockStart.gifContractedBlock.gif {
 4    ASSERT(hPrevInstance == NULL);
 5
 6    int nReturnCode = -1;
 7    CWinThread* pThread = AfxGetThread();//返回的就是指向theApp的指针,以为C***App继承自CWinApp,CWinApp继承自CWinThread
 8    CWinApp* pApp = AfxGetApp();//返回的就是指向theApp的指针
 9
10    // AFX internal initialization
11    if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
12        goto InitFailure;
13
14    // App global initializations (rare)
15    if (pApp != NULL && !pApp->InitApplication())
16        goto InitFailure;
17
18    // Perform specific initializations
19    if (!pThread->InitInstance())
20ExpandedSubBlockStart.gifContractedSubBlock.gif    {
21        if (pThread->m_pMainWnd != NULL)
22ExpandedSubBlockStart.gifContractedSubBlock.gif        {
23            TRACE0("Warning: Destroying non-NULL m_pMainWnd\n");
24            pThread->m_pMainWnd->DestroyWindow();
25        }

26        nReturnCode = pThread->ExitInstance();
27        goto InitFailure;
28    }

29    nReturnCode = pThread->Run();
30
31InitFailure:
32#ifdef _DEBUG
33    // Check for missing AfxLockTempMap calls
34    if (AfxGetModuleThreadState()->m_nTempMapLock != 0)
35ExpandedSubBlockStart.gifContractedSubBlock.gif    {
36        TRACE1("Warning: Temp map lock count non-zero (%ld).\n",
37            AfxGetModuleThreadState()->m_nTempMapLock);
38    }

39    AfxLockTempMaps();
40    AfxUnlockTempMaps(-1);
41#endif
42
43    AfxWinTerm();
44    return nReturnCode;
45}

   在AfxWinMain中,AfxWinInitpApp->InitApplication(),主要做的事情是:初始化应用程序内部的环境,完成MFC自己内部的一些设置等。这个函数和外部没多大干系。应用程序的很多工作都是在pThread->InitInstance()中完成,类比上节课的知识,他主要做的事情是:

  a.设计窗口类:WNDCLASS

  b.注册窗口类:RegisterClass

  c.创建窗口类:CreateWindow

  d.显示窗口类:ShowWindow

  e.更新窗口类:UpdateWindow

然后又到nReturnCode = pThread->Run();这是一个消息循环,处理系统消息。

有上面可以看出,在AfxWinMain中的执行流程和Win32下的流程差不多(第一节课讲的内容)。

3.  我们首先看pThread->InitInstance(),这是由虚函数,而pThread指向的是theApp,所以其处他所调用的是子类C***App类中的函数,C***App的InitInstance函数我们可以修改他,先看看他的Code大体是什么样子的:

 

代码
BOOL CTestApp::InitInstance()
{
    AfxEnableControlContainer()

#ifdef _AFXDLL
    Enable3dControls();             //  Call this when using MFC in a shared DLL
#else
    Enable3dControlsStatic();    
//  Call this when linking to MFC statically
#endif

    SetRegistryKey(_T( " Local AppWizard-Generated Applications " ));

    LoadStdProfileSettings();  
//  Load standard INI file options (including MRU)

 


    CSingleDocTemplate
*  pDocTemplate;
    pDocTemplate 
=   new  CSingleDocTemplate(
        IDR_MAINFRAME,
        RUNTIME_CLASS(CTestDoc),
        RUNTIME_CLASS(CMainFrame),       
//  main SDI frame window
        RUNTIME_CLASS(CTestView));
    AddDocTemplate(pDocTemplate);
// 其处是将Doc类,CMainFrame,CView三个类用文档模板类关联到一起,在装载到代表当前应用程序的全局对象theApp中。

     //  Parse command line for standard shell commands, DDE, file open
    CCommandLineInfo cmdInfo;
    ParseCommandLine(cmdInfo);

    
//  Dispatch commands specified on the command line
     if  ( ! ProcessShellCommand(cmdInfo))
        
return  FALSE;
/* 这个函数CWinApp::ProcessShellCommand主要完成的工作为:
  a.设计窗口类:WNDCLAS
  b.注册窗口类:RegisterClass
  c.创建窗口类:CreateWindow
他不是一个虚函数,而且在CWinThread中没有这个成员函数,所以在其处调用的ProcessShellCommand一定是早已经在CwinApp中定义好的函数,他所应该完成的工作都已经定制好的了。
*/
    
//  The one and only window has been initialized, so show and update it.
    m_pMainWnd -> ShowWindow(SW_SHOW ); // 显示窗口,调用API函数ShowWindow
    m_pMainWnd -> UpdateWindow (); // 更新窗口,调用API函数UpdateWindow

    
return  TRUE;
}

 说明:

对于上面代码中的函数ProcessShellCommand完成的功能,我个人分析是:
  a.设计窗口类:WNDCLAS
  b.注册窗口类:RegisterClass
  c.创建窗口类:CreateWindow
他不是一个虚函数,而且在CWinThread中没有这个成员函数,所以在其处调用的ProcessShellCommand一定是早已经在CwinApp中定义好的函数,他所应该完成的工作都已经定制好的了。
这是我个人的观点,老师在视频中并没有说明。因为这三个工作肯定在C***App::InitInstance()中完成,通过跟踪,发现只可能在这里面完成的。

 

  那么ProcessShellCommand是如何完成这个功能的呢? 首先可以肯定的调用线路是:CFrameWnd::LoadFrame()——>Cwnd::Create()——>Cwnd::CreateEx()——>::CreateWindowEx();其中“——>”表示调用关系。可见创建窗口主要是在CFrameWnd::LoadFrame()中完成的。

另外,我根据老师说明,从函数ProcessShellCommand进入进行跟踪,发现如下的一条线路通向CFrameWnd::LoadFrame(),在其列举出仅供参考,线路如下(其中“——>”表示调用关系):

ProcessShellCommand()——>OnCmd()——>AfxDispatchCmdMsg()——>pfn_Command()——>OFileNew()——>OpenDocumentFile()——>CreateNewFrame()——>LoadFrame()

  在跟踪过程中,我发现系统多次调用PreCreateWindow、CreateEx、AfxEndDeferRegisterClass ,但是那好像都是都是注册工具栏、状态栏等的(实际上工具栏、状态栏等都是窗口),

没有发现注册主窗口,可能是跟丢了...

  那现在看看CreateEx这个函数:

代码
 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 ExpandedBlockStart.gifContractedBlock.gif {
 6    // allow modification of several common create parameters
 7    CREATESTRUCT cs;
 8    cs.dwExStyle = dwExStyle;
 9    cs.lpszClass = lpszClassName;
10    cs.lpszName = lpszWindowName;
11    cs.style = dwStyle;
12    cs.x = x;
13    cs.y = y;
14    cs.cx = nWidth;
15    cs.cy = nHeight;
16    cs.hwndParent = hWndParent;
17    cs.hMenu = nIDorHMenu;
18    cs.hInstance = AfxGetInstanceHandle();
19    cs.lpCreateParams = lpParam;
20
21    if (!PreCreateWindow(cs))
22ExpandedSubBlockStart.gifContractedSubBlock.gif    {//此处调用的PreCreateWindow,事实上调用的是C***App的成员函数,他是虚函数,可以在C***App中被从写,这样可以在窗口被创建前,可以通过结构体CREATESTRUCT更改窗口风格等,
23        PostNcDestroy();
24        return FALSE;
25    }

26
27    AfxHookWindowCreate(this);
28    HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass,//开始创建窗口
29            cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,
30            cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);
31
32    if (!AfxUnhookWindowCreate())
33        PostNcDestroy();        // cleanup if CreateWindowEx fails too soon
34
35    if (hWnd == NULL)
36        return FALSE;
37    ASSERT(hWnd == m_hWnd); // should have been set in send msg hook
38    return TRUE;
39}

 注意上面PreCreateWindow的功能。

我们在看看AfxEndDeferRegisterClass的代码:

代码
BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister)
{
    
//  mask off all classes that are already registered
    AFX_MODULE_STATE *  pModuleState  =  AfxGetModuleState();

    LONG fRegisteredClasses 
=   0 ;

    
//  common initialization
    WNDCLASS wndcls;
    memset(
& wndcls,  0 sizeof (WNDCLASS));    //  start with NULL defaults
    wndcls.lpfnWndProc  =  DefWindowProc; // 是窗口类的窗口过程为系统的默认窗口过程。在MFC中,对于外部用户,已不再使用win32中的消息循环了,用户正真接触到的是消息响应机制。
    wndcls.hInstance  =  AfxGetInstanceHandle();
    wndcls.hCursor 
=  afxData.hcurArrow;

    INITCOMMONCONTROLSEX init;
    init.dwSize 
=   sizeof (init);

    
//  work to register classes as specified by fToRegister, populate fRegisteredClasses as we go
........ 
    if  (fToRegister  &  AFX_WNDCONTROLBAR_REG)
    {
        
//  Control bar windows
        wndcls.style  =   0 ;    //  control bars don't handle double click
        wndcls.lpszClassName  =  _afxWndControlBar;
        wndcls.hbrBackground 
=  (HBRUSH)(COLOR_BTNFACE  +   1 );
        
if  (AfxRegisterClass( & wndcls)) // 注册窗口
            fRegisteredClasses  |=  AFX_WNDCONTROLBAR_REG;
    }
    
if  (fToRegister  &  AFX_WNDCOMMCTL_TAB_REG)
    {
        init.dwICC 
=  ICC_TAB_CLASSES;
        fRegisteredClasses 
|=  _AfxInitCommonControls( & init, AFX_WNDCOMMCTL_TAB_REG);
    }
    
if  (fToRegister  &  AFX_WNDCOMMCTL_PROGRESS_REG)
    {
        init.dwICC 
=  ICC_PROGRESS_CLASS;
        fRegisteredClasses 
|=  _AfxInitCommonControls( & init, AFX_WNDCOMMCTL_PROGRESS_REG);
    }
.......
}

 

接下来的类容就是一些零散的笔记了

1.      在Win32 SDK 或者MFC中,函数后面加"Ex"的一般表示为另一个函数的扩展函数,如CreateWindowEx为CreateWindow的扩展函数.

 

2.     在MFC中定义了一下用于应用程序框架类的全局函数,这些全局函数都是以AFX开头的,在程序中都可以调用.

3.一般我们看到的"一个窗口",实际上他可能有多个窗口组成,如单文档程序,里面的就有两个窗口:CMainFrame和CView.

4.     AfxGetAppAfxGetThread 返回的都是theApp对象.

5.   在MFC程序中当调用的API函数或者全局函数和MFC中的成员函数或者成员变量在名字上有冲突时,可以在API函数或者全局函数前面加上"::",表示他们是API函数或者全局函数;

6.  当一个窗口销毁时,CWnd对象不一定立即被销毁,因为销毁窗口(DestroyWindow())只是将窗口句柄置为NULL,而CWnd对象只是包含了一个窗口句柄,CWnd对象的成员句柄不知为空后,Cwnd对象可能还会存在。

 

转载于:https://www.cnblogs.com/sqzxcv/archive/2009/11/28/1612446.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值