MFC 程序来龙去脉_子文档 4

MFC 程序来龙去脉_子文档 4

探究 pThread->InitInstance()

由上面解释可知 pThread 指向 theApp 。尽管类型不是CFirstMFCDemoApp* ,但必定是其父类。由 OO 多态性可知该语句调用的是子类方法 CFirstMFCDemoApp::InitInstance() 

查看 CFirstMFCDemoApp::InitInstance() 源码:

BOOL CFirstMFCDemoApp::InitInstance()

{

// 如果一个运行在Windows XP 上的应用程序清单指定要

// 使用ComCtl32.dll 版本或更高版本来启用可视化方式,

//则需要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();

// 标准初始化

// 如果未使用这些功能并希望减小

// 最终可执行文件的大小,则应移除下列

// 不需要的特定初始化例程

// 更改用于存储设置的注册表项

// TODO: 应适当修改该字符串,

// 例如修改为公司或组织名

SetRegistryKey(_T("应用程序向导生成的本地应用程序"));

LoadStdProfileSettings(4);  // 加载标准INI 文件选项(包括MRU)

// 注册应用程序的文档模板。文档模板

// 将用作文档、框架窗口和视图之间的连接

CSingleDocTemplate* pDocTemplate;

pDocTemplate = new CSingleDocTemplate(

IDR_MAINFRAME,

RUNTIME_CLASS(CFirstMFCDemoDoc),

RUNTIME_CLASS(CMainFrame),       // SDI 框架窗口

RUNTIME_CLASS(CFirstMFCDemoView));

if (!pDocTemplate)

return FALSE;

AddDocTemplate(pDocTemplate);

// 分析标准外壳命令、DDE、打开文件操作的命令行

CCommandLineInfo cmdInfo;

ParseCommandLine(cmdInfo);

// 调度在命令行中指定的命令。如果

// /RegServer/Register/Unregserver /Unregister 启动应用程序,则返回FALSE

if (!ProcessShellCommand(cmdInfo))

return FALSE;

// 唯一的一个窗口已初始化,因此显示它并对其进行更新

m_pMainWnd->ShowWindow(SW_SHOW);

m_pMainWnd->UpdateWindow();

// 仅当具有后缀时才调用DragAcceptFiles

//  SDI 应用程序中,这应在ProcessShellCommand 之后发生

return TRUE;

}

m_pMainWnd->ShowWindow(SW_SHOW);

m_pMainWnd->UpdateWindow();

两行代码可知InitInstance 实现了窗口的显示和更新。但是窗口尚未注册和创建,并且 m_pMainWnd 尚未初始化。

寻找 m_pMainWnd 初始化时机

我们看看 pFrame 到底是个什么,它干了什么事情。

查看CSingDocTemplate::OpenDocumentFile() 源码(C:\Program Files\Microsoft Visual Studio 9.0\VC\atlmfc\src\mfc\docsingl.cpp):

CDocument* CSingleDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,

BOOL bMakeVisible)

// if lpszPathName == NULL => create new file of this type

{

CDocument* pDocument = NULL;

CFrameWnd* pFrame = NULL;

BOOL bCreated = FALSE;      // => doc and frame created

BOOL bWasModified = FALSE;

if (m_pOnlyDoc != NULL)

{

// already have a document - reinit it

pDocument = m_pOnlyDoc;

if (!pDocument->SaveModified())

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 be created below

bCreated = TRUE;

}

if (pDocument == NULL)

{

AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);

return NULL;

}

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;       // explicit delete 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 returned FALSE.\n");

if (bCreated)

pFrame->DestroyWindow();    // will destroy document

return NULL;

}

}

else

{

CWaitCursor wait;

// open an existing document

bWasModified = pDocument->IsModified();

pDocument->SetModifiedFlag(FALSE);  // not dirty for open

if (!pDocument->OnOpenDocument(lpszPathName))

{

// user has been alerted to what failed in OnOpenDocument

TRACE(traceAppMsg, 0, "CDocument::OnOpenDocument returned FALSE.\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);

}

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

return pDocument;

}

(1)分析语句 pFrame = (CFrameWnd*)AfxGetMainWnd(); 没有找到AfxGetMainWnd()源码。网上说:返回主框架的指针。也就是 m_pMainWnd

(2)分析语句 pFrame = CreateNewFrame(pDocument, NULL);  查看CDocTemplate::CreateNewFrame 源码:

CFrameWnd* CDocTemplate::CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther)

{

if (pDoc != NULL)

ASSERT_VALID(pDoc);

// create a frame 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);

return NULL;

}

CFrameWnd* pFrame = (CFrameWnd*)m_pFrameClass->CreateObject();

if (pFrame == NULL)

{

TRACE(traceAppMsg, 0, "Warning: Dynamic create of frame %hs failed.\n",

m_pFrameClass->m_lpszClassName);

return NULL;

}

