mfc流程执行,theApp初始化,DECLARE_DYNAMIC和m_pMainWnd

来源:互联网 发布:js bind方法带参数 编辑:程序博客网 时间:2024/05/22 12:47

    每次创建一个MFC应用程序,不管是Doc/View类型的,还是对话框类型的,都会发现MFC会自动为我们创建一个CXXXApp的类,其中XXX表示我们的工程名。其实很多时候,我们都不会用到这个类,也不会在其中添加属性和方法,而是创建我们自己的类。那么MFC为什么会生成这个类呢?下面我以简单的MFC对话框程序为例子,谈一下自己的理解。

    利用Visual Studio 2010创建一个MFC对话框应用项目Test,VisualStudio会为我们创建CTestApp、CTestDlg两个类。很好理解CTestDlg这个类,它就是我要创建的对话框的类,可以在资源管理器中查看并编辑对话框的属性,以及在CTestDlg中重写对话框的初始化函数、处理对话框消息等。但是CTestApp是干什么的呢?

    CTestApp中其实就定义了一个构造函数和一个InitInstance函数,构造函数也基本上没有做任何事情,所以InitInstance应该是最重要的。打开InitInstance函数,其中最重要的功能就是定义一个对话框CTestDlg的对象实例,然后显示这个对话框。无论是Win32程序还是Console程序,都会有一个入口函数(主函数),WinMain或是main,那么在生成的Test项目中,我们根本找不到主函数的踪影,那是不是MFC程序不需要入口函数呢?肯定不是。

    Win32程序的WinMain函数中,需要注册窗口类、创建窗口后,Windows窗口才能够显示出来。CTestApp继承自MFC的CWinApp类,CWinApp中定义了InitApplication、InitInstance两个方法,其中,InitApplication是针对每个应用程序执行一次的初始化函数,其中就包括注册窗口类、设置窗口的窗口函数等属性。InitInstance函数式针对每个程序实例执行一次的初始化函数,但它并没有创建窗口,而是通过虚函数的形式将创建窗口的操作交由子类CTestApp去实现。通过CWinApp我们不难看出,实际上,MFC将Win32程序的初始化操作封装到CWinApp中去实现了。

    那么WinMain函数呢?通过单步调试,可以在appmodul.cpp文件中发现_tWinMain函数,它就是WinMain的函数。_tWinMain函数会调用AfxWinMain函数,在AfxWinMain函数中可以发现CWinApp的踪迹了,它通过AfxGetApp获得一个CWinApp的指针,然后调用它的InitApplication以及InitInstance函数。由于通过CWinApp的指针调用的都是虚函数,所以我们无法断定它究竟调用的哪一个具体类的实现,这就要通过AfxGetApp函数才能知道了。AfxGetApp的调用路径如下:

    AfxGetApp ——> afxCurrentWinApp ——> AfxGetModuleState()->m_pCurrentWinApp ——> _afxThreadState->m_pModuleState

    _afxThreadState是由宏EXTERN_THREAD_LOCAL(class_name, ident_name)传入参数_AFX_THREAD_STATE和_afxThreadState定义的一个全局的外部变量:extern CThreadLocal<class_name> ident_name;,即_afxThreadState是一个CThreadLocal对象。该类有一个成员变量pModuleState指向AFX_MODULE_STATE对象,而AFX_MODULE_STATE的成员就包括一个CWinApp指针。那么这个CWinApp指针是什么时候设置的呢?

    由于_afxThreadState是一个全局对象,所以它必定会在WinMain函数执行前就已经构造出来了。研究CWinApp的代码发现,在它的构造函数中,会通过_AFX_CMDTARGET_GETSTATE宏调用AfxGetModuleState函数,它得到的就是一个_afxThreadState对象,之后,CWinApp通过pModuleState->m_pCurrentWinApp = this;语句将这个全局对象的成员指向了自己。

    接下来的问题是,什么时候构造了CWinApp对象呢?MFC在工程一创建时就定义了CTestApp类,继承自CWinApp,并定义了全局变量CTestApp theApp,由于theApp也是全局对象,所以它的构造也必定是在WinMain函数之前完成的。这样,在theApp构造时会调用父类CWinApp的构造函数,将全局CWinApp指针指向了自己。

    由于虚函数的特性,WinMain中通过CWinApp指针调用的InitApplication和InitInstance函数,实际上是由CTestApp实现的,而CTestApp只继承了InitInstance函数,所以InitApplication仍然是CWinApp的默认实现。之后就如我们所见,CTestApp的InitInstance函数中,创建对话框并显示对话框了。通过改写InitInstance函数,我们就能够创建不同的窗口并显示了。

    因此,theApp对象的作用是事关全局的,它起到了初始化整个应用程序的功能,并将所有复杂的操作都通过MFC框架封装起来,而我们只需要在InitInstance函数中去创建自己关心的窗口就好了。





 

