目录
1. MFC向导生成类
2. 框架流程
2.1 WinMain
2.2 全局对象:theApp
2.3 AfxWinMain函数
1.AfxWinMain:
2.AfxGetThread函数(thrdcore.cpp):
3.AfxGetApp是一个全局函数,定义于(afxwin1.inl)中:
2.4 InitInstance函数(Test.h)
2.5 窗口类的设计
2.6 窗口类的注册
2.7 窗口创建
2.8 显示窗口和更新窗口
1. MFC向导生成类
MFC库是开发Windows应用程序的C++接口。MFC提供了面向对象的框架,程序开发人员可以基于这一框架开发Windows应用程序。MFC采用面向对象设计,将大部分的Windows API封装到C++类中,以类成员函数的形式提供给程序开发人员调用。
下面看一下MFC应用程序向导帮助生成的这些代码:
单击Visual Studio菜单栏上的【视图】菜单,单击【类视图】菜单项,这将打开类视图子窗口,方便查看项目中的类组织结构。在“类视图”中展开Test根节点,可以看到项目中所有的五个类,如图所示:
提示: 如果要查看某个类的成员信息,可以直接选中某个类,就会在下方的窗格中显示该类拥有的函数和属性。双击跳转到定义类的头文件或源文件。
对于一个单文档应用程序(即我们在创建项目时第二步应用程序类型中选择的“单个文档”),都有一个CMainFrame类、一个以“C+项目名+App”为名字的类、一个以“C+项目名+Doc”为名字的类和一个以“C+项目名+View”为名字的类。
五个类都有一个基类,这些基类都是MFC中的类。
2. 框架流程
创建Win32应用程序
进入 WinMain 函数、
设计窗口类、
注册窗口类、
产生窗口、
显示窗口、
更新窗口,
最后进入消息循环,将消息路由给窗口过程函数去处理。
但在当前项目在当前Test项目中查找不到WinMain函数:
MFC 源代码的具体路径(可作参考):
C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Tools\MSVC\14.16.27023\atlmfc\src\mfc 可以用vscode打开,查找WinMain
2.1 WinMain
在 appmodul.cpp中
extern "C" int WINAPI
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine, int nCmdShow)
#pragma warning(suppress: 4985)
{
// call shared/exported WinMain
return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}
2.2 全局对象:theApp
加上断点时,发现 CTestApp 构造在WinMain函数之前调用,是因为程序中定义了一个CTestApp类型的全局对象:theApp。
提示: MFC程序的全局变量都放置在类视图窗口中的“全局函数和变量”分支下,单击该分支即可看到程序当前所有的全局函数和变量。双击某个全局变量,即可定位到该变量的定义处。
// CTestApp:
// 有关此类的实现,请参阅 Test.cpp
//
class CTestApp : public CWinApp
{
public:
CTestApp() noexcept;
// 重写
public:
virtual BOOL InitInstance();
virtual int ExitInstance();
// 实现
afx_msg void OnAppAbout();
DECLARE_MESSAGE_MAP()
};
extern CTestApp theApp;
程序执行的顺序:theApp全局对象定义处、CTestApp构造函数、WinMain函数。
在程序入口main函数加载时,系统就已经为全局变量或全局对象分配了存储空间,并为它们赋了初始值。
应用程序的实例是由实例句柄(WinMain函数的参数hInstance)来标识的。而对 MFC 程序来说,通过产生一个应用程序类的对象来唯一标识应用程序的实例。每一个MFC程序有且仅有一个从应用程序类(CWinApp)派生的类。每一个MFC程序实例有且仅有一个该派生类的实例化对象,也就是theApp全局对象。该对象就表示了应用程序本身。
当一个子类在构造之前会先调用其父类的构造函数。因此 theApp 对象的构造函数 CTestApp 在调用之前,会调用其父类CWinApp的构造函数,从而就把我们程序自己创建的类与Microsoft提供的基类关联起来了。CWinApp的构造函数完成程序运行时的一些初始化工作。
CWinApp::CWinApp(LPCTSTR lpszAppName)
{
if (lpszAppName != NULL)
m_pszAppName = _tcsdup(lpszAppName);
else
m_pszAppName = NULL;
// initialize CWinThread state
AFX_MODULE_STATE* pModuleState = _AFX_CMDTARGET_GETSTATE();
ENSURE(pModuleState);
AFX_MODULE_THREAD_STATE* pThreadState = pModuleState->m_thread;
ENSURE(pThreadState);
ASSERT(AfxGetThread() == NULL);
pThreadState->m_pCurrentWinThread = this;
ASSERT(AfxGetThread() == this);
m_hThread = ::GetCurrentThread();
m_nThreadID = ::GetCurrentThreadId();
// initialize CWinApp state
ASSERT(afxCurrentWinApp == NULL); // only one CWinApp object please
pModuleState->m_pCurrentWinApp = this;
ASSERT(AfxGetApp() == this);
// in non-running state until WinMain
m_hInstance = NULL;
m_hLangResourceDLL = NULL;
m_pszHelpFilePath = NULL;
m_pszProfileName = NULL;
m_pszRegistryKey = NULL;
m_pszExeName = NULL;
m_pszAppID = NULL;
m_pRecentFileList = NULL;
m_pDocManager = NULL;
m_atomApp = m_atomSystemTopic = NULL;
m_lpCmdLine = NULL;
m_pCmdInfo = NULL;
m_pDataRecoveryHandler = NULL;
// initialize wait cursor state
m_nWaitCursorCount = 0;
m_hcurWaitCursorRestore = NULL;
// initialize current printer state
m_hDevMode = NULL;
m_hDevNames = NULL;
m_nNumPreviewPages = 0; // not specified (defaults to 1)
// initialize DAO state
m_lpfnDaoTerm = NULL; // will be set if AfxDaoInit called
// other initialization
m_bHelpMode = FALSE;
m_eHelpType = afxWinHelp;
m_nSafetyPoolSize = 512; // default size
m_dwRestartManagerSupportFlags = 0; // don't support Restart Manager by default
m_nAutosaveInterval = 5 * 60 * 1000; // default autosave interval is 5 minutes (only has effect if autosave flag is set)
m_bTaskbarInteractionEnabled = TRUE;
// Detect the kind of OS:
OSVERSIONINFO osvi;
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
// Fix for warnings when building against WinBlue build 9444.0.130614-1739
// warning C4996: 'GetVersionExW': was declared deprecated
// externalapis\windows\8.1\sdk\inc\sysinfoapi.h(442)
// Deprecated. Use VerifyVersionInfo* or IsWindows* macros from VersionHelpers.
#pragma warning( disable : 4996 )
::GetVersionEx(&osvi);
#pragma warning( default : 4996 )
m_bIsWindows7 = (osvi.dwMajorVersion == 6) && (osvi.dwMinorVersion >= 1) || (osvi.dwMajorVersion > 6);
// Taskbar initialization:
m_bComInitialized = FALSE;
m_pTaskbarList = NULL;
m_pTaskbarList3 = NULL;
m_bTaskBarInterfacesAvailable = TRUE;
}
CWinApp的构造函数中有这样两行代码:
pThreadState->m_pCurrentWinThread = this;
pModuleState->m_pCurrentWinApp = this;
m_pCurrentWinThread对象的类型是CWinThread,该类是CWinApp的父类。根据C++继承性原理,这个this对象代表的是子类CTestApp的对象,即theApp。
CWinApp的构造函数有一个LPCTSTR类型的形参:lpszAppName,但是CTestApp的构造函数没有参数。
如果基类的构造函数带有一个形参,那么子类构造函数需要显式地调用基类带参数的构造函数。
如果某个函数的参数有默认值,那么在调用该函数时可以传递该参数的值,也可以不传递,直接使用默认值即可。
在CWinApp 类名上单击鼠标右键,利用【转到定义】命令,定位到CWinApp类的定义处,代码下:CWinApp构造函数的形参确实有一个默认值(NULL)。这样,在调用CWinApp类的构造函数时,就不用显式地去传递这个参数的值。
class CWinApp : public CWinThread
{
DECLARE_DYNAMIC(CWinApp)
public:
// Constructor
explicit CWinApp(LPCTSTR lpszAppName = NULL); // app name defaults to EXE name
...
}
2.3 AfxWinMain函数
知识点 :因为c++不是完全面向对象的函数,为了让各个类有机地结合在一起,就要定义一些全局函数。全局函数(在每个类中都可以使用),应用程序框架函数。Afx前缀的函数代表应用程序框架(Application Framework)函数。应用程序框架实际上是一套辅助我们生成应用程序的框架模型。该模型把多个类进行了一个有机的集成,可以根据该模型提供的方案来设计我们自己的应用程序。在 MFC 中,以Afx为前缀的函数都是全局函数,可以在程序的任何地方调用它们。
winmain.cpp
int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
_In_ 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)
{
TRACE(traceAppMsg, 0, "Warning: Destroying non-NULL m_pMainWnd\n");
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)
{
TRACE(traceAppMsg, 0, "Warning: Temp map lock count non-zero (%ld).\n",
AfxGetModuleThreadState()->m_nTempMapLock);
}
AfxLockTempMaps();
AfxUnlockTempMaps(-1);
#endif
AfxWinTerm();
return nReturnCode;
}
1.AfxWinMain:
首先调用AfxGetThread函数获得一个CWinThread类型的指针。接着调用AfxGetApp函数获得一个CWinApp类型的指针。MFC类库组织结构图中可以知道CWinApp派生于CWinThread。
CWinThread* pThread = AfxGetThread();
CWinApp* pApp = AfxGetApp();
2.AfxGetThread函数(thrdcore.cpp):
位置:C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Tools\MSVC\14.16.27023\atlmfc\src\mfc
CWinThread* AFXAPI AfxGetThread()
{
// check for current thread in module thread state
AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();
CWinThread* pThread = pState->m_pCurrentWinThread;
return pThread;
}
if (pApp != NULL && !pApp->InitApplication())
AfxGetThread函数返回的就是在CWinApp构造函数中保存的this指针(如下代码)。对Test程序来说,这个this指针实际上指向的是CTestApp的全局对象:theApp。
连接:CWinApp的构造函数中有这样两行代码:
pThreadState->m_pCurrentWinThread = this;
pModuleState->m_pCurrentWinApp = this;
3.AfxGetApp是一个全局函数,定义于(afxwin1.inl)中:
位置:C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Tools\MSVC\14.16.27023\atlmfc\include
_AFXWIN_INLINE CWinApp* AFXAPI AfxGetApp()
{ return afxCurrentWinApp; }
afxCurrentWinApp,定义于(afxwin.h*)中,代码如下:
#define afxCurrentWinApp AfxGetModuleState()->m_pCurrentWinApp
AfxGetApp函数返回的是在CWinApp构造函数中保存的this指针。对Test程序来说,这个this指针实际上指向的是CTestApp的对象:theApp。也就是说,对Test程序来说,pThread和pApp所指向的都是CTestApp类的对象,即theApp全局对象。
2.4 InitInstance函数(Test.h)
pApp首先调用InitApplication函数,该函数完成MFC内部管理方面的工作。接着,调用pThread的InitInstance函数。在Test程序中,可以发现从CWinApp派生的应用程序类CTestApp也有一个InitInstance函数,其声明代码如下:
virtual BOOL InitInstance();
从其定义(virtual)可以知道,InitInstance函数是一个虚函数。根据类的多态性原理,可以知道AfxWinMain函数在这里调用的实际上是子类CTestApp的InitInstance函数。CTestApp类的InitInstance函数定义代码如下所示:
BOOL CTestApp::InitInstance()
{
// 如果一个运行在 Windows XP 上的应用程序清单指定要
// 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式,
//则需要 InitCommonControlsEx()。 否则,将无法创建窗口。
INITCOMMONCONTROLSEX InitCtrls;
InitCtrls.dwSize = sizeof(InitCtrls);
// 将它设置为包括所有要在应用程序中使用的
// 公共控件类。
InitCtrls.dwICC = ICC_WIN95_CLASSES;
InitCommonControlsEx(&InitCtrls);
CWinApp::InitInstance();
// 初始化 OLE 库
if (!AfxOleInit())
{
AfxMessageBox(IDP_OLE_INIT_FAILED);
return FALSE;
}
AfxEnableControlContainer();
EnableTaskbarInteraction(FALSE);
// 使用 RichEdit 控件需要 AfxInitRichEdit2()
// AfxInitRichEdit2();
// 标准初始化
// 如果未使用这些功能并希望减小
// 最终可执行文件的大小,则应移除下列
// 不需要的特定初始化例程
// 更改用于存储设置的注册表项
// TODO: 应适当修改该字符串,
// 例如修改为公司或组织名
SetRegistryKey(_T("应用程序向导生成的本地应用程序"));
LoadStdProfileSettings(4); // 加载标准 INI 文件选项(包括 MRU)
// 注册应用程序的文档模板。 文档模板
// 将用作文档、框架窗口和视图之间的连接
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CTestDoc),
RUNTIME_CLASS(CMainFrame), // 主 SDI 框架窗口
RUNTIME_CLASS(CTestView));
if (!pDocTemplate)
return FALSE;
AddDocTemplate(pDocTemplate);
// 分析标准 shell 命令、DDE、打开文件操作的命令行
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
// 调度在命令行中指定的命令。 如果
// 用 /RegServer、/Register、/Unregserver 或 /Unregister 启动应用程序,则返回 FALSE。
if (!ProcessShellCommand(cmdInfo))
return FALSE;
// 唯一的一个窗口已初始化,因此显示它并对其进行更新
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
return TRUE;
}
2.5 窗口类的设计
窗口类的注册(wincore.cpp):AfxEndDeferRegisterClass 函数
在InitInstance中是通过ProcessShellCommand这个函数完成窗口的设计,注册,等操作
注册窗口是通过调用AfxEndDeferRegisterClass完成注册操作注册窗口时在正常情况下:是通过先调用子类PrecreateWindow调用父类PrecreateWindow再在调用AfxEndDeferRegisterClass来注册,如果是单文档(SDI)就会先执行一次AfxEndDeferRegisterClass注册,
ProcessShellCommand()——>OnCmdMsg()——>AfxDispatchCmdMsg()——>pfn_Command()——>OnFileNew()——>OpenDocumentFile()——>CreateNewFrame()——>LoadFrame()
|
BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister)
{
// mask off all classes that are already registered
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
fToRegister &= ~pModuleState->m_fRegisteredClasses;
if (fToRegister == 0)
return TRUE;
LONG fRegisteredClasses = 0;
// common initialization
WNDCLASS wndcls;
memset(&wndcls, 0, sizeof(WNDCLASS)); // start with NULL defaults
wndcls.lpfnWndProc = DefWindowProc;
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_WND_REG)
{
// Child windows - no brush, no icon, safest default class styles
wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
wndcls.lpszClassName = _afxWnd;
if (AfxRegisterClass(&wndcls))
fRegisteredClasses |= AFX_WND_REG;
}
if (fToRegister & AFX_WNDOLECONTROL_REG)
{
// OLE Control windows - use parent DC for speed
wndcls.style |= CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
wndcls.lpszClassName = _afxWndOleControl;
if (AfxRegisterClass(&wndcls))
fRegisteredClasses |= AFX_WNDOLECONTROL_REG;
}
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_WNDMDIFRAME_REG)
{
// MDI Frame window (also used for splitter window)
wndcls.style = CS_DBLCLKS;
wndcls.hbrBackground = NULL;
if (_AfxRegisterWithIcon(&wndcls, _afxWndMDIFrame, AFX_IDI_STD_MDIFRAME))
fRegisteredClasses |= AFX_WNDMDIFRAME_REG;
}
if (fToRegister & AFX_WNDFRAMEORVIEW_REG)
{
// SDI Frame or MDI Child windows or views - normal colors
wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
wndcls.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
if (_AfxRegisterWithIcon(&wndcls, _afxWndFrameOrView, AFX_IDI_STD_FRAME))
fRegisteredClasses |= AFX_WNDFRAMEORVIEW_REG;
}
if (fToRegister & AFX_WNDCOMMCTLS_REG)
{
// this flag is compatible with the old InitCommonControls() API
init.dwICC = ICC_WIN95_CLASSES;
fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WIN95CTLS_MASK);
fToRegister &= ~AFX_WIN95CTLS_MASK;
}
if (fToRegister & AFX_WNDCOMMCTL_UPDOWN_REG)
{
init.dwICC = ICC_UPDOWN_CLASS;
fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_UPDOWN_REG);
}
if (fToRegister & AFX_WNDCOMMCTL_TREEVIEW_REG)
{
init.dwICC = ICC_TREEVIEW_CLASSES;
fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_TREEVIEW_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);
}
if (fToRegister & AFX_WNDCOMMCTL_LISTVIEW_REG)
{
init.dwICC = ICC_LISTVIEW_CLASSES;
fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_LISTVIEW_REG);
}
if (fToRegister & AFX_WNDCOMMCTL_HOTKEY_REG)
{
init.dwICC = ICC_HOTKEY_CLASS;
fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_HOTKEY_REG);
}
if (fToRegister & AFX_WNDCOMMCTL_BAR_REG)
{
init.dwICC = ICC_BAR_CLASSES;
fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_BAR_REG);
}
if (fToRegister & AFX_WNDCOMMCTL_ANIMATE_REG)
{
init.dwICC = ICC_ANIMATE_CLASS;
fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_ANIMATE_REG);
}
if (fToRegister & AFX_WNDCOMMCTL_INTERNET_REG)
{
init.dwICC = ICC_INTERNET_CLASSES;
fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_INTERNET_REG);
}
if (fToRegister & AFX_WNDCOMMCTL_COOL_REG)
{
init.dwICC = ICC_COOL_CLASSES;
fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_COOL_REG);
}
if (fToRegister & AFX_WNDCOMMCTL_USEREX_REG)
{
init.dwICC = ICC_USEREX_CLASSES;
fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_USEREX_REG);
}
if (fToRegister & AFX_WNDCOMMCTL_DATE_REG)
{
init.dwICC = ICC_DATE_CLASSES;
fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_DATE_REG);
}
if (fToRegister & AFX_WNDCOMMCTL_LINK_REG)
{
init.dwICC = ICC_LINK_CLASS;
fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_LINK_REG);
}
if (fToRegister & AFX_WNDCOMMCTL_PAGER_REG)
{
init.dwICC = ICC_PAGESCROLLER_CLASS;
fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_PAGER_REG);
}
// save new state of registered controls
pModuleState->m_fRegisteredClasses |= fRegisteredClasses;
// special case for all common controls registered, turn on AFX_WNDCOMMCTLS_REG
if ((pModuleState->m_fRegisteredClasses & AFX_WIN95CTLS_MASK) == AFX_WIN95CTLS_MASK)
{
pModuleState->m_fRegisteredClasses |= AFX_WNDCOMMCTLS_REG;
fRegisteredClasses |= AFX_WNDCOMMCTLS_REG;
}
// must have registered at least as many classes as requested
return (fToRegister & fRegisteredClasses) == fToRegister;
}
2.6 窗口类的注册
AfxRegisterClass函数,注册窗口类:
AfxRegisterClass函数首先获得窗口类信息。如果该窗口类已经注册,则直接返回一个真值;如果尚未注册,就调用RegisterClass函数注册该窗口类。
// like RegisterClass, except will automatically call UnregisterClass
// 和RegisterClass一样,except会自动调用UnregisterClass
BOOL AFXAPI AfxRegisterClass(WNDCLASS* lpWndClass)
{
WNDCLASS wndcls;//获得窗口类信息
if (GetClassInfo(lpWndClass->hInstance, lpWndClass->lpszClassName,
&wndcls))
{
// class already registered
return TRUE;//如果该窗口类已经注册,则直接返回一个真值
}
if (!RegisterClass(lpWndClass))//如果尚未注册,就调用RegisterClass函数注册该窗口类
{
TRACE(traceAppMsg, 0, _T("Can't register window class named %Ts\n"),
lpWndClass->lpszClassName);
return FALSE;
}
BOOL bRet = TRUE;
if (afxContextIsDLL)
{
AfxLockGlobals(CRIT_REGCLASSLIST);
TRY
{
// class registered successfully, add to registered list
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
pModuleState->m_strUnregisterList+=lpWndClass->lpszClassName;
pModuleState->m_strUnregisterList+='\n';
}
CATCH_ALL(e)
{
AfxUnlockGlobals(CRIT_REGCLASSLIST);
THROW_LAST();
// Note: DELETE_EXCEPTION not required.
}
END_CATCH_ALL
AfxUnlockGlobals(CRIT_REGCLASSLIST);
}
return bRet;
}
MFC 应用程序 Test,实际上有两个窗口。其中一个是CMainFrame类(MainFrm.h)的对象所代表的应用程序框架窗口,该类有一个PreCreateWindow函数,这是在窗口产生之前被调用的。该函数的默认实现代码如下所示。
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if( !CFrameWnd::PreCreateWindow(cs) )
return FALSE;
// TODO: 在此处通过修改
// CREATESTRUCT cs 来修改窗口类或样式
return TRUE;
}
该函数首先调用CFrameWnd的PreCreateWindow函数。后者的定义位于源文件winfrm.cpp中,代码下。
// CFrameWnd second phase creation
// CFrameWnd第二阶段创建
BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs)
{
if (cs.lpszClass == NULL)
{
VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));
cs.lpszClass = _afxWndFrameOrView; // COLOR_WINDOW background
}
if (cs.style & FWS_ADDTOTITLE)
cs.style |= FWS_PREFIXTITLE;
cs.dwExStyle |= WS_EX_CLIENTEDGE;
return TRUE;
}
// 该函数中调用了AfxDeferRegisterClass函数,读者可以在afximpl.h文件中找到后者的定义,定义代码如下: AfxDeferRegisterClass函数(afximpl.h):
#define AfxDeferRegisterClass(fClass) AfxEndDeferRegisterClass(fClass)
2.7 窗口创建
在MFC程序中,窗口的创建功能是由 CWnd 类的 CreateEx 函数实现的,该函数的声明位于afxwin.h文件中,具体代码如下所示。CreateEx 函数(wincore.cpp)定义:
BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,
LPCTSTR lpszWindowName, DWORD dwStyle,
int x, int y, int nWidth, int nHeight,
HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam)
{
ASSERT(lpszClassName == NULL || AfxIsValidString(lpszClassName) ||
AfxIsValidAtom(lpszClassName));
ENSURE_ARG(lpszWindowName == NULL || AfxIsValidString(lpszWindowName));
// allow modification of several common create parameters
CREATESTRUCT cs;
cs.dwExStyle = dwExStyle;
cs.lpszClass = lpszClassName;
cs.lpszName = lpszWindowName;
cs.style = dwStyle;
cs.x = x;
cs.y = y;
cs.cx = nWidth;
cs.cy = nHeight;
cs.hwndParent = hWndParent;
cs.hMenu = nIDorHMenu;
cs.hInstance = AfxGetInstanceHandle();
cs.lpCreateParams = lpParam;
if (!PreCreateWindow(cs))
{
PostNcDestroy();
return FALSE;
}
AfxHookWindowCreate(this);
HWND hWnd = CreateWindowEx(cs.dwExStyle, cs.lpszClass,
cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,
cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);
#ifdef _DEBUG
if (hWnd == NULL)
{
TRACE(traceAppMsg, 0, "Warning: Window creation failed: GetLastError returns 0x%8.8X\n",
GetLastError());
}
#endif
if (!AfxUnhookWindowCreate())
PostNcDestroy(); // cleanup if CreateWindowEx fails too soon
if (hWnd == NULL)
return FALSE;
ASSERT(hWnd == m_hWnd); // should have been set in send msg hook
return TRUE;
}
在MFC底层代码中,CFrameWnd类的Create函数内部调用了上述CreateEx函数。而前者又由CFrameWnd类的LoadFrame函数调用。Create函数(winfrm.cpp)定义:
BOOL CFrameWnd::Create(LPCTSTR lpszClassName,
LPCTSTR lpszWindowName,
DWORD dwStyle,
const RECT& rect,
CWnd* pParentWnd,
LPCTSTR lpszMenuName,
DWORD dwExStyle,
CCreateContext* pContext)
{
HMENU hMenu = NULL;
if (lpszMenuName != NULL)
{
// load in a menu that will get destroyed when window gets destroyed
HINSTANCE hInst = AfxFindResourceHandle(lpszMenuName, ATL_RT_MENU);
if ((hMenu = ::LoadMenu(hInst, lpszMenuName)) == NULL)
{
TRACE(traceAppMsg, 0, "Warning: failed to load menu for CFrameWnd.\n");
PostNcDestroy(); // perhaps delete the C++ object
return FALSE;
}
}
m_strTitle = lpszWindowName; // save title for later
if (!CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle,
rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
pParentWnd->GetSafeHwnd(), hMenu, (LPVOID)pContext))
{
TRACE(traceAppMsg, 0, "Warning: failed to create CFrameWnd.\n");
if (hMenu != NULL)
DestroyMenu(hMenu);
return FALSE;
}
return TRUE;
}
CFrameWnd类派生于CWnd类,根据类的继承性原理,CFrameWnd类就继承了CWnd类的CreateEx函数。因此,CFrameWnd类的Create函数内调用的实际上就是CWnd类的CreateEx函数。
2.8 显示窗口和更新窗口
Test程序的应用程序类TestApp从它的基类CWinThread继承了一个名为m_pMainWnd的成员变量(公有成员)。该变量是一个 CWnd 类型的指针,它保存了应用程序框架窗口对象的指针。也就是说,是指向 CMainFrame 对象的指针。在 CTestApp 类的 InitInstance函数实现内部有如下两行代码。
// 调度在命令行中指定的命令。 如果
// 用 /RegServer、/Register、/Unregserver 或 /Unregister 启动应用程序,则返回 FALSE。
if (!ProcessShellCommand(cmdInfo))
return FALSE;//此时窗口创建完成
// 唯一的一个窗口已初始化,因此显示它并对其进行更新
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
return TRUE;
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
这两行代码的功能是显示应用程序框架窗口和更新这个窗口。