ASSERT_KINDOF(CFrameWnd, pFrame);

if (context.m_pNewViewClass == NULL)

TRACE(traceAppMsg, 0, "Warning: creating frame with no default view.\n");

// create new from resource

if (!pFrame->LoadFrame(m_nIDResource,

WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE,   // default frame styles

NULL, &context))

{

TRACE(traceAppMsg, 0, "Warning: CDocTemplate couldn't create a frame.\n");

// frame will be deleted in PostNcDestroy cleanup

return NULL;

}

// it worked !

return pFrame;

}

分析语句:

CFrameWnd* pFrame =

                  (CFrameWnd*)m_pFrameClass->CreateObject(); 

m_pFrameClass类型为CRuntimeClass* ,方法CreateObject() 创建了一个CFrameWnd对象(等效于 pFrame = new CFrameWnd())。

分析语句:

!pFrame->LoadFrame(m_nIDResource,

WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE,   // default frame styles

NULL, &context);

查看 CFrameWnd::LoadFrame 源码:

BOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle,

CWnd* pParentWnd, CCreateContext* pContext)

{

// only do this once

ASSERT_VALID_IDR(nIDResource);

ASSERT(m_nIDHelp == 0 || m_nIDHelp == nIDResource);

m_nIDHelp = nIDResource;    // ID for help context (+HID_BASE_RESOURCE)

CString strFullString;

if (strFullString.LoadString(nIDResource))

AfxExtractSubString(m_strTitle, strFullString, 0);    // first sub-string

VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));

// attempt to create the window

LPCTSTR lpszClass = GetIconWndClass(dwDefaultStyle, nIDResource);

CString strTitle = m_strTitle;

if (!Create(lpszClass, strTitle, dwDefaultStyle, rectDefault,

  pParentWnd, ATL_MAKEINTRESOURCE(nIDResource), 0L, pContext))

{

return FALSE;   // will self destruct on failure normally

}

// save the default menu handle

ASSERT(m_hWnd != NULL);

m_hMenuDefault = m_dwMenuBarState == AFX_MBS_VISIBLE ? ::GetMenu(m_hWnd) : m_hMenu;

// load accelerator resource

LoadAccelTable(ATL_MAKEINTRESOURCE(nIDResource));

if (pContext == NULL)   // send initial update

SendMessageToDescendants(WM_INITIALUPDATE, 0, 0, TRUE, TRUE);

return TRUE;

}

终于见到我们想要的窗口注册和创建代码:

AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG))

!Create(lpszClass, strTitle, dwDefaultStyle, rectDefault,

  pParentWnd, ATL_MAKEINTRESOURCE(nIDResource), 0L, pContext)

继续看看做了哪些事情吧!

(1)查看AfxDeferRegisterClass()源码(C:\Program Files\Microsoft Visual Studio 9.0\VC\atlmfc\src\mfc\afximpl.h):

#define AfxDeferRegisterClass(fClass) \

                          AfxEndDeferRegisterClass(fClass)

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 mamy classes as requested

return (fToRegister & fRegisteredClasses) == fToRegister;

}

查看 AfxRegisterClass 函数的功能:“利用这个函数在使用MFCDLL中注册一个窗口类。如果你使用了这个函数,则当DLL卸载时,这个类会自动注销。在非DLL项目中,AfxRegisterClass标识符被定义为一个宏,映像到 Windows 函数 RegisterClass ,因为应用程序中注册的 类是自动注销的。如果你用 AfxRegisterClass 来代替RegisterClass ,那么你的代码不管是用于应用程序还是用于DLL都不用于改变。 

2)查看 CFrameWnd::Create 源码:

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;

}

查看 CFrame::CreateEx 源码:

BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,

LPCTSTR lpszWindowName, DWORD dwStyle,

const RECT& rect, CWnd* pParentWnd, UINT nID,

LPVOID lpParam /* = NULL */)

{

return CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle,

rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,

pParentWnd->GetSafeHwnd(), (HMENU)(UINT_PTR)nID, lpParam);

}

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 = ::AfxCtxCreateWindowEx(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;

}

因为CWndCFrameWnd的父类所以CreateEx调用的PreCreateWindow实际是CFrameWnd::PreCreateWindow可以说明PreCreateWindow 在窗口创建之前调用,我们可以在PreCreateWindow修改窗口属性 CREATESTRUCT cs ,修改成我们想要的窗口属性。

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)

{

if( !CFrameWnd::PreCreateWindow(cs) )

return FALSE;

// TODO: 在此处通过修改

//  CREATESTRUCT cs 来修改窗口类或样式

return TRUE;

}


posted @ 2011-09-20 00:01  独酌逸醉  阅读(1082)  评论(0编辑  收藏  举报