DECLARE_DYNAMIC和IMPLEMENT_DYNAMIC宏

分类: VC++ 12829人阅读 评论(11) 收藏 举报
classpascalstructmacrosnullc

    IMPLEMENT_DYNAMIC是实现“运行时类型识别”宏,与之相对应的是DECLARE_DYNAMIC(声明“运行时类型识别”宏)。也就是说你在.CPP文件中如果看见有IMPLEMENT_DYNAMIC,则在.H文件中必定有DECLARE_DYNAMIC的声明。 
DECLARE_DYNAMIC/DEClARE_DYNAMIC是为了确定运行时对象属于哪一个类而定义的宏。 
DEClARE_DYNCREATE/IMPLEMENT_DYNCREATE是为了“动态创建"类的实例而定义的宏。new可以用来创建对象,但不是动态的。比如说,你要在程序中实现根据拥护输入的类名来创建类的实例,下面的做法是通不过的: 
char szClassName[60]; 
cin >> szClassName; 
CObject* pOb=new szClassName; //通不过 
这里就要用到DEClARE_DYNCREATE/IMPLEMENT_DYNCREATE定义的功能了。

 

定义:

 

//////////////////////////////////////////////////////////////////////////////
// Helper macros for declaring CRuntimeClass compatible classes

#ifdef _AFXDLL
#define DECLARE_DYNAMIC(class_name) /
protected: /
    static CRuntimeClass* PASCAL _GetBaseClass(); /
public: /
    static const CRuntimeClass class##class_name; /
    static CRuntimeClass* PASCAL GetThisClass(); /
    virtual CRuntimeClass* GetRuntimeClass() const; /

#define _DECLARE_DYNAMIC(class_name) /
protected: /
    static CRuntimeClass* PASCAL _GetBaseClass(); /
public: /
    static CRuntimeClass class##class_name; /
    static CRuntimeClass* PASCAL GetThisClass(); /
    virtual CRuntimeClass* GetRuntimeClass() const; /

#else
#define DECLARE_DYNAMIC(class_name) /
public: /
    static const CRuntimeClass class##class_name; /
    virtual CRuntimeClass* GetRuntimeClass() const; /

#define _DECLARE_DYNAMIC(class_name) /
public: /
    static CRuntimeClass class##class_name; /
    virtual CRuntimeClass* GetRuntimeClass() const; /

#endif

 

 

引用:

 

DECLARE_DYNAMIC/IMPLEMENT_DYNAMIC

#define DECLARE_DYNAMIC(class_name)/    
public:/
    static CRuntimeClass class##class_name;/
    //声明一个类型为CRuntimeClass的静态public成员变量,变量名是由字符串"class"
    //与所指定的类的类名组成。举例而言,如果你写DECLARE_DYNAMIC(CMyView),则等于声明了一个
    // static CRuntimeClass classCMyView静态变量

    virtual CRuntimeClass* GetRuntimeClass() const;/
    //声明一个虚函数,函数名为GetRuntimeClass,返回值为CRuntimeClass类型的指针
    //无参数,并且是个const函数

#define IMPLEMENT_DYNAMIC(class_name,bass_class_name)/
       _IMPLEMENT_RUNTIMECLASS(class_name,base_class_name,0xFFFF,NULL)

