MFC 详解一之重要的类

来源:互联网 发布:ikbc c87换mac键位 编辑:程序博客网 时间:2024/06/06 07:42

最近好久没用vs下的mfc工程了,感觉对于mfc越来越感到莫名的陌生不了解,网上也看到好多关于mfc的深入了解的博客,其中liyi268这位前辈对于mfc六大机制总结的很到位,还有就是《深入浅出 MFC》侯俊杰的这本书更是没话说,一直犹豫要不要写mfc方面的博客,最终想想为什么不写呢,也许自身的水平有限,也许投入写mfc博客的时间会比较长,但是自身总结一方面能加深印象,促使自己更加深入的了解,一方面也希望大神们给予指证,发现一些自身没发现的问题。

正文开始前,想先总结下mfc类层次关系中几个重要的类和它里面一些重要的函数作用


一:CObject :

CObject 是大多数MFC类的根类或基类。CObject类有很多有用的特性:对运行时类信息的支持,对动态创建的支持,对串行化的支持,对象诊断输出,以下是摘自mfc里的CObject类的一部分

class AFX_NOVTABLE CObject{public:virtual CRuntimeClass * GetRuntimeClass() const; // (1)public:// 与运行时类信息、串行化相关的函数BOOL IsSerializable() const;BOOL IsKindOf(const CRuntimeClass* pClass) const;// (2)// Overridablesvirtual void Serialize(CArchive& ar);// #if defined(_DEBUG) || defined(_AFXDLL)virtual void AssertValid() const;//virtual void Dump(CDumpContext& dc) const;#endifpublic:static const CRuntimeClass classCObject;  #ifdef _AFXDLLstatic CRuntimeClass* PASCAL _GetBaseClass();static CRuntimeClass* PASCAL GetThisClass();#endif};

接下来对CObject做一些简单的解析

(1): CRuntimeClass在MFC中定义为一个数据结构,在文件AFX.H中声明,它是用来串起MFC从CObject继承下来的所有类(相当于一根绳,只要你牵住绳的一头你就可以得到绳上的所有数据)通过关联(即通过链表的方法连接起来)CRuntimeClass对象从而能在运行时得到类的实例或者它的基类

每个从CObject中派生的类都有一个CRuntimeClass对象同它关联以完成在运行时得到类实例的信息或者是它的基类。 在afx.h中CRuntimeClass的定义如下:

struct CRuntimeClass{LPCSTR m_lpszClassName; //类名int m_nObjectSize; //包含CRuntimeClass对象的类sizeof的大小UINT m_wSchema; // 分类编号(对不可分类的类,该值为-1)。CObject* (PASCAL* m_pfnCreateObject)(); // 指向一个建立实例的构造函数,创建一个类的对象,抽象类则返回NULL,运行时类型识别时返回的是NULL,而动态创建有效// CObject*(* m_pfnCreateObject)(); 指向函数的指针 如:int (*fn)();#ifdef _AFXDLLCRuntimeClass* (PASCAL* m_pfnGetBaseClass)();//是一个指向函数的指针,该函数返回基类的CRuntimeClass结构。 #elseCRuntimeClass* m_pBaseClass; // 如果你的应用程序是静态地链接到MFC的,则是一个指向基类的CRuntimeClass结构的指针。#endif//以上m_pBaseClass的指针(函数)是MFC运行时确定类层次的关键,它一个简单的单向链表CObject* CreateObject(); //这个函数给予CObject派生类运行时动态建立的能力,从CObject派生的类可以支持动态创建,这是在运行时创建一个指定类的对象的能力//例如,文档,视和框架类就应该支持动态创建。CreateObject成员函数可以用来实现这个功能,在运行时为这些类创建对象。BOOL IsDerivedFrom(const CRuntimeClass* pBaseClass) const;   //判断是不是一个基类的子类//使用它可以判断某类是否是从pBaseClass指向的类在派生来。*/void Store(CArchive& ar) const;static CRuntimeClass* PASCAL Load(CArchive& ar, UINT* pwSchemaNum);CRuntimeClass* m_pNextClass; // linked list of registered classes};

m_pBaseClass指向基类的CRuntimeClass结构指针,通过m_pNextClass就可以将类型关系连接起来,只要拿住表头pFirstClass就能拿到整一张关系

继承链表

而GetRuntimeClass()这函数,就可以拿到这跟绳子的头。

(2)Iskindof() ,能在执行期侦测某个对象是否属于某种类

它将参数所指定的某个CRuntimeClass

BOOL CObject::IsKindof(const CRuntimeClass *pClass) const{CRuntimeClass * pClassThis = GetRuntimeClass();while(pClassThis != NULL){if(pClass == pClassThis){return TRUE;}pClassThis = PClassThis->m_pBaseClass;}return FALSE;}

注意的是追踪路线,是靠m_pBaseClass,并不是m_pNextClass,

例如:CView * PView = new CView();

PView ->IsKindof(RUNTIME_CLASS(CWinApp));

IsKindof()函数内的参数就是 &CWinApp::classCWinApp,而GetRuntimeClass()先取得的是&CView::classCView,之后(CView,CWnd,CCmdTarget,CObject)循序向上查找

二:CCmdTarget 

该类派生于CObject,它封装了MFC的消息映射机制,希望接收系统事件和窗口消息的类都从它派生,如CDocument和CWnd分支。此外,在系统繁忙,无法响应窗口消息时,鼠标光标应该显示为沙漏形等待状态,CCmdTarget类封装了3个成员函数完成该功能。封装COM的 IDispatch接口是它的另一项主要功能。IDispatch是COM的标准接口,不含指针操作的语言(如VB)以及描述性语言(如Web脚本语言和VBA)都通过该接口操作COM组件。CCmdTarget类以一种类似消息映射的机制提供IDispatch接口,所以使用MFC可以轻松地编写AUTOMATION客户程序和组件,

对于Com编程,以前在基于Directshow播放器中有用到过,这里简单提一下,具体Com细节待总结后再分享出来,所谓COM(Componet Object Model,组件对象模型),是一种说明如何建立可动态互变组件的规范,可以将一个单独的复杂程序划分为多个独立的模块进行开发,这里的每一个独立模块都是一个自给自足的组件,可以采取不同的开发语言去设计每一个组件,在运行时将这些组件通过接口组装起来以形成所需要的应用程序,遵循COM标准的任何一个组件都是可以被用来组合成应用程序的。至于对组件采取的是何种编程语言则是无关紧要的,可以自由选取,真正意义上的组件,应具备如下特征:
  1) 实现了对开发语言的封装。
  2) 以二进制形式发布。
  3) 能够在不妨碍已有用户的情况下被升级。
  4) 在网络上的位置必须能够被透明的重新分配。

