关闭

MFC学习步步高03

955人阅读 评论(0) 收藏 举报

 本人同意他人对我的文章引用,但请在引用时注明出处,谢谢.作者:蒋志强 

我们接着上次学习的方面继续.为了便于查看,我们把上次分析的MFC代码贴过来:

int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
   LPTSTR lpCmdLine, 
int nCmdShow)
{    ASSERT(hPrevInstance == NULL);

    
int nReturnCode = -1;
    CWinThread
* pThread = AfxGetThread();
    CWinApp
* pApp = AfxGetApp();

    
// AFX internal initialization
    if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
        
goto InitFailure;

    
// App global initializations (rare)
    if (pApp != NULL && !pApp->InitApplication())
        
goto InitFailure;

    
// Perform specific initializations
    if (!pThread->InitInstance())
    
{
        
if (pThread->m_pMainWnd != NULL)
        
{
            TRACE0(
"Warning: Destroying non-NULL m_pMainWnd ");
            pThread
->m_pMainWnd->DestroyWindow();
        }

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

    nReturnCode 
= pThread->Run();

InitFailure:
#ifdef _DEBUG
    
// Check for missing AfxLockTempMap calls
    if (AfxGetModuleThreadState()->m_nTempMapLock != 0)
    
{
        TRACE1(
"Warning: Temp map lock count non-zero (%ld). ",
            AfxGetModuleThreadState()
->m_nTempMapLock);
    }

    AfxLockTempMaps();
    AfxUnlockTempMaps(
-1);
#endif

    AfxWinTerm();
    
return nReturnCode;
}

 

pThread 和CWinApp指针指向同一个CWinApp对象后,程序执行AfxWinInit进行一些初始化工作,这个函数微软没有给我们提供源代码.所以没有办法进去看内部实现.然后程序执行InitApplication和InitInstance.

InitApplication执行应用程序只执行一次的初始化工作,InitApplication是CWinApp中的方法,没有被我们自己的CWinApp子类重载,所以执行CWinApp中的InitApplication,而且该方法微软也没有提供源代码给我们.下面执行pThread的InitInstance方法,由于InitInstance是虚函数,被CWinApp类重载,又被我们自己的CWinApp子类重载,所以程序实际上执行我们自己工程中的CWinApp的子类的InitInstance方法.我们将着重看一下这个方法.因为这个方法执行以后,程序下一步就执行消息循环了.

 nReturnCode = pThread->Run();这个语句就是进入程序的消息循环.因为CWinThread类中的Run就是做的消息接受和分发工作,见下面的源代码:

int CWinThread::Run()
{
    ASSERT_VALID(
this);

    
// for tracking the idle time state
    BOOL bIdle = TRUE;
    LONG lIdleCount 
= 0;

    
// acquire and dispatch messages until a WM_QUIT message is received.
    for (;;)
    
{
        
// phase1: check to see if we can do idle work
        while (bIdle &&
            
!::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE))
        
{
            
// call OnIdle while in bIdle state
            if (!OnIdle(lIdleCount++))
                bIdle 
= FALSE; // assume "no idle" state
        }


        
// phase2: pump messages while available
        do
        
{
            
// pump message, but quit on WM_QUIT
            if (!PumpMessage())
                
return ExitInstance();

            
// reset "no idle" state after pumping "normal" message
            if (IsIdleMessage(&m_msgCur))
            
{
                bIdle 
= TRUE;
                lIdleCount 
= 0;
            }


        }
 while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));
    }


    ASSERT(FALSE);  
// not reachable
}

当Run方法返回的时候,程序就结束了.

现在我们来自己看看InitInstance具体做的事情:

BOOL CMfcstudy001App::InitInstance()
{
    AfxEnableControlContainer();

    
// Standard initialization
    
// If you are not using these features and wish to reduce the size
    
//  of your final executable, you should remove from the following
    
//  the specific initialization routines you do not need.

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

    
// Change the registry key under which our settings are stored.
    
// TODO: You should modify this string to be something appropriate
    
// such as the name of your company or organization.
    SetRegistryKey(_T("Local AppWizard-Generated Applications"));

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

    
// Register the application's document templates.  Document templates
    
//  serve as the connection between documents, frame windows and views.

    CSingleDocTemplate
* pDocTemplate;
    pDocTemplate 
= new CSingleDocTemplate(
        IDR_MAINFRAME,
        RUNTIME_CLASS(CMfcstudy001Doc),
        RUNTIME_CLASS(CMainFrame),       
// main SDI frame window
        RUNTIME_CLASS(CMfcstudy001View));
    AddDocTemplate(pDocTemplate);

    
// 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;

    
// The one and only window has been initialized, so show and update it.
    m_pMainWnd->ShowWindow(SW_SHOW);
    m_pMainWnd
->UpdateWindow();

    
return TRUE;
}