#define _IMPLEMENT_RUNTIMECLASS(class_name,base_class_name,wSchema,pfnNew)/
    static char _lpsz##class_name[]= #class_name;/
    //定义一个C类型字符串静态变量,变量名由"_lpsz"和指定类的类名组成,变量值为该指定类型的名字
    //比如是CMyView,那么定义的就是static char _lpszCMyView="CMyView";

    CRuntimeClass class_name::class##class_name = {/
        _lpsz##class_name,sizeof(class_name),wSchema,pfnNew,/
            RUNTIME_CLASS(base_class_name),NULL};/
    //给之前在DECLARE_DYNAMIC里定义的CRuntimeClass类型的静态成员变量赋值
    //当然,除最后一个m_pNextClass没有赋值(赋值为NULL,它由下面的结构处理)

    static AFX_CLASSINIT _init_##class_name(&class_name::class##class_name);/
    //初始化一个名为"_init_##class_name"的AFX_CLASSINIT静态结构,主要作用是给指定的class_name的
    //class##class_name静态变量的最后一个成员m_pNextClass赋值,具体见下面解释AFX_CLASSINIT中

    CRuntimeClass* class_name::GetRuntimeClass() const/
        { return &class_name::class##class_name;}/
    //之前在DECLARE_DYNAMIC里定义的GetRuntimeClass的实现,很简单,就一个return语句。

#define RUNTIME_CLASS(class_name)/
        (&class_name::class##class_name)
//这部分之所以单独define出一个宏,主要是为了方便从某个指定的class直接得到它的CRuntimeclass静态成员

//以下是解释AFX_CLASSINIT结构,注意,这不是一个宏
//为了看得更加清楚,我按照struct定义的惯常格式来写这个struct的定义
struct AFX_CLASSINIT {
    AFX_CLASSINIT(CRuntimeClass *pNewClass);
};

AFX_CLASSINIT::AFX_CLASSINIT(CRuntimeClass *pNewClass)
{
    pNewClass->m_pNextClass = CRuntimeClass::pFirstClass;
    //让m_pNextClass指向pFirstClass所指的CRuntimeClass变量

    CRuntimeClass::pFirstClass = pNewClass;
    //让pFirstClass指向pNewClass所指的变量,也就是本class的CRuntimeClass静态变量
}











找了很多地方都没有找到单文档的初始化,今天我自己来找。。

 

工具

: UltraEdit, VS2005, AJC Grep 1.1.1 试用版,

 

参考文献

:深入浅出MFC

 

目标

:找到m_pMainWnd的赋值的地方

 

m_pMainWnd

定义于

 

C:/Program Files/Microsoft Visual Studio .NET 2003/Vc7/atlmfc/include/afxwin.h

 

class

CWinThread : public CCmdTarget

 

{

 

    CWnd* m_pMainWnd;      

// main window (usually same AfxGetApp()->m_pMainWnd)

 

}

 

 

 

绪:

 

我的单文档DEMO

 

BOOL CtestApp::InitInstance()

 

{

 

      

 

        InitCommonControls();

 

 

 

        CWinApp::InitInstance();

 

 

 

        AfxEnableControlContainer();

 

 

 

        CSingleDocTemplate* pDocTemplate;

 

        pDocTemplate = new CSingleDocTemplate(

 

                IDR_MAINFRAME,

 

                RUNTIME_CLASS(CtestDoc),

 

                RUNTIME_CLASS(CMainFrame),       //  SDI 框架窗口

 

                RUNTIME_CLASS(CtestView));

 

        AddDocTemplate(pDocTemplate);

 

    

 

        CCommandLineInfo cmdInfo;

 

        ParseCommandLine(cmdInfo);

 

        ProcessShellCommand(cmdInfo);

 

 

 

        // 唯一的一个窗口已初始化因此显示它并对其进行更新

 

        m_pMainWnd->ShowWindow(SW_SHOW);

 

        m_pMainWnd->UpdateWindow();

 

        // 仅当存在后缀时才调用 DragAcceptFiles

 

        //   SDI 应用程序中这应在 ProcessShellCommand  之后发生

 

        return TRUE;

 

}

 

 

 

 

 

MFC的主函数

 

Microsoft Visual Studio .NET 2003/Vc7/atlmfc/src/mfc/winmian.cpp

 

int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPTSTR lpCmdLine, int nCmdShow)

 

{

 

        int nReturnCode = -1;

 

        CWinThread* pThread = AfxGetThread();

 

        CWinApp* pApp = AfxGetApp();

 

        AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow); 

 

        pApp->InitApplication();

 

        pThread->InitInstance();

 

        nReturnCode = pThread->Run();

 

        AfxWinTerm();

 

        return nReturnCode;

 

}

 

 

 

上面构造了 pThread ,pApp;其中pApp指向theApp

 

AfxGetApp AfxWin1.inl

 

AfxGetThreadAfxWin.h中定义实现于

C:/Program Files/Microsoft Visual Studio .NET 2003/Vc7/atlmfc/src/mfc/thrdcore.cpp

 

 

 

为了简化过程,上面的代码中的判断语句被我删掉了!

 

 

 

开始

 

第一步:

 

搜索到 m_pMainWnd 的赋值的地方

 

C:/Program Files/Microsoft Visual Studio .NET 2003/Vc7/atlmfc/src/mfc/docsingl.cpp

 

(1.1)CDocument* CSingleDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName, BOOL bMakeVisible)

 

{

 

                ............

 

        if (bCreated && pThread->m_pMainWnd == NULL)

 

        {

 

                // set as main frame (InitialUpdateFrame will show the window)

 

               

pThread->m_pMainWnd = pFrame;

 

        }

 

......................

 

 }

 

C:/Program Files/Microsoft Visual Studio .NET 2003/Vc7/atlmfc/src/mfc/thrdcore.cpp

 

(1.2)UINT APIENTRY _AfxThreadEntry(void* pParam)

 

{  

      

 

 

if (pApp != NULL &&pThread->m_pMainWnd == NULL && pApp->m_pMainWnd->GetSafeHwnd() != NULL)

 

   {

 

           // just attach the HWND

 

           threadWnd.Attach(pApp->m_pMainWnd->m_hWnd);

 

          

pThread->m_pMainWnd = &threadWnd;

 

    }

 

}

 

