MFC的流程跟踪
(窗口的设计,注册,创建)
跟踪MFC的程序流程,我们一步一步来,不要怕麻烦!
1 首先是MFC的入口函数:
extern "C"int WINAPI
_tWinMain(HINSTANCEhInstance, HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine, int nCmdShow)
#pragma warning(suppress:4985)
{
// callshared/exported WinMain
returnAfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}
2 然后进入AfxWinMain(hInstance, hPrevInstance, lpCmdLine,nCmdShow);
int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCEhPrevInstance,
_In_ LPTSTR lpCmdLine, int nCmdShow)
{
ASSERT(hPrevInstance == NULL);
// pThread,pApp都是指向the App全局对象,不过这两个指针都是the App的父类指针
intnReturnCode = -1;
CWinThread* pThread = AfxGetThread();
CWinApp* pApp = AfxGetApp();
// 这个是完成MFC的初始化
if(!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
gotoInitFailure;
// 应用程序全局初始化,主要是文档相关
if(pApp != NULL && !pApp->InitApplication())
gotoInitFailure;
// 调用子类的Instance函数来实现初始化,这个是重点,窗口的设计
// 注册,创建都在这个函数里面了,中间会调用很多函数
if(!pThread->InitInstance())
{
if(pThread->m_pMainWnd != NULL)
{
TRACE(traceAppMsg,0, "Warning: Destroying non-NULLm_pMainWnd\n");
pThread->m_pMainWnd->DestroyWindow();
}
nReturnCode =pThread->ExitInstance();
gotoInitFailure;
}
// 消息循环
nReturnCode = pThread->Run();
InitFailure:
#ifdef _DEBUG
// Check formissing 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();
returnnReturnCode;
}
2.1 接下来进入AfxWinInit看看
BOOL AFXAPIAfxWinInit(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance,
_In_z_ LPTSTR lpCmdLine, _In_ int nCmdShow)
{
ASSERT(hPrevInstance == NULL);
// handlecritical errors and avoid Windows message boxes
SetErrorMode(SetErrorMode(0) |
SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
// setresource handles
AFX_MODULE_STATE* pModuleState =AfxGetModuleState();
pModuleState->m_hCurrentInstanceHandle= hInstance;
pModuleState->m_hCurrentResourceHandle= hInstance;
pModuleState->CreateActivationContext();
// fill inthe initial state for the application
CWinApp* pApp = AfxGetApp();
if(pApp != NULL)
{
//Windows specific initialization (not done if no CWinApp)
pApp->m_hInstance =hInstance;
hPrevInstance; // Obsolete.
pApp->m_lpCmdLine =lpCmdLine;
pApp->m_nCmdShow =nCmdShow;
pApp->SetCurrentHandles();
}
// initializethread specific data (for main thread)
if(!afxContextIsDLL)
AfxInitThread();
returnTRUE;
}
这个函数主要就是完成MFC的初始化,也不需要多解释。其实如果我在控制台程序创建时,勾选了MFC的库,那么在_tmain函数中也会出现这样一个东西,如下:
int _tmain(int argc,TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
HMODULE hModule =::GetModuleHandle(NULL);
if (hModule != NULL)
{
// 初始化 MFC 并在失败时显示错误
if (!AfxWinInit(hModule,NULL, ::GetCommandLine(), 0))
{
// TODO: 更改错误代码以符合您的需要
_tprintf(_T("错误: MFC 初始化失败\n"));
nRetCode = 1;
}
else
{
// TODO: 在此处为应用程序的行为编写代码。
}
}
else
{
// TODO: 更改错误代码以符合您的需要
_tprintf(_T("错误: GetModuleHandle 失败\n"));
nRetCode = 1;
}
return nRetCode;
}
2.2 再看看BOOLCWinApp::InitApplication()
BOOLCWinApp::InitApplication()
{
if (CDocManager::pStaticDocManager !=NULL)
{
if (m_pDocManager == NULL)
m_pDocManager =CDocManager::pStaticDocManager;
CDocManager::pStaticDocManager= NULL;
}
if (m_pDocManager != NULL)
m_pDocManager->AddDocTemplate(NULL);
else
CDocManager::bStaticInit =FALSE;
LoadSysPolicies();
return TRUE;
}
这个函数主要就是管理文档的,对目前跟踪流程也没多大用处,先不管它
2.3 接下来再进入我们最关心的函数Instance看看,就是在这个函数中我们进行了窗口的设计,注册,创建,让我们一步一步地揭开它神秘的面纱吧。
BOOL Cmfc4StdApp::InitInstance()
{
InitCtrls.dwSize = sizeof(InitCtrls);
InitCtrls.dwICC = ICC_WIN95_CLASSES;
InitCommonControlsEx(&InitCtrls);
CWinAppEx::InitInstance();
if(!AfxOleInit())
{
AfxMessageBox(IDP_OLE_INIT_FAILED);
returnFALSE;
}
AfxEnableControlContainer();
EnableTaskbarInteraction(FALSE);
SetRegistryKey(_T(“应用程序向导生成的本地应用程序”));
LoadStdProfileSettings(4); // 加载标准的INI文件
InitContextMenuManager();
InitKeyboardManager();
InitTooltipManager();
CMFCToolTipInfo ttParams;
ttParams.m_bVislManagerTheme = TRUE;
theApp.GetTooltipManager()->SetTooltipParams(AFX_TOOLTIP_TYPE_ALL,
RUNTIME_CLASS(CMFCToolTipCtrl),&ttParams);
// 注册应用程序的文档模板,文档模板将用作文档,框架,视口之间的连接
CSingleDocTemplate* pDocTemplate;
pDocTemplate = newCSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(Cmfc4StdDoc),
RUNTIME_CLASS(CMainFrame), // 主SDI框架
RUNTIME_CLASS(Cmfc4StdView));
if(!pDocTemplate)
returnFALSE;
AddDocTemplate(pDocTemplate);
// 分析标准shell命令,DDE,并打开文件操作
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
// 调度在命令行中指定的命令,如果用/RegServer、/Register、/Unregserver 或 /Unregister
// 启动应用程序,则返回false;所有的秘密都在这个函数里了
if(!ProcessShellCommand(cmdInfo))
returnFALSE;
//唯一的一个主窗口已经初始化,因此显示它并对其进行更新
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
//仅当有后缀时才调用DragAcceptFiles,在SDI应用程序中,这应该在ProcessShellCommand之后
returnTRUE;
}
2.3.1 我们进入ProcessShellCommand看看
BOOLCWinApp::ProcessShellCommand(CCommandLineInfo& rCmdInfo)
{
BOOL bResult = TRUE;
switch(rCmdInfo.m_nShellCommand)
{
caseCCommandLineInfo::RestartByRestartManager://为了方便,删除了执行语句
case CCommandLineInfo::FileNew:
if(!AfxGetApp()->OnCmdMsg(ID_FILE_NEW, 0, NULL, NULL))
OnFileNew();
if(m_pMainWnd == NULL)
bResult= FALSE;
break;
caseCCommandLineInfo::FileOpen: //为了方便,删除了执行语句
caseCCommandLineInfo::FilePrintTo: //为了方便,删除了执行语句
caseCCommandLineInfo::FilePrint: //为了方便,删除了执行语句
caseCCommandLineInfo::FileDDENoShow: //为了方便,删除了执行语句
caseCCommandLineInfo::FileDDE: //为了方便,删除了执行语句
caseCCommandLineInfo::AppRegister: //为了方便,删除了执行语句
caseCCommandLineInfo::AppUnregister: //为了方便,删除了执行语句
}
returnbResult;
}
可以看出,这个函数就是处理各种命令的函数,我们的程序在这里会跳到用特殊颜色标记的地方执行,我们可以看一看,接下来进入的就是OnCmdMsg(ID_FILE_NEW, 0, NULL, NULL))
2.3.1.1 进入函数BOOL CCmdTarget::OnCmdMsg
BOOLCCmdTarget::OnCmdMsg(UINT nID, int nCode, void* pExtra,
AFX_CMDHANDLERINFO* pHandlerInfo)
{
//此处省略各种条件编译语句
#ifdef _DEBUG
if (nCode == CN_COMMAND)
TRACE(traceCmdRouting,1, "SENDING command id 0x%04X to %hstarget.\n", nID,
GetRuntimeClass()->m_lpszClassName);
else if (nCode >CN_COMMAND)
TRACE(traceCmdRouting,1, "SENDING control notification %d fromcontrol id 0x%04X to %hs window.\n", nCode, nID,GetRuntimeClass()->m_lpszClassName);
#endif //_DEBUG
return _AfxDispatchCmdMsg(this, nID, nCode,
lpEntry->pfn,pExtra, lpEntry->nSig, pHandlerInfo);
}
}
returnFALSE; // nothandled
}
主框架调用这个函数来分发命令消息,或者处理用户接口的更新命令,程序会在这个函数中进入AfxDisPatchCmdMsg中,那我们就进到里面探探。
2.3.1.1.1
AFX_STATIC BOOLAFXAPI _AfxDispatchCmdMsg(CCmdTarget* pTarget, UINT nID, int nCode, AFX_PMSG pfn, void*pExtra, UINT_PTR nSig, AFX_CMDHANDLERINFO* pHandlerInfo)
AFX_STATIC BOOLAFXAPI _AfxDispatchCmdMsg(CCmdTarget* pTarget, UINT nID, int nCode,
AFX_PMSG pfn, void*pExtra, UINT_PTR nSig, AFX_CMDHANDLERINFO* pHandlerInfo)
//return TRUE to stop routing
{
ENSURE_VALID(pTarget);
UNUSED(nCode); // unused inrelease builds
unionMessageMapFunctions mmf;
mmf.pfn = pfn;
BOOL bResult = TRUE; // default is ok
if(pHandlerInfo != NULL)
{
//just fill in the information, don't do it
pHandlerInfo->pTarget =pTarget;
pHandlerInfo->pmf =mmf.pfn;
returnTRUE;
}
switch(nSig)
{
caseAfxSigCmd_v:
//normal command or control notification
ASSERT(CN_COMMAND == 0); // CN_COMMANDsame as BN_CLICKED
ASSERT(pExtra == NULL);
(pTarget->*mmf.pfnCmd_v_v)();
break;
}
returnbResult;
}
这个也是消息相关的函数,程序会进入特殊颜色标记的部分,那我们就进去看一看
2.3.1.1.1.a
void CWinApp::OnFileNew()
{
if(m_pDocManager != NULL)
m_pDocManager->OnFileNew();
}
此时程序进入了OnFileNew()函数,我们继续跟踪进入看看:
2.3.1.1.1.b
void CDocManager::OnFileNew()
{
if(m_templateList.IsEmpty())
{
TRACE(traceAppMsg, 0, "Error: no document templates registered withCWinApp.\n");
AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);
return;
}
// 获取第一个文档模板,对于单文档只有一个文档模板
CDocTemplate* pTemplate =(CDocTemplate*)m_templateList.GetHead();
if(m_templateList.GetCount() > 1)
{
//more than one document template to choose from
//bring up dialog prompting user
CNewTypeDlgdlg(&m_templateList);
INT_PTR nID = dlg.DoModal();
if(nID == IDOK)
pTemplate =dlg.m_pSelectedTemplate;
else
return; // none - cancel operation
}
ASSERT(pTemplate != NULL);
ASSERT_KINDOF(CDocTemplate, pTemplate);
pTemplate->OpenDocumentFile(NULL);
//if returns NULL, the user has already been alerted
}
函数会调试至此进入特殊颜色标记部分,进入继续看看
2.3.1.1.1.c
CDocument*CSingleDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName, BOOL bMakeVisible)
{
return OpenDocumentFile(lpszPathName,TRUE, bMakeVisible);
}
2.3.1.1.1.d
CDocument*CSingleDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName, BOOL bAddToMRU, BOOLbMakeVisible)
{
CDocument* pDocument = NULL;
CFrameWnd* pFrame = NULL;
BOOL bCreated = FALSE; // => docand frame created
BOOL bWasModified = FALSE;
if(m_pOnlyDoc != NULL)
{
//already have a document - reinit it
pDocument = m_pOnlyDoc;
if(!pDocument->SaveModified())
{
// set a flag to indicate that the document being openedshould not
// be removed from the MRU list, if it was being openedfrom there
g_bRemoveFromMRU =FALSE;
return NULL; // leave the original one
}
pFrame =(CFrameWnd*)AfxGetMainWnd();
ASSERT(pFrame != NULL);
ASSERT_KINDOF(CFrameWnd,pFrame);
ASSERT_VALID(pFrame);
}
else
{
//create a new document
pDocument = CreateNewDocument();
ASSERT(pFrame == NULL); // will becreated below
bCreated = TRUE;
}
if(pDocument == NULL)
{
AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);
returnNULL;
}
ASSERT(pDocument == m_pOnlyDoc);
if(pFrame == NULL)
{
ASSERT(bCreated);
//create frame - set as main document frame
BOOL bAutoDelete =pDocument->m_bAutoDelete;
pDocument->m_bAutoDelete =FALSE;
// don't destroy if something goes wrong
pFrame = CreateNewFrame(pDocument, NULL);
pDocument->m_bAutoDelete =bAutoDelete;
if(pFrame == NULL)
{
AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);
delete pDocument; // explicitdelete on error
return NULL;
}
}
if(lpszPathName == NULL)
{
//create a new document
SetDefaultTitle(pDocument);
//avoid creating temporary compound file when starting up invisible
if(!bMakeVisible)
pDocument->m_bEmbedded= TRUE;
if(!pDocument->OnNewDocument())
{
// user has been alerted to what failed in OnNewDocument
TRACE(traceAppMsg,0, "CDocument::OnNewDocument returnedFALSE.\n");
if (bCreated)
pFrame->DestroyWindow(); // will destroydocument
return NULL;
}
}
else
{
CWaitCursor wait;
//open an existing document
bWasModified =pDocument->IsModified();
pDocument->SetModifiedFlag(FALSE); // not dirty foropen
if(!pDocument->OnOpenDocument(lpszPathName))
{
// user has been alerted to what failed in OnOpenDocument
TRACE(traceAppMsg,0, "CDocument::OnOpenDocument returnedFALSE.\n");
if (bCreated)
{
pFrame->DestroyWindow(); // will destroy document
}
else if(!pDocument->IsModified())
{
// original document is untouched
pDocument->SetModifiedFlag(bWasModified);
}
else
{
// we corrupted the original document
SetDefaultTitle(pDocument);
if (!pDocument->OnNewDocument())
{
TRACE(traceAppMsg,0, "Error: OnNewDocument failed after trying"
"to open a document - trying to continue.\n");
// assume we can continue
}
}
return NULL; // open failed
}
pDocument->SetPathName(lpszPathName,bAddToMRU);
pDocument->OnDocumentEvent(CDocument::onAfterOpenDocument);
}
CWinThread* pThread = AfxGetThread();
ASSERT(pThread);
if(bCreated && pThread->m_pMainWnd == NULL)
{
//set as main frame (InitialUpdateFrame will show the window)
pThread->m_pMainWnd =pFrame;
}
InitialUpdateFrame(pFrame, pDocument,bMakeVisible);
returnpDocument;
}
(1)pDocument =CreateNewDocument()
CDocument*CDocTemplate::CreateNewDocument()
{
// defaultimplementation constructs one from CRuntimeClass
if(m_pDocClass == NULL)
{
TRACE(traceAppMsg, 0, "Error: you must overrideCDocTemplate::CreateNewDocument.\n");
ASSERT(FALSE);
returnNULL;
}
//创建一个新的文档文档对象,是RUNTIME_CLASS(CFirstDoc)中的对象
CDocument* pDocument = (CDocument*)m_pDocClass->CreateObject();
if(pDocument == NULL)
{
TRACE(traceAppMsg, 0, "Warning: Dynamic create of document type %hsfailed.\n",
m_pDocClass->m_lpszClassName);
returnNULL;
}
ASSERT_KINDOF(CDocument, pDocument);
AddDocument(pDocument);
returnpDocument;
}
(2)CFrameWnd*CDocTemplate::CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther)
{
if(pDoc != NULL)
ASSERT_VALID(pDoc);
// create aframe wired to the specified document
ASSERT(m_nIDResource != 0); // must have a resource ID to load from
CCreateContext context;
context.m_pCurrentFrame = pOther;
context.m_pCurrentDoc = pDoc;
context.m_pNewViewClass = m_pViewClass;
context.m_pNewDocTemplate = this;
if(m_pFrameClass == NULL)
{
TRACE(traceAppMsg, 0, "Error: you must override CDocTemplate::CreateNewFrame.\n");
ASSERT(FALSE);
returnNULL;
}
// 创建一个运行时的框架对象
CFrameWnd* pFrame =(CFrameWnd*)m_pFrameClass->CreateObject();
if(pFrame == NULL)
{
TRACE(traceAppMsg, 0, "Warning: Dynamic create of frame %hsfailed.\n",
m_pFrameClass->m_lpszClassName);
returnNULL;
}
ASSERT_KINDOF(CFrameWnd, pFrame);
if(context.m_pNewViewClass == NULL)
TRACE(traceAppMsg, 0, "Warning: creating frame with no defaultview.\n");
// create newfrom resource
if (!pFrame->LoadFrame(m_nIDResource,
WS_OVERLAPPEDWINDOW| FWS_ADDTOTITLE, // default frame styles
NULL,&context))
{
TRACE(traceAppMsg, 0, "Warning: CDocTemplate couldn't create aframe.\n");
//frame will be deleted in PostNcDestroy cleanup
returnNULL;
}
// it worked!
returnpFrame;
}
(2)_1
BOOLCMainFrame::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle, CWnd* pParentWnd,CCreateContext* pContext)
(2)_11
BOOLCFrameWndEx::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle, CWnd*pParentWnd, CCreateContext* pContext)
{
m_Impl.m_nIDDefaultResource =nIDResource;
m_Impl.LoadLargeIconsState();
if (!CFrameWnd::LoadFrame(nIDResource, dwDefaultStyle,pParentWnd, pContext))
{
returnFALSE;
}
m_Impl.OnLoadFrame();
returnTRUE;
}
(2)_111
BOOLCFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle,
CWnd* pParentWnd, CCreateContext*pContext)
{
// only dothis once
ASSERT_VALID_IDR(nIDResource);
ASSERT(m_nIDHelp == 0 || m_nIDHelp ==nIDResource);
m_nIDHelp = nIDResource; // ID for helpcontext (+HID_BASE_RESOURCE)
CString strFullString;
if(strFullString.LoadString(nIDResource))
AfxExtractSubString(m_strTitle,strFullString, 0); // first sub-string
VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));
// attempt tocreate the window
LPCTSTR lpszClass = GetIconWndClass(dwDefaultStyle,nIDResource);
CString strTitle = m_strTitle;
if (!Create(lpszClass, strTitle,dwDefaultStyle, rectDefault,
pParentWnd, ATL_MAKEINTRESOURCE(nIDResource),0L, pContext))
{
returnFALSE; //will self destruct on failure normally
}
// save thedefault menu handle
ASSERT(m_hWnd != NULL);
m_hMenuDefault = m_dwMenuBarState ==AFX_MBS_VISIBLE ? ::GetMenu(m_hWnd) : m_hMenu;
// loadaccelerator resource
LoadAccelTable(ATL_MAKEINTRESOURCE(nIDResource));
if(pContext == NULL) // send initial update
SendMessageToDescendants(WM_INITIALUPDATE,0, 0, TRUE, TRUE);
returnTRUE;
}
跳了半天,真够麻烦的,不过好的是我们终于看到了绿洲了,上面特殊颜色标记的东西不就是迹象吗,所以我们继续搞下去,坚持到底就是光明!
(2)_111x AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG)
BOOL AFXAPIAfxEndDeferRegisterClass(LONG fToRegister)
{
// mask offall classes that are already registered
AFX_MODULE_STATE* pModuleState =AfxGetModuleState();
fToRegister &=~pModuleState->m_fRegisteredClasses;
if(fToRegister == 0)
returnTRUE;
LONG fRegisteredClasses = 0;
// commoninitialization
WNDCLASS wndcls;
memset(&wndcls,0, sizeof(WNDCLASS)); // start with NULLdefaults
wndcls.lpfnWndProc= DefWindowProc;
wndcls.hInstance= AfxGetInstanceHandle();
wndcls.hCursor= afxData.hcurArrow;
INITCOMMONCONTROLSEX init;
init.dwSize = sizeof(init);
//此处省略各种款式类型的窗口
// work toregister classes as specified by fToRegister, populate fRegisteredClasses as wego
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;
}
//此处省略各种款式类型的窗口
// save newstate of registered controls
pModuleState->m_fRegisteredClasses|= fRegisteredClasses;
// specialcase 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 haveregistered at least as mamy classes as requested
return(fToRegister & fRegisteredClasses) == fToRegister;
}
从特殊颜色标记的语句可以看出,在窗口注册函数中,其实包含着窗口类的设计,程序会在
if (_AfxRegisterWithIcon(&wndcls, _afxWndFrameOrView,AFX_IDI_STD_FRAME))
fRegisteredClasses|= AFX_WNDFRAMEORVIEW_REG)
中调用_AfxRegisterWithIcon函数,接下来我们再看看这个函数,做了些神马
(2)_111y
AFX_STATIC BOOLAFXAPI _AfxRegisterWithIcon(WNDCLASS* pWndCls,
LPCTSTR lpszClassName, UINT nIDIcon)
{
pWndCls->lpszClassName =lpszClassName;
HINSTANCE hInst =AfxFindResourceHandle(
ATL_MAKEINTRESOURCE(nIDIcon),ATL_RT_GROUP_ICON);
if((pWndCls->hIcon = ::LoadIconW(hInst, ATL_MAKEINTRESOURCEW(nIDIcon))) ==NULL)
{
//use default icon
pWndCls->hIcon =::LoadIcon(NULL, IDI_APPLICATION);
}
return AfxRegisterClass(pWndCls);
}
我们可以看到,在这个函数中,继续完成窗口类的设计,包括光标,图标,以及窗口类名字的设置,在这些设置完后,函数返回了 AfxRegisterClass(pWndCls);
我们再跳进去看看,这个函数是做了些神马东西
(2)_111z
BOOL AFXAPIAfxRegisterClass(WNDCLASS* lpWndClass)
{
WNDCLASS wndcls;
if(AfxCtxGetClassInfo(lpWndClass->hInstance, lpWndClass->lpszClassName,
&wndcls))
{
//class already registered
returnTRUE;
}
if (!::AfxCtxRegisterClass(lpWndClass))
{
TRACE(traceAppMsg, 0, _T("Can't register window class named %s\n"),
lpWndClass->lpszClassName);
returnFALSE;
}
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);
}
returnbRet;
}
我们继续调试程序到特殊颜色标记的地方,会进入下面的函数
(2)_111z1
AFX_ISOLATIONAWARE_STATICLINK_FUNC(ATOM,RegisterClassW,(constWNDCLASSW*lpWndClass),(lpWndClass),0)
止于此,我们终于看到了熟悉的函数RegisterClass了吧。我们再把设计和注册窗口的过程给捋一捋。
AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG)
AfxEndDeferRegisterClass(LONGfToRegister)
AfxRegisterClass(pWndCls);
AfxCtxRegisterClass(lpWndClass)
AFX_ISOLATIONAWARE_STATICLINK_FUNC(ATOM,RegisterClassW,(constWNDCLASSW*lpWndClass),(lpWndClass),0)
仅仅一个设计和注册窗口就被搞了这么多步。
(2)_112
LPCTSTRCFrameWnd::GetIconWndClass(DWORD dwDefaultStyle, UINT nIDResource)
{
ASSERT_VALID_IDR(nIDResource);
HINSTANCE hInst =AfxFindResourceHandle(
ATL_MAKEINTRESOURCE(nIDResource),ATL_RT_GROUP_ICON);
HICON hIcon = ::LoadIconW(hInst,ATL_MAKEINTRESOURCEW(nIDResource));
if(hIcon != NULL)
{
CREATESTRUCT cs;
memset(&cs, 0, sizeof(CREATESTRUCT));
cs.style = dwDefaultStyle;
PreCreateWindow(cs);
// will fill lpszClassName with default WNDCLASS name
// ignore instance handle from PreCreateWindow.
WNDCLASS wndcls;
if(cs.lpszClass != NULL &&
AfxCtxGetClassInfo(AfxGetInstanceHandle(),cs.lpszClass, &wndcls) &&
wndcls.hIcon !=hIcon)
{
// register a very similar WNDCLASS
return AfxRegisterWndClass(wndcls.style,
wndcls.hCursor,wndcls.hbrBackground, hIcon);
}
}
returnNULL; //just use the default
}
(2)_1121
BOOLCMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if( !CFrameWndEx::PreCreateWindow(cs))
returnFALSE;
// TODO: 在¨²此ä?处ä|通ª¡§过y修T改?
// CREATESTRUCT cs 来¤¡ä修T改?窗ä¡ã口¨²类¤¨¤或¨°样¨´式º?
returnTRUE;
}
(2) _1122
BOOLCFrameWndEx::PreCreateWindow(CREATESTRUCT& cs)
{
m_dockManager.Create(this);
m_Impl.SetDockingManager(&m_dockManager);
m_Impl.RestorePosition(cs);
return CFrameWnd::PreCreateWindow(cs);
}
(2) _1123
BOOLCFrameWnd::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;
returnTRUE;
}
开始的时候我以为在这里又注册了一次窗口,这个问题令我蛋疼了好久,我不明白为什么要这么做,理由是什么。后来经过调用发现出现这样的函数并不代表一定要注册窗口,如果窗口已经注册了,就不会再继续注册了,MFC就是这样子,会在多个地方调用注册窗口程序,如果窗口类修改了,它就会真的去重新注册窗口,否则就不重新注册窗口。如:
BOOL AFXAPIAfxEndDeferRegisterClass(LONG fToRegister)
{
// mask offall classes that are already registered
AFX_MODULE_STATE* pModuleState =AfxGetModuleState();
fToRegister &=~pModuleState->m_fRegisteredClasses;
if(fToRegister == 0) //fToRegister
returnTRUE;
….
}
BOOL AFXAPIAfxRegisterClass(WNDCLASS* lpWndClass)
{
WNDCLASS wndcls;
if(AfxCtxGetClassInfo(lpWndClass->hInstance, lpWndClass->lpszClassName,
&wndcls))
{
//class already registered
returnTRUE;
}
…
}
这里执行完毕就清楚了,其实这里的PreCreateWindow并没有重新注册窗口,因为我们并没有改变窗口类的内容。
在LPCTSTRCFrameWnd::GetIconWndClass(DWORD dwDefaultStyle, UINT nIDResource)
最后会调用这样一个函数:
if (cs.lpszClass != NULL &&
AfxCtxGetClassInfo(AfxGetInstanceHandle(),cs.lpszClass, &wndcls) &&
wndcls.hIcon !=hIcon)
{
// register a very similar WNDCLASS
return AfxRegisterWndClass(wndcls.style,
wndcls.hCursor,wndcls.hbrBackground, hIcon);
}
这个函数还是真的执行了注册窗口的函数,至于微软说注册一个非常类似的窗口类,我还没有仔细考究。
所有这些都做完之后,就开始CreateWindow了
(2)_113 Create(lpszClass, strTitle, dwDefaultStyle,rectDefault,
pParentWnd, ATL_MAKEINTRESOURCE(nIDResource), 0L, pContext)
窗口的创建就在这个Create函数里面了。
BOOLCFrameWnd::Create(LPCTSTR lpszClassName,
LPCTSTR lpszWindowName,
DWORD dwStyle,
constRECT& rect,
CWnd* pParentWnd,
LPCTSTR lpszMenuName,
DWORD dwExStyle,
CCreateContext* pContext)
{…
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);
returnFALSE;
}
…
}
(2)_1131
BOOLCWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,
LPCTSTR lpszWindowName, DWORD dwStyle,
int x, int y, int nWidth, int nHeight,
HWND hWndParent, HMENU nIDorHMenu,LPVOID lpParam)
到了这里基本上MFC的窗口设计,注册,创建的过程给弄完了。
但是MFC的消息映射机制还没有讲解,接下来去解析。