VC+/MFC ABC

简介:


VC/MFC ABC

1. MFC 简介

Mircrosoft Foundation Classes集成在Visual Studio里面,可以用来快速开发图形应用程序。它以C++类的形式封装了windows API,并且包括:

  • 应用程序框架(为减轻程序开发人员的工作量)

  • windows句柄封装类、内建组建

1.2. 主要背景

源自AFX(Application Framework), 生于20世纪80年代末、90年代初。

具体细节:

早在1989年,Microsoft的程序员们开始试图将C++和面向对象的编程概念应用于Windows编程中,以编写出一个可以使Windows编程更加简便的应用程序框架。他们把这个应用程序框架叫做AFX (AFX这个词来源于Application Framework,但奇怪的是这个词组中并没有包含“X”这个字母)。直到今天,AFX小组早已不存在了,AFX这个名称也于1994年初不再使用,但在Visual C++MFC中,AFX的影子却随处可见,很多全局函数、结构和宏的标识符都被加上了AFX的前缀。 

  最初的AFX版本在经过一年的艰苦之后诞生,却未能被大多数Windows程序员所接受。AFX的确是经过了精心的规划和编码,并且,它也提供了对Windows API的高度抽象,建立了全新的面向对象的AFX API,但最要命的是AFX API库根本不兼容于现有的Windows API。由此导致的最严重后果是大量的SDK代码无法移植,而程序员将学习两种完全不同的编程方法。 

  AFX不得不重新做所有的一切,他们所创建的新的应用程序框架是一套扩展的C++类,它封装和映射了Windows API,这就是MFC的前身。过去的AFX小组也变成了MFC小组。最终,MFC的第一个公开版本于19923月随Microsoft C/C++ 7.0一起推出。那时距Windows 3.1发布尚有好几个月。在MFC 1.0中还没有文档/视结构,但有类CObjectCArchive。在12个月之后,MFC 2.0Microsoft新的编程工具Visual C++ 1.0一道出炉。与MFC 1.0一样,MFC 2.0仍是16位的,因为32位的Windows NT 3.1直到19937月才问世。在MFC 2.0中,增加了对文档/视结构、OLE 1.0Windows 3.1公用对话框的支持和消息映射等。在Windows NT 3.1面世一个月以后,Microsoft推出了32版本的Visual C++MFC 2.1,它实际上是MFC 2.0Win32接口。 


经过近20年的发展,MFC已发展成一个稳定和涵盖极广的C++类库,为成千上万的Win32程序员所使用。MFC库是可扩展的,它和Windows技术的最新发展到目前为止始终是同步的。并且,MFC类库使用了标准的Windows命名约定和编码格式,所以有经验的Windows SDK程序员很容易过渡到MFCMFC结合了Windows SDK编程概念和面向对象的程序设计技术,从而具有极大灵活性和易用性


1.3 特点

包装了许多win32例程,封装了里面很多函数;

由外部mfcxxx.dll动态库实现主要框架;

统一的函数入口,和windosSDK编程中的函数入口一致:()

程序运行都要有入口函数,在之前的C++教程中都是main函数,而Windows应用程序的入口函数是WinMain函数,MFC程序也是从WinMain函数开始的。

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) 



2. MFC 核心

2.1.消息映射机制(onEventName() ---- EventName)

http://blog.csdn.net/ouyang_linux007/article/details/7638434



2.1.1什么是消息

消息简单的说就是指通过输入设备向程序发出指令要执行某个操作具体的某个操作是你的一系列代码。称为消息处理函数



2.1.2 windows SDK对消息的处理

SDK中消息其实非常容易理解,当窗口建立后便会有一个函数(窗口处理函数)开始执行一个消息循环,我们还可以清楚的看到消息处理的脉络。一个switch case语句就可以搞定,消息循环直到遇到WM_QUIT消息才会结束,其余的消息均被拦截后调用相应的处理函数。



2.1.3 MFC框架中对消息的处理

UpdateWindow(hWindow);      

  1.   {      

  2.     MSG msg;      

  3.     while(GetMessage(&msg,0,0,0))      

  4.     {      

  5.       TranslateMessage(&msg);      

  6.       DispatchMessage(&msg);      

  7.     }      

  8.     return (int)msg.wParam;      

  9.   }      

  10. }      


