第 4章 MFC基本应用程序的建立
4.1 Windows编程基础
4.2 编制一个 MFC应用程序
4.3 使用 MFC AppWizard
4.4 使用 ClassWizard
作业
4.1Windows编程基础
4.1.1简单的 Windows应用程序
[例 Ex_HelloMsg] 一个简单的 Windows应用程序。
#include <windows.h>
int WINAPI WinMain (HINSTANCE hInstance,HINSTANCE hPrevInstance,
LPSTR lpCmdLine,int nCmdShow)
{ MessageBox (NULL,"你好,我的 Visual C++世界! ","问候 ",0) ;
return 0 ;
}
运行上述程序需要进行以下步骤:
选择,File”?“New”。单击 Projects,选中 Win32 Application项。
在 Project Name框中键入项目名称 Ex_HelloMsg。在 Location下的编辑框中直接键入文件夹名称,或单击 Browse按钮 (...)选择一个已有的文件夹。
单击 [OK]。选中 An empty project项。单击 [Finish],单击 [OK]系统将自动创建此应用程序。
选择,File”?“New”。单击 File标签,在左边的列表框中选择 C++ Source
File项,在右边的 File框中键入 Ex_HelloMsg.cpp,单击 [OK]。
4.1Windows编程基础
输入上面的代码,运行程序,如图。
从程序可以看出,
传统的 DOS程序以 main函数作为进入程序的初始入口点,但在 Windows应用程序中,main函数被 WinMain函数取而代之,WinMain函数的原型如下:
int WINAPI WinMain (
HINSTANCE hInstance,// 当前实例句柄
HINSTANCE hPrevInstance,// 前一实例句柄
LPSTR lpCmdLine,// 指向命令行参数的指针
int nCmdShow) // 窗口的显示状态
句柄是一个标识 Windows资源 (如菜单、图标、窗口等 )和设备等对象的变量,
或者是一个对操作系统资源的间接引用。
每一个 Windows应用程序都需要 Windows.h头文件,它还包含了其他的一些
Windows头文件。这些头文件定义了 Windows的所有数据类型、函数调用、
数据结构和符号常量。
MessageBox是一个 Win32 API(Application Programming Interface,应用程序接口 )函数,用来弹出一个对话框窗口,显示短信息。该函数第一个参数用来指定父窗口句柄,即对话框所在的窗口句柄。第二、三个参数分别用来指定显示的消息内容和对话框窗口的标题,最后一个参数用来指定在对话框中显示的按钮。
图 4.1 Ex_HelloMsg运行结果
4.1Windows编程基础
[例 Ex_HelloWin] 一个完整的 Windows应用程序。
#include <windows.h>
LRESULT CALLBACK WndProc (HWND,UINT,WPARAM,LPARAM); // 窗口过程
int WINAPI WinMain (HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR
lpCmdLine,int nCmdShow)
{ HWND hwnd ; // 窗口句柄
MSG msg ; // 消息
WNDCLASS wndclass ; // 窗口类
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc= WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra= 0 ;
wndclass.hInstance= hInstance ;
wndclass.hIcon= LoadIcon (NULL,IDI_APPLICATION) ;
wndclass.hCursor= LoadCursor (NULL,IDC_ARROW) ;
wndclass.hbrBackground= (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName= NULL ;
wndclass.lpszClassName=,HelloWin”; // 窗口类名
if (!RegisterClass (&wndclass)) // 注册窗口
{ MessageBox (NULL,"窗口注册失败! ","HelloWin",0) ;
return 0 ;
}
4.1Windows编程基础
hwnd = CreateWindow ("HelloWin",// 窗口类名
"我的窗口 ",// 窗口标题
WS_OVERLAPPEDWINDOW,// 窗口样式
CW_USEDEFAULT,// 窗口最初的 x 位置
CW_USEDEFAULT,// 窗口最初的 y 位置
CW_USEDEFAULT,// 窗口最初的 x 大小
CW_USEDEFAULT,// 窗口最初的 y 大小
NULL,// 父窗口句柄
NULL,// 窗口菜单句柄
hInstance,// 应用程序实例句柄
NULL) ; // 创建窗口的参数
ShowWindow (hwnd,nCmdShow) ; // 显示窗口
UpdateWindow (hwnd) ; // 更新窗口,包括窗口的客户区
// 进入消息循环:当从应用程序消息队列中检取的消息是 WM_QUIT时,则退出循环。
while (GetMessage (&msg,NULL,0,0))
{ TranslateMessage (&msg) ; // 转换某些键盘消息
DispatchMessage (&msg) ; // 将消息发送给窗口过程,这里是
WndProc
}
return msg.wParam ;
}
4.1Windows编程基础
LRESULT CALLBACK WndProc (HWND hwnd,UINT message,WPARAM wParam,
LPARAM lParam)
{ switch (message)
{ case WM_CREATE,// 窗口创建产生的消息
return 0 ;
case WM_LBUTTONDOWN:
MessageBox (NULL,"你好,我的 Visual C++世界! ","问候 ",0) ;
return 0 ;
case WM_DESTROY,// 当窗口关闭时产生的消息
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd,message,wParam,lParam) ; // 执行默认的消息处理
}
4.1Windows编程基础程序运行,单击鼠标左键,如图。
窗口过程函数 WndProc用来接收和处理各种不同的消息,WinMain函数通常要完成:
调用 API函数 RegisterClass注册应用程序的窗口类。
调用相关 API函数创建和显示窗口,并进行必要的初始化。 CreateWindow
创建已注册窗口类的窗口。 Windows每一个窗口都有一些基本属性。窗口类就是充当这些属性的模板。
创建和启动应用程序的消息循环。 Windows应用程序接受各种不同的消息。
Windows系统首先将消息放入消息队列中,应用程序的消息循环就是从应用程序的消息队列中检取消息,并将消息发送相应的窗口过程函数中作进一步处理。
如果接收到 WM_QUIT消息,则退出应用程序图 4.2 Ex_HelloWin运行结果
4.1Windows编程基础应用程序的基本流程,如图。
图 4.3 Windows应用程序的基本流程
4.1Windows编程基础
4.1.2Windows编程特点
消息驱动机制
DOS程序是通过调用系统的函数来获得用户输入的,Windows程序则是通过操作系统发送的消息来处理用户输入的。
无论是系统产生的动作或是运行应用程序产生的动作,都称为事件 (Events)
产生的消息 (Message)。在应用程序中,通过接收消息、分发消息、处理消息来和用户进行交互。许多消息都经过了严格的定义,并且适用于所有的应用程序。
图形设备接口 (GDI )
DOS环境中,要在打印机上打印一幅图形是非常复杂的事件。 Windows则提供了一个抽象的接口,称为图形设备接口 (Graphical Device Interface,
简称 GDI),使得用户直接利用系统的 GDI函数就能方便实现输入或输出,而不必关心与系统相连的外部设备的类型。
基于资源的程序设计
Windows应用程序常常包含众多图形元素,每一个这样的元素都作为一种可以装入应用程序的资源来存放。这些资源可以被编辑、修改,也可以被其他应用程序所共享。 VC中提供的许多编辑器能,所见即所得,地进行不同类型资源的设计、编辑等。
4.1Windows编程基础
动态链接库
提供一些特定结构的函数,能被应用程序在运行过程中装入和连接,多个程序可以共享同一个动态链接库。从编程角度,动态链接库可以提高程序模块的灵活性,它本身是可以单独设计、编译和调试的。
Windows提供了应用程序可利用的丰富的函数调用,大多数用于实现其用户界面和在显示器上显示的文本和图形,都是通过动态链接库来实现的。
Windows中,KERNEL32用来处理存储器低层功能、任务和资源管理等核心服务; GDI32用来提供图形设备接口,管理用户界面和图形绘制; USER32
负责窗口的管理。
进程和线程
Windows是一个 32位多任务操作系统,采用进程和线程的管理模式。
进程是装入内存中正在执行的应用程序。进程包括私有的虚拟地址空间、
代码、数据及其它操作系统资源。进程包括了一个或多个在进程上下文内运行的线程。
线程是操作系统分配 CPU时间的基本实体。线程可以执行应用程序代码的任何部分,包括当前正在被其它线程执行的那些部分。同一进程的所有线程共享同样的虚拟地址空间、全局变量和操作系统资源。
一个应用程序,包括一个或多个进程,每个进程由一个或多个线程构成。
4.1Windows编程基础
4.1.3Windows基本数据类型
4.1Windows编程基础需要说明的是:
这些基本数据类型都是以大写字符出现。
凡是数据类型的前缀是 P或 LP,表示该类型是一个指针或长指针数据类型。
前缀是 H,表示是句柄类型。前缀是 U,表示是无符号数据类型。
还提供一些宏来处理上述基本数据类型。 LOBYTE和 HIBYTE分别用来获取 16
位数值中的低位和高位字节; LOWORD和 HIWORD分别用来获取 32位数值中的低位和高位字; MAKEWORD是将两个 16位无符号值结合成一个 32位无符号值。
4.2编制一个 MFC应用程序
4.2.1MFC概述
1987年微软公司推出了第一代 Windows产品,并为应用程序设计者提供了
Win16 API,在此基础上推出了 Windows GUI(图形用户界面 ),然后采用面向对象技术对 API进行封装。 1992年推出应用程序框架产品 AFX(Application
Frameworks),并在 AFX的基础上进一步发展为 MFC产品。 MFC类的基本层次结构如图。
图 4.4 MFC类的基本层次结构
CCmdTarget
CWnd
CDocument
CFrameWnd
CObject
CWinTread
CWinApp
CView CDialog及控件
CMDIChildWndCMDIFrameWnd CMiniFrameWnd
4.2编制一个 MFC应用程序
4.2.2设计一个 MFC程序
[例 Ex_HelloMFC] 一个 MFC应用程序。
#include <afxwin.h> // MFC头文件
class CHelloApp,public CWinApp // 声明应用程序类
{ public:
virtual BOOL InitInstance(); };
CHelloApp theApp; // 建立应用程序类的实例
class CMainFrame,public CFrameWnd // 声明主窗口类
{ public:
CMainFrame()
{ // 创建主窗口
Create(NULL,"我的窗口 ",WS_OVERLAPPEDWINDOW,
CRect(0,0,400,300));
}
protected:
afx_msg void OnLButtonDown(UINT nFlags,CPoint point);
DECLARE_MESSAGE_MAP()
};
// 消息映射入口
BEGIN_MESSAGE_MAP(CMainFrame,CFrameWnd)
ON_WM_LBUTTONDOWN() // 单击鼠标左键消息的映射宏
END_MESSAGE_MAP()
4.2编制一个 MFC应用程序
void CMainFrame::OnLButtonDown(UINT nFlags,CPoint point)
{ MessageBox ("你好,我的 Visual C++世界! ","问候 ",0) ;
CFrameWnd::OnLButtonDown(nFlags,point);
}
// 每当应用程序首次执行时都要调用的初始化函数
BOOL CHelloApp::InitInstance()
{ m_pMainWnd = new CMainFrame();
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
return TRUE;
}
运行上述 MFC程序需要进行以下步骤:
选择,File”?“New”。单击 Projects,选中 Win32 Application项,创建一个
Ex_HelloMFC空应用程序项目。
选择,File”?“New”。单击 File标签,在左边的列表框中选择 C++ Source
File项,在右边的 File框中键入 Ex_HelloMsg.cpp,单击 [OK]。
输入上面的代码。选择,Project”?“Settings”,选择,General”标签。在
,Microsoft Foundation Classes”组合框中,选择,Use MFC in a Shared
DLL”。单击 [OK]。
程序运行后,单击鼠标左键,就会弹出一个对话框,结果同 Ex_HelloWin。
4.2编制一个 MFC应用程序
4.2.3理解程序代码
MFC是使用 afxwin.h来代替头文件 windows.h。
运行应用程序时,自动调用应用程序框架内部的 WinMain函数,并自动查找该应用程序类 CHelloApp(从 CWinApp派生 )的全局变量 theApp,然后自动调用 CHelloApp的虚函数 InitInstance,该函数会进一步调用相应的函数来完成主窗口的构造和显示工作。上述程序中 InitInstance的执行过程。
首先执行的是:
m_pMainWnd = new CMainFrame();
创建从 CFrameWnd类派生而来的用户框架窗口 CMainFrame类对象,继而调用该类的构造函数,使得 Create函数被调用,完成了窗口创建工作。
然后执行后面两句:
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
用作窗口的显示和更新。
最后返回 TRUE,表示窗口创建成功。
由于应用程序类 CWinApp是用来调用 WinMain以及实例的初始化,因此每一个 MFC应用程序有且只能一个这样的应用程序类,且需要一个全局的对象实例,如上述程序中的 theApp。
4.2编制一个 MFC应用程序
InitInstance完成初始化后,调用基类 CWinApp的成员函数 Run,执行应用程序的消息循环。 Run检查到消息队列为空时,调用基类 CWinApp的成员函数 OnIdle进行空闲时的后台处理工作。若消息队列为空且又没有后台工作要处理时,则应用程序一直处于等待状态,一直等到有消息为止。当程序结束后,调用基类 CWinApp的成员函数 ExitInstance,完成终止应用程序的收尾工作。
在 MFC中,一个消息的处理往往是通过独特的 MFC消息映射机制来进行的。
消息映射 (Message Map)机制,指 MFC类中将消息与消息处理函数联系起来,
一一对应的机制。任何一个从类 CCmdTarget派生的类理论上均可处理消息,
且都有相应的消息映射函数。
按照 MFC的消息映射机制,映射一个消息的过程由三个部分组成:
在处理消息的类中,使用消息宏 DECLARE_MESSAGE_MAP()声明对消息映射的支持,并在该宏之前声明消息处理函数。
使用 BEGIN_MESSAGE_MAP和 END_MESSAGE_MAP宏在类声明之后的地方定义该类支持的消息映射入口点,所有消息映射宏都添加在这里,
当然不同的消息 MFC都会有不同的消息映射宏。
定义消息处理函数。
为了使该消息能被其他对象接收并处理,在函数中常常需要调用基类中的相关消息处理函数。 MFC的 ClassWizard(类向导 )能自动完成消息的上述映射过程。
4.3使用 MFC AppWizard
4.3.1应用程序框架类型
4.3使用 MFC AppWizard
4.3.2创建一个单文档应用程序
开始选择,File”?“New”,选择 Projects标签;选择 MFC AppWizard(exe)的项目类型 (该类型用于创建可执行的 Windows应用程序 ),将项目工作文件夹定位在,D:\Visual C++ 6.0程序,,并在,Project Name”框中输入项目名
Ex_SDIHello,如图,单击 [OK]。
图 4.5 MFC AppWizard的,New”对话框
4.3使用 MFC AppWizard
第一步
从应用程序类型 Single Document,Multiple Document和 Dialog Based中选择
SDI。
决定应用程序中是否需要 MFC的,文档 /视图,结构的支持。若不选定此项,
则程序中的磁盘文件的打开、保存以及文档和视图的相互作用等功能需要用户来实现,且将跳过 Step 2~Step 5,直接弹出,Step 6”对话框。
选择资源所使用的语言,这里是,中文 [中国 ]”,单击 [Next]按钮。
图 4.6 SDI的,Step 1”对话框
4.3使用 MFC AppWizard
第二步单击 [Next]按钮进入下一步。
第三步单击 [Next]按钮进入下一步。
图 4.7 SDI的,Step 2”对话框 图 4.8 SDI的,Step 3”对话框
4.3使用 MFC AppWizard
第四步单击 [Next]按钮进入下一步。
图 4.9 SDI的,Step 4”对话框
4.3使用 MFC AppWizard
第五步在弹出的对话框 (如图 )中出现三个方面的选项,供用户来选择:
应用程序的主窗口是 MFC标准风格还是窗口左边有切分窗口的浏览器风格;
在源文件中是否加入注释用来引导用户编写程序代码;
使用动态链接库还是静态链接库。
保留缺省状态,单击 [Next]按钮进行下一步 。
图 4.10 SDI的,Step 5”对话框
4.3使用 MFC AppWizard
第六步单击 [Finish]按钮出现如图 4.12所示的对话框图 4.11 SDI的,Step 6”对话框 图 4.12 项目信息对话框
4.3使用 MFC AppWizard
编译并运行图 4.13 应用程序运行结果菜单栏工具栏状态栏标题栏文档窗口
4.3使用 MFC AppWizard
4.3.3理解程序框架
应用类 CEx_SDIHelloApp
下面首先来看看 Ex_SDIHello应用程序的 Ex_SDIHello.h文件:
...
class CEx_SDIHelloApp,public CWinApp
{ public:
CEx_SDIHelloApp();
//{{AFX_VIRTUAL(CEx_SDIHelloApp)
public:
virtual BOOL InitInstance();
//}}AFX_VIRTUAL
// Implementation(实现 )
//{{AFX_MSG(CEx_SDIHelloApp)
afx_msg void OnAppAbout();
// NOTE - the ClassWizard will add and remove member functions here.
// DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_MSG
DECLARE_MESSAGE_MAP() // 声明消息映射
};
..,
4.3使用 MFC AppWizard
代码中,
//{{AFX_VIRTUAL(类名 )
...
//}}AFX_VIRTUAL
是 ClassWizard定义的专门用作虚函数重载的标记,表示该程序块中的虚函数的重载是由 ClassWizard来自动管理的,一般不需要去更改。同样,
//{{AFX_MSG(类名 )
...
//}}AFX_MSG
是 ClassWizard定义的专门用作消息映射声明的标记。
代码中的,//TODO:”以及英文等注释是由 MFC AppWizard为用户自动生成的。若不需要这些注释,可在应用程序向导的第五步对话框中,将,Would
you like to generate source file comments?”项选为,No,thank you”。
下面再来看看该 Ex_SDIHello应用程序的 Ex_SDIHello.cpp源文件:
#include "stdafx.h" // 预编译处理的头文件
#include "Ex_SDIHello.h" // 应用类的头文件
#include "MainFrm.h" // 主框架类的头文件
#include "Ex_SDIHelloDoc.h" // 文档类的头文件
#include "Ex_SDIHelloView.h" // 视图类的头文件
4.3使用 MFC AppWizard
//消息映射开始
BEGIN_MESSAGE_MAP(CEx_SDIHelloApp,CWinApp) // 消息映射宏
//{{AFX_MSG_MAP(CEx_SDIHelloApp)
// 映射,帮助,菜单项中的,关于 Ex_SDIHello”命令消息,当用户选择了
// 该命令时,将执行函数 OnAppAbout
ON_COMMAND(ID_APP_ABOUT,OnAppAbout)
// NOTE - the ClassWizard will add and remove mapping macros here.
// DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_MSG_MAP
// 映射,文件,菜单项中的,新建,和,打开,命令消息,当用户选择了
,新建,
// 或,打开,命令时,将相应执行 CWinApp::OnFileNew或
CWinApp::OnFileOpen
ON_COMMAND(ID_FILE_NEW,CWinApp::OnFileNew)
ON_COMMAND(ID_FILE_OPEN,CWinApp::OnFileOpen)
// 映射,文件,菜单项中的,打印设置,命令消息,当用户选择了
// 该命令时,将执行函数 CWinApp::OnFilePrintSetup
ON_COMMAND(ID_FILE_PRINT_SETUP,CWinApp::OnFilePrintSetup)
END_MESSAGE_MAP() // 消息映射宏
4.3使用 MFC AppWizard
// 消息映射开始结束
CEx_SDIHelloApp::CEx_SDIHelloApp() // 构造函数
{ }
CEx_SDIHelloApp theApp; // 定义的一个应用类对象,表示一个实例
BOOL CEx_SDIHelloApp::InitInstance()
{,..
}
...
void CEx_SDIHelloApp::OnAppAbout()
{ CAboutDlg aboutDlg; // 定义的 CAboutDlg对象
aboutDlg.DoModal(); // 调用相应的库函数,显示
CAboutDlg对话框
}
代码中,
//{{AFX_MSG_MAP(类名 )
..
//}}AFX_MSG_MAP
是 ClassWizard定义的专门用作消息映射的标记,表示该程序块中的消息映射函数是由 ClassWizard来自动管理的,用户一般不需要去更改。
4.3使用 MFC AppWizard
最主要的 InitInstance函数体代码:
BOOL CEx_SDIHelloApp::InitInstance()
{ // 若用户在 MFC AppWizard的第三步中,选中了,ActiveX Controls”,
// 则表示所创建的应用程序支持 ActiveX控件
AfxEnableControlContainer();
// 若用户在 MFC AppWizard的第四步中,选中了,3D controls”
// 则表示所创建的应用程序支持 Windows 95版本前的 3D控件风格
#ifdef _AFXDLL
Enable3dControls(); // 使用动态的 3D控件
#else
Enable3dControlsStatic(); // 使用静态的 3D控件
#endif
// 在系统注册表中登记应用程序的主键值,以便将一些与应用程序
// 相关的参数存放在该主键值下
SetRegistryKey(_T("Local MFC AppWizard-Generated Applications"));
// 从注册表中调入应用程序的一些标准参数值,
LoadStdProfileSettings();
4.3使用 MFC AppWizard
// 若用户在 MFC AppWizard的第一步中,选择了,Single document”类型,
// 则进行下列的单文档模板的创建及其初始化操作。
CSingleDocTemplate* pDocTemplate; // 定义一个单文档模板指针变量
pDocTemplate = new CSingleDocTemplate(// 登记并创建单文档应用程序模板
IDR_MAINFRAME,// 菜单、快捷键等的资源标识号
RUNTIME_CLASS(CEx_SDIHelloDoc),// 文档类
RUNTIME_CLASS(CMainFrame),// 主框架窗口类
RUNTIME_CLASS(CEx_SDIHelloView)); // 视图类
AddDocTemplate(pDocTemplate); // 向应用程序添加文档模板
// 分列命令行标准命令如 DDE、文件打开等
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
// 传送命令行指定的命令,并执行相应的操作
if (!ProcessShellCommand(cmdInfo))
return FALSE;
// 对主框架窗口进行初始化以便显示和更新
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
return TRUE;
}
4.3使用 MFC AppWizard
RUNTIME_CLASS是一个运行类的宏定义,返回 CRuntimeClass类指针。借助 CRuntimeClass类结构能在应用程序运行过程中获得该类对象及其基类的相关信息,从而可以实现运行时类型检查。
CSingleDocTemplate是一个单文档模板类,将用户应用程序项目中的资源、
主框架窗口类、文档类以及视图类建立了联系。 AddDocTemplate负责将这些联系嵌入应用程序中。类似的,还有用于多文档应用程序的多文档模板类
CMultiDocTemplate,但与单文档不同的是,多文档模板可以创建多个视、
多个文档,
很多程序都需要从命令行输入参数,它是通过 ParseCommandLine函数保存在由 CCommandLineInfo类定义的对象中,命令行最终的命令和参数是通过
ProcessShellCommand执行的。与 DOS命令行操作不同的是,Windows应用程序命令行参数是通过选择,开始,?,运行,菜单命令,在弹出的运行对话框中指定的。
4.3使用 MFC AppWizard
文档类 CEx_SDIHelloDoc
CEx_SDIHelloDoc类的 Ex_SDIHelloDoc.h文件:
...
class CEx_SDIHelloDoc,public CDocument
{ protected,
CEx_SDIHelloDoc(); // 构造函数
DECLARE_DYNCREATE(CEx_SDIHelloDoc)
...
public:
//{{AFX_VIRTUAL(CEx_SDIHelloDoc)
public:
virtual BOOL OnNewDocument();// 当新建一个文档时,自动调用该函数
virtual void Serialize(CArchive& ar);// 当文档打开或保存时,自动调用该函数
//}}AFX_VIRTUAL
// Implementation
public:
virtual ~CEx_SDIHelloDoc();
#ifdef _DEBUG // 若应用程序是调试版本
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
...
4.3使用 MFC AppWizard
// 产生消息映射函数
protected:
//{{AFX_MSG(CEx_SDIHelloDoc)
// NOTE - the ClassWizard will add and remove member functions
here.
// DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
...
用户的文档类 CEx_SDIHelloDoc是从基类 CDocument派生而来。
AssertValid和 Dump是用于调试版本的两个虚函数。 AssertValid的目的是启用,断言,机制来检验对象的正确性、合法性,而 Dump的目的是为他人分析用户自己定义的类提供一种机制,用来输出类的名称或其他数据内容。
4.3使用 MFC AppWizard
视图类 CEx_SDIHelloView
CEx_SDIHelloView类的 Ex_SDIHelloView.h文件:
class CEx_SDIHelloView,public CView
{ protected,
CEx_SDIHelloView();
DECLARE_DYNCREATE(CEx_SDIHelloView)
public:
CEx_SDIHelloDoc* GetDocument(); // 用于返回文档类指针
public:
//{{AFX_VIRTUAL(CEx_SDIHelloView)
public:
virtual void OnDraw(CDC* pDC); // 用于绘制的虚函数
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
// 用于设置文档窗口风格的虚函数
protected:
// 以下虚函数供实现打印与打印预览功能时重载。若用户在 MFC AppWizard
// 的第四步中未选中,Printing and print preview”时,则不会出现下列虚函数。
virtual BOOL OnPreparePrinting(CPrintInfo* pInfo);
virtual void OnBeginPrinting(CDC* pDC,CPrintInfo* pInfo);
virtual void OnEndPrinting(CDC* pDC,CPrintInfo* pInfo);
//}}AFX_VIRTUAL
4.3使用 MFC AppWizard
// Implementation
public:
virtual ~CEx_SDIHelloView();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
...
protected:
//{{AFX_MSG(CEx_SDIHelloView)
// NOTE - the ClassWizard will add and remove member functions here.
// DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
#ifndef _DEBUG
// 内联函数,用于返回在文档模板定义的文档类指针
inline CEx_SDIHelloDoc* CEx_SDIHelloView::GetDocument()
{ return (CEx_SDIHelloDoc*)m_pDocument; }
#endif
CEx_SDIHelloView的成员函数 GetDocument是用 inline声明成一个内联函数。
4.3使用 MFC AppWizard
CEx_SDIHelloView类的 Ex_SDIHelloView.cpp文件:
#include "stdafx.h"
#include "Ex_SDIHello.h"
#include "Ex_SDIHelloDoc.h"
#include "Ex_SDIHelloView.h"
...
IMPLEMENT_DYNCREATE(CEx_SDIHelloView,CView)
BEGIN_MESSAGE_MAP(CEx_SDIHelloView,CView)
//{{AFX_MSG_MAP(CEx_SDIHelloView)
//}}AFX_MSG_MAP
// 为,文件,菜单下的,打印,..”和,打印预览,映射标准打印命令
ON_COMMAND(ID_FILE_PRINT,CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT,CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW,CView::OnFilePrintPreview)
END_MESSAGE_MAP()
CEx_SDIHelloView::CEx_SDIHelloView()
{ }
CEx_SDIHelloView::~CEx_SDIHelloView()
{ }
BOOL CEx_SDIHelloView::PreCreateWindow(CREATESTRUCT& cs)
{ return CView::PreCreateWindow(cs); }
4.3使用 MFC AppWizard
void CEx_SDIHelloView::OnDraw(CDC* pDC)
{ CEx_SDIHelloDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc); }
BOOL CEx_SDIHelloView::OnPreparePrinting(CPrintInfo* pInfo)
{ // default preparation
return DoPreparePrinting(pInfo);
}
void CEx_SDIHelloView::OnBeginPrinting(CDC* /*pDC*/,CPrintInfo*
/*pInfo*/)
{ // TODO,add extra initialization before printing }
void CEx_SDIHelloView::OnEndPrinting(CDC* /*pDC*/,CPrintInfo*
/*pInfo*/)
{ // TODO,add cleanup after printing }
4.3使用 MFC AppWizard
// 以下是用于调试的函数
#ifdef _DEBUG
void CEx_SDIHelloView::AssertValid() const
{ CView::AssertValid(); }
void CEx_SDIHelloView::Dump(CDumpContext& dc) const
{ CView::Dump(dc); }
CEx_SDIHelloDoc* CEx_SDIHelloView::GetDocument()
{ ASSERT(m_pDocument-
>IsKindOf(RUNTIME_CLASS(CEx_SDIHelloDoc)));
//,断言,m_pDocument指针可以指向的 CEx_SDIHelloDoc类
// 是一个 RUNTIME_CLASS类型
return (CEx_SDIHelloDoc*)m_pDocument;
}
#endif //_DEBUG
4.3使用 MFC AppWizard
视图类 CEx_SDIHelloView是从基类 CView派生而来的。说明:
各种类型的输入都可以由视图来响应、处理,并且打印和打印预览也是在视图类中完成的。这种文档和视图的结合,称为,文档 /视图结构,机制,是
MFC应用程序框架的核心,可以进行消息的处理、文档的格式化及文档数据的可视化处理等;它不但使文档数据和视图分离,而且能简化应用程序并减少代码冗余。
PreCreateWindow虚函数是在相应窗口创建前被系统自动调用的。在此函数中,可以更改其 CREATESTRUCT结构内容,将改变相应窗口的风格,
OnDraw是个非常有用的虚函数,当应用程序中的窗口状态或大小发生改变时,系统均会调用此函数重新绘制文档窗口的客户区。用户可以将一些绘图有关的代码添加此函数中,能在视图中进行图形的绘制。
例如,下面的代码:
void CEx_SDIHelloView::OnDraw(CDC* pDC)
{ CEx_SDIHelloDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
pDC->TextOut(100,100,"Hello,World!");
}
4.3使用 MFC AppWizard
主框架窗口类 CMainFrame
,关于,对话框类 CAboutDlg
MFC涉及到的机制有:
文档 /视图机制:它使用户应用程序类、文档类、视图类以及框架类之间有机地结合在一起,是 MFC最引人注目的机制。
消息映射机制:它是实现对各种不同消息的处理。
数据映射机制:是实现对话框中变量与控件之间的数据交换和数据校验。
运行时类型检查机制:它通过 GetRuntimeClass,IsKindOf、宏
DECLARE_DYNAMIC和宏 IMPLEMENT_DYNAMIC来实现的。
诊断信息转储机制:它是通过 AssertValid,Dump和宏 TRACE来实现的。
实现文档 /视图机制是通过 MFC应用程序向导自动完成的,而消息和数据映射则是通过 MFC的 ClassWizard来自动进行的。
4.4使用 ClassWizard
4.4.1ClassWizard概述
打开 MFC的 ClassWizard的方法:
选择,View”?“ClassWizard”菜单或直接使用 Ctrl+W快捷键。
在源代码文件的文档编辑窗口中,右击鼠标,选择 ClassWizard命令。
当 ClassWizard打开后,就会弹出如图的 MFC ClassWizard对话框。
图 4.15 MFC ClassWiard对话框
4.4使用 ClassWizard
4.4.2消息和消息映射
消息分类
Windows应用程序中的消息主要有三种类型。
窗口消息 (Windows message)
主要指由 WM_开头的消息,一般由窗口类和视图类对象来处理。窗口消息往往带有参数,以标志处理消息的方法。
控件的通知消息 (Control notifications)
当控件的状态发生改变时,控件就会向其父窗口发送 WM_COMMAND通知消息。应用程序框架处理控件消息的方法和窗口消息相同,但按钮的
BN_CLICKED通知消息除外,它的处理方法与命令消息相同。
命令消息 (Command message)
主要包括由用户交互对象 (菜单、工具条的按钮、快捷键等 )发送的
WM_COMMAND通知消息。命令消息的处理方式与其他两种消息不同,它能够被多种对象接收、处理,这些对象包括文档类、文档模板类、应用程序本身以及窗口和视类等;而窗口消息和控件的通知消息是由窗口对象接收并处理的,这里的窗口对象是指从 CWnd中派生的类的对象,它包括
CFrameWnd,CMDIFrameWnd,CMDIChildWnd,CView,CDialog以及从这些类派生的对象等。
4.4使用 ClassWizard
ClassWizard映射消息的一般方法
4.4使用 ClassWizard
例如,向 CEx_SDIHelloView中添加 WM_LBUTTOMDOWN的消息映射,则可按下列步骤进行:
(1)按 Ctrl+W快捷键打开 MFC ClassWizard对话框。
(2)在 Class name组合框中,将类名选定为 CEx_SDIHelloView。
(3)在 Object IDs列表框中选定 CEx_SDIHelloView,而在 Messages列表中选定 WM_LBUTTOMDOWN消息。
(4)双击 Messages列表中的 WM_LBUTTOMDOWN消息或单击 [Add Function]
按钮,都会在 CEx_SDIHelloView类中添加该消息的映射函数
OnLButtonDown,同时在 Member funcions列表中显示这一消息映射函数和被映射的消息,如图。
图 4.16 映射 WM_LBUTTONDOWN消息
4.4使用 ClassWizard
(5)单击 [Edit Code],转向文档窗口,定位到 OnLButtonDown源代码处。
(6)添加下列代码:
void CEx_SDIHelloView::OnLButtonDown(UINT nFlags,CPoint point)
{ // TODO,Add your message handler code here and/or call default
MessageBox ("你好,我的 Visual C++世界! ","问候 ",0) ;
CView::OnLButtonDown(nFlags,point);
}
(7)程序运行后,在窗口客户区左击,弹出一个消息对话框。
查看 CEx_SDIHelloView程序代码,可以发现,ClassWizard为
WM_LBUTTOMDOWN的消息映射作了以下三个方面内容的安排:
在头文件 Ex_SDIHelloView.h中声明消息处理函数 OnLButtonDown:
protected:
//{{AFX_MSG(CEx_SDIHelloView)
afx_msg void OnLButtonDown(UINT nFlags,CPoint point);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
4.4使用 ClassWizard
在 Ex_SDIHelloView.cpp源文件前面的消息映射入口处,添加映射宏:
BEGIN_MESSAGE_MAP(CEx_SDIHelloView,CView) // 消息映射开始
//{{AFX_MSG_MAP(CEx_SDIHelloView)
ON_WM_LBUTTONDOWN()
//}}AFX_MSG_MAP
…
END_MESSAGE_MAP()
// 消息映射结束在 Ex_SDIHelloView.cpp文件中写入一个空的消息处理函数的模板,框架:
void CEx_SDIHelloView::OnLButtonDown(UINT nFlags,CPoint point)
{ // TODO,Add your message handler code here and/or call default
CView::OnLButtonDown(nFlags,point);
}
根据 ClassWizard产生的上述消息映射过程,可以手动添加一些 ClassWizard
不支持的消息映射函数,以完成特定的功能。
鼠标和键盘消息各自都有相应的消息处理宏和预定义消息处理函数,因此消息映射函数名称不再需要用户重新定义。对于菜单和按钮等命令消息来说,
用 ClassWizard映射时会弹出一个对话框,用来指定消息映射函数的名称。
若指定的消息映射函数需要删除,则需要先在 ClassWizard对话框的
Messages列表中选定要删除的消息映射函数,然后单击 [Delete Function]按钮,最后关闭 ClassWizard对话框,并在该消息映射函数所在的类实现文件
(.cpp)中将映射函数定义的代码全部删除。
4.4使用 ClassWizard
键盘和鼠标消息
按下一个键或组合键时,将 WM_KEYDOWN或 WM_SYSKEYDOWN放入具有输入焦点的应用程序窗口的消息队列中。键被释放时,把 WM_KEYUP或
WM_SYSKEYUP消息放入消息队列中。对字符键,会在这两个消息之间产生
WM_CHAR消息。,
ClassWizard能自动添加了当前类的 WM_KEYDOWN和 WM_KEYUP击键消息处理函数的调用,它们具有下列函数原型:
afx_msg void OnKeyDown( UINT nChar,UINT nRepCnt,UINT nFlags );
afx_msg void OnKeyUp( UINT nChar,UINT nRepCnt,UINT nFlags );
afx_msg是 MFC用于定义消息函数的标志,参数 nChar表示虚拟键代码,
nRepCnt表示当用户按住一个键时的重复计数,nFlags表示击键消息标志。
虚拟键代码指与设备无关的键盘编码。最常用的虚拟键代码已被定义在
Winuser.h中。
MFC中的 ClassWizard也提供相应的字符消息处理框架,并自动添加了当前类的 WM_CHAR消息处理函数调用,它具有下列函数原型:
afx_msg void OnChar( UINT nChar,UINT nRepCnt,UINT nFlags );
nChar表示键的 ASCII码,nRepCnt表示当用户按住一个键时的重复计数,
nFlags表示字符消息标志。
对鼠标进行操作时会产生对应的消息。通常,只将键盘消息发送给具有输入焦点的窗口,但鼠标消息不受这种限制。只要鼠标移过窗口的客户区时,
就会向该窗口发送 WM_MOUSEMOVE(移动鼠标 )消息。
4.4使用 ClassWizard
这里的客户区是指窗口中用于输出文档的区域。在窗口的客户区中按下或释放一个鼠标键时,还会产生如表所示的消息。
ClassWizard会将映射成类似 afx_msg void OnXXXX的消息处理函数,原型:
afx_msg void OnXXXX( UINT nFlags,CPoint point );
point表示鼠标光标在屏幕的 (x,y)坐标; nFlags表示鼠标按钮和键盘组合情况,
它可以是下列值的组合 (MK前缀表示,鼠标键,):
MK_CONTROL —— 键盘上的 Ctrl键被按下
MK_LBUTTON —— 鼠标左按钮被按下
MK_MBUTTON —— 鼠标中按钮被按下
MK_RBUTTON —— 鼠标右按钮被按下
MK_SHIFT —— 键盘上的 Shift键被按下想知道某个键被按下,可用对应的标识与 nFlags进行逻辑,与,(&)运算,所得结果若为 TRUE(非 0)时,则表示该键被按下。
4.4使用 ClassWizard
映射计时器消息有一种常用的输入设备就是计时器,它周期性地按一定的时间间隔向应用程序发送 WM_TIMER消息。由于它能实现,实时更新,以及,后台运行,
等功能,因而在应用程序中计时器是一个难得的程序方法。
应用程序是通过 CWnd的 SetTimer函数来设置并启动计时器的,函数原型:
UINT SetTimer( UINT nIDEvent,UINT nElapse,
void (CALLBACK EXPORT* lpfnTimer)(HWND,UINT,UINT,DWORD) );
nIDEvent指定该计时器的标识值 (不能为 0),应用程序需要多个计时器时可多次调用该函数,但每一个计时器的标识值应是唯一的,各不相同
nElapse表示计时器的时间间隔 (单位为毫秒 ),lpfnTimer是一个回调函数的指针,该函数由应用程序来定义,用来处理计时器 WM_TIMER消息。一般情况下该参数为 NULL,此时 WM_TIMER消息被放入到应用程序消息队列中供 CWnd对象处理。
SetTimer函数成功调用后返回新计时器的标识值。应用程序不再使用计时器时,调用 CWnd:,KillTimer函数来停止 WM_TIMER消息的传送,原型:
BOOL KillTimer( int nIDEvent );
nIDEvent和用户调用 SetTimer函数设置的计时器标识值是一致的。
对于 WM_TIMER消息,ClassWizard会将其映射成具有下列消息处理函数:
afx_msg void OnTimer( UINT nIDEvent );
4.4使用 ClassWizard
其他窗口消息的映射系统中,除了用户输入产生的消息外,还有许多系统根据应用程序的状态和运行过程产生的消息,有时也需要用户进行处理。
(1)WM_CREATE消息窗口对象创建后,向视图发送的第一个消息;如果用户有什么工作需要在初始化时处理,就可在该消息处理函数中加入所需代码。由于 WM_CREATE
消息发送时,窗口对象还未完成,窗口还不可见,因此在该消息处理函数
OnCreate内,不能调用那些依赖于窗口处于完成激活状态的函数。
(2)WM_CLOSE或 WM_DESTROY消息从系统菜单中关闭窗口或者父窗口被关闭时,都会发送 WM_CLOSE消息;
而 WM_DESTROY消息是在窗口从屏幕消失后发送的,因此它紧随
WM_CLOSE之后。
(3)WM_PAINT消息当窗口的大小发生变化、窗口内容发生变化、窗口间的层叠关系发生变化或调用函数 UpdateWindow或 RedrawWindow时,系统都将产生 WM_PAINT
消息,表示要重新绘制窗口的内容。该消息处理函数的原型是;
afx_msg void OnPaint();
用 ClassWizard映射该消息的目的是执行自己的图形绘制代码。
4.4使用 ClassWizard
4.4.3类的添加和删除用 MFC ClassWizard给项目添加一个类通常是按下列步骤进行的:
(1)按快捷键 Ctrl+W启动 MFC ClassWizard对话框。单击 [Add Class],选择
New命令,如图所示的 New Class对话框。
图 4.17 New Class对话框
4.4使用 ClassWizard
(2)对话框中,Name框是用来输入用户定义的类名,注意要以,C”字母打头,以保持与 MFC标识符命名规则一致; File Name是该类的源代码文件名,
单击 [Change]按钮可改变源文件名称及其在磁盘中的位置; Base class框用来指定该类的基类; Dialog ID框是当选择 CDialog作为基类时指定对话框的资源 ID号。最下面的 Automation是用来设置对自动化的支持。
(3)单击 [OK]按钮,一个新类就会自动添加到项目中。
而当添加的类需要删除时,则需要按下列步骤进行:
(1)将 Visual C++ 6.0打开的所有文档窗口关闭。
(2)将项目工作区窗口切换到 FileView页面,展开 Source Files和 Header Files
结点,分别选定要删除类的对应,h和,cpp文件,按下 Delete键,删除这两个文件。
(3)选择,File”?“Close Workspace”菜单命令,关闭项目。
(4)从实际的文件夹中删除对应的,h和,cpp文件与,clw文件。
需要注意的是,当下一次打开 MFC ClassWizard对话框时,就会弹出 Select
Source Files对话框,这时只要单击右下的 [Add All]按钮即可。
作业
P375:1,5
P408:实验 8
4.1 Windows编程基础
4.2 编制一个 MFC应用程序
4.3 使用 MFC AppWizard
4.4 使用 ClassWizard
作业
4.1Windows编程基础
4.1.1简单的 Windows应用程序
[例 Ex_HelloMsg] 一个简单的 Windows应用程序。
#include <windows.h>
int WINAPI WinMain (HINSTANCE hInstance,HINSTANCE hPrevInstance,
LPSTR lpCmdLine,int nCmdShow)
{ MessageBox (NULL,"你好,我的 Visual C++世界! ","问候 ",0) ;
return 0 ;
}
运行上述程序需要进行以下步骤:
选择,File”?“New”。单击 Projects,选中 Win32 Application项。
在 Project Name框中键入项目名称 Ex_HelloMsg。在 Location下的编辑框中直接键入文件夹名称,或单击 Browse按钮 (...)选择一个已有的文件夹。
单击 [OK]。选中 An empty project项。单击 [Finish],单击 [OK]系统将自动创建此应用程序。
选择,File”?“New”。单击 File标签,在左边的列表框中选择 C++ Source
File项,在右边的 File框中键入 Ex_HelloMsg.cpp,单击 [OK]。
4.1Windows编程基础
输入上面的代码,运行程序,如图。
从程序可以看出,
传统的 DOS程序以 main函数作为进入程序的初始入口点,但在 Windows应用程序中,main函数被 WinMain函数取而代之,WinMain函数的原型如下:
int WINAPI WinMain (
HINSTANCE hInstance,// 当前实例句柄
HINSTANCE hPrevInstance,// 前一实例句柄
LPSTR lpCmdLine,// 指向命令行参数的指针
int nCmdShow) // 窗口的显示状态
句柄是一个标识 Windows资源 (如菜单、图标、窗口等 )和设备等对象的变量,
或者是一个对操作系统资源的间接引用。
每一个 Windows应用程序都需要 Windows.h头文件,它还包含了其他的一些
Windows头文件。这些头文件定义了 Windows的所有数据类型、函数调用、
数据结构和符号常量。
MessageBox是一个 Win32 API(Application Programming Interface,应用程序接口 )函数,用来弹出一个对话框窗口,显示短信息。该函数第一个参数用来指定父窗口句柄,即对话框所在的窗口句柄。第二、三个参数分别用来指定显示的消息内容和对话框窗口的标题,最后一个参数用来指定在对话框中显示的按钮。
图 4.1 Ex_HelloMsg运行结果
4.1Windows编程基础
[例 Ex_HelloWin] 一个完整的 Windows应用程序。
#include <windows.h>
LRESULT CALLBACK WndProc (HWND,UINT,WPARAM,LPARAM); // 窗口过程
int WINAPI WinMain (HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR
lpCmdLine,int nCmdShow)
{ HWND hwnd ; // 窗口句柄
MSG msg ; // 消息
WNDCLASS wndclass ; // 窗口类
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc= WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra= 0 ;
wndclass.hInstance= hInstance ;
wndclass.hIcon= LoadIcon (NULL,IDI_APPLICATION) ;
wndclass.hCursor= LoadCursor (NULL,IDC_ARROW) ;
wndclass.hbrBackground= (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName= NULL ;
wndclass.lpszClassName=,HelloWin”; // 窗口类名
if (!RegisterClass (&wndclass)) // 注册窗口
{ MessageBox (NULL,"窗口注册失败! ","HelloWin",0) ;
return 0 ;
}
4.1Windows编程基础
hwnd = CreateWindow ("HelloWin",// 窗口类名
"我的窗口 ",// 窗口标题
WS_OVERLAPPEDWINDOW,// 窗口样式
CW_USEDEFAULT,// 窗口最初的 x 位置
CW_USEDEFAULT,// 窗口最初的 y 位置
CW_USEDEFAULT,// 窗口最初的 x 大小
CW_USEDEFAULT,// 窗口最初的 y 大小
NULL,// 父窗口句柄
NULL,// 窗口菜单句柄
hInstance,// 应用程序实例句柄
NULL) ; // 创建窗口的参数
ShowWindow (hwnd,nCmdShow) ; // 显示窗口
UpdateWindow (hwnd) ; // 更新窗口,包括窗口的客户区
// 进入消息循环:当从应用程序消息队列中检取的消息是 WM_QUIT时,则退出循环。
while (GetMessage (&msg,NULL,0,0))
{ TranslateMessage (&msg) ; // 转换某些键盘消息
DispatchMessage (&msg) ; // 将消息发送给窗口过程,这里是
WndProc
}
return msg.wParam ;
}
4.1Windows编程基础
LRESULT CALLBACK WndProc (HWND hwnd,UINT message,WPARAM wParam,
LPARAM lParam)
{ switch (message)
{ case WM_CREATE,// 窗口创建产生的消息
return 0 ;
case WM_LBUTTONDOWN:
MessageBox (NULL,"你好,我的 Visual C++世界! ","问候 ",0) ;
return 0 ;
case WM_DESTROY,// 当窗口关闭时产生的消息
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd,message,wParam,lParam) ; // 执行默认的消息处理
}
4.1Windows编程基础程序运行,单击鼠标左键,如图。
窗口过程函数 WndProc用来接收和处理各种不同的消息,WinMain函数通常要完成:
调用 API函数 RegisterClass注册应用程序的窗口类。
调用相关 API函数创建和显示窗口,并进行必要的初始化。 CreateWindow
创建已注册窗口类的窗口。 Windows每一个窗口都有一些基本属性。窗口类就是充当这些属性的模板。
创建和启动应用程序的消息循环。 Windows应用程序接受各种不同的消息。
Windows系统首先将消息放入消息队列中,应用程序的消息循环就是从应用程序的消息队列中检取消息,并将消息发送相应的窗口过程函数中作进一步处理。
如果接收到 WM_QUIT消息,则退出应用程序图 4.2 Ex_HelloWin运行结果
4.1Windows编程基础应用程序的基本流程,如图。
图 4.3 Windows应用程序的基本流程
4.1Windows编程基础
4.1.2Windows编程特点
消息驱动机制
DOS程序是通过调用系统的函数来获得用户输入的,Windows程序则是通过操作系统发送的消息来处理用户输入的。
无论是系统产生的动作或是运行应用程序产生的动作,都称为事件 (Events)
产生的消息 (Message)。在应用程序中,通过接收消息、分发消息、处理消息来和用户进行交互。许多消息都经过了严格的定义,并且适用于所有的应用程序。
图形设备接口 (GDI )
DOS环境中,要在打印机上打印一幅图形是非常复杂的事件。 Windows则提供了一个抽象的接口,称为图形设备接口 (Graphical Device Interface,
简称 GDI),使得用户直接利用系统的 GDI函数就能方便实现输入或输出,而不必关心与系统相连的外部设备的类型。
基于资源的程序设计
Windows应用程序常常包含众多图形元素,每一个这样的元素都作为一种可以装入应用程序的资源来存放。这些资源可以被编辑、修改,也可以被其他应用程序所共享。 VC中提供的许多编辑器能,所见即所得,地进行不同类型资源的设计、编辑等。
4.1Windows编程基础
动态链接库
提供一些特定结构的函数,能被应用程序在运行过程中装入和连接,多个程序可以共享同一个动态链接库。从编程角度,动态链接库可以提高程序模块的灵活性,它本身是可以单独设计、编译和调试的。
Windows提供了应用程序可利用的丰富的函数调用,大多数用于实现其用户界面和在显示器上显示的文本和图形,都是通过动态链接库来实现的。
Windows中,KERNEL32用来处理存储器低层功能、任务和资源管理等核心服务; GDI32用来提供图形设备接口,管理用户界面和图形绘制; USER32
负责窗口的管理。
进程和线程
Windows是一个 32位多任务操作系统,采用进程和线程的管理模式。
进程是装入内存中正在执行的应用程序。进程包括私有的虚拟地址空间、
代码、数据及其它操作系统资源。进程包括了一个或多个在进程上下文内运行的线程。
线程是操作系统分配 CPU时间的基本实体。线程可以执行应用程序代码的任何部分,包括当前正在被其它线程执行的那些部分。同一进程的所有线程共享同样的虚拟地址空间、全局变量和操作系统资源。
一个应用程序,包括一个或多个进程,每个进程由一个或多个线程构成。
4.1Windows编程基础
4.1.3Windows基本数据类型
4.1Windows编程基础需要说明的是:
这些基本数据类型都是以大写字符出现。
凡是数据类型的前缀是 P或 LP,表示该类型是一个指针或长指针数据类型。
前缀是 H,表示是句柄类型。前缀是 U,表示是无符号数据类型。
还提供一些宏来处理上述基本数据类型。 LOBYTE和 HIBYTE分别用来获取 16
位数值中的低位和高位字节; LOWORD和 HIWORD分别用来获取 32位数值中的低位和高位字; MAKEWORD是将两个 16位无符号值结合成一个 32位无符号值。
4.2编制一个 MFC应用程序
4.2.1MFC概述
1987年微软公司推出了第一代 Windows产品,并为应用程序设计者提供了
Win16 API,在此基础上推出了 Windows GUI(图形用户界面 ),然后采用面向对象技术对 API进行封装。 1992年推出应用程序框架产品 AFX(Application
Frameworks),并在 AFX的基础上进一步发展为 MFC产品。 MFC类的基本层次结构如图。
图 4.4 MFC类的基本层次结构
CCmdTarget
CWnd
CDocument
CFrameWnd
CObject
CWinTread
CWinApp
CView CDialog及控件
CMDIChildWndCMDIFrameWnd CMiniFrameWnd
4.2编制一个 MFC应用程序
4.2.2设计一个 MFC程序
[例 Ex_HelloMFC] 一个 MFC应用程序。
#include <afxwin.h> // MFC头文件
class CHelloApp,public CWinApp // 声明应用程序类
{ public:
virtual BOOL InitInstance(); };
CHelloApp theApp; // 建立应用程序类的实例
class CMainFrame,public CFrameWnd // 声明主窗口类
{ public:
CMainFrame()
{ // 创建主窗口
Create(NULL,"我的窗口 ",WS_OVERLAPPEDWINDOW,
CRect(0,0,400,300));
}
protected:
afx_msg void OnLButtonDown(UINT nFlags,CPoint point);
DECLARE_MESSAGE_MAP()
};
// 消息映射入口
BEGIN_MESSAGE_MAP(CMainFrame,CFrameWnd)
ON_WM_LBUTTONDOWN() // 单击鼠标左键消息的映射宏
END_MESSAGE_MAP()
4.2编制一个 MFC应用程序
void CMainFrame::OnLButtonDown(UINT nFlags,CPoint point)
{ MessageBox ("你好,我的 Visual C++世界! ","问候 ",0) ;
CFrameWnd::OnLButtonDown(nFlags,point);
}
// 每当应用程序首次执行时都要调用的初始化函数
BOOL CHelloApp::InitInstance()
{ m_pMainWnd = new CMainFrame();
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
return TRUE;
}
运行上述 MFC程序需要进行以下步骤:
选择,File”?“New”。单击 Projects,选中 Win32 Application项,创建一个
Ex_HelloMFC空应用程序项目。
选择,File”?“New”。单击 File标签,在左边的列表框中选择 C++ Source
File项,在右边的 File框中键入 Ex_HelloMsg.cpp,单击 [OK]。
输入上面的代码。选择,Project”?“Settings”,选择,General”标签。在
,Microsoft Foundation Classes”组合框中,选择,Use MFC in a Shared
DLL”。单击 [OK]。
程序运行后,单击鼠标左键,就会弹出一个对话框,结果同 Ex_HelloWin。
4.2编制一个 MFC应用程序
4.2.3理解程序代码
MFC是使用 afxwin.h来代替头文件 windows.h。
运行应用程序时,自动调用应用程序框架内部的 WinMain函数,并自动查找该应用程序类 CHelloApp(从 CWinApp派生 )的全局变量 theApp,然后自动调用 CHelloApp的虚函数 InitInstance,该函数会进一步调用相应的函数来完成主窗口的构造和显示工作。上述程序中 InitInstance的执行过程。
首先执行的是:
m_pMainWnd = new CMainFrame();
创建从 CFrameWnd类派生而来的用户框架窗口 CMainFrame类对象,继而调用该类的构造函数,使得 Create函数被调用,完成了窗口创建工作。
然后执行后面两句:
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
用作窗口的显示和更新。
最后返回 TRUE,表示窗口创建成功。
由于应用程序类 CWinApp是用来调用 WinMain以及实例的初始化,因此每一个 MFC应用程序有且只能一个这样的应用程序类,且需要一个全局的对象实例,如上述程序中的 theApp。
4.2编制一个 MFC应用程序
InitInstance完成初始化后,调用基类 CWinApp的成员函数 Run,执行应用程序的消息循环。 Run检查到消息队列为空时,调用基类 CWinApp的成员函数 OnIdle进行空闲时的后台处理工作。若消息队列为空且又没有后台工作要处理时,则应用程序一直处于等待状态,一直等到有消息为止。当程序结束后,调用基类 CWinApp的成员函数 ExitInstance,完成终止应用程序的收尾工作。
在 MFC中,一个消息的处理往往是通过独特的 MFC消息映射机制来进行的。
消息映射 (Message Map)机制,指 MFC类中将消息与消息处理函数联系起来,
一一对应的机制。任何一个从类 CCmdTarget派生的类理论上均可处理消息,
且都有相应的消息映射函数。
按照 MFC的消息映射机制,映射一个消息的过程由三个部分组成:
在处理消息的类中,使用消息宏 DECLARE_MESSAGE_MAP()声明对消息映射的支持,并在该宏之前声明消息处理函数。
使用 BEGIN_MESSAGE_MAP和 END_MESSAGE_MAP宏在类声明之后的地方定义该类支持的消息映射入口点,所有消息映射宏都添加在这里,
当然不同的消息 MFC都会有不同的消息映射宏。
定义消息处理函数。
为了使该消息能被其他对象接收并处理,在函数中常常需要调用基类中的相关消息处理函数。 MFC的 ClassWizard(类向导 )能自动完成消息的上述映射过程。
4.3使用 MFC AppWizard
4.3.1应用程序框架类型
4.3使用 MFC AppWizard
4.3.2创建一个单文档应用程序
开始选择,File”?“New”,选择 Projects标签;选择 MFC AppWizard(exe)的项目类型 (该类型用于创建可执行的 Windows应用程序 ),将项目工作文件夹定位在,D:\Visual C++ 6.0程序,,并在,Project Name”框中输入项目名
Ex_SDIHello,如图,单击 [OK]。
图 4.5 MFC AppWizard的,New”对话框
4.3使用 MFC AppWizard
第一步
从应用程序类型 Single Document,Multiple Document和 Dialog Based中选择
SDI。
决定应用程序中是否需要 MFC的,文档 /视图,结构的支持。若不选定此项,
则程序中的磁盘文件的打开、保存以及文档和视图的相互作用等功能需要用户来实现,且将跳过 Step 2~Step 5,直接弹出,Step 6”对话框。
选择资源所使用的语言,这里是,中文 [中国 ]”,单击 [Next]按钮。
图 4.6 SDI的,Step 1”对话框
4.3使用 MFC AppWizard
第二步单击 [Next]按钮进入下一步。
第三步单击 [Next]按钮进入下一步。
图 4.7 SDI的,Step 2”对话框 图 4.8 SDI的,Step 3”对话框
4.3使用 MFC AppWizard
第四步单击 [Next]按钮进入下一步。
图 4.9 SDI的,Step 4”对话框
4.3使用 MFC AppWizard
第五步在弹出的对话框 (如图 )中出现三个方面的选项,供用户来选择:
应用程序的主窗口是 MFC标准风格还是窗口左边有切分窗口的浏览器风格;
在源文件中是否加入注释用来引导用户编写程序代码;
使用动态链接库还是静态链接库。
保留缺省状态,单击 [Next]按钮进行下一步 。
图 4.10 SDI的,Step 5”对话框
4.3使用 MFC AppWizard
第六步单击 [Finish]按钮出现如图 4.12所示的对话框图 4.11 SDI的,Step 6”对话框 图 4.12 项目信息对话框
4.3使用 MFC AppWizard
编译并运行图 4.13 应用程序运行结果菜单栏工具栏状态栏标题栏文档窗口
4.3使用 MFC AppWizard
4.3.3理解程序框架
应用类 CEx_SDIHelloApp
下面首先来看看 Ex_SDIHello应用程序的 Ex_SDIHello.h文件:
...
class CEx_SDIHelloApp,public CWinApp
{ public:
CEx_SDIHelloApp();
//{{AFX_VIRTUAL(CEx_SDIHelloApp)
public:
virtual BOOL InitInstance();
//}}AFX_VIRTUAL
// Implementation(实现 )
//{{AFX_MSG(CEx_SDIHelloApp)
afx_msg void OnAppAbout();
// NOTE - the ClassWizard will add and remove member functions here.
// DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_MSG
DECLARE_MESSAGE_MAP() // 声明消息映射
};
..,
4.3使用 MFC AppWizard
代码中,
//{{AFX_VIRTUAL(类名 )
...
//}}AFX_VIRTUAL
是 ClassWizard定义的专门用作虚函数重载的标记,表示该程序块中的虚函数的重载是由 ClassWizard来自动管理的,一般不需要去更改。同样,
//{{AFX_MSG(类名 )
...
//}}AFX_MSG
是 ClassWizard定义的专门用作消息映射声明的标记。
代码中的,//TODO:”以及英文等注释是由 MFC AppWizard为用户自动生成的。若不需要这些注释,可在应用程序向导的第五步对话框中,将,Would
you like to generate source file comments?”项选为,No,thank you”。
下面再来看看该 Ex_SDIHello应用程序的 Ex_SDIHello.cpp源文件:
#include "stdafx.h" // 预编译处理的头文件
#include "Ex_SDIHello.h" // 应用类的头文件
#include "MainFrm.h" // 主框架类的头文件
#include "Ex_SDIHelloDoc.h" // 文档类的头文件
#include "Ex_SDIHelloView.h" // 视图类的头文件
4.3使用 MFC AppWizard
//消息映射开始
BEGIN_MESSAGE_MAP(CEx_SDIHelloApp,CWinApp) // 消息映射宏
//{{AFX_MSG_MAP(CEx_SDIHelloApp)
// 映射,帮助,菜单项中的,关于 Ex_SDIHello”命令消息,当用户选择了
// 该命令时,将执行函数 OnAppAbout
ON_COMMAND(ID_APP_ABOUT,OnAppAbout)
// NOTE - the ClassWizard will add and remove mapping macros here.
// DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_MSG_MAP
// 映射,文件,菜单项中的,新建,和,打开,命令消息,当用户选择了
,新建,
// 或,打开,命令时,将相应执行 CWinApp::OnFileNew或
CWinApp::OnFileOpen
ON_COMMAND(ID_FILE_NEW,CWinApp::OnFileNew)
ON_COMMAND(ID_FILE_OPEN,CWinApp::OnFileOpen)
// 映射,文件,菜单项中的,打印设置,命令消息,当用户选择了
// 该命令时,将执行函数 CWinApp::OnFilePrintSetup
ON_COMMAND(ID_FILE_PRINT_SETUP,CWinApp::OnFilePrintSetup)
END_MESSAGE_MAP() // 消息映射宏
4.3使用 MFC AppWizard
// 消息映射开始结束
CEx_SDIHelloApp::CEx_SDIHelloApp() // 构造函数
{ }
CEx_SDIHelloApp theApp; // 定义的一个应用类对象,表示一个实例
BOOL CEx_SDIHelloApp::InitInstance()
{,..
}
...
void CEx_SDIHelloApp::OnAppAbout()
{ CAboutDlg aboutDlg; // 定义的 CAboutDlg对象
aboutDlg.DoModal(); // 调用相应的库函数,显示
CAboutDlg对话框
}
代码中,
//{{AFX_MSG_MAP(类名 )
..
//}}AFX_MSG_MAP
是 ClassWizard定义的专门用作消息映射的标记,表示该程序块中的消息映射函数是由 ClassWizard来自动管理的,用户一般不需要去更改。
4.3使用 MFC AppWizard
最主要的 InitInstance函数体代码:
BOOL CEx_SDIHelloApp::InitInstance()
{ // 若用户在 MFC AppWizard的第三步中,选中了,ActiveX Controls”,
// 则表示所创建的应用程序支持 ActiveX控件
AfxEnableControlContainer();
// 若用户在 MFC AppWizard的第四步中,选中了,3D controls”
// 则表示所创建的应用程序支持 Windows 95版本前的 3D控件风格
#ifdef _AFXDLL
Enable3dControls(); // 使用动态的 3D控件
#else
Enable3dControlsStatic(); // 使用静态的 3D控件
#endif
// 在系统注册表中登记应用程序的主键值,以便将一些与应用程序
// 相关的参数存放在该主键值下
SetRegistryKey(_T("Local MFC AppWizard-Generated Applications"));
// 从注册表中调入应用程序的一些标准参数值,
LoadStdProfileSettings();
4.3使用 MFC AppWizard
// 若用户在 MFC AppWizard的第一步中,选择了,Single document”类型,
// 则进行下列的单文档模板的创建及其初始化操作。
CSingleDocTemplate* pDocTemplate; // 定义一个单文档模板指针变量
pDocTemplate = new CSingleDocTemplate(// 登记并创建单文档应用程序模板
IDR_MAINFRAME,// 菜单、快捷键等的资源标识号
RUNTIME_CLASS(CEx_SDIHelloDoc),// 文档类
RUNTIME_CLASS(CMainFrame),// 主框架窗口类
RUNTIME_CLASS(CEx_SDIHelloView)); // 视图类
AddDocTemplate(pDocTemplate); // 向应用程序添加文档模板
// 分列命令行标准命令如 DDE、文件打开等
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
// 传送命令行指定的命令,并执行相应的操作
if (!ProcessShellCommand(cmdInfo))
return FALSE;
// 对主框架窗口进行初始化以便显示和更新
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
return TRUE;
}
4.3使用 MFC AppWizard
RUNTIME_CLASS是一个运行类的宏定义,返回 CRuntimeClass类指针。借助 CRuntimeClass类结构能在应用程序运行过程中获得该类对象及其基类的相关信息,从而可以实现运行时类型检查。
CSingleDocTemplate是一个单文档模板类,将用户应用程序项目中的资源、
主框架窗口类、文档类以及视图类建立了联系。 AddDocTemplate负责将这些联系嵌入应用程序中。类似的,还有用于多文档应用程序的多文档模板类
CMultiDocTemplate,但与单文档不同的是,多文档模板可以创建多个视、
多个文档,
很多程序都需要从命令行输入参数,它是通过 ParseCommandLine函数保存在由 CCommandLineInfo类定义的对象中,命令行最终的命令和参数是通过
ProcessShellCommand执行的。与 DOS命令行操作不同的是,Windows应用程序命令行参数是通过选择,开始,?,运行,菜单命令,在弹出的运行对话框中指定的。
4.3使用 MFC AppWizard
文档类 CEx_SDIHelloDoc
CEx_SDIHelloDoc类的 Ex_SDIHelloDoc.h文件:
...
class CEx_SDIHelloDoc,public CDocument
{ protected,
CEx_SDIHelloDoc(); // 构造函数
DECLARE_DYNCREATE(CEx_SDIHelloDoc)
...
public:
//{{AFX_VIRTUAL(CEx_SDIHelloDoc)
public:
virtual BOOL OnNewDocument();// 当新建一个文档时,自动调用该函数
virtual void Serialize(CArchive& ar);// 当文档打开或保存时,自动调用该函数
//}}AFX_VIRTUAL
// Implementation
public:
virtual ~CEx_SDIHelloDoc();
#ifdef _DEBUG // 若应用程序是调试版本
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
...
4.3使用 MFC AppWizard
// 产生消息映射函数
protected:
//{{AFX_MSG(CEx_SDIHelloDoc)
// NOTE - the ClassWizard will add and remove member functions
here.
// DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
...
用户的文档类 CEx_SDIHelloDoc是从基类 CDocument派生而来。
AssertValid和 Dump是用于调试版本的两个虚函数。 AssertValid的目的是启用,断言,机制来检验对象的正确性、合法性,而 Dump的目的是为他人分析用户自己定义的类提供一种机制,用来输出类的名称或其他数据内容。
4.3使用 MFC AppWizard
视图类 CEx_SDIHelloView
CEx_SDIHelloView类的 Ex_SDIHelloView.h文件:
class CEx_SDIHelloView,public CView
{ protected,
CEx_SDIHelloView();
DECLARE_DYNCREATE(CEx_SDIHelloView)
public:
CEx_SDIHelloDoc* GetDocument(); // 用于返回文档类指针
public:
//{{AFX_VIRTUAL(CEx_SDIHelloView)
public:
virtual void OnDraw(CDC* pDC); // 用于绘制的虚函数
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
// 用于设置文档窗口风格的虚函数
protected:
// 以下虚函数供实现打印与打印预览功能时重载。若用户在 MFC AppWizard
// 的第四步中未选中,Printing and print preview”时,则不会出现下列虚函数。
virtual BOOL OnPreparePrinting(CPrintInfo* pInfo);
virtual void OnBeginPrinting(CDC* pDC,CPrintInfo* pInfo);
virtual void OnEndPrinting(CDC* pDC,CPrintInfo* pInfo);
//}}AFX_VIRTUAL
4.3使用 MFC AppWizard
// Implementation
public:
virtual ~CEx_SDIHelloView();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
...
protected:
//{{AFX_MSG(CEx_SDIHelloView)
// NOTE - the ClassWizard will add and remove member functions here.
// DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
#ifndef _DEBUG
// 内联函数,用于返回在文档模板定义的文档类指针
inline CEx_SDIHelloDoc* CEx_SDIHelloView::GetDocument()
{ return (CEx_SDIHelloDoc*)m_pDocument; }
#endif
CEx_SDIHelloView的成员函数 GetDocument是用 inline声明成一个内联函数。
4.3使用 MFC AppWizard
CEx_SDIHelloView类的 Ex_SDIHelloView.cpp文件:
#include "stdafx.h"
#include "Ex_SDIHello.h"
#include "Ex_SDIHelloDoc.h"
#include "Ex_SDIHelloView.h"
...
IMPLEMENT_DYNCREATE(CEx_SDIHelloView,CView)
BEGIN_MESSAGE_MAP(CEx_SDIHelloView,CView)
//{{AFX_MSG_MAP(CEx_SDIHelloView)
//}}AFX_MSG_MAP
// 为,文件,菜单下的,打印,..”和,打印预览,映射标准打印命令
ON_COMMAND(ID_FILE_PRINT,CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT,CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW,CView::OnFilePrintPreview)
END_MESSAGE_MAP()
CEx_SDIHelloView::CEx_SDIHelloView()
{ }
CEx_SDIHelloView::~CEx_SDIHelloView()
{ }
BOOL CEx_SDIHelloView::PreCreateWindow(CREATESTRUCT& cs)
{ return CView::PreCreateWindow(cs); }
4.3使用 MFC AppWizard
void CEx_SDIHelloView::OnDraw(CDC* pDC)
{ CEx_SDIHelloDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc); }
BOOL CEx_SDIHelloView::OnPreparePrinting(CPrintInfo* pInfo)
{ // default preparation
return DoPreparePrinting(pInfo);
}
void CEx_SDIHelloView::OnBeginPrinting(CDC* /*pDC*/,CPrintInfo*
/*pInfo*/)
{ // TODO,add extra initialization before printing }
void CEx_SDIHelloView::OnEndPrinting(CDC* /*pDC*/,CPrintInfo*
/*pInfo*/)
{ // TODO,add cleanup after printing }
4.3使用 MFC AppWizard
// 以下是用于调试的函数
#ifdef _DEBUG
void CEx_SDIHelloView::AssertValid() const
{ CView::AssertValid(); }
void CEx_SDIHelloView::Dump(CDumpContext& dc) const
{ CView::Dump(dc); }
CEx_SDIHelloDoc* CEx_SDIHelloView::GetDocument()
{ ASSERT(m_pDocument-
>IsKindOf(RUNTIME_CLASS(CEx_SDIHelloDoc)));
//,断言,m_pDocument指针可以指向的 CEx_SDIHelloDoc类
// 是一个 RUNTIME_CLASS类型
return (CEx_SDIHelloDoc*)m_pDocument;
}
#endif //_DEBUG
4.3使用 MFC AppWizard
视图类 CEx_SDIHelloView是从基类 CView派生而来的。说明:
各种类型的输入都可以由视图来响应、处理,并且打印和打印预览也是在视图类中完成的。这种文档和视图的结合,称为,文档 /视图结构,机制,是
MFC应用程序框架的核心,可以进行消息的处理、文档的格式化及文档数据的可视化处理等;它不但使文档数据和视图分离,而且能简化应用程序并减少代码冗余。
PreCreateWindow虚函数是在相应窗口创建前被系统自动调用的。在此函数中,可以更改其 CREATESTRUCT结构内容,将改变相应窗口的风格,
OnDraw是个非常有用的虚函数,当应用程序中的窗口状态或大小发生改变时,系统均会调用此函数重新绘制文档窗口的客户区。用户可以将一些绘图有关的代码添加此函数中,能在视图中进行图形的绘制。
例如,下面的代码:
void CEx_SDIHelloView::OnDraw(CDC* pDC)
{ CEx_SDIHelloDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
pDC->TextOut(100,100,"Hello,World!");
}
4.3使用 MFC AppWizard
主框架窗口类 CMainFrame
,关于,对话框类 CAboutDlg
MFC涉及到的机制有:
文档 /视图机制:它使用户应用程序类、文档类、视图类以及框架类之间有机地结合在一起,是 MFC最引人注目的机制。
消息映射机制:它是实现对各种不同消息的处理。
数据映射机制:是实现对话框中变量与控件之间的数据交换和数据校验。
运行时类型检查机制:它通过 GetRuntimeClass,IsKindOf、宏
DECLARE_DYNAMIC和宏 IMPLEMENT_DYNAMIC来实现的。
诊断信息转储机制:它是通过 AssertValid,Dump和宏 TRACE来实现的。
实现文档 /视图机制是通过 MFC应用程序向导自动完成的,而消息和数据映射则是通过 MFC的 ClassWizard来自动进行的。
4.4使用 ClassWizard
4.4.1ClassWizard概述
打开 MFC的 ClassWizard的方法:
选择,View”?“ClassWizard”菜单或直接使用 Ctrl+W快捷键。
在源代码文件的文档编辑窗口中,右击鼠标,选择 ClassWizard命令。
当 ClassWizard打开后,就会弹出如图的 MFC ClassWizard对话框。
图 4.15 MFC ClassWiard对话框
4.4使用 ClassWizard
4.4.2消息和消息映射
消息分类
Windows应用程序中的消息主要有三种类型。
窗口消息 (Windows message)
主要指由 WM_开头的消息,一般由窗口类和视图类对象来处理。窗口消息往往带有参数,以标志处理消息的方法。
控件的通知消息 (Control notifications)
当控件的状态发生改变时,控件就会向其父窗口发送 WM_COMMAND通知消息。应用程序框架处理控件消息的方法和窗口消息相同,但按钮的
BN_CLICKED通知消息除外,它的处理方法与命令消息相同。
命令消息 (Command message)
主要包括由用户交互对象 (菜单、工具条的按钮、快捷键等 )发送的
WM_COMMAND通知消息。命令消息的处理方式与其他两种消息不同,它能够被多种对象接收、处理,这些对象包括文档类、文档模板类、应用程序本身以及窗口和视类等;而窗口消息和控件的通知消息是由窗口对象接收并处理的,这里的窗口对象是指从 CWnd中派生的类的对象,它包括
CFrameWnd,CMDIFrameWnd,CMDIChildWnd,CView,CDialog以及从这些类派生的对象等。
4.4使用 ClassWizard
ClassWizard映射消息的一般方法
4.4使用 ClassWizard
例如,向 CEx_SDIHelloView中添加 WM_LBUTTOMDOWN的消息映射,则可按下列步骤进行:
(1)按 Ctrl+W快捷键打开 MFC ClassWizard对话框。
(2)在 Class name组合框中,将类名选定为 CEx_SDIHelloView。
(3)在 Object IDs列表框中选定 CEx_SDIHelloView,而在 Messages列表中选定 WM_LBUTTOMDOWN消息。
(4)双击 Messages列表中的 WM_LBUTTOMDOWN消息或单击 [Add Function]
按钮,都会在 CEx_SDIHelloView类中添加该消息的映射函数
OnLButtonDown,同时在 Member funcions列表中显示这一消息映射函数和被映射的消息,如图。
图 4.16 映射 WM_LBUTTONDOWN消息
4.4使用 ClassWizard
(5)单击 [Edit Code],转向文档窗口,定位到 OnLButtonDown源代码处。
(6)添加下列代码:
void CEx_SDIHelloView::OnLButtonDown(UINT nFlags,CPoint point)
{ // TODO,Add your message handler code here and/or call default
MessageBox ("你好,我的 Visual C++世界! ","问候 ",0) ;
CView::OnLButtonDown(nFlags,point);
}
(7)程序运行后,在窗口客户区左击,弹出一个消息对话框。
查看 CEx_SDIHelloView程序代码,可以发现,ClassWizard为
WM_LBUTTOMDOWN的消息映射作了以下三个方面内容的安排:
在头文件 Ex_SDIHelloView.h中声明消息处理函数 OnLButtonDown:
protected:
//{{AFX_MSG(CEx_SDIHelloView)
afx_msg void OnLButtonDown(UINT nFlags,CPoint point);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
4.4使用 ClassWizard
在 Ex_SDIHelloView.cpp源文件前面的消息映射入口处,添加映射宏:
BEGIN_MESSAGE_MAP(CEx_SDIHelloView,CView) // 消息映射开始
//{{AFX_MSG_MAP(CEx_SDIHelloView)
ON_WM_LBUTTONDOWN()
//}}AFX_MSG_MAP
…
END_MESSAGE_MAP()
// 消息映射结束在 Ex_SDIHelloView.cpp文件中写入一个空的消息处理函数的模板,框架:
void CEx_SDIHelloView::OnLButtonDown(UINT nFlags,CPoint point)
{ // TODO,Add your message handler code here and/or call default
CView::OnLButtonDown(nFlags,point);
}
根据 ClassWizard产生的上述消息映射过程,可以手动添加一些 ClassWizard
不支持的消息映射函数,以完成特定的功能。
鼠标和键盘消息各自都有相应的消息处理宏和预定义消息处理函数,因此消息映射函数名称不再需要用户重新定义。对于菜单和按钮等命令消息来说,
用 ClassWizard映射时会弹出一个对话框,用来指定消息映射函数的名称。
若指定的消息映射函数需要删除,则需要先在 ClassWizard对话框的
Messages列表中选定要删除的消息映射函数,然后单击 [Delete Function]按钮,最后关闭 ClassWizard对话框,并在该消息映射函数所在的类实现文件
(.cpp)中将映射函数定义的代码全部删除。
4.4使用 ClassWizard
键盘和鼠标消息
按下一个键或组合键时,将 WM_KEYDOWN或 WM_SYSKEYDOWN放入具有输入焦点的应用程序窗口的消息队列中。键被释放时,把 WM_KEYUP或
WM_SYSKEYUP消息放入消息队列中。对字符键,会在这两个消息之间产生
WM_CHAR消息。,
ClassWizard能自动添加了当前类的 WM_KEYDOWN和 WM_KEYUP击键消息处理函数的调用,它们具有下列函数原型:
afx_msg void OnKeyDown( UINT nChar,UINT nRepCnt,UINT nFlags );
afx_msg void OnKeyUp( UINT nChar,UINT nRepCnt,UINT nFlags );
afx_msg是 MFC用于定义消息函数的标志,参数 nChar表示虚拟键代码,
nRepCnt表示当用户按住一个键时的重复计数,nFlags表示击键消息标志。
虚拟键代码指与设备无关的键盘编码。最常用的虚拟键代码已被定义在
Winuser.h中。
MFC中的 ClassWizard也提供相应的字符消息处理框架,并自动添加了当前类的 WM_CHAR消息处理函数调用,它具有下列函数原型:
afx_msg void OnChar( UINT nChar,UINT nRepCnt,UINT nFlags );
nChar表示键的 ASCII码,nRepCnt表示当用户按住一个键时的重复计数,
nFlags表示字符消息标志。
对鼠标进行操作时会产生对应的消息。通常,只将键盘消息发送给具有输入焦点的窗口,但鼠标消息不受这种限制。只要鼠标移过窗口的客户区时,
就会向该窗口发送 WM_MOUSEMOVE(移动鼠标 )消息。
4.4使用 ClassWizard
这里的客户区是指窗口中用于输出文档的区域。在窗口的客户区中按下或释放一个鼠标键时,还会产生如表所示的消息。
ClassWizard会将映射成类似 afx_msg void OnXXXX的消息处理函数,原型:
afx_msg void OnXXXX( UINT nFlags,CPoint point );
point表示鼠标光标在屏幕的 (x,y)坐标; nFlags表示鼠标按钮和键盘组合情况,
它可以是下列值的组合 (MK前缀表示,鼠标键,):
MK_CONTROL —— 键盘上的 Ctrl键被按下
MK_LBUTTON —— 鼠标左按钮被按下
MK_MBUTTON —— 鼠标中按钮被按下
MK_RBUTTON —— 鼠标右按钮被按下
MK_SHIFT —— 键盘上的 Shift键被按下想知道某个键被按下,可用对应的标识与 nFlags进行逻辑,与,(&)运算,所得结果若为 TRUE(非 0)时,则表示该键被按下。
4.4使用 ClassWizard
映射计时器消息有一种常用的输入设备就是计时器,它周期性地按一定的时间间隔向应用程序发送 WM_TIMER消息。由于它能实现,实时更新,以及,后台运行,
等功能,因而在应用程序中计时器是一个难得的程序方法。
应用程序是通过 CWnd的 SetTimer函数来设置并启动计时器的,函数原型:
UINT SetTimer( UINT nIDEvent,UINT nElapse,
void (CALLBACK EXPORT* lpfnTimer)(HWND,UINT,UINT,DWORD) );
nIDEvent指定该计时器的标识值 (不能为 0),应用程序需要多个计时器时可多次调用该函数,但每一个计时器的标识值应是唯一的,各不相同
nElapse表示计时器的时间间隔 (单位为毫秒 ),lpfnTimer是一个回调函数的指针,该函数由应用程序来定义,用来处理计时器 WM_TIMER消息。一般情况下该参数为 NULL,此时 WM_TIMER消息被放入到应用程序消息队列中供 CWnd对象处理。
SetTimer函数成功调用后返回新计时器的标识值。应用程序不再使用计时器时,调用 CWnd:,KillTimer函数来停止 WM_TIMER消息的传送,原型:
BOOL KillTimer( int nIDEvent );
nIDEvent和用户调用 SetTimer函数设置的计时器标识值是一致的。
对于 WM_TIMER消息,ClassWizard会将其映射成具有下列消息处理函数:
afx_msg void OnTimer( UINT nIDEvent );
4.4使用 ClassWizard
其他窗口消息的映射系统中,除了用户输入产生的消息外,还有许多系统根据应用程序的状态和运行过程产生的消息,有时也需要用户进行处理。
(1)WM_CREATE消息窗口对象创建后,向视图发送的第一个消息;如果用户有什么工作需要在初始化时处理,就可在该消息处理函数中加入所需代码。由于 WM_CREATE
消息发送时,窗口对象还未完成,窗口还不可见,因此在该消息处理函数
OnCreate内,不能调用那些依赖于窗口处于完成激活状态的函数。
(2)WM_CLOSE或 WM_DESTROY消息从系统菜单中关闭窗口或者父窗口被关闭时,都会发送 WM_CLOSE消息;
而 WM_DESTROY消息是在窗口从屏幕消失后发送的,因此它紧随
WM_CLOSE之后。
(3)WM_PAINT消息当窗口的大小发生变化、窗口内容发生变化、窗口间的层叠关系发生变化或调用函数 UpdateWindow或 RedrawWindow时,系统都将产生 WM_PAINT
消息,表示要重新绘制窗口的内容。该消息处理函数的原型是;
afx_msg void OnPaint();
用 ClassWizard映射该消息的目的是执行自己的图形绘制代码。
4.4使用 ClassWizard
4.4.3类的添加和删除用 MFC ClassWizard给项目添加一个类通常是按下列步骤进行的:
(1)按快捷键 Ctrl+W启动 MFC ClassWizard对话框。单击 [Add Class],选择
New命令,如图所示的 New Class对话框。
图 4.17 New Class对话框
4.4使用 ClassWizard
(2)对话框中,Name框是用来输入用户定义的类名,注意要以,C”字母打头,以保持与 MFC标识符命名规则一致; File Name是该类的源代码文件名,
单击 [Change]按钮可改变源文件名称及其在磁盘中的位置; Base class框用来指定该类的基类; Dialog ID框是当选择 CDialog作为基类时指定对话框的资源 ID号。最下面的 Automation是用来设置对自动化的支持。
(3)单击 [OK]按钮,一个新类就会自动添加到项目中。
而当添加的类需要删除时,则需要按下列步骤进行:
(1)将 Visual C++ 6.0打开的所有文档窗口关闭。
(2)将项目工作区窗口切换到 FileView页面,展开 Source Files和 Header Files
结点,分别选定要删除类的对应,h和,cpp文件,按下 Delete键,删除这两个文件。
(3)选择,File”?“Close Workspace”菜单命令,关闭项目。
(4)从实际的文件夹中删除对应的,h和,cpp文件与,clw文件。
需要注意的是,当下一次打开 MFC ClassWizard对话框时,就会弹出 Select
Source Files对话框,这时只要单击右下的 [Add All]按钮即可。
作业
P375:1,5
P408:实验 8