在MFC程序中,我们并不经常直接调用Windows
MFC提供数百个类,最重要的、也是编写任何VC++应用程序都必不可少的两个类CWinApp和CFrameWnd,这两个类是编写复杂庞大应用程序的基石。
1>封装特性:构成MFC框架的是MFC类库而MFC类库又是C++的一个类库。这些类封装WIN32应用程序编程接口,OLE(Object
2>继承特性:MFC抽象出了众多类的共同特性,并设计出一些基类作为实现其他类的基础,这些类中最重要的类是CObject类和CCmdTarget类,程序员可以从适当的MFC类中派生出自己的类,实现特定的功能达到编程的目的。
3>虚拟和消息映射:MFC是以C++为基础,当然支持虚函数,但作为一个编程框架必须要解决的是效率问题:如果MFC仅仅通过虚函数来支持动态约束必然会产生大量的虚函数表这样编程框架过于臃肿而且消耗更多的内存。但是MFC建立了消息映射机制这样降低了内存的使用却大大提高了效率
消息映射是一个将消息和成员函数相互关联的表,当应用程序的框架窗口接收到一个消息时,MFC将搜索该窗口的消息映射,如果存在一个处理消息的处理程序,那么就调用该处理程序.
它通过宏来实现消息到成员函数的映射,而且这些函数不必是虚拟的成员函数,这样不需要为消息映射函数生成一个很大的虚拟函数表(V表),节省内存。
MFC消息映射机制:
将消息与消息处理函数联系起来,形成一一对应的机制。
消息映射宏
声明:
1 DECLARE_MESSAGE_MAP
定义:
1 BEGIN_MESSAGE_MAP
2
3 ON_COMMAND
4
5 ON_CONTROL
6
7 ON_MESSAGE
8
9 END_MESSAGE_MAP
1 DECLARE_MESSAGE_MAP
定义:
1 BEGIN_MESSAGE_MAP
2
3 ON_COMMAND
4
5 ON_CONTROL
6
7 ON_MESSAGE
8
9 END_MESSAGE_MAP
MFC主要组成部分:类、宏和全局函数。
类是MFC中最主要的内容。MFC类是以层次结构方式组织起来的。MFC中的类分成两部分,除了一些辅助类,大多数的MFC类是直接或间接从根类CObject派生而来。
MFC宏主要功能:消息映射、运行时对象类型服务、诊断服务、异常处理。
MFC约定:全局函数以“Afx”为前缀,全局变量以“afx”为前缀
MFC类的层次关系
CObject项目类)->CCmdTarget(消息响应类)->
{
CWinThread(线程类)->CWinApp(Window应用程序类)
CDocument(文档类)
CWnd(窗体类)->[
}
CObject类---由于MFC中大部分类是从CObject类继承而来的,CObject类描述了几乎所有的MFC类的一些公共特性,CObject类为程序员提供了对象诊断、运行时类型识别和序列化等功能。
CCmdTarget类---由CObject类直接派生而来,它负责将消息发送到能够响应这些消息的对象。它是所有能进行消息映射的MFC类的基类。
CWinApp类---在任何MFC应用程序中有且仅有一个CWinApp派生类的对象,它代表了程序中运行的主线程,也代表了应用程序本身。
CWnd类---由CCmdTarget类直接派生而来,该类及其派生类的实例是一个窗口。CWnd类代表了MFC中最基本的GUI对象,它是一个功能最完善、成员函数最多的MFC类。
CFrameWnd类---是CWnd类的派生类,主要用来掌管一个窗口,它取代了SDK应用程序中窗口函数WndProc()的地位。CFrameWnd类的对象是一个框架窗口,包括边框、标题栏、菜单、最大化按钮、最小化按钮和一个激活的视图。
CDocument类---在应用程序中作为用户文档类的基类,它代表了用户存储或打开的一个文件。
CView类---是MFC中一个很基本的类,它作为其它MFC视图类和用户视图派生类的基类。
从API编程到MFC编程的过渡:
WinMain()
{
}
WndProc()
{
}
MFC
描述 | Windows句柄 | MFC |
窗口 | HWND | CWnd |
设备上下文 | HDC | CDC |
菜单 | HMENU | CMenu |
笔 | HPEN | CPen |
刷子 | HBRUSH | CBrush |
字体 | HFONT | CFont |
位图 | HBITMAP | CBitmap |
套接字 | SOCKET | CSocket |
三、手工创建一个MFC应用程序:
注意:创建MFC程序,要创建一个Win32空项目,并要选择项目属性中的”在共享DLL文件中使用MFC,然后新建我们的文件
例子:在”hello.h”头文件中添写如下代码:
1 class CMyApp:public CWinApp
2
3 {
4
5 public:
6
7 virtual BOOL InitInstance(); //虚函数
8
9 };
10
11 class CMainWindow:public CFrameWnd
12
13 {
14
15 public:
16
17 CMainWindow();
18
19 protected:
20
21 afx_msg void OnPaint();
22
23 DECLARE_MESSAGE_MAP(); //声明消息映射
24
25 };
在”hello.cpp”源文件中添写如下代码:
1 #include
2
3 #include “hello.h"
4
5 CMyApp myApp;
6
7 BOOL CMyApp::InitInstance()
8
9 {
10
11 m_pMainWnd = new CMainWindow;
12
13
14
15 m_pMainWnd->ShowWindow(m_nCmdShow);
16
17 m_pMainWnd->UpdateWindow();
18
19 return TRUE;
20
21 }
22
23 BEGIN_MESSAGE_MAP(CMainWindow,CFrameWnd)
24
25 ON_WM_PAINT()
26
27 END_MESSAGE_MAP() //消息映射
28
29 CMainWindow::CMainWindow() //构造函数初始化
30
31 {
32
33 Create(NULL,“我的第一个MFC应用程序”); //创建窗体
34
35 }
36
37 void CMainWindow::OnPaint()
38
39 { CPaintDC dc(this);
40
41 CRect rect;
42
43 GetClientRect(&rect);
44
45 dc.DrawText("Hello MFC",-1,&rect, DT_SINGLELINE|DT_CENTER|DT_VCENTER);
46
47 }
CWinApp是应用程序类,在MFC应用程序中必须从这个类派生出一个类,该派生类是MFC应用程序的入口
必须定义这个派生类的对象,并且只能有一个这个对象代表整个应用程序。
成员函数:InitInstance()
CFrameWnd类
作用:为应用程序提供一个窗口,同时实现消息处理功能。
成员函数:
功能:创建窗体,将之赋于CFrameWnd对象上。
BOOL
MFC应用程序的核心就是基于CWinApp类的应用程序对象,CWinApp提供了消息循环来检索消息并将消息调度给应用程序的窗口.我们在编写MFC应用程序时,要包含afxwin.h,
一个MFC应用程序可以有且仅有一个应用程序对象,对象必须声明为在全局范围内有效(也就是全局对象),以便它在程序开始时即在内存中被实例化
我们的Hello
CMyApp
CMyApp的类声明在hello.h中代码如下:
1 class CMyApp:public CWinApp
2
3 {
4
5 public:
6
7 virtual BOOL InitInstance();
8
9 };
CMyApp没有声明任何的数据成员,只是重写了一个从CWinApp类中继承来的函数,在应用程序的生存期内InitInstance的调用比较早,是在应用程序开始运行以后而窗口创建之前,除非InitIstance创建一个窗口,否则应用程序是不会有窗口,这正是为什么即使最小的MFC应用程序也必须从CWinApp派生出一个类并重写CWinApp::InitIstance的原因
InitInstance函数:CWinApp::InitInstance是一个虚函数,其默认操作仅包含一条语句:return
InitInstance是用来执行程序每次开始时都需要进行的初始化工作最好的地方
在hello.cpp中,CMyApp的InitInstance通过实例化hello的CMainWindow类来创建hello窗口,语句:
m_pMainWnd
构造了一个CMainWindow对象指针,并将其地址复制到了应用程序对象的m_pMainWnd数据成员中,窗口创建以后,InitInstance就会通过CMainWindow指针调用ShowWindow和UpdateWindow函数显示它:
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
ShowWindow和UpdateWindow是所有窗口对象共用的CWnd成员函数其中包括CFrameWnd类的对象,CMainWindow就是从CFrameWnd派生出来的.
要从MFC程序调用一个常规的Windows
通过生成窗口对象并调用其Create函数,MFC应用程序可以创建一个窗口,在CMyApp::InitInstance中,hello创建了一个CMainWindow对象,CMainWindow的构造函数生成在屏幕上看到的窗口:
Create(NULL,”我的第一个MFC应用程序”);
CPaintDC
MFC的CPaintDC类是从MFC的CDC类派生的,CDC类封装了Windows设备环境,以及包含了绘制到屏幕、打印机和其他设备的几十个成员函数
在MFC中如何处理消息呢?
在SDK中我们利用的是消息循环和窗口过程函数对消息进行消息处理.
在MFC中我们用的是消息映射机制.
下面是将消息映射添加到一个类中需要做的全部工作.
1>通过将DECLARE_MESSAGE_MAP语句添加到类声明中,声明消息映射.
2>通过放置标识消息的宏来执行消息映射,相应的类将在对BEGIN_MESSAGE_MAP和END_MESSAGE_MAP的调用之间处理消息
3>添加成员函数来处理消息
1、构造CWinApp派生类的对象
2、系统调用WinMain()
3、WinMain调用InitInstance,在该函数中创建CFrameWnd派生类对象,调用Create函数创建窗口、调用ShowWindow函数显示窗口。
4、之后内部机制调用Run,接受用户的消息,并将消息导向默认的处理函数。当接收到WM_QUIT消息时,Run内部调用ExitInstance,退出程序。
MFC采用消息映射(Message
消息映射:在MFC中把消息处理函数和它所要处理的特定的消息连接起来的一种机制。
它通过宏来实现消息到成员函数的映射,而且这些函数不必是虚拟的成员函数,这样不需要为消息映射函数生成一个很大的虚拟函数表(V表),节省内存。
MFC消息映射机制包括一组消息映射宏。一条消息映射宏把一个Windows消息和其消息处理函数联结起来。
MFC应用程序框架提供了消息映射功能。
在类的实现源文件中用BEGIN_MESSAGE_MAP()和END_MESSAGE_MAP()宏来定义消息映射。
在类定义的结尾用DECLARE_MESSAGE_MAP()宏来声明使用消息映射。
Hello的CmainWindow类只处理一种消息类型—WM_PAINT,因此其消息映射的实现如下所示:
1 BEGIN_MESSAGE_MAP(CMainWindow,CFrameWnd);
2
3 ON_WM_PAINT()
4
5 END_MESSAGE_MAP()
BEGIN_MESSAGE_MAP开始了消息映射,并标识了消息映射所属的类和该类的基类
END_MESSAGE_MAP()结束消息映射.
ON_WM_PAINT()在BEGIN_MESSAGE_MAP和END_MESSAGE_MAP()之间,称做消息条目,在MFC为100多种Window消息提供了宏.
afx_msg
DECLARE_MESSAGE_MAP()
afx_msg
DECLARE_MESSAGE_MAP()声明消息映射
MFC把消息主要分为三大类:
(1)、标准Windows消息(WM_XXX)
使用宏:ON_WM_XXX()
(2)、命令消息:(WM_COMMAND)
来自于菜单、工具条、按钮等的消息
使用宏:
特点:由用户指定消息处理函数
3、”Notification消息”
BOOL
BSTR
BYTE
COLORREF
DWORD
LONG
LPCTSTR
LPVOID
MFC特有的数据类型:
1>POSITION
2>LPRECT:32位指针,指向一个不变的矩形区域结构
关于CDC(设备上下文)
可以简单的如此理解:
DC(DEVICE
CDC和HDC简单使用:
CDC是设备上下文类;
HDC是设备上下文句柄;
1 //CDC-->HDC与HDC->CDC
2
3 CDC dc;
4
5 HDC hdc;
6
7 hdc=dc.m_hDC;
8
9 dc=CDC::FromHandle(hdc);
10
11 // or hdc=dc.GetSafeHdc();
挂一段正规文档(来源MSDN):
设备上下文
设备上下文是一种包含有关某个设备(如显示器或打印机)的绘制属性信息的
CPaintDC
CClientDC
CMetaFileDC
鼠标绘图
框架程序中的大多数绘图(由此,大部分设备上下文参与)都在视图的
在这种情况中,可以使用
再来一段比较清晰容易理解的。
一、区别与联系
HDC是句柄;
CDC是MFC封装的Windows设备相关的一个类;
CClientDC是CDC的衍生类,产生对应于Windows客户区的对象
HDC是WINDOWS的一种数据类型,是设备描述句柄。
而CDC是MFC里的一个类,它封装了几乎所有的关于HDC的操作。
也可以这样说,HDC定义的变量指向一块内存,这块
二、获得CDC
CDC*
pDC=GetDC();//原型CDC*
三、获得hdc
HDC
1,hDC=GetDC(pCpp->hWnd);
2,pDC->m_hDC;
3,(SDK中找不到相关类的支持)
4,hDC=::GetDC(HWND
四、转换(?,未确定用法的正确性)
CDC*
HDC
pDC=Attach(hDC);
hDC=GetSafeHDC(pDC);
pDC->m_hDC=hDC;
下面一段翻译自MSDN
http://zoudaokou2006.blog.163.com/blog/static/66650867200812910281360/
CDC、HDC、pDC (zz)
1.CDC
pDC是类指针
HDC是windows句柄
通过pDC获得hdc:
HDC
通过hdc获得pDC:
CDC
pDC->Attach(hdc);
2.hDC和CDC有本质区别
HDC是WINDOWS的一种数据类型,是设备描述句柄。而CDC是MFC里的一个类,它封装了几乎所有的关于HDC的操作。也可以这样说,HDC定义的变量指向一块内存,这内存用来描述一个设备的相关的内容,所以也可以认为HDC定义的是一个指针;而CDC类定义一个对象,这个对象拥有HDC定义的一个设备描述表,同时也包含与HDC相关的操作的函数。这与HPEN和CPen,POINT与CPoint之间的差别是一样的。
CDC是对hDC的相关操作进行封装,例如CDC的一个TextOut函数隐去其错误检测,完全可以简化到这样程度
1 CDC:TextOut( int x, int y, const CString& str )
2 {
3 TextOut( m_hDC, x, y, (LPCTSTR)str, str.GetLength() );
4 }
m_hDC就是CDC的成员变量HDC
CDC有一个operator
你可以把它当成一个HDC使用
3.this是dc输出目标窗口的指针,通过它可以得到窗口句柄,对象带参构造这有什么奇怪的呢?
HDC,CDC,CClientDC的区别和联系是什么?
简而言之,HDC是句柄;CDC是MFC封装的Windows
pDC
HDC
通过pDC获得hdc:
HDC
通过hdc获得pDC:
CDC
pDC->Attach(hdc);
HDC是WINDOWS的一种数据类型,是设备描述句柄。
而CDC是MFC里的一个类,它封装了几乎所有的关于
HDC的操作。
也可以这样说,HDC定义的变量指向一块内存,这块
内存用来描述一个设备的相关的内容,所以也可以
认为HDC定义的是一个指针;而CDC类定义一个对象,
这个对象拥有HDC定义的一个设备描述表,同时也包
含与HDC相关的操作的函数。
这与HPEN和CPen,POINT与CPoint之间的差别是一样
的
显然
CDC和HDC的区别与转换
源自:
一、区别与联系
HDC是句柄;CDC是MFC封装的Windows
HDC是WINDOWS的一种数据类型,是设备描述句柄。
而CDC是MFC里的一个类,它封装了几乎所有的关于HDC的操作。
也可以这样说,HDC定义的变量指向一块内存,这块
这与HPEN和CPen,POINT与CPoint之间的差别是一样的。
二、获得CDC
CDC*
pDC=GetDC();
三、获得hdc
HDC
1,hDC=GetDC(pCxp->hWnd);
2,pDC->m_hDC;
3,
MEMDCXP
GetMemDCXP(&Mdcxp);
hDC
4,hDC=::GetDC(HWND
四、转换
CDC*
HDC
pDC=Attach(hDC);
hDC=GetSafeHDC(pDC);
pDC->m_hDC==hDC
设备描述表
设备描述表
DC(Device
设备描述表是一个定义一组图形对象及其属性、影响输出的图形方式(数据)结构。windows提供设备描述表,用于应用程序和物理设备之间进行交互,从而提供了应用程序设计的平台无关性。设备描述表又称为设备上下文,或者设备环境。
设备描述表是一种数据结构,它包括了一个设备(如显示器和打印机)的绘制属性相关的信息。所有的绘制操作通过设备描述表进行。设备描述表与大多
设备描述表总是与某种系统硬件设备相关。比如屏幕设备描述表与显示设备相关,打印机设备描述表与打印设备相关等等。
屏幕设备描述表,一般我们简单地称其为设备描述表。它与显示设备具有一定的对应关系,在windows
windows
如何理解设备描述表???
设备描述表是一个定义一组图形对象及其属性、影响输出的图形方式(数据)结构。windows提供设备描述表,用于应用程序和物理设备之间进行交互,从而提供了应用程序设计的平台无关性。设备描述表又称为设备上下文,或者设备环境。
通过设备描述表调用显示卡驱动,由显示卡驱动程序访问显存实现绘图输出,因此在GDI下面是无法直接访问到显存的。这种方式主要是提供了统一的编程界面,使得编程人员不用考虑具体的设备的特性,一切和设备打交道的工作交由系统完成,编程者只要调用统一的GDI函数即可。设备描述表在内存中是存在的。
GDI是Graphics
在Windows操作系统下,绝大多数具备图形界面的应用程序都离不开GDI,我们利用GDI所提供的众多函数就可以方便的在屏幕、打印机及其它输出设备上输出图形,文本等操作。GDI的出现使程序员无需要关心硬件设备及设备驱动,就可以将应用程序的输出转化为硬件设备上的输出,实现了程序开发者与硬件设备的隔离,大大方便了开发工作。
GDI是如何实现输出的?
要想在屏幕或者其它输出设备上输出图形或者文字,那么我们就必须先获得一个称为设备描述表(
设备描述表句柄一旦获得,那么系统将使用默认的属性值填充设备描述表结构。
如果有必要,我们可以使用一些GDI函数获取和改变设备描述表中的属性值。
如何使用设备描述表
本文示例源代码下载
Windows
本文示例源代码下载
Windows
下载示例工程 DCDemo
1、按钮
void
{
HDC
::MoveToEx(hDC,0,0,NULL);
LineTo(hDC,200,20);
::ReleaseDC(NULL,hDC);
} 该段代码具体演示了如何使用API
HWND
HDC
);编译运行程序,按下按钮,发现屏幕的左上角处画了一条直线。
2、按钮
void
{
HDC
::MoveToEx(hDC,0,0,NULL);
LineTo(hDC,200,50);
::ReleaseDC(m_hWnd,hDC);
} 这段代码与前一段代码唯一的区别是GetDC的参数不再是NULL,而改为CWnd的成员变量m_hWnd,即对话框窗口的句柄。对比两段代码运行结果,深入体会API函数
3、按钮IDC_GETDC_CWND的函数
void
{
CDC
pDC->MoveTo(0,0);
pDC->LineTo(200,100);
ReleaseDC(pDC);
} 该段代码演示使用MFC
4、按钮IDC_CCLIENTDC的函数
http://www.hackbase.com/lib/2007-02-22/13807.html
windows
从CDC
下面,分别讨论派生出的四种设备描述表。
CCientDC
代表窗口客户区的设备描述表。其构造函数CClientDC(CWnd
CPaintDC
仅仅用于响应WM_PAINT消息时绘制窗口,因为它的构造函数调用了::BeginPaint获取设备描述表HDC,并且使用成员函数Attach把它和CPaintDC对象捆绑在一起;析构函数使用成员函数Detach把设备描述表句柄HDC分离出来,并调用::EndPaint释放设备描述表HDC,而::BeginPaint和::EndPaint仅仅在响应WM_PAINT时使用。
CMetaFileDC
用于生成元文件。
CWindowDC
代表整个窗口区(包括非客户区)的设备描述表。其构造函数CWindowDC(CWnd
要使用设备描述表,一般有如下步骤:
获取或者创建设备描述表;
必要的话,改变设备描述表的属性;
使用设备描述表完成绘制操作;
释放或删除设备描述表。
Common设备描述表通过::GetDC,::GetDCEx,::BeginPaint来获得一个设备描述表,用毕,用::ReleaseDC或::EndPaint释放设备描述表;
Printer设备描述表通过::CreateDC创建设备描述表,用::DeleteDC删除设备描述表。
Memory设备描述表通过::CreateCompatibleDC创建设备描述表,用::DeleteDC删除。
Information设备描述表通过::CreateIC创建设备描述表,用::DeleteDC删除