但在封装了APIMFC中,消息似乎变的有些复杂了,我们看不到熟悉的switch case语句了,取而代之的是一个叫消息映射的东西。为什么MFC要引入消息映射机制,你可以想象一下,在现在的程序开发活动中,你的一个程序是否拥有多个窗体,主窗口就算只有一个,那菜单、工具条、控件这些都是子窗口,那我们需要写多少个switch case,并且还要为每个消息分配一个消息处理函数,这样做是多么的复杂呀。因此MFC采用了一种新的机制。利用一个数组,将窗口消息和相对应的消息处理函数进行映射,你可以理解成这是一个表。这种机制就是消息映射。这张表在窗口基类CWnd定义,派生类的消息映射表如果你没有动作它是空的,也就是说如果你不手工的增加消息处理函数,则当派生窗口接受一个消息时会执行父类的消息处理函数。这样做显然是高效的。

MFC提供的消息结构
同时MFC定义了下面的两个主要结构:
AFX_MSGMAP_ENTRY
struct AFX_MSGMAP_ENTRY{
UINT nMessage;   // Windows
消息的ID
UINT nCode;  // 
控制消息的通知
UINT nID;    // Windows
控制消息的ID
UINT nLastID;   //
表示是一个指定范围的消息被映射的范围
UINT nSig;  //
表示消息的动作标识
AFX_PMSG pfn;    // 
指向消息处理函数的指针
};
AFX_MSGMAP
struct AFX_MSGMAP{
#ifdef _AFXDLL
const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();
#else
const AFX_MSGMAP* pBaseMap;
#endif
const AFX_MSGMAP_ENTRY* lpEntries;
};
///AFX_MSGMAP
可以得到基类的消息映射入口地址和得到本身的消息映射入口地址。

MFC
下一个消息的处理过程是一般是这样的
1
_AfxCbtFilterHook截获消息(这是一个钩子函数)
2
_AfxCbtFilterHook把窗口过程设定为AfxWndProc
3
函数AfxWndProc接收Windows操作系统发送的消息。
4
、函数AfxWndProc调用函数AfxCallWndProc进行消息处理。
5
、函数AfxCallWndProc调用CWnd类的方法WindowProc进行消息处理。

如何添加自己的消息?
我们已经了解了WINDOW的消息机制,如何加入我们自己的消息呢?好我们来看
一个标准的消息处理程序是这个样子的
CWnd类中预定义了标准Windows消息(WM_XXXX  WMWINDOW MESSAGE的缩写)的默认处理程序。类库基于消息名命名这些处理程序。例如,WM_PAINT消息的处理程序在CWnd中被声明为:
afx_msg void OnPaint();
afx_msg 
关键字通过使这些处理程序区别于其他CWnd成员函数来表明C++ virtual关键字的作用。但是请注意,这些函数实际上并不是虚拟的,而是通过消息映射实现的。我们在本文的一开始便说明了为什么要这样做。
所有能够进行消息处理的类都是基于CCmdTarget类的,也就是说CCmdTarget类是所有可以进行消息处理类的父类。CCmdTarget类是MFC处理命令消息的基础和核心。

若要重写基类中定义的处理程序,只需在派生类中定义一个具有相同原型的函数,并创建此处理程序的消息映射项。我们通过ClassWizard可以建立大多数窗口消息或自定义的消息,通过ClassWizard可以自动建立消息映射,和消息处理函数的框架,我们只需要把我们要做的事情填空,添加你要做的事情到处理函数。这个非常简单,就不细说了。但是也许我们需要添加一些ClassWizard不支持的窗口消息或自定义消息,那么就需要我们亲自动手建立消息映射和消息处理的框架,通常步骤如下:
第一步:定义消息Microsoft推荐用户自定义消息至少是WM_USER+100,因为很多新控件也要使用WM_USER消息。
#define WM_MYMESSAGE (WM_USER + 100)

