IMPLEMENT_DYNAMIC是实现“运行时类型识别”宏,与之相对应的是DECLARE_DYNAMIC(声明“运行时类型识别”宏)。也就是说你在.CPP文件中如果看见有IMPLEMENT_DYNAMIC,则在.H文件中必定有DECLARE_DYNAMIC的声明。
DECLARE_DYNAMIC/DEClARE_DYNAMIC是为了确定运行时对象属于哪一个类而定义的宏。
DEClARE_DYNCREATE/IMPLEMENT_DYNCREATE是为了“动态创建"类的实例而定义的宏。new可以用来创建对象,但不是动态的。比如说,你要在程序中实现根据拥护输入的类名来创建类的实例,下面的做法是通不过的:
char szClassName[60];
cin >> szClassName;
CObject* pOb=new szClassName; //通不过
这里就要用到DEClARE_DYNCREATE/IMPLEMENT_DYNCREATE定义的功能了。
定义:
//
// Helper macros for declaring CRuntimeClass compatible classes
#ifdef _AFXDLL
#define DECLARE_DYNAMIC(class_name) /
protected: /
static CRuntimeClass* PASCAL _GetBaseClass(); /
public: /
static const CRuntimeClass class##class_name; /
static CRuntimeClass* PASCAL GetThisClass(); /
virtual CRuntimeClass* GetRuntimeClass() const; /
#define _DECLARE_DYNAMIC(class_name) /
protected: /
static CRuntimeClass* PASCAL _GetBaseClass(); /
public: /
static CRuntimeClass class##class_name; /
static CRuntimeClass* PASCAL GetThisClass(); /
virtual CRuntimeClass* GetRuntimeClass() const; /
#else
#define DECLARE_DYNAMIC(class_name) /
public: /
static const CRuntimeClass class##class_name; /
virtual CRuntimeClass* GetRuntimeClass() const; /
#define _DECLARE_DYNAMIC(class_name) /
public: /
static CRuntimeClass class##class_name; /
virtual CRuntimeClass* GetRuntimeClass() const; /
#endif
引用:
DECLARE_DYNAMIC/IMPLEMENT_DYNAMIC
#define DECLARE_DYNAMIC(class_name)/
public:/
static CRuntimeClass class##class_name;/
//声明一个类型为CRuntimeClass的静态public成员变量,变量名是由字符串"class"
//与所指定的类的类名组成。举例而言,如果你写DECLARE_DYNAMIC(CMyView),则等于声明了一个
// static CRuntimeClass classCMyView静态变量
virtual CRuntimeClass* GetRuntimeClass() const;/
//声明一个虚函数,函数名为GetRuntimeClass,返回值为CRuntimeClass类型的指针
//无参数,并且是个const函数
#define IMPLEMENT_DYNAMIC(class_name,bass_class_name)/
_IMPLEMENT_RUNTIMECLASS(class_name,base_class_name,0xFFFF,NULL)
#define _IMPLEMENT_RUNTIMECLASS(class_name,base_class_name,wSchema,pfnNew)/
static char _lpsz##class_name[]= #class_name;/
//定义一个C类型字符串静态变量,变量名由"_lpsz"和指定类的类名组成,变量值为该指定类型的名字
//比如是CMyView,那么定义的就是static char _lpszCMyView="CMyView";
CRuntimeClass class_name::class##class_name = {/
_lpsz##class_name,sizeof(class_name),wSchema,pfnNew,/
RUNTIME_CLASS(base_class_name),NULL};/
//给之前在DECLARE_DYNAMIC里定义的CRuntimeClass类型的静态成员变量赋值
//当然,除最后一个m_pNextClass没有赋值(赋值为NULL,它由下面的结构处理)
static AFX_CLASSINIT _init_##class_name(&class_name::class##class_name);/
//初始化一个名为"_init_##class_name"的AFX_CLASSINIT静态结构,主要作用是给指定的class_name的
//class##class_name静态变量的最后一个成员m_pNextClass赋值,具体见下面解释AFX_CLASSINIT中
CRuntimeClass* class_name::GetRuntimeClass() const/
{ return &class_name::class##class_name;}/
//之前在DECLARE_DYNAMIC里定义的GetRuntimeClass的实现,很简单,就一个return语句。
#define RUNTIME_CLASS(class_name)/
(&class_name::class##class_name)
//这部分之所以单独define出一个宏,主要是为了方便从某个指定的class直接得到它的CRuntimeclass静态成员
//以下是解释AFX_CLASSINIT结构,注意,这不是一个宏
//为了看得更加清楚,我按照struct定义的惯常格式来写这个struct的定义
struct AFX_CLASSINIT {
AFX_CLASSINIT(CRuntimeClass *pNewClass);
};
AFX_CLASSINIT::AFX_CLASSINIT(CRuntimeClass *pNewClass)
{
pNewClass->m_pNextClass = CRuntimeClass::pFirstClass;
//让m_pNextClass指向pFirstClass所指的CRuntimeClass变量
CRuntimeClass::pFirstClass = pNewClass;
//让pFirstClass指向pNewClass所指的变量,也就是本class的CRuntimeClass静态变量
}
找了很多地方都没有找到单文档的初始化,今天我自己来找。。
工具
: UltraEdit, VS2005, AJC Grep 1.1.1 试用版,
参考文献
:深入浅出MFC
目标
:找到m_pMainWnd的赋值的地方
m_pMainWnd
定义于
C:/Program Files/Microsoft Visual Studio .NET 2003/Vc7/atlmfc/include/afxwin.h
class
CWinThread : public CCmdTarget
{
CWnd* m_pMainWnd;
// main window (usually same AfxGetApp()->m_pMainWnd)
}
绪:
我的单文档DEMO
BOOL CtestApp::InitInstance()
{
InitCommonControls();
CWinApp::InitInstance();
AfxEnableControlContainer();
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CtestDoc),
RUNTIME_CLASS(CMainFrame), // 主 SDI 框架窗口
RUNTIME_CLASS(CtestView));
AddDocTemplate(pDocTemplate);
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
ProcessShellCommand(cmdInfo);
// 唯一的一个窗口已初始化,因此显示它并对其进行更新
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
// 仅当存在后缀时才调用 DragAcceptFiles
,
// 在 SDI 应用程序中,这应在 ProcessShellCommand 之后发生
return TRUE;
}
MFC的主函数
Microsoft Visual Studio .NET 2003/Vc7/atlmfc/src/mfc/winmian.cpp
int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPTSTR lpCmdLine, int nCmdShow)
{
int nReturnCode = -1;
CWinThread* pThread = AfxGetThread();
CWinApp* pApp = AfxGetApp();
AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
pApp->InitApplication();
pThread->InitInstance();
nReturnCode = pThread->Run();
AfxWinTerm();
return nReturnCode;
}
上面构造了 pThread ,pApp;其中pApp指向theApp
;
AfxGetApp 在AfxWin1.inl中
AfxGetThread在AfxWin.h中定义,实现于
C:/Program Files/Microsoft Visual Studio .NET 2003/Vc7/atlmfc/src/mfc/thrdcore.cpp
为了简化过程,上面的代码中的判断语句被我删掉了!
开始
第一步:
搜索到 m_pMainWnd 的赋值的地方
C:/Program Files/Microsoft Visual Studio .NET 2003/Vc7/atlmfc/src/mfc/docsingl.cpp
(1.1)CDocument* CSingleDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName, BOOL bMakeVisible)
{
............
if (bCreated && pThread->m_pMainWnd == NULL)
{
// set as main frame (InitialUpdateFrame will show the window)
pThread->m_pMainWnd = pFrame;
}
......................
}
C:/Program Files/Microsoft Visual Studio .NET 2003/Vc7/atlmfc/src/mfc/thrdcore.cpp
(1.2)UINT APIENTRY _AfxThreadEntry(void* pParam)
{
if (pApp != NULL &&pThread->m_pMainWnd == NULL && pApp->m_pMainWnd->GetSafeHwnd() != NULL)
{
// just attach the HWND
threadWnd.Attach(pApp->m_pMainWnd->m_hWnd);
pThread->m_pMainWnd = &threadWnd;
}
}
初步估计,可能是在(1.1)中初始化。猜测理由:初始化的时候,可能需要打开一个默认的空文档。
第二步:
2.1 查找OpenDocumentFile调用的地方,
如果我的猜测方向没有错的话,那么在搜索到的文件中,应该是void CDocManager::OnFileNew()中调用的
void CDocManager::OnFileNew()
{
CDocTemplate* pTemplate = (CDocTemplate*)m_templateList.GetHead();
CNewTypeDlg dlg(&m_templateList);
INT_PTR nID = dlg.DoModal();
if (nID == IDOK)
pTemplate = dlg.m_pSelectedTemplate;
else
return; // none - cancel operation
pTemplate->OpenDocumentFile(NULL);
// if returns NULL, the user has already been alerted
}
上述函数位于C:/Program Files/Microsoft Visual Studio .NET 2003/Vc7/atlmfc/src/mfc/docmgr.cpp中
2.2 接着找OnFileNew的调用地方。
这个可能比较多,具体什么地方调用的我还不敢估计;先全部找出来在说!
呵呵,很幸运,我在MFC目录下搜索,只有两个文件调用过这个函数;
C:/Program Files/Microsoft Visual Studio .NET 2003/Vc7/atlmfc/src/mfc/apppdlg.cpp
void CWinApp::OnFileNew()
{
if (m_pDocManager != NULL)
m_pDocManager->OnFileNew();
}
BOOL CWinApp::ProcessShellCommand(CCommandLineInfo& rCmdInfo)
{
BOOL bResult = TRUE;
switch (rCmdInfo.m_nShellCommand)
{
case CCommandLineInfo::FileNew:
if (!AfxGetApp()->OnCmdMsg(ID_FILE_NEW, 0, NULL, NULL))
OnFileNew();
if (m_pMainWnd == NULL)
bResult = FALSE;
break;
// If we've been asked to open a file, call OpenDocumentFile()
case CCommandLineInfo::FileOpen:
if (!OpenDocumentFile(rCmdInfo.m_strFileName))
bResult = FALSE;
break;
……...…………………………………………………………………..
}
OK ,找到了,CWinApp::ProcessShellCommand调用CWinApp::OnFileNew,CWinApp::OnFileNew调用
CDocManager::OnFileNew()
现在找到了什么地方初始化的m_pMainWnd,但是这个消息循环是怎么调用的呢?难道初始化的时候,一开始CCommandLineInfo::FileNew:就有这个消息?
2.3 再接再厉
C:/Program Files/Microsoft Visual Studio .NET 2003/Vc7/atlmfc/include/afxwin.h中有这个定义
enum { FileNew, FileOpen, FilePrint, FilePrintTo, FileDDE, AppRegister,AppUnregister, FileNothing = -1 } m_nShellCommand;
C:/Program Files/Microsoft Visual Studio .NET 2003/Vc7/atlmfc/include/appcore.cpp中有如下定义:
CCommandLineInfo
的构造函数如下:
CCommandLineInfo::CCommandLineInfo()
{
m_bShowSplash = TRUE;
m_bRunEmbedded = FALSE;
m_bRunAutomated = FALSE;
m_nShellCommand = FileNew;
}
void CWinApp::ParseCommandLine(CCommandLineInfo& rCmdInfo)
{
for (int i = 1; i < __argc; i++)
{
LPCTSTR pszParam = __targv[i];
BOOL bFlag = FALSE;
BOOL bLast = ((i + 1) == __argc);
if (pszParam[0] == '-' || pszParam[0] == '/')
{
// remove flag specifier
bFlag = TRUE;
++pszParam;
}
rCmdInfo.ParseParam(pszParam, bFlag, bLast);
}
}
OK,搞定了,在初始化的时候,就把rCmdInfo.m_nShellCommand的值赋为FileNew,那么在调用的时候就很自然的执行Switch中的第一个case了哈。
于是调用CWinApp::OnFileNew,在调用CDocManager::OnFileNew(),接着调用pTemplate->OpenDocumentFile(NULL);最后在这个函数中初始化了m_pMainWnd。找到了!!!
新手路上,不知道这个是不是对的,还望高手指点!