MFC 程序来龙去脉
MFC 程序来龙去脉
——寻找与Win32 App的关联
声明
本文由张杰原创,参考了侯俊杰先生的《深入浅出MFC》,源码摘自 Microsoft Visual Studio 9.0\VC。
个人能力有限,文章必定存在很多错误。我的邮箱是:chinajiezhang@gmail.com chinajiezhang@163.com 欢迎您来邮件斧正。当然您也可以加我 msn: chinazhangjie@hotmail.com 交流。
本文可供传播、交流、学习使用,但请不要用于商业用途。转载请标明此声明,谢谢您的合作。
使用Visual Studio 2008 创建一个单文档工程(对话框和多文档分析方法类似)命名为:FirstMFCDemo。
我们从全局变量开始探索(理由有二:(1)全局变量在主函数之前初始化;(2) FirstMFCDemo 除了类之外其他,类总需要代码来调用吧),在 FirstMFCDemo.h 下有一个全局变量 theApp 类型为:CFirstMFCDemoApp 。查看 CFirstMFCDemoApp 构造函数,构造函数为空函数,于是向其父类 CWinApp 追寻。
CWinApp —— 取代 WinMain 的地位
查看 CWinApp 源码(c:\Program Files\Microsoft Visual Studio 9.0\VC\atlmfc\include\afxwin.h):
class CWinApp : public CWinThread
{
// ……
HINSTANCE m_hInstance;
LPTSTR m_lpCmdLine;
int m_nCmdShow;
LPCTSTR m_pszAppName;
LPCTSTR m_pszRegistryKey;
CDocManager* m_pDocManager;
public:
LPCTSTR m_pszExeName;
LPCTSTR m_pszHelpFilePath;
LPCTSTR m_pszProfileName;
// Hooks for your initialization code
virtual BOOL InitApplication();
// overrides for implementation
virtual BOOL InitInstance();
virtual int Run();
virtual BOOL OnIdle(LONG lCount); // return TRUE if more idle processing
……
};
我们发现几行熟悉的代码:
HINSTANCE m_hInstance;
LPTSTR m_lpCmdLine;
int m_nCmdShow;
和 WinMain 的参数相同。于是我们猜测:
virtual BOOL InitApplication();
virtual BOOL InitInstance();
virtual int Run();
virtual BOOL OnIdle(LONG lCount);
完成了窗口的注册、创建、更新、显示和运行等操作。
再看 CWinApp 父类 CWinThread 源码(c:\Program Files\Microsoft Visual Studio 9.0\VC\atlmfc\include\afxwin.h):
class CWinThread : public CCmdTarget
{
DECLARE_DYNAMIC(CWinThread)
friend BOOL AfxInternalPreTranslateMessage(MSG* pMsg);
public:
// Constructors
CWinThread();
BOOL CreateThread(DWORD dwCreateFlags = 0, UINT nStackSize = 0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);
// Attributes
CWnd* m_pMainWnd; // main window (usually same AfxGetApp()->m_pMainWnd)
CWnd* m_pActiveWnd; // active main window (may not be m_pMainWnd)
BOOL m_bAutoDelete; // enables 'delete this' after thread termination
// only valid while running
HANDLE m_hThread; // this thread's HANDLE
operator HANDLE() const;
DWORD m_nThreadID; // this thread's ID
int GetThreadPriority();
BOOL SetThreadPriority(int nPriority);
// Operations
DWORD SuspendThread();
DWORD ResumeThread();
BOOL PostThreadMessage(UINT message, WPARAM wParam, LPARAM lParam);
// Overridables
// thread initialization
virtual BOOL InitInstance();
// running and idle processing
virtual int Run();
virtual BOOL PreTranslateMessage(MSG* pMsg);
virtual BOOL PumpMessage(); // low level message pump
virtual BOOL OnIdle(LONG lCount); // return TRUE if more idle processing
virtual BOOL IsIdleMessage(MSG* pMsg); // checks for special messages
// thread termination
virtual int ExitInstance(); // default will 'delete this'
// Advanced: exception handling
virtual LRESULT ProcessWndProcException(CException* e, const MSG* pMsg);
// Advanced: handling messages sent to message filter hook
virtual BOOL ProcessMessageFilter(int code, LPMSG lpMsg);
// Advanced: virtual access to m_pMainWnd
virtual CWnd* GetMainWnd();
// Implementation
public:
virtual ~CWinThread();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
void CommonConstruct();
virtual void Delete();
// 'delete this' only if m_bAutoDelete == TRUE
public:
// constructor used by implementation of AfxBeginThread
CWinThread(AFX_THREADPROC pfnThreadProc, LPVOID pParam);
// valid after construction
LPVOID m_pThreadParams; // generic parameters passed to starting function
AFX_THREADPROC m_pfnThreadProc;
// set after OLE is initialized
void (AFXAPI* m_lpfnOleTermOrFreeLib)(BOOL, BOOL);
COleMessageFilter* m_pMessageFilter;
protected:
BOOL DispatchThreadMessageEx(MSG* msg); // helper
void DispatchThreadMessage(MSG* msg); // obsolete
};
m_pMainWnd 记录了创建窗口的句柄,等效于 Win32 App 中的hWnd。
CFrameWnd (CMainFrame的父类)—— 取代 WndProc 的地位
查看 CMainFrame 源码:
class CMainFrame : public CFrameWnd
{
protected: // 仅从序列化创建
CMainFrame();
DECLARE_DYNCREATE(CMainFrame)
public:
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
// 实现
public:
virtual ~CMainFrame();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
protected: // 控件条嵌入成员
CStatusBar m_wndStatusBar;
CToolBar m_wndToolBar;
// 生成的消息映射函数
protected:
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
DECLARE_MESSAGE_MAP()
};
可以猜测出 OnCreate 响应的是 WM_CREATE 消息。 但是怎样将 OnCreate 和 WM_CREATE 消息或者说和 Window Produce 联系起来呢? 方法是利用上面几个神秘的宏……
查看 CWinApp的构造函数(C:\Program Files\Microsoft Visual Studio 9.0\VC\atlmfc\src\mfc\appcore.cpp):
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_pRecentFileList = NULL;
m_pDocManager = NULL;
m_atomApp = m_atomSystemTopic = NULL;
m_lpCmdLine = NULL;
m_pCmdInfo = 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
}
请看:
AFX_MODULE_STATE* pModuleState = _AFX_CMDTARGET_GETSTATE();
pModuleState->m_pCurrentWinApp = this;
查看 _AFX_CMDTARGET_GETSTATE();源码:
#ifdef _AFXDLL
#define _AFX_CMDTARGET_GETSTATE() (m_pModuleState)
#else
#define _AFX_CMDTARGET_GETSTATE() (AfxGetModuleState())
#endif
可以发现 _AFX_CMDTARGET_GETSTATE() 最终被 m_pModuleState 替换。而 m_pModuleState 是 CCmdTarget 的成员,CCmdTarget 是 CWinApp 的父类,因此 m_pModuleState 也是 theApp 的一部分。
查看 AFX_MODULE_STATE 的定义:
class AFX_MODULE_STATE : public CNoTrackObject
{
public:
#ifdef _AFXDLL
AFX_MODULE_STATE(BOOL bDLL, WNDPROC pfnAfxWndProc, DWORD dwVersion,
BOOL bSystem = FALSE);
#else
explicit AFX_MODULE_STATE(BOOL bDLL);
#endif
~AFX_MODULE_STATE();
CWinApp* m_pCurrentWinApp;
HINSTANCE m_hCurrentInstanceHandle;
HINSTANCE m_hCurrentResourceHandle;
LPCTSTR m_lpszCurrentAppName;
BYTE m_bDLL; // TRUE if module is a DLL, FALSE if it is an EXE
BYTE m_bSystem; // TRUE if module is a "system" module, FALSE if not
BYTE m_bReserved[2]; // padding
DWORD m_fRegisteredClasses; // flags for registered window classes
// runtime class data
#ifdef _AFXDLL
CRuntimeClass* m_pClassInit;
#endif
CTypedSimpleList<CRuntimeClass*> m_classList;
// OLE object factories
#ifndef _AFX_NO_OLE_SUPPORT
#ifdef _AFXDLL
COleObjectFactory* m_pFactoryInit;
#endif
CTypedSimpleList<COleObjectFactory*> m_factoryList;
#endif
// number of locked OLE objects
long m_nObjectCount;
BOOL m_bUserCtrl;
// AfxRegisterClass and AfxRegisterWndClass data
CString m_strUnregisterList;
#ifdef _AFXDLL
WNDPROC m_pfnAfxWndProc;
DWORD m_dwVersion; // version that module linked against
#endif
// variables related to a given process in a module
// (used to be AFX_MODULE_PROCESS_STATE)
void (PASCAL *m_pfnFilterToolTipMessage)(MSG*, CWnd*);
#ifdef _AFXDLL
// CDynLinkLibrary objects (for resource chain)
CTypedSimpleList<CDynLinkLibrary*> m_libraryList;
// special case for MFCXXLLL.DLL (localized MFC resources)
HINSTANCE m_appLangDLL;
#endif
#ifndef _AFX_NO_OCC_SUPPORT
// OLE control container manager
COccManager* m_pOccManager;
// locked OLE controls
CTypedSimpleList<COleControlLock*> m_lockList;
#endif
#ifndef _AFX_NO_DAO_SUPPORT
_AFX_DAO_STATE* m_pDaoState;
#endif
#ifndef _AFX_NO_OLE_SUPPORT
// Type library caches
CTypeLibCache m_typeLibCache;
CTypeLibCacheMap* m_pTypeLibCacheMap;
#endif
// define thread local portions of module state
CThreadLocal<AFX_MODULE_THREAD_STATE> m_thread;
//Fusion: declare pointer to array of pointers to isolation aware dll wrappers (ex: comctl32).
CDllIsolationWrapperBase** m_pDllIsolationWrappers;
//Defaults to TRUE. When FALSE - MFC will not activate context in AFX_MAINTAIN_STATE2 (used by AFX_MANAGE_STATE).
BOOL m_bSetAmbientActCtx;
//Handle of the module context.
HANDLE m_hActCtx;
void CreateActivationContext();
// bool indicating the return value of InitNetworkAddressControl() (from shell32.dll)
BOOL m_bInitNetworkAddressControl;
// bool indicating whether or not InitNetworkAddressControl() (from shell32.dll) have been called for CNetAddressCtrl
BOOL m_bInitNetworkAddressControlCalled;
};
又可以发现 AFX_MODULE_STATE 的成员m_pCurrentWinApp 的 类型是 CWinApp* 。
由此观之,
pModuleState->m_pCurrentWinApp = this;
解释为用 CWinApp 中的一个CWinApp指针变量来存储自己派生类对象的地址。 等效于:
theApp.m_pCurrentWinApp = &theApp;
隐晦不明的 WinMain
theApp 完成初始化以后,WinMain 登场。WinMain 通过链接器直接加到应用程序代码中。查看 WinMain 源码(C:\Program Files\Microsoft Visual Studio 9.0\VC\atlmfc\src\mfc\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);
}
查看 AfxWinMain 源码(C:\Program Files\Microsoft Visual Studio 9.0\VC\atlmfc\src\mfc\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;
}
整理相关代码:
CWinThread* pThread = AfxGetThread();
CWinApp* pApp = AfxGetApp();
AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow)
pApp->InitApplication()
pThread->InitInstance()
nReturnCode = pThread->Run();
我们分块来探究:
(1)CWinThread* pThread = AfxGetThread(); 和 CWinApp* pApp = AfxGetApp()
(2)AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow)
(5)nReturnCode = pThread->Run()
这5步完成了:窗口的初始化,注册、创建、更新和显示、运行(消息循环)。等窗口接收到 WM_QUIT 消息,之后窗口关闭。