第二步:实现消息处理函数。该函数使用WPRAMLPARAM参数并返回LPESULT
LPESULT CMainFrame::OnMyMessage(WPARAM wParam, LPARAM lParam)
{
// TODO: 
处理用户自定义消息,填空就是要填到这里。
return 0;
}
第三步:在类头文件的AFX_MSG块中说明消息处理函数:
// {{AFX_MSG(CMainFrame)
afx_msg LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
第四步:在用户类的消息块中,使用ON_MESSAGE宏指令将消息映射到消息处理函数中
ON_MESSAGE( WM_MYMESSAGE, OnMyMessage )

可以看出,用户自定义的消息和我们通过ClassWizard添加的消息一样,都是利用了ON_MESSAGE,建立的消息映射。

其实消息类别可以分成多种,上面说的只是其中之一。有三种主要的消息类别:(以下部分摘自MSDN
1
Windows消息
此类消息主要包括以前缀WM_开头的消息,WM_COMMAND除外。Windows消息由窗口和视图处理。此类消息往往带有用于确定如何处理消息的参数。
2
、控件通知
此类消息包括从控件和其他子窗口发送到其父窗口的WM_COMMAND通知消息。例如,当用户在编辑控件(Edit Control) 中执行可能更改文本的操作后,该编辑控件(Edit Control) 将向其父级发送包含EN_CHANGE控件通知代码的WM_COMMAND消息。该消息的窗口处理程序以某种适当的方式响应此通知消息,例如在控件中检索该文本。
框架像传送其他WM_消息一样传送控件通知消息。但是有一个例外的情况,即当用户单击按钮时由按钮发送的BN_CLICKED控件通知消息。该消息被作为命令消息特别处理,并像其他命令一样传送。
3
、命令消息
此类消息包括用户界面对象(菜单、工具栏按钮和快捷键)发出的WM_COMMAND通知消息。框架处理命令的方式与处理其他消息不同,可以使用更多种类的对象处理命令。
Windows
消息和控件通知消息由窗口来处理(窗口是从CWnd类派生的类的对象)。包括CFrameWndCMDIFrameWndCMDIChildWndCViewCDialog以及从这些基类派生的您自己的类。这些对象封装了HWND——Windows窗口的句柄。
命令消息可以由范围更广的对象(文档、文档模板以及应用程序对象本身)处理,而不仅仅由窗口和视图处理。当某一命令直接影响到某个特定对象时,应当让该对象处理此命令。例如,“文件”菜单中的“打开”命令在逻辑上与应用程序相关联:该应用程序接收到此命令时会打开指定的文档。因此“打开”命令的处理程序是应用程序类的成员函数。

命令消息我们比较常见的便是菜单项和工具条了,大家可以看到他的消息映射宏和窗口消息不太一样,一般的形式是这样的
ON_COMMAND(id,memberFxn)
第一个参数是命令ID,一个ID号对应一个消息处理,当然你可以让多个ID共用一个处理函数。常见的应用例如:菜单项打开文档的ID和工具条按钮打开文档的ID同时使用一个处理函数,或者直接将它们的ID设成相同的。

还有一种消息叫通知消息。例如树型控件的等一些复杂的控件在单击后需要传递更多的信息,例如光标的位置和当前项的一个结构,所以MFC为控件的每个通知消息也定义了一个宏,它长成了这个样子:
ON_CONTROL(EN_CHANGE,id,memberFxn)

还有很多种消息存在于MFC,宏定义有区别,大家可以触类旁通。

窗口消息有上百个。你可以从MSDN上查到WM_开头的,或者查看CWnd的成员函数,会给你列出很多,别忘了还有很多非窗口消息。雷神无法一一列出,也没有必要。大家查一下就行了。不过对一些常用的、新的控件消息和特殊的通知消息我还是把他们列出几个表,大家做个参考吧。

 

MFC中处理消息的顺序

1.     AfxWndProc()接收消息,寻找消息所属的CWnd对象,然后调用AfxCallWndProc( )

2.     AfxCallWndProc()存储消息(消息标识符和消息参数)供未来参考,然后调用WindowProc( )

3.     WindowProc()发送消息给OnWndMsg( ),如果消息未被处理,则发送给DefWindowproc( )

4.     OnWndMsg()首先按字节对消息进行排序,对于WM_COMMAND消息,调用OnCommand()消息响应函数;对于WM_NOTIFY消息调用OnNotify()消息响应函数。任何被遗漏的消息将是标准消息。OnWndMsg()函数搜索类的消息映像,以找到一个能处理任何窗口消息的处理函数。如果OnWndMsg()函数不能找到这样的处理函数的话,则把消息返回到WindowProc()函数,由它将消息发送给DefWindowProc()函数。

5.     OnCommand()查看这是不是一个控件通知(lParam参数不为NULL),如果它是,OnCommand()函数会试图将消息映射到制造通知的控件;如果它不是一个控件通知,或者控件拒绝映射的消息,OnCommand()就会调用OnCmdMsg()函数。

6.     OnNotify( )也试图将消息映射到制造通知的控件;如果映射不成功,OnNotify( )就调用相同的OnCmdMsg( )函数。

7.    根据接收消息的类,OnCmdMsg()函数将在一个称为命令传递(Command Routing)的过程中潜在的传递命令消息和控件通知。例如:如果拥有该窗口的类是一个框架类,则命令和控件通知消息也被传递到视图和文档类,并为该类寻找一个消息处理函数。

 

MFC中创建窗口的顺序

1.     PreCreateWindow()是一个重载函数,在窗口被创建前,可以在该重载函数中改变创建参数(可以设置窗口风格等等)

2.     PreSubclassWindow()也是一个重载函数,允许首先子分类一个窗口OnGetMinMaxInfo()为消息响应函数,响应的是WM_GETMINMAXINFO消息,允许设置窗口的最大或者最小尺寸。

3.     OnNcCreate()也是一个消息响应函数,响应WM_NCCREATE消息,发送消息以告诉窗口的客户区即将被创建。

4.     OnNcCalcSize()也是消息响应函数,响应WM_NCCALCSIZE消息,作用是允许改变窗口客户区大小。

5.     OnCreate()也是消息响应函数,响应WM_CREATE消息,发送消息告诉一个窗口已经被创建。

6.     OnSize()也是消息响应函数,响应WM_SIZE消息,发送该消息以告诉该窗口大小已经发生变化。

7.     OnMove()也是消息响应函数,响应WM_MOVE消息,发送此消息说明窗口在移动。

8.     OnChildNotify()为重载函数,作为部分消息映射被调用,告诉父窗口即将被告知一个窗口刚刚被创建。


有时候还可以自己定义一些消息,主要需要使用到下面的一些函数:

SendMessage();

PostMessage();


参考链接: http://www.icodeguru.com/VC&MFC/MFCReference/


2.2.类之间层次关系图

Win32自带了很多控件、组件,提供了几乎所有常用的图形类。为了便于快速选择合适的类类型,需要了解、熟悉常用的组件及其相关父类、子类。

下面的链接给出了所有的MFC类之间的关系:

http://www.icodeguru.com/VC&MFC/MFCReference/



4.参考资料和转自链接

http://www.jizhuomi.com/software/257.html(介绍MFC相对最全面的网页)
http://www.jizhuomi.com/software/147.html

http://blog.csdn.net/huangxy10/article/details/8495930

http://baike.baidu.com/link?url=nFFjspKrXzFe_Rc1SZGUK0f7LBMtQFPPxpM2sgGv9IJcvIbfvtCrr0XVBARHBdoeQbnKQS8JVBpDLLGDWNLQixGhtbTvyk5Dnk6wcVQBZqy























本文转自存储之厨51CTO博客,原文链接:http://blog.51cto.com/xiamachao/1841689 ,如需转载请自行联系原作者





相关文章
|
开发工具 Windows
[分享]总结:VC小知识!-1
说明: 获得notepad.exe的路径正规上来说用GetWindowsDirectory函数得到, 如果是调用 win95下的画笔,应该用访问注册表的方法获得其路径,要作成一个比较考究的程序,考虑应该全面点.
解决Visual Studio 2005中找不到MFC80UD.dll的问题
今天用VS2005建立一个MFC项目,在运行时遇到下面这个找不到MFC80UD.dll的问题。 这个问题可能是Manifest 引起的,因此我们可以通过修改项目->属性->清单工具->输入输出,把“嵌入清单”选“否”,然后编译、链接、运行即可。
1197 0