MFC内部组织原理结构

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 {
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 {
 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())
20      {
21          if (pThread->m_pMainWnd != NULL)
22          {
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
31 InitFailure:
32 #ifdef _DEBUG
33      //  Check for missing AfxLockTempMap calls
34      if (AfxGetModuleThreadState()->m_nTempMapLock !=  0)
35      {
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 {
 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))
22      {//此处调用的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,  0sizeof(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.     AfxGetApp和AfxGetThread 返回的都是theApp对象.

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

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

你可能感兴趣的:(MFC内部组织原理结构)