初步估计可能是在(1.1)中初始化。猜测理由初始化的时候可能需要打开一个默认的空文档。

 

第二步:

 

2.1 查找OpenDocumentFile调用的地方,

 

如果我的猜测方向没有错的话,那么在搜索到的文件中,应该是void CDocManager::OnFileNew()中调用的

 

void CDocManager::OnFileNew()

 

{

 

         CDocTemplate* pTemplate = (CDocTemplate*)m_templateList.GetHead();

 

         CNewTypeDlg dlg(&m_templateList);

 

         INT_PTR nID = dlg.DoModal();

 

         if (nID == IDOK)

 

                   pTemplate = dlg.m_pSelectedTemplate;

 

         else

 

                   return;     // none - cancel operation

 

        

 

        

pTemplate->OpenDocumentFile(NULL);

 

                   // if returns NULL, the user has already been alerted

 

}

 

上述函数位于C:/Program Files/Microsoft Visual Studio .NET 2003/Vc7/atlmfc/src/mfc/docmgr.cpp

 

 

 

2.2 接着找OnFileNew的调用地方。

 

这个可能比较多,具体什么地方调用的我还不敢估计;先全部找出来在说!

 

呵呵,很幸运,我在MFC目录下搜索,只有两个文件调用过这个函数;

 

C:/Program Files/Microsoft Visual Studio .NET 2003/Vc7/atlmfc/src/mfc/apppdlg.cpp

 

void CWinApp::OnFileNew()

 

{

 

        if (m_pDocManager != NULL)

 

              

 m_pDocManager->OnFileNew();

 

}

 

 

 

BOOL CWinApp::ProcessShellCommand(CCommandLineInfo& rCmdInfo)

 

{

 

        BOOL bResult = TRUE;

 

        switch (rCmdInfo.m_nShellCommand)

 

        {

 

        case CCommandLineInfo::FileNew:

 

                if (!AfxGetApp()->OnCmdMsg(ID_FILE_NEW, 0, NULL, NULL))

 

                     

  OnFileNew();

 

                if (m_pMainWnd == NULL)

 

                        bResult = FALSE;

 

                break;

 

 

 

                // If we've been asked to open a file, call OpenDocumentFile()

 

 

 

        case CCommandLineInfo::FileOpen:

 

                if (!OpenDocumentFile(rCmdInfo.m_strFileName))

 

                        bResult = FALSE;

 

                break;

 

……...…………………………………………………………………..

 

}

 

OK ,找到了,CWinApp::ProcessShellCommand调用CWinApp::OnFileNewCWinApp::OnFileNew调用

CDocManager::OnFileNew()

 

 

 

现在找到了什么地方初始化的m_pMainWnd,但是这个消息循环是怎么调用的呢?难道初始化的时候,一开始CCommandLineInfo::FileNew:就有这个消息?

 

 

 

2.3 再接再厉

 

C:/Program Files/Microsoft Visual Studio .NET 2003/Vc7/atlmfc/include/afxwin.h中有这个定义

 

enum { FileNew, FileOpen, FilePrint, FilePrintTo, FileDDE, AppRegister,AppUnregister, FileNothing = -1 } m_nShellCommand;

 

 

 

C:/Program Files/Microsoft Visual Studio .NET 2003/Vc7/atlmfc/include/appcore.cpp中有如下定义:

 

CCommandLineInfo

的构造函数如下:

 

CCommandLineInfo::CCommandLineInfo()

 

{

 

        m_bShowSplash = TRUE;

 

        m_bRunEmbedded = FALSE;

 

        m_bRunAutomated = FALSE;

 

        m_nShellCommand = FileNew;

 

}

 

 

 

void CWinApp::ParseCommandLine(CCommandLineInfo& rCmdInfo)

 

{

 

        for (int i = 1; i < __argc; i++)

 

        {

 

                LPCTSTR pszParam = __targv[i];

 

                BOOL bFlag = FALSE;

 

                BOOL bLast = ((i + 1) == __argc);

 

                if (pszParam[0] == '-' || pszParam[0] == '/')

 

                {

 

                        // remove flag specifier

 

                        bFlag = TRUE;

 

                        ++pszParam;

 

                }

 

                rCmdInfo.ParseParam(pszParam, bFlag, bLast);

 

        }

 

}

 

 

 

OK,搞定了,在初始化的时候,就把rCmdInfo.m_nShellCommand的值赋为FileNew,那么在调用的时候就很自然的执行Switch中的第一个case了哈。

 

于是调用CWinApp::OnFileNew,在调用CDocManager::OnFileNew(),接着调用pTemplate->OpenDocumentFile(NULL);最后在这个函数中初始化了m_pMainWnd找到了!!!

新手路上,不知道这个是不是对的,还望高手指点!