孙鑫VC++第三章 2.基于MFC的程序框架剖析

目录

 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()

多次调用PreCreateWindow、CreateEx、AfxEndDeferRegisterClass 注册工具栏、状态栏等.

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

该函数首先调用CFrameWndPreCreateWindow函数。后者的定义位于源文件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();

这两行代码的功能是显示应用程序框架窗口和更新这个窗口。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
孙鑫vc是一种特殊的混合编程语言,它结合了C语言和Verilog语言的特点。在深入详解孙鑫vc代码之前,我们先了解一下它的一些特性。 首先,孙鑫vc具有高度的可定制性。用户可以根据自己的需求选择C语言和Verilog语言中的特性来编写代码。这种灵活性使得孙鑫vc可以适用于不同的应用领域。 其次,孙鑫vc支持并行计算。它提供了一种简单而有效的方式来利用硬件资源进行并行计算,提高程序的执行效率。 另外,孙鑫vc还具有强大的调试功能。它能够在运行时对代码进行监控和调试,帮助开发者快速定位问题并进行修复。 深入详解孙鑫vc代码包括以下几个方面: 首先,我们可以从代码的结构和组织方式入手。孙鑫vc代码一般由多个模块组成,每个模块包含了各自的功能和接口。 其次,我们需要了解代码中使用的变量和数据类型。在孙鑫vc中,可以使用C语言和Verilog语言中的数据类型,如整型、浮点型等。了解这些数据类型的使用方法和限制对理解代码非常重要。 然后,我们需要分析代码中的控制流和算法。这包括了代码中的条件语句、循环语句等,以及算法的实现细节。通过对控制流和算法的分析,我们可以更好地理解代码的逻辑和实现原理。 最后,我们还需要关注代码中的接口和数据传输方式。在孙鑫vc中,模块之间通过接口进行数据的传递和交互。了解接口的定义和使用方式对于理解代码的功能和模块之间的关系非常重要。 综上所述,深入详解孙鑫vc代码需要从代码的结构和组织方式、变量和数据类型、控制流和算法、接口和数据传输方式等多个方面进行分析和理解。通过对这些方面的研究,我们可以更好地理解孙鑫vc代码,并且能够对代码进行修改和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值