总结下CCmdTarget这个类的作用:

1)消息发送,virtual BOOL OnCmdMsg(UINT nID,int nCode,void* pExtra,AFX_CMDHANDLERINFO* pHandlerInfo );
在运行时,OnCmdMsg把命令分派到其它对象上或者调用CCmdTarget::OnCmdMsg(此函数进行真正的消息映射查找)自己处理命令。MFC应用程序通过BEGIN_MESSAGE_MAP,END_MESSAGE_MAP()这两个宏,会为CCmdTarget派生类创建一个称为<消息映射表>的<静态数据结构>,该消息映射结构将消息映射到对象所对应的消息处理函数上,当从根据消息队列中取得该消息时,就会根据这张映射结构通知相应的消息函数处理消息。
2)设置光标,成员含数BeginWaitCursor()将光标改为沙漏形状,提示程序正在进行某种操作。当操作完成时,含数EndWaitCursor()用于将光标改回到 BeginWaitCursor()之前的形状。当处于等待状态时由于某些外部操作改变了光标形状后,含数 RestoreWaitCursor()用于将光标还原为等待状态3
3)支持自动化:CCmdTarget类支持程序通过Com接口进行交互操作

三:CWinThread

class CWinThread : public CCmdTarget{public:CWinThread();BOOL CreateThread(DWORD dwCreateFlags = 0, UINT nStackSize = 0,LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);// AttributesCWnd* m_pMainWnd; // 保存指向应用程序的主窗口的指针(usually same AfxGetApp()->m_pMainWnd)CWnd* m_pActiveWnd;     // 指向容器应用程序的主窗口BOOL m_bAutoDelete;     // 指定线程结束时是否要销毁对象 HANDLE m_hThread;       //  当前线程的句柄DWORD m_nThreadID;      // 当前线程的ID// OperationsDWORD SuspendThread();//增加一个线程的挂起计数DWORD ResumeThread();//减少一个线程的挂起计数 BOOL PostThreadMessage(UINT message, WPARAM wParam, LPARAM lParam);//向另外的CWinThread对象传递一条消息// Overridablesvirtual BOOL InitInstance();//重载以实现线程实例的初始化virtual int Run(); //线程的具有消息收发功能的控制函数,可重载以定制缺省的消息循环virtual BOOL PreTranslateMessage(MSG* pMsg);//在消息被发送到Windows函数TranslateMessage和DispatchMessage之前过滤消息virtual BOOL OnIdle(LONG lCount); // 重载以进行线程特定的空闲操作  virtual int ExitInstance(); // 重载以进行线程终止时的清理工作 // Advanced: handling messages sent to message filter hookvirtual BOOL ProcessMessageFilter(int code, LPMSG lpMsg);//在特定的消息到达应用程序之前截获消息virtual CWnd* GetMainWnd();};
上面载录的是一些相对重要的函数方法,基本上一个派生CWinThread的子类,里面重写InitInstance(),ExitInstance(),Run()父类的虚函数,再调用CreateThread()方法;就会进到子类重写的Run()函数,进行线程处理,而里面加个标志位就能进行线程循环,而重写PreTranslateMessage(MSG * pMsg)函数,在对消息DispatchMessage派发消息之前就能截取所有消息。

四:CWinApp

MFC希望把程序的主函数的函数体部分也当做一个对象来处理,为此提供了应用程序类CWinApp,CWinApp类中还有三个可以重写的虚成员函数InitApplication(),InitInstance(),Run()。其中成员函数InitInstance()是为了程序创建窗口和显示窗口所设置的。因此在设计程序时,必须在CWinApp类的基础上派上出自己的应用程序类,并且对函数InitInstance进行重写,以实现对窗口的不同要求。
常用函数如:
AfxGetApp 返回指向应用程序的CWinApp对象的指针.
AfxGetInstanceHandle 返回应用程序的实例的句柄.
GfxGetResoureHandle 返回程序的资源的句柄.
AfxGetAppName 返回应用程序的名称.

五:CDocument

不管是SDI应用程序还是MDI应用程序,文档类都是从CDocument类派生出来的。
在文档/视图结构中,文档的主要任务通常是对数据库进行管理和维护,数据将保存在文档类的成员变量中,视图通过对这些变量的访问来获取或送回数据,并能通过这种方式来更新并显示数据。
文档还负责将数据存储到永久的存储介质中,通常是磁盘或数据库,这个存取过程称之为串行化。
此外,文档还可以处理来自菜单、工具栏、加速键等对象的命令消息(WM_ COMMAND)。除WM_COMMAND外,文档不能处理其他的Window消息。

每一种类型的文档都有与之对应的文档模板进行管理,文档模板负责创建和管理当前类型的所有文档,文档与文档关联的视图和与视图关联的框架窗口都由文档模板所创建。
程序框架提供了两个文档模板类:CSingleDocTemplate和CMultiDocTemplate,分别用于支持SDI应用程序和MDI应用程序。
SDI应用程序一次只能使用一种类型的文件,如Windows中的画图、记事本等。
MDI应用程序则可以同时使用多种类型的文件,如Word、Excel等。

六:CWnd

CWnd类提供了微软基础类库中所有窗口类的基本功能
(1)CWnd类提供Create()成员函数创建windows窗口,可通过参数设置窗口大小,风格种类,同时CWnd类提供了对窗口类的一些操作方法,例如MoveWindow();GetDlgItem();SetWindowText();SetFont();移动窗口位置,获取窗口下的子窗口,设置文本,字体等等。还有一些更新界面,对图形界面绘图的一些处理。
句柄是类或者对象标识自己的句柄。凡是窗口都有一个句柄用来标识自己要是比较了解窗口的句柄的概念,这里有个问题:向一般的对窗口的操作都必须带入相应窗口的句柄,可是你会发现这边CWnd类对窗口的操作是不带入窗口句柄的,原因很简单在CWnd类中将这个句柄作为一个成员变量直接封装,所以CWnd类的成员函数都没有句柄这个参数了,这也就是CWnd 和HWND m_hWnd的区别,HWND是句柄,CWnd是MFC窗口类,CWnd中包含HWND句柄成员对象m_hWnd,两则可以互相装换:
CWnd * pCWnd;
HWND Handle;
pCWnd=FromHandle(Handle);
Handle=GetSafeHwnd(pCWnd);
(2)自定义消息传递sendMessage(),postMessage(),前者阻塞的,或者非阻塞的,即前面的调用sendMessage()是要等消息处理函数完成后,才能继续做接下去的事情,而postMessage(),则是将消息放到消息队列中,就返回了,消息循环GetMessage()按顺序处理消息队列中的消息
(3)剪切板,定时器:剪切板能实现进程间的通信,具体原理是GlobalAlloc()分配内存,此内存不为任一进程私有,由操作系统管理,而剪切板能在这一块内存放入数据,从而能实现进程间通信,需要强调的一点是GlobalAlloc()分配的内存是在剪切板放入数据后,再为这块数据分配内存的。定时器SetTimer()就不多介绍了
(4)消息的处理函数:作用占CWnd窗口类很大一块比重,首先MFC消息有三种类型:
标准消息:除WM_COMMAND之外,所有WM_开头的消息,例如WM_DESTROY(),WM_SIZE()
命令消息:WM_COMMAND消息,来自菜单、加速键或工具栏按钮的消息
通知消息:从控件产生的消息
对这三类消息,CWnd都有相应的消息处理函数

这里谈了6个MFC中重要的类,有些点也并没有深入进去,简单总结了下这6个类在MFC框架中所扮演的作用,和它里面一些常用的成员函数,在自己的类调用父类某个方法时能知道这个方法是在哪个父类中实现的。已经有好久没更新博客了,看到这篇博客差不多楼主已经跳槽了。毕业后的第一次跳槽,也算划过另一个篇章,到了另一家公司开启我的程序猿之旅。

0 0
原创粉丝点击