上一页 下一页 返回

3.4 应用程序执行机制

3.4.1 WinMain函数

  在DOS下,程序的执行是从main函数开始的。在Windows下,对应的函数是WinMain。但是,如果浏览Hello程序的所有的方法和全局函数,是找不到WinMain函数的。MFC考虑到典型的Windows程序需要的大部分初始化工作都是标准化的,因此把WinMain函数隐藏在应用程序的框架中,编译时会自动将该函数链接到可执行文件中。程序员可以重写WinMain函数,但一般不需要这么做。
  下面的程序清单3-1给出了WinMain函数的代码。其中,_tWinMain函数在\DevStudio\Vc\Mfc\src\AppModul.cpp中定义,它所调用的AfxWinMain函数在同一目录下的WinMain.cpp中定义。名字是_tWinMain函数而不是WinMain,是考虑到对不同字符集的支持,在tchar.h中有_tWinMain的宏定义。在ANSI字符集下编译时,_tWinMain就变成WinMain,在Unicode下编译时,_tWinMain就变成wWinMain。

提示:Unicode是具有固定宽度、统一的文本和字符的编码标准。由于Unicode采用的是16位编码,因此可以包含世界各地的书写系统的字符和技术符号(如中文也在Unicode之中),从而克服了ASCII码在表示多语言文本上的不足之处,扩大了ASCII码7位编码方案的好处。Unicode同等地对待所有的字符,并且在表示各种语言的任何字符时既不需要换码序列(escape)也不需要控制代码。Win32和Visual C++很好的支持Unicode字符集。

清单3-1 _tWinMain函数定义

// export WinMain to force linkage to this module

extern int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,

LPTSTR lpCmdLine, int nCmdShow);

 

#ifdef _MAC

extern "C" int PASCAL

#else

extern "C" int WINAPI

#endif

_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,

LPTSTR lpCmdLine, int nCmdShow)

{

// call shared/exported WinMain

return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);

}

 

AfxWinMain函数定义:

/////////////////////////////////////////////////////////////////////////////

// Standard WinMain implementation

// Can be replaced as long as 'AfxWinInit' is called first

 

int AFXAPI AfxWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,

LPTSTR lpCmdLine, int nCmdShow)

{

ASSERT(hPrevInstance == NULL);

 

int nReturnCode = -1;

CWinApp* pApp = AfxGetApp();

 

// AFX internal initialization

if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))

goto InitFailure;

 

// App global initializations (rare)

ASSERT_VALID(pApp);

if (!pApp->InitApplication())

goto InitFailure;

ASSERT_VALID(pApp);

 

// Perform specific initializations

if (!pApp->InitInstance())

{

if (pApp->m_pMainWnd != NULL)

{

TRACE0("Warning: Destroying non-NULL m_pMainWnd\n");

pApp->m_pMainWnd->DestroyWindow();

}

nReturnCode = pApp->ExitInstance();

goto InitFailure;

}

ASSERT_VALID(pApp);

 

nReturnCode = pApp->Run();

ASSERT_VALID(pApp);

 

InitFailure:

#ifdef _DEBUG

// Check for missing AfxLockTempMap calls

if (AfxGetModuleThreadState()->m_nTempMapLock != 0)

{

TRACE1("Warning: Temp map lock count non-zero (%ld).\n",

AfxGetModuleThreadState()->m_nTempMapLock);

}

AfxLockTempMaps();

AfxUnlockTempMaps(-1);

#endif

 

AfxWinTerm();

return nReturnCode;

}

  应用程序执行时,Windows自动调用应用程序框架内部的WinMain函数。如清单3-1所示,WinMain函数会查找该应用程序的一个全局构造对象,这个对象是由CWinApp派生类构造的,有且只有一个。它是一个全局对象,因此在程序启动时,它就已经被构造好了。
  随后,WinMain将调用这个对象的InitApplication和InitInstance成员函数,完成应用程序实例的初
始化工作。随后,WinMain调用Run成员函数,运行应用程序的消息循环。在程序结束时,WinMain调用AfxWinTerm函数,做一些清理工作。

3.4.2 应用程序类

  每个应用程序必须从CWinApp派生出自己的应用程序类,并定义一个全局的对象。该应用程序类包含了Windows下应用程序的初始化、运行和结束过程。基于框架建立的应用程序必须有一个(且只能有一个)从CWinApp派生的类的对象。在Hello程序中,我们从CWinApp中派生出一个CHelloApp类,并定义了一个全局对象theApp。CHelloApp类在hello.cpp中定义。
  要访问应用程序类构造的对象,可以调用全局函数AfxGetApp()。AfxGetApp()返回一个指向全局对象的指针。可以通过对它进行强制类型转换,转换为我们派生的应用程序类。

比如:

CHelloApp* pApp=(CHelloApp*)AfxGetApp();

  在CHelloApp应用程序类中,我们还重载了CWinApp的成员函数InitInstance。InitInstance函数主要完成以下工作:设置注册数据库,载入标准设置(最近打开文件列表等)、注册文档模板。其中注册文档模板过程中隐含地创建了主窗口。接着,处理命令行参数,显示窗口,然后返回、进入消息循环。下面的程序清单3.2给出了Hello程序的InitInstance函数代码。

 

清单3.2 InitInstance函数

// CHelloApp initialization

 

BOOL CHelloApp::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.

// 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(CHelloDoc),

RUNTIME_CLASS(CMainFrame), // main SDI frame window

RUNTIME_CLASS(CHelloView));

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;

}

  在CWinApp的派生类中,必须重载InitInstance函数,因为CWinApp并不知道应用程序需要什么样的窗口,它可以多文档窗口、单文档窗口,也可以是基于对话框的。

Run成员函数
  
WinMain在初始化应用程序实例后,就调用Run函数来处理消息循环。Run成员函数不断执行消息循环,检查消息队列中有没有消息。如果有消息,Run将其派遣,交由框架去处理,然后返回继续消息循环。如果没有消息,Run将调用OnIdle来做用户或框架可能需要在空闲时才做的工作,象后面我们讲到的用户接口更新消息处理等。如果既没有消息要处理,也没有空闲时的处理工作要做,则应用程序将一直等待,直到有事件发生。当应用程序结束时,Run将调用ExitInstance。消息循环的流程图如图3-10所示。

T3_10.tif (197232 bytes)

图3-10 Run成员函数的消息循环

 

关闭应用程序

    用户可以通过选择File-Exit菜单或点主窗口的关闭按钮,关闭主框架窗口,来终止应用程序。此时,应用程序类首先删除m_pMainWnd主框架窗口对象,然后退出Run函数,进而退出WinMain,在退出WinMain后删除TheApp对象。