MFC源码剖析之——MFC来龙去脉.doc_第1页
MFC源码剖析之——MFC来龙去脉.doc_第2页
MFC源码剖析之——MFC来龙去脉.doc_第3页
MFC源码剖析之——MFC来龙去脉.doc_第4页
MFC源码剖析之——MFC来龙去脉.doc_第5页
已阅读5页,还剩9页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

MFC源码剖析之MFC来龙去脉.txt熬夜,是因为没有勇气结束这一天;赖床,是因为没有勇气开始这一天。朋友,就是将你看透了还能喜欢你的人。MFC源码剖析之MFC来龙去脉以传统的C/SDK 撰写Windows 程序,最大的好处是可以清楚看见整个程序的来龙去脉和消息动向,然而这些重要的动线在MFC 应用程序中却隐晦不明,因为它们被ApplicationFramework 包起来了。这一章主要目的除了解释MFC 应用程序的长像,也要从MFC 源代码中检验出一个Windows 程序原本该有的程序进入点(WinMain)、视窗类别注册(RegisterClass)、窗口产生(CreateWindow)、消息循环(Message Loop)、窗口函数(WindowProcedure)等等动作,抽丝剥茧彻底了解一个MFC 程序的诞生与结束,以及生命过程。先来看个一般的C/SDK Windows程序:/ Generic.cpp 一般的windows程序流程演示#include LRESULT CALLBACK MyWinProc(HWND,UINT,WPARAM,LPARAM);/6int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd)WNDCLASS wc;wc.cbClsExtra=0;wc.cbWndExtra=0;wc.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);wc.hCursor=LoadCursor(NULL,IDC_ARROW);wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);wc.hInstance=hInstance;wc.lpfnWndProc=MyWinProc;wc.lpszClassName=generic;wc.lpszMenuName=NULL;wc.style=CS_VREDRAW | CS_HREDRAW;/窗口的样式RegisterClass(&wc);/注册窗口类1HWND hWnd;hWnd=CreateWindow(generic,Title,WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,hInstance,NULL);/产生窗口2ShowWindow(hWnd,SW_NORMAL);/显示窗口3UpdateWindow(hWnd);/更新窗口4MSG msg;while(GetMessage(&msg,NULL,0,0)TranslateMessage(&msg);DispatchMessage(&msg);/消息循环5return 0;LRESULT CALLBACK MyWinProc(HWND hwnd,/窗口句柄UINT uMSG,/消息标志符WPARAM wParam,/消息的第一个参数LPARAM lParam/消息的第二个参数)switch(uMSG)case WM_LBUTTONDOWN:/鼠标左键点击MessageBox(hwnd,点击,HelloWorld,MB_OK);break;case WM_CLOSE:/窗口被关闭消息if(IDYES=MessageBox(hwnd,是否真的要关闭窗口?,提示,MB_YESNO)DestroyWindow(hwnd);break;case WM_PAINT:/窗口被绘制时消息HDC hDc;PAINTSTRUCT ps;hDc=BeginPaint(hwnd,&ps);TextOut(hDc,0,10,第一行字符,10);EndPaint(hwnd,&ps);break;case WM_DESTROY:/窗口被销毁是消息PostQuitMessage(0);/有了这个才可以使while(GetMessage(&MSG, , ,)为假break;default:/处理我不感兴趣的消息return DefWindowProc(hwnd,uMSG,wParam,lParam);return 0;从上述的原码中可以看出windows程序设计有个基本的脉络:Register Class(WNDCLASS*)Create WindowShow WindowUpdate Window Message LoopWindow procedure。但我们从MFC中看不出有这么一个流程,甚至是看不到程序的入口function named WinMain。即便是基于MFC的应用程序,建立窗口类也是会遵循如下的过程:设计窗口类-注册窗口类-生成窗口-显示窗口-更新窗口-消息循环-消息路由到窗口过程函数处理。下面就剖析一下在MFC中是如何完成上述过程的。(1)每个应用程序都有且仅有一个应用类的全局变量theApp,全局变量先于WinMain函数进行处理。(2)WinMain函数体在APPMODUL.CPP文件中,定义如下:extern C int WINAPI_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPTSTR lpCmdLine, int nCmdShow)/ call shared/exported WinMainreturn AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);其中#define _tWinMain WinMain(3)AfxWinMain函数体在WINMAIN.CPP文件中,里面有如下两句话:CWinThread* pThread = AfxGetThread();CWinApp* pApp = AfxGetApp();其实这里得到的这两个指针都是指向全局的对象theApp的;接下来有函数调用pThread-InitInstance(),根据多态性,会调用CXXXApp类中的InitInstance()函数。该 函数很重要,在对该函数的调用中就会完成:设计窗口类-注册窗口类-生成窗口-显示窗口-更新窗口。接下来,该函数中会继续调用pThread-Run(),这就完成了:消息循环-消息路由到窗口过程函数处理。(4)进入CXXXApp:InitInstance()函数体中,对于单文档应用程序,调用ProcessShellCommand(cmdInfo),通过调用该函数就会完成:设计窗口类-注册窗口类-生成窗口。再接下来就会调用m_pMainWnd-ShowWindow(SW_SHOW);m_pMainWnd-UpdateWindow();这就完成了:显示窗口-更新窗口。(5)在函数CWinApp:ProcessShellCommand(CCommandLineInfo& rCmdInfo)中会进入到如下的case分支:case CCommandLineInfo:FileNew:if (!AfxGetApp()-OnCmdMsg(ID_FILE_NEW, 0, NULL, NULL)(6)进入函数CCmdTarget:OnCmdMsg(UINT nID, int nCode, void* pExtra,AFX_CMDHANDLERINFO* pHandlerInfo),调用_AfxDispatchCmdMsg(this, nID, nCode,lpEntry-pfn, pExtra, lpEntry-nSig, pHandlerInfo);(7)进入函数AFXAPI _AfxDispatchCmdMsg(CCmdTarget* pTarget, UINT nID, int nCode,AFX_PMSG pfn, void* pExtra, UINT nSig, AFX_CMDHANDLERINFO* pHandlerInfo),调用case AfxSig_vv:/ normal command or control notificationASSERT(CN_COMMAND = 0); / CN_COMMAND same as BN_CLICKEDASSERT(pExtra = NULL);(pTarget-*mmf.pfn_COMMAND)();(8)进入CWinApp:OnFileNew(),调用m_pDocManager-OnFileNew();这个函数很特殊,它本身是个消 息响应函数,当我们点击ID为ID_FILE_NEW的菜单时,会产生一个命令消息,由于命令消息可以被CCmdTarget类及其派生类来捕获,而 CWinApp是从CCmdTarget派生出来的,因此可以捕获这个消息。当应用程序创建完成并成功显示后,当我们点击文件菜单下的新建菜单项时,就会 首先进入这个函数,然后再依次执行下去,最后就会执行到pDocument-OnNewDocument()中,往往我们会对这个函数不解,不知 道它为什么会响应ID_FILE_NEW的命令消息,至此真相大白了。顺便说一句,为什么程序在刚启动的时候,我们并没有点击菜单项,为什么会自动的产生 这个消息呢?这是因为在CXXXXApp:InitInstance()函数中有“CCommandLineInfo cmdInfo;”这个类的构造函数是这样的:CCommandLineInfo:CCommandLineInfo()m_bShowSplash = TRUE;m_bRunEmbedded = FALSE;m_bRunAutomated = FALSE;m_nShellCommand = FileNew;,因此就会在第(5)步骤的时候进入到“case CCommandLineInfo:FileNew:”这个分支中,就相当于产生了这样一个FileNew的消息。同理对于ID为 ID_FILE_OPEN(在CWinApp:OnFileOpen()中响应)、ID_FILE_SAVE(在 CDocument:OnFileSave()中响应)等等在MFC向导为我们生成的单文档类中找不到消息响应的入口时,其实都是在基类CWinApp或者CDocument类中进行了响应。对于CXXXXDoc:Serialize(CArchive& ar)函数也是通过ID_FILE_SAVE和ID_FILE_OPEN产生命令消息后就行响应从而才调用该函数的。(9)进入CDocManager:OnFileNew(),CDocManager类有一个成员变量是CPtrListm_templateList;该变量保存了一个文档模版链表指针,在CDocManager:OnFileNew()函数体中会调用 CDocTemplate* pTemplate = (CDocTemplate*)m_templateList.GetHead();得到链表中的头,也就是第一个文档模版,后面就会用得到的这个指针去 调用pTemplate-OpenDocumentFile(NULL);紧接着就会有一个判断,用来确定该链表中是否只有一项,如果链表中保存 了多个文档模版,则会弹出一个对话框,来让我们选择到底是使用哪一套文档模版来构建应用程序,相信大家也都见到过这种情况吧。对了,还有一点要说明的 是:pTemplate是一个CDocTemplate的指针,但接下来程序为什么会进入到CSingleDocTemplate:OpenDocumentFile的函数体内呢,这是因为CDocTemplate类中的OpenDocumentFile函数被定义为纯虚函数,而CSingleDocTemplate类又是从CDocTemplate类派生出来的,并且实 现了该函数,因此就会进入到子类的函数体中了。(10)进入CDocument* CSingleDocTemplate:OpenDocumentFile(LPCTSTR lpszPathName,BOOL bMakeVisible),先调用CreateNewDocument()创建文档类,再调用pFrame =CreateNewFrame(pDocument, NULL);创建框架类和视图类,从这里也可以看出MFC体系结构中文档、框架、视图“三位一体”的模式,在这一个函数中同时创建三个类;再会调用pDocument-OnNewDocument();因此就会进入到子类的文档类中的pDocument-OnNewDocument()中了。(11)进入CFrameWnd* CDocTemplate:CreateNewFrame(CDocument* pDoc, CFrameWnd*pOther),调用if (!pFrame-LoadFrame(m_nIDResource,WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, / default frame stylesNULL, &context)(12)进入BOOL CFrameWnd:LoadFrame(UINT nIDResource, DWORD dwDefaultStyle,CWnd* pParentWnd, CCreateContext* pContext),调用VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG);(13)进入BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister),该函数内部就完成了:设计窗口类-注册窗口类。MFC通过给我们提供好一些已经订制好的窗口类,我们不需要自己再设计 窗口类,只需要到那些订制好的窗口类“仓库”中寻找一种适合我们需要的窗口类就可以了,然后通过AfxRegisterClass函数注册窗口类。还需要 说明的是,再后续的跟踪过程中,我们会发现还会进入到AfxEndDeferRegisterClass函数中进行设计和注册窗口类,这主要是因为单文档 应用程序比较特殊,它提前通过这样的一种途径进行了窗口类的设计和注册步骤,其实是应该在BOOL CMainFrame:PreCreateWindow(CREATESTRUCT& cs)函数的调用中完成窗口类的设计和注册的,这一点我们要清楚,也就是说设计和注册窗口类的正宗发源地应该是 PreCreateWindow(CREATESTRUCT& cs)。此外,我们还会注意到在该函数体的前部分有一语句为“wndcls.lpfnWndProc = DefWindowProc;”因此所有窗口类的窗口过程函数都是DefWindowProc,这一点在后面的跟踪中可以看到,每次生成窗口之后都会调用 几次DefWindowProc函数。也就是说MFC都是让我们采用默认的窗口过程函数,这并不是说我们因此就不能使用自己的窗口过程函数实现个性化的消 息处理了,MFC采用了一种基于消息映射的机制完成了消息个性化处理。(14)回到BOOL CFrameWnd:LoadFrame(UINT nIDResource, DWORD dwDefaultStyle,CWnd* pParentWnd, CCreateContext* pContext)中,调用LPCTSTR lpszClass =GetIconWndClass(dwDefaultStyle, nIDResource);(15)进入LPCTSTR CFrameWnd:GetIconWndClass(DWORD dwDefaultStyle, UINTnIDResource),调用PreCreateWindow(cs);(16)进入BOOL CMainFrame:PreCreateWindow(CREATESTRUCT& cs),调用CFrameWnd:PreCreateWindow(cs)(17)进入BOOL CFrameWnd:PreCreateWindow(CREATESTRUCT& cs),调用VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG);又一次设计和注册窗口类(18)回到BOOL CFrameWnd:LoadFrame(UINT nIDResource, DWORD dwDefaultStyle,CWnd* pParentWnd, CCreateContext* pContext)中,调用if (!Create(lpszClass,lpszTitle, dwDefaultStyle, rectDefault, pParentWnd, MAKEINTRESOURCE(nIDResource),0L, pContext)(19)进入BOOL CFrameWnd:Create(LPCTSTR lpszClassName,LPCTSTR lpszWindowName,DWORD dwStyle,const RECT& 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)(20)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),调用if (!PreCreateWindow(cs),接下来调用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);好了,终于让我们找到生成窗口的地方了函数:CreateWindowEx!(21)进入int CMainFrame:OnCreate(LPCREATESTRUCT lpCreateStruct),调用if(CFrameWnd:OnCreate(lpCreateStruct) = -1)(22)进入int CFrameWnd:OnCreate(LPCREATESTRUCT lpcs),调用returnOnCreateHelper(lpcs, pContext);(23)进入int CFrameWnd:OnCreateHelper(LPCREATESTRUCT lpcs, CCreateContext*pContext),调用if (CWnd:OnCreate(lpcs) = -1)(24)进入_AFXWIN_INLINE int CWnd:OnCreate(LPCREATESTRUCT),调用return(int)Default();(25)进入LRESULT CWnd:Default(),调用returnDefWindowProc(pThreadState-m_lastSentMsg.message,pThreadState-m_lastSentMsg.wParam, pThreadState-m_lastSentMsg.lParam);(26)进入LRESULT CWnd:DefWindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam),调用return :CallWindowProc(m_pfnSuper, m_hWnd, nMsg, wParam, lParam);(27)回到int CFrameWnd:OnCreateHelper(LPCREATESTRUCT lpcs, CCreateContext*pContext),调用if (!OnCreateClient(lpcs, pContext)(28)进入BOOL CFrameWnd:OnCreateClient(LPCREATESTRUCT, CCreateContext*pContext),调用if (CreateView(pContext, AFX_IDW_PANE_FIRST) = NULL)(29)进入CWnd* CFrameWnd:CreateView(CCreateContext* pContext, UINT nID),调用if (!pView-Create(NULL, NULL, AFX_WS_DEFAULT_VIEW, CRect(0,0,0,0), this, nID,pContext)(30)进入BOOL CWnd:Create(LPCTSTR lpszClassName,LPCTSTR lpszWindowName, DWORD dwStyle,const RECT& rect,CWnd* pParentWnd, UINT nID,CCreateContext* pContext),调用return CreateEx(0, lpszClassName, lpszWindowName,dwStyle | WS_CHILD,rect.left, rect.top,rect.right - rect.left, rect.bottom - rect.top,pParentWnd-GetSafeHwnd(), (HMENU)nID, (LPVOID)pContext);(31)进入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),重复生成框架类CMainFrame的过程来生成CXXXView,因为它也是一个窗口类,因此也需要进行那一系列过程才能最终显示更新出来。调用的顺序是这个样子的:PreCreateWindow(cs)-BOOLCXXXView:PreCreateWindow(CREATESTRUCT&cs)-CView:PreCreateWindow(cs)-VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG);-:CreateWindowEx(.)-CWnd:DefWindowProc-:CallWindowProc(.)-.-CXXXView:OnCreate-CView:OnCreate-CWnd:OnCreate-.写到这里,基本上就清楚了,中间的省略号表示的部分大多数都是在与窗口过程函数有关的,因为在生成窗口的时候需要响应一些消息,因此需要调用一些窗口过程 函数,每次在调用:CreateWindowEx(.)函数后都会调用一些窗口过程函数,然后再去调用该窗口类对应的OnCreate函数,其实在 调用OnCreate函数之前调用CreateWindowEx只是生成了一个窗口,至于这个窗口里面要放置些什么东西,以及该如何装饰该窗口,则就需要 由OnCreate来完成了,往往我们都会在OnCreate函数的后面(这样做是为了不影响窗口本身应该布置的格局)添加一些代码,创建我们自己的东 西,比如我们通常会在CMainFrame类的OnCreate函数后面放置一些Create代码,来创建我们自己的可停靠的工具栏或者按钮之类的东西, 当然我们也可以在CXXXView类的OnCreate函数的后面添加一些代码,来创建我们需要的东西,比如按钮之类的东西。在完成了从设计、注册到生成 窗口的过程之后,往往还需要显示更新,有些时候,我们不必要每次都显示的调用CWnd的ShowWindow和UpdateWindow两个函数,我们可 以在创建的时候,给窗口风格中添加WS_VISIBLE即可,因此有些时候会跟踪不到ShowWindow和UpdateWindow两个函数这两个函 数,因为窗口在创建的时候就可见了。总的来说,先初始化应用类,然后注册生成框架类,然后再注册生成视图类,然后注册生成视图类OnCreate函数后面用户添加的、用Create来准备创 建的窗口,然后再注册生成框架类的OnCreate函数后面需要生成的m_wndToolBar、m_wndStatusBar以及我们自己添加的要创建 的窗口类,最后在回到应用类的初始化的函数体中,调用框架类的显示和更新函数,然后再进入由框架类定义的窗口的消息循环中。消息循环的过程是这个样子的:(1)调用int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPTSTR lpCmdLine, int nCmdShow)函数中的pThread-Run()(2)进入int CWinApp:Run(),调用return CWinThread:Run();(3)进入int CWinThread:Run(),调用if (!PumpMessage()(4)进入BOOL CWinThread:PumpMessage(),调用if (!:GetMessage(&m_msgCur, NULL,NULL, NULL)(5)回到BOOL CWinThread:PumpMessage(),调用:TranslateMessage(&m_msgCur);:DispatchMessage(&m_msgCur);(6)回到int CWinThread:Run(),调用while (:PeekMessage(&m_msgCur, NULL, NULL,NULL, PM_NOREMOVE);(7)再重复(4)-(6)的步骤下面给出int CWinThread:Run()中消息循环的部分代码:do/ pump message, but quit on WM_QUITif (!PumpMessage()return ExitInstance();/ reset no idle state after pumping normal messageif (IsIdleMessage(&m_msgCur)bIdle = TRUE;lIdleCount = 0; while (:PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE);这段代码其实本质上与我们基于Win32 SDK手写的代码:/消息循环MSG msg;while(GetMessage(&msg,NULL,0,0)/简单的说,函数TranslateMessage就是把WM_KEYDOWN和WM_KEYUP翻译成WM_CHAR消息,没有该函数就不能产生WM_CHAR消息。TranslateMessage(&msg);:DispatchMessage(&msg);是一致的。Visual C+ MFC 中常用宏的含义AND_CATCHAND_CATCHAND_CATCH(exception_class,exception _object_point_name)说明:定 义一个代码块,它用于获取废除当前TRY块中的附加异常类型。使用CATCH宏以获得一个异常类型,然后使用AND_CATCH宏获得随后的异常处理代码 可以访问异常对象(若合适的话)已得到关于异常的特别原因的更多消息。在AND_CATCH块中调用THROW_LAST宏以便把处理过程移到下个外部异 常框架。AND_CATCH可标记CATCH或AND_CATCH块的末尾。注释:AND_CATCH块被定义成为一个C+作用域(由花括号来描述)。若用户在此作用域定义变量,那么记住他们只在此作用域中可以访问。他也用于exception_object_pointer_name变量。ASSERTASSERT(booleanExpression)说明:计 算变量的值。如果结构的值为0,那么此宏便打印一个诊断消息并且成讯运行失败。如果条件为非0,那么什么也不做。 诊断消息的形式为: assertion failed in file in line 其中name是元文件名,num是源文件中运行失败的中断号。 在Release版中,ASSERT不计算表达式的值也就不中断程序。如果必须计算此表达式的值且不管环境如何那么用VERIFY代替ASSERT。注释:ASSERT只能在Debug版中用ASSERT_VAILDASSERT_VAILD(pObject)说明:用 于检测关于对象的内部状态的有效性。ASSERT_VALID调用此对象的AssertValid成员函数(把它们作为自己的变量来传递)。在 Release版中ASSERT_VALID什么也不做。在DEBUG版中,他检查指针,以不同于NULL的方式进行检查,并调用对象自己的 AssertValid成员函数。如果这些检测中有任何一个失败的话,那么他会以与ASSERT相同的方法显示一个警告的消息。注释:此函数只在DEBUG版中有效。BEGIN_MESSAGE_MAPBEGIN_MESSAGE_MAP(the class,baseclass)说明:使用BEGIN_MESSAGE_MAP开始用户消息映射的定义。在定义用户类函数的工具(.cpp)文件中,以BEGIN_MESSAGE_MAP宏开始消息映射,然后为每个消息处理函数增加宏项,接着以END_MESSAGE_MAP宏完成消息映射。CATCHCATCH(exception_class,exception_object_pointer_name)说明:使 用此用定义一个代码块,此代码用来获取当前TRY块中都一个异常类型。异常处理代码可以访问异常对象,如何合适的话,就会得到关于异常的特殊原因的更多消 息。调用THROW_LAST宏以把处理过程一下一个外部异常框架,如果exceptionclass是类CExceptioon,那么会获取所有异常 类型。用户可以使用CObject:IsKindOf成员函数以确定那个特别异常被排除。一种获取异常的最好方式是使用顺序的AND_CATCH语句, 每个带一个不同的异常类型。此异常类型的指针由宏定义,用户不必定义。注释:此CATCH块被定义作一个C+范围(由花括号描述)。如用户在此范围定义变量,那么它们只在吃范围内可以访问。他还可以用于异常对象的指针名。DEBUG_NEW#define new DEBUG_NEW说明:帮 助查找内存错误。用户在程序中使用DEBUG_NEW,用户通常使用new运算符来从堆上分配。在Debug模式下(但定义了一个DEBUG符 号),DEBUG_NEW为它分配的每个对象记录文件名和行号。然后,在用户使用CMemoryState:DumpAllObjectSince成员 函数时,每个以DEBUG_NEW分配的对象分配的地方显示出文件名和行号。 为了使用DEBUG_NEW,应在用户的资源文件中插入以下指令: #define new DEBUG_NEW 一旦用户插入本指令,预处理程序将在使用new的地方插入DEBUG_NEW,而MFC作其余的工作。但用户编译自己的程序的一个发行版 时,DEBUG_NEW便进行简单的new操作,而且不产生文件名和行号消息。DECLARE_DYNAMICDECLARE_DYNAMIC(class_name)说明:但 从CObject派生一个类时,此宏增加关于一个对象类的访问运行时间功能。把DECLARE_DYNAMIC宏加入类的头文件中,然后在全部需要访问词 类对象的.CPP文件中都包含此模块。如果像所描述那样使用DELCARE_DYNAMIC和IMPLEMENT_DYNAMIC宏,那么用户便可使用 RUNTIME_CLASS宏和CObject:IsKindOf函数以在运行时间决定对象类。如果DECLARE_DYNAMIC包含在类定义中,那 么IMPLEMETN_DYNAMIC必须包含在类工具中。DECLARE_DYNCREATEDECLARE_DYNCREATE(class_name)说明:使 用DECLARE_DYNCRETE宏以便允许CObject派生类的对象在运行时刻自动建立。主机使用此功能自动建立新对象,例如,但它在串行化过程中 从磁盘读一个对象时,文件及视图和框架窗应该支持动态建立,因为框架需要自动建立它。把DECLARE_DYNCREATE宏加入类的.H文件中,然后在 全部需要访问此类对象的.CPP文件中包含这一模式。如果DECLARE_DYNCREATE包含在类定义中,那么 IMPLEMENT_DYNCREATE必须包含在类工具中。DECLARE_MESSAGE_MAPDECLARE_MESSAGE_MAP()说明:用 户程序中的每个CCmdTarget派生类必须提供消息映射以处理消息。在类定义的末尾使用DECLARE_MESSAGE_MAP宏。接着,在定义类成 员函数的.CPP文件中,使用BEGIN_MESSAGE_MAP宏,每个用户消息处理函数的宏项下面的列表以及END_MESSAGE_MAP宏。注释:如果在DECLARE_MESSAGE_MAP之后定义任何一个成员,那么必须为他们指定一个新存取类型(公共的,私有的,保护的)。DECLARE_SERIALDECLARE_SERIAL(class_name)说明:DECLARE_SERIAL 为一个可以串行化的CObject派生类产生必要的C+标题代码。串行化是把某个对象的内容从一个文件读出和写入一文件。在.H文件中使用 DECLARE_SERIAL宏,接着在需要访问此类对象的全部.CPP文件中包含此文件。如果DECLARE_SERIAL包含在类定义中,那么 IMPLEMENT_SERIAL必须包含在类工具中。DECLARE_SERIAL宏包含全部DECLARE_DYNAMIC,IMPLEMENT_DYCREATE的功能。END_CATCHEND_CATCH说明:标识最后的CATCH或AND_CATCH块的末尾。END_MESSAGE_MAPEND_MESSAGE_MAP说明:使用END_MESSAGE_MAP宏结束用户的消息映射定义IMPLEMENT_DYNAMICIMPLEMENT_DYNAMIC(class_name,base_class_name)说明:通过运行时在串行结构中为动态CObject派生类访问类名和位置来产生必要的C+代码。在.CPP文件中使用IMPLEMENT_DYNAMIC宏,接着一次链接结果对象代码IMPLEMENT_DYNCREATEIMPLEMENT_DYNCREATE(class_name,base_class_name)说明:通 过DECLARE_DYNCREATE宏来使用IMPLEMENT_DYNCREATE宏,以允许CObject派生类对象在运行时自动建立。主机使用此 功能自动建立对象,例如,但它在串行化过程中从磁盘读去一个对象时,他在类工具里加入IMPLEMENT_DYNCREATE宏。若用户使用DECLARE_DYNCREATE和IMPLEMENT_DYNCREATE宏,那么接着使用RUNTIME_CLASS宏和CObject:IsKindOf成员函数以在运行时确定对象类。若declare_dyncreate包含在定义中,那么IMPLEMENT_DYNCREATE必须包含在类工具中。IMPLEMENT_SERIALIMPLEMENT_SERIAL(class_name,base_class_name,wSchema)说明:通过运行时在串行结构中动态CObject派生类访问类名和位置来建立必要的C+代码。在.CPP文件中使用IMPLEMENT_SERIAL宏,然后一次链接结果对象代码。ON_COMMANDON_COMMAND(id,memberFxn)说明:此 宏通过ClassWizard或手工插入一个消息映射。它表明那个函数将从一个命令用户接口(例如一个菜单项或toolbar按钮)处理一个命令消息。当 一个命令对象通过指定的ID接受到一个Windows WM_COMMAND消息时,ON_COMMAND将调用成员函数memberFxn处理此消息。在用户的消息映射中,对于每个菜单或加速器命令(必须被 映射到一个消息处理函数)应该确实有一个ON_COMMAND宏语句。ON_CONTROLON_CONTROL(wNotifyCode,id,memberFxn)说明:表明哪个函数将处理一个常规控制表示消息。控制标识消息是那些从一个控制夫发送到母窗口的消息。ON_MESSAGEON_MESSAGE(message,memberFxn)说明:指 明哪个函数将处理一用户定义消息。用户定义消息通常定义在WM_USER到0x7FF范围内。用户定义消息是那些不是标准Windows WM_MESSAGE消息的任何消息。在用户的消息映射中,每个必须被映射到一个消息处理函数。用户定义消息应该有一个ON_MESSAGE宏语句。ON_REGISTERED_MESSAGEON_REGISTERED_MESSAGE(nmessageVarible,memberFxn)说明:Windows的RegisterWindowsMesage函数用于定义一个新窗口

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论