我们看到代码中new CSingleDocTemplate这个语句.它非常的重要.在单文档(多文档也类似的)MFC程序中,CDocument,CView,CMainFrameWnd结合在一起,组织成文档/视图结构.

文档/视图 结构是MFC设计者 帮助VC程序员构建清晰程序结构所做的努力.该结构把程序的数据部分放在CDocuemnt类中,然后CView类根据CDocument类中的数据做相应的显示处理.这样将数据与数据表示,这样程序结构的逻辑就会很清晰.CView是显示的窗口,在其上面还有一个CMainFrameWnd窗口,该窗口充当的是CView的容器作用.MFC使用CDocumentTemplate(在我们的单文档程序中就是CSingleDocumentTemplate)将CDocument,CView,CMainFrameWnd组织起来.这个CDocumentTemplate的对象被CDocManager所管理,CDocManager将该CDocumentTemplate加入到CDocManager的文档模板列表中.CWinApp中有一个CDocManager成员,就是使用该CDocManager成员维护一个文档模板队列,并在合适的时候使用该文档模版列表中的文档模板来动态创建该文档模板所关联的CMainFrameWnd和CDocument,CView类的对象.关于如何使用文档模版来动态创建,涉及到RTTI(RunTime Type Indentificaion动态类型识别)和Dynamic Creation(动态创建),我们以后再详细讲.我们现在知道,MFC的CDocManager可以使用文档模板做这样的事情就行了.

创建了CSingleDocumentTemplate对象时,使用了RUNTIME_CLASS这个宏.这个宏是这样定义的

#define RUNTIME_CLASS(class_name) ((CRuntimeClass*)(&class_name::class##class_name))

所以在new CSingleDocumentTemplate的时候,传的是CRuntimeClass类型的指针,CRuntimeClass是MFC中用来完成动态类型识别和动态对象创建工作的一个类,我们在以后详细讲解.我们知道用它传给文档模板,然后文档模版有了CMainFrameWnd,CView,CDocument对应的CRuntimeClass,然后CWinApp调用AddDocTemplate(pDocTemplate)方法,CWinApp的该方法实际上是去直接调用内部CDocManager对象成员的AddDocTemplate方法,就把模版加进去了.

程序走到这里后,下面调用ProcessShellCommand方法,该方法调用的时候,就是我们之前说到的,CDocManager会在"合适的时候"使用文档模版来动态创建CMainFrameWnd,CView,CDocument对象.

在调用ProcessShellCommand的时候,就是我们所谓的"合适的时候",这个时候窗口,文档,视图都被动态创建了,并且通过文档模板关联在一起.因为之前我们提到程序执行的初始化操作,就已经做了窗口类的注册这些操作.所以这个时候就可以直接创建窗口.究竟是怎么关联在一起的,究竟是怎么动态创建的,在Run方法中的消息循环中,得到的消息怎么分发到各个类中的?这是我们以后再详细探讨的问题.
动态创建窗口后,就是我们在SDK编程中熟悉的ShowWindow,UpDataWindow了.从InitInstance函数返回后,就是进入CWinThread的Run方法了.在这里程序进入消息循环.Run方法返回时,就是消息循环结束,程序退出.

好了,这次我们就先到这里.我们再复习一下我们了解到的知识:我们知道了MFC的大致程序流程,我们知道了MFC如何隐藏WinMain函数的,MFC在进入WinMain函数前创建了什么东西,知道了在MFC的WinMain中使用什么方式构建文档/视图结构的程序,CWinApp中的CDocManager的工作,知道了消息循环在哪里进行的.

我们以后要了解的是文档/视图结构具体是怎么工作的,Run方法中的消息是怎么分配到各个类中的,CDocManager是怎么实现动态创建对象的,CDocManager创建文档/视图对象的合适时候具体是何时,动态类型识别和动态类型创建是怎么做的,各个类的消息映射是怎么实现的.

To be continued......

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:91076次
    • 积分:1106
    • 等级:
    • 排名:千里之外
    • 原创:29篇
    • 转载:0篇
    • 译文:1篇
    • 评论:88条
    最新评论