Windows编程基础新手入门

上传人:无*** 文档编号:60700320 上传时间:2022-03-09 格式:DOC 页数:21 大小:495KB
收藏 版权申诉 举报 下载
Windows编程基础新手入门_第1页
第1页 / 共21页
Windows编程基础新手入门_第2页
第2页 / 共21页
Windows编程基础新手入门_第3页
第3页 / 共21页
资源描述:

《Windows编程基础新手入门》由会员分享,可在线阅读,更多相关《Windows编程基础新手入门(21页珍藏版)》请在装配图网上搜索。

1、21第1章 Windows编程基础Windows编程基础基于Windows的编程方式有两种。一种是使用Windows的API(Application Programming Interface,应用程序编程接口)函数,通常用C/C+语言按相应的程序框架进行编程。这些程序框架往往就程序应用提供相应的文档、范例和软件开发工具包(Software Development Kit,SDK),所以这种编程方式有时又称为SDK方式。另一种是使用“封装”方式,例如Visual C+的MFC方式,它是将SDK中的绝大多数函数、数据等按C+“类”的形式进行封装,并提供相应的应用程序框架和编程操作。事实上,无论是

2、哪种编程方式,人们最关心的内容有三个:一是程序入口,二是窗口、资源等的创建和使用,三是键盘、鼠标等所产生的事件或消息的接收和处理。本章就来讨论这些内容。1.1 从main到WinMain学习编程往往从简单的例子入手,例如一个C程序常有下列简单的框架代码:#include int main() printf(Hello World!n);/* 输出 */ return 0;/* 指定返回值 */事实上,该程序已包括C程序中最常用的#include指令、必须的程序入口main函数、库函数printf调用和return语句。由于此程序是在早期的DOS(Disk Operating System,磁盘

3、操作系统)环境的字符模型下运行的,因而printf函数所输出的都是字符流,也就是说,它在屏幕上输出一行文本“Hello World!”。在Windows环境下,这里的屏幕就由控制台窗口来兼作,而基于Windows的上述C程序代码框架肯定是有所不同的。特别地,由于目前所在的Windows环境基本上都是32位,所以这里的Windows程序平台就是Win32,Windows编程可直接理解为是Win32编程。1.1.1 Windows等价程序等价的Windows程序可以写成:HelloMsg.c #include int WINAPI WinMain (HINSTANCE hInstance, HIN

4、STANCE hPrevInstance, PSTR szCmdLine, int nCmdShow) MessageBox (NULL, TEXT(Hello, World!), TEXT(Hello), 0) ; return 0 ;在深入剖析上述程序之前,先来看一看在Visual C+ 6.0中的编辑、连接和运行的 过程: 图1.1 “每日提示”对话框 选择“开始”“程序”Microsoft Visual Studio 6.0 Microsoft Visual C+ 6.0,运行Visual C+ 6.0。第一次运行时,将显示如图1.1所示的“每日提示”对话框。单击“下一条”按钮,可看到

5、有关各种操作的提示。如果在“启动时显示提示”复选框中单击鼠标,去除复选框的选中标记“”,那么下一次运行Visual C+ 6.0,将不再出现此对话框。单击“关闭”按钮关闭此对话框,进入Visual C+ 6.0开发环境。 选择“文件”“新建”菜单命令,打开应用程序向导,显示出“新建”对话框,如图1.2所示。选择“工程”选项卡,从列表框中选中Win32 Application(Win32 应用程序)项(图1.2中的标记1)。 单击“位置”编辑框右侧的“浏览”按钮(图1.2中的标记2),从弹出的 “选择目录”对话框指定项目所在的文件夹,如图1.3所示(图中的数字标记表示最经常 的操作次序,下同)。

6、单击“确定”按钮,退出“选择目录”对话框,回到 “新建”对话框中。需要说明的是,为了便于程序的管理和查找,本书所涉及的程序均放入Visual C+ 6.0的工作文件夹“Visual C+程序”中,第1章程序放入子文件夹“第1章”中,第2章程序放入子文件夹“第2章”,依此类推。 在“新建”对话框的“工程名称”编辑框(图1.2中的标记3)中,输入项目名称Ex_HelloMsg,保留“平台”下Win32复选框的默认“选中”状态,单击“确定”按钮进入下一步。 出现Win32 Application向导的“步骤1共1步”对话框,从中可选择要创建的应用程序类型:“一个空工程”、“一个简单的Win32程序”

7、和“一个典型的Hello World! 程序”,如图1.4所示。它们的区别在于:“一个空工程”仅创建Win32应用程序文件框架,不含任何代码;“一个简单的Win32程序”是在“一个空工程”基础上添加了程序框架(有入口函数、#include指令等);“一个典型的Hello World!程序”在“一个简单的Win32程序”基础上增加了MessageBox函数调用,用来输出“Hello World!”。 图1.2 “新建”对话框“工程”选项卡 图1.3 “选择目录”对话框 选中“一个空工程”,单击“完成”按钮,弹出“新建工程信息”对话框,如图1.5所示。单击“确定”按钮,系统将按前面的选择自动创建此

8、应用程序。 图1.4 应用程序的向导对话框 图1.5 “新建工程信息”对话框 再次选择“文件”“新建”菜单命令,Visual C+将打开“新建”对话框并自动切换到“文件”选项卡,如图1.6所示。在左侧的文件类型列表中选中C+ Source File(C+源文件),在右侧的“文件名”编辑框中输入“HelloMsg.c”或输入“HelloMsg.cpp”(文件扩展名也可不输入,系统会自动添加cpp扩展名,cpp是C Plus Plus的缩写,是C+的 意思)。 单击“确定”按钮,系统将在创建的Win32项目工程Ex_HelloMsg中创建并添加一个新的文件HelloMsg.c,同时打开该文件窗口。

9、现在可以在HelloMsg.c中输入前面例HelloMsg.c中的代码了。输完后,单击编译工具条上的“生成工具”按钮或直接按F7键,系统开始对Ex_HelloMsg项目工程中的文件进行编译、连接,同时在输出窗口中观察出现的内容,当出现Ex_HelloMsg.exe-0 error(s), 0 warning(s)表示Ex_HelloMsg.exe可执行文件已经正确无误地生成了。同时也可看到在文档窗口中所有代码的颜色都发生改变,这是Visual C+ 6.0的文本编辑器所具有的语法颜色功能(绿色表示注释,蓝色表示关键字等)。 单击编译工具条上的“运行工具”按钮或直接按Ctrl+F5键,就可以运行

10、刚刚生成的Ex_HelloMsg.exe,结果如图1.7所示。单击“确定”按钮,Hello对话框退出。 图1.6 创建并添加程序文件 图1.7 开发环境和运行结果1.1.2 头文件HelloMsg.c是一个#include预处理指令开始,实际上在用C/C+编写的Windows应用程序的头部都可以看到这样的指令:#include 头文件Windows.h是最主要的包含头文件,它还包含了其他一些Windows头文件。 例如:windef.h:基本类型定义winbase.h:内核函数wingdi.h:用户接口函数winuser.h:图形设备接口函数这些头文件定义了Windows的所有数据类型、函数调

11、用、数据结构和符号常量,它们是Windows应用程序文档中的一个重要部分。1.1.3 程序入口函数在C/C+程序中,其入口函数都是main。但在Windows程序中,这个入口函数由WinMain来代替。该函数是在winbase.h中声明的,其原型如下:intWINAPIWinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd);可以看出,这个WinMain函数除了形参名、个数与main函数不同外,类型名也有了新的变化。下面就来分析: WinMain函数被声明成为返回一个int值,同时WinM

12、ain函数名前还有WINAPI标识符的修饰。WINAPI是一种“调用约定”宏,它在windef.h中有如下定义:#define WINAPI _stdcall所谓“调用约定”,就是指程序生成机器码后,函数调用的多个参数是按怎样的次 序来传递,同时函数调用结束后堆栈由谁来恢复,以及编译器对函数名的修饰约定等的 协议。函数调用约定“协议”有许多,其中由WINAPI宏指定的_stdcall是一个常见的协议,内容包括:参数从右向左压入堆栈;函数自身修改堆栈;机器码中的函数名前面自动加下划线,而函数后面接符号和参数的字节数。特别地,Visual C+的MFC方式却采用了_cdecl调用约定:参数从右向左

13、压入堆栈;传递参数的内存栈由调用者来维护(正因为如此可实现变参函数);机器码中的函数名只在前面自动加下划线。 WinMain函数的第一个和第二个参数都是HINSTANCE(实例句柄)类型。HINSTANCE中,H表示Handle,是“句柄”的意思。在Windows编程中,句柄是一个应用程序用来识别某些资源、状态、模块等的数字。由于句柄唯一标识着对应的资源、状态、模块等,因而使用句柄就是使(调)用相应的资源、状态、模块。当应用程序运行多次时,每一次都是应用程序的“实例”。由于同一个应用程序的所有实例都共享着应用程序的资源,因而程序通过检查hPrevInstance参数就可确定自身的其他实例是否正

14、在运行。 WinMain函数的第三个参数lpCmdLine用来指定程序的命令行,其参数类型为LPSTR。但在HelloMsg.c中,却将其改为PSTR。这两种数据类型都是合法的,也都是指向字符串的指针类型。其中的STR是“STRING,字符串”的含义,是指以0结尾的字符串,LP前缀表示“长指针”,在Win32中它与“P”前缀表示的“指针”含义相同。 WinMain函数的第四个参数nShowCmd用来指定程序最初显示的方式,它可以是正常、最大化或最小化来显示程序窗口。纵观上述参数和类型名可以发现它们的命名规则:l C/C+的类型名仍保留其小写,但新的类型都是用大写字母来命名。l 参数名(变量名)

15、都是采用“匈牙利表示法”的命名规则来定义的。它的主要方法是将变量名前后加上表示“类型”和“作用”的“前缀(小写)”,而变量名本身由“状态”、“属性”和“含义”等几个部分组成,每一个部分的名称可以是全称,也可以是缩写,但通常只有第一个字母是大写。例如,hPrevInstance则是由前缀h(表示“句柄”类型)+状态Prev(表示“以前的”)+属性Instance(表示“实例”)组成的。1.1.4 MessageBox函数MessageBox是一个Win32 API函数,用来弹出一个对话框窗口,显示短信息。该函数具有下列原型:int MessageBox( HWND hWnd, LPCTSTR l

16、pText, LPCTSTR lpCaption, UINT uType);其中,第一个参数hWnd用来指定父窗口句柄,即对话框所在的窗口句柄。第二、三个参数分别用来指定显示的消息内容(lpText)和对话框窗口的标题(lpCaption),最后一个参数用来指定在对话框中显示的预定义的按钮和图标标识,它们是在winuser.h定义的一组以MB_开始的常数组合。例如,下面是在HelloMsg.c中改变MessageBox的第四个参数。#include int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR sz

17、CmdLine, int nCmdShow) MessageBox (NULL, TEXT(Hello, World!), TEXT(Hello), MB_ICONQUESTION | MB_ABORTRETRYIGNORE ) ; return 0 ;图1.8 第四个参数的作用程序运行后,结果如图1.8所示。可见,MB_ICONQUESTION 用来指定在对话框中显示图标,而MB_ABORTRETRYIGNORE用来指定“终止”、“重试”和“忽略”按钮,类似这样的预定义标识还有很多,在以后讨论到MFC中的CWnd:MessageBox函数时还要讨论,故这里不再赘述。在程序HelloMsg.c

18、中,调用MessageBox的实参中还涉及TEXT宏。在Windows编程中,TEXT宏是用来对UNICODE编码的字符串的支持。UNICODE是使用两个字节表示一个字符,这样单字节的ANSI字符和双字节的“汉字”的表示就统一起来了。在程序中使用TEXT文本,无论在何Windows环境下均可显示正确的内容,而不会出现乱码的情形。另外,还有_TEXT 和_T宏等,在Visual C+中,它们的作用是等同的。1.2 窗口和消息MessageBox是通过创建的默认“窗口”来显示简单的信息:窗口标题、一行或多行文本、图标和按钮等。在Windows环境中,一个“窗口”就是屏幕上的一个矩形区域,它接收用户

19、的输入,并以文本或图形方式来显示内容。事实上,“窗口”就是用户操作的区域界面,在编程中除创建等操作外,还要处理用户输入、窗口本身事件所产生的“消息”。1.2.1 程序框架代码为了能处理上述两个部分的内容:窗口创建和消息处理,Windows提供了相应的程序框架,如下面的例子。HelloWin.c #include LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);/ 窗口过程int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,

20、int nCmdShow) HWND hwnd ;/ 窗口句柄 MSG msg ;/ 消息 WNDCLASSwndclass ;/ 窗口类 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

21、= LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL; wndclass.lpszClassName = HelloWin;/ 窗口类名 if (!RegisterClass (&wndclass)/ 注册窗口 MessageBox (NULL, 窗口注册失败!, HelloWin, 0) ; return 0 ; / 创建窗口 hwnd = CreateWindow (HelloWin, / 窗口类名 我的窗口,

22、/ 窗口标题 WS_OVERLAPPEDWINDOW, / 窗口样式 CW_USEDEFAULT, / 窗口最初的 x 位置 CW_USEDEFAULT, / 窗口最初的 y 位置 480, / 窗口最初的 x 大小 320, / 窗口最初的 y 大小 NULL, / 父窗口句柄 NULL,/ 窗口菜单句柄 hInstance, / 应用程序实例句柄 NULL) ; / 创建窗口的参数 ShowWindow (hwnd, nCmdShow) ;/ 显示窗口 UpdateWindow (hwnd) ;/ 更新窗口,包括窗口的客户区 / 进入消息循环:当从应用程序消息队列中检取的消息是WM_QUI

23、T时,则退出循环 while (GetMessage (&msg, NULL, 0, 0) TranslateMessage (&msg) ;/ 转换某些键盘消息 DispatchMessage (&msg) ;/ 将消息发送给窗口过程,这里是WndProc return msg.wParam ;LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)HDChdc;PAINTSTRUCTps;RECTrc;switch (message)case WM_CREATE:/ 窗口创建产生的消息

24、return 0 ; case WM_PAINT:hdc= BeginPaint( hwnd, &ps );GetClientRect( hwnd, &rc );/ 获取窗口客户区大小DrawText( hdc, TEXT(Hello Windows!), -1, &rc, DT_SINGLELINE | DT_CENTER |DT_VCENTER );EndPaint( hwnd, &ps ); return 0 ; case WM_DESTROY:/ 当窗口关闭时产生的消息PostQuitMessage (0) ;return 0 ; return DefWindowProc (hwnd,

25、 message, wParam, lParam) ; / 执行默认的消息处理创建并运行上述程序时,先创建一个Ex_HelloWin“空工程”,然后再创建并添加新的源文件hellowin.c,输入上述代码,然后编连和运行,结果如图1.9所示(里面的框是加上去的,它的区域叫“客户区”)。虽然与Ex_HelloMsg示例相比,Ex_HelloWin要复杂得多,但总可以将其分解成是两个基本函数的程序结构。一个就是前面所讨论的WinMain函数,另一个是用户定义的窗口过程函数WndProc。窗口过程函数WndProc用来接收和处理各种不同的消息。图1.9 hellowin.c运行结果1.2.2 注册窗

26、口类在为程序创建窗口之前,必须首先调用创建RegisterClass注册应用程序的窗口类。该函数只要一个参数,即一个指向类型为WNDCLASS的结构指针。它包含了一个窗口的基本属性,如窗口边框、窗口标题栏文字、窗口大小和位置、鼠标、背景色、处理窗口消息函数的名称等。事实上,注册的过程也就是将这些属性告诉系统,然后再调用CreateWindow函数创建出窗口。WNDCLASS结构具有下列原型:typedef struct UINT style;/ 窗口的风格 WNDPROC lpfnWndProc;/ 指定窗口的消息处理函数的窗口过程函数 int cbClsExtra;/ 指定分配给窗口类结构之

27、后的额外字节数 int cbWndExtra;/ 指定分配给窗口实例之后的额外字节数 HINSTANCE hInstance;/ 指定窗口过程所对应的实例句柄 HICON hIcon;/ 指定窗口的图标 HCURSOR hCursor;/ 指定窗口的鼠标指针 HBRUSH hbrBackground;/ 指定窗口的背景画刷 LPCTSTRlpszMenuName;/ 窗口的菜单资源名称 LPCTSTR lpszClassName;/ 该窗口类的名称 WNDCLASS, *PWNDCLASS;从中可以看出:该结构有10个域(成员),其中第一个域style表示窗口类的风格,它往往是由一些基本的预定

28、义风格通过位的“或”操作(操作符位“|”)组合而成的。例如,在HelloWin.c中,有:WNDCLASSwndclass ;/ 窗口类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 = LoadCu

29、rsor (NULL, IDC_ARROW) ;wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;wndclass.lpszMenuName = NULL;wndclass.lpszClassName = HelloWin;/ 窗口类名可以看到,wndclass.style被设为CS_VREDRAW | CS_HREDRAW,表示只要窗口的高度或宽度发生变化,都会重画整个窗口。第二个域lpfnWndProc的值为WndProc。表明该窗口类的消息处理函数是WndProc函数。这里,可简单直接地输入消息处理(窗口过程)函数的函数名即可。

30、接下来的cbClsExtra和cbWndExtra在大多数情况下都会设为0。然后的hInstance成员,给它的值是由WinMain传来的应用程序的实例句柄,表明该窗口与该实例是相关联的。事实上,只要是注册窗口类,该成员的值始终是该程序的实例句柄。下面的hIcon,是要给这个窗口指定一个图标,LoadIcon (NULL, IDI_APPLICATION)就是调用系统内部预先定义好的标识符为IDC_APPLICATION的图标作为该窗口的图标。同样,LoadCursor (NULL, IDC_ARROW)就是调用预定义的箭型鼠标指针。hbrBackground域用来定义窗口的背景画刷颜色,也就

31、是该窗口的背景色。调用GetStockObject (WHITE_BRUSH)可以获得系统内部预先定义好的白色画刷作为窗口的背景色。这里LoadIcon、LoadCursor、GetStockObject等都是Windows的API函数,在程序中可直接 调用。lpszMenuName域的值若为NULL,则表示该窗口将没有菜单。否则,需要指定表示菜单资源的字符串。 WNDCLASS结构的最后一个域lpszClassName是要给这个窗口类起一个唯一的名称,因为Windows操作系统中有许许多多的窗口类,必须用一个独一无二的名称来代表它们。通常,可以用程序名来直接作为这个窗口类的名称,它在创建窗口

32、的CreateWindow函数中用到。1.2.3 创建和显示窗口当窗口类注册完毕之后,并不会有窗口显示出来,因为注册的过程仅仅是为创建窗口所做的准备工作。实际创建一个窗口是通过调用CreateWindow函数完成的。窗口类中已经预先定义了窗口的一般属性,而CreateWindow中的参数可以进一步指定一个窗口的更具体的属性,在HelloWin.c程序中,是用下列调用CreateWindow函数的代码来创建窗 口的: hwnd = CreateWindow (HelloWin, / 窗口类名,要与注册时指定的相同 我的窗口,/ 窗口标题 WS_OVERLAPPEDWINDOW, / 窗口样式 C

33、W_USEDEFAULT, / 窗口最初的 x 位置 CW_USEDEFAULT, / 窗口最初的 y 位置 480, / 窗口最初的 x 大小 320, / 窗口最初的 y 大小 NULL, / 父窗口句柄 NULL,/ 窗口菜单句柄 hInstance, / 应用程序实例句柄 NULL) ; / 创建窗口的参数CreateWindow函数的第一个参数是创建该窗口所使用的窗口类的名称,注意这个名称应与前面所注册的窗口类的名称一致。第三个参数为创建的窗口的风格,它们通常是一些预定义风格的“|”组合。其中,WS_OVERLAPPEDWINDOW表示创建一个层叠式窗口,有边框、标题栏、系统菜单、最

34、大化和最小化按钮等。CreateWindow函数后面的参数中,仍用到了该应用程序的实例句柄hInstance。如果窗口创建成功,返回值是新窗口的句柄,否则返回NULL。窗口创建后,并不会在屏幕上显示出来。要能真正把窗口显示在屏幕上,还得使用ShowWindow函数,其原型如下: BOOL ShowWindow( HWND hWnd, int nCmdShow ); 其中,参数hWnd指定要显示得窗口的句柄,nCmdShow表示窗口的显示方式,这里指定为从WinMain函数的nCmdShow所传递而来的值。由于ShowWindow函数的执行优先级不高,所以当系统正忙着执行其他的任务时,窗口不会立

35、即显示出来,此时,调用UpdateWindow函数以可以立即显示窗口。同时,它将会给窗口过程发出WM_PAINT消息。1.2.4 消息和消息处理1消息循环在Win32编程中,消息循环是相当重要的一个概念,看似很难,但使用起来却是非常简单。在WinMain函数的最后,有下列代码:while (GetMessage (&msg, NULL, 0, 0) TranslateMessage (&msg) ;/ 转换某些键盘消息DispatchMessage (&msg) ;/ 将消息发送给窗口过程,这里是WndProcWindows应用程序可以接收以各种形式输入的信息,这包括键盘、鼠标动作、计时器产生

36、的消息,也可以是其他应用程序发来的消息等。Windows系统自动监控所有的输入设备,并将其消息放入该应用程序的消息队列中。GetMessage函数就是用来从应用程序的消息队列中按照先进先出的原则将这些消息一个个地取出来,放进一个MSG结构中去。它的原型如下:BOOL GetMessage( LPMSG lpMsg, / 指向一个MSG结构的指针,用来保存消息 HWND hWnd, / 指定哪个窗口的消息将被获取 UINT wMsgFilterMin,/ 指定获取的主消息值的最小值 UINT wMsgFilterMax/ 指定获取的主消息值的最大值);GetMessage函数用来将获取的消息复制

37、到一个MSG结构中。如果队列中没有任何消息,该函数将一直空闲直到队列中又有消息时再返回。如果队列中已有消息,它将取出一个后返回。MSG结构包含Windows消息的完整信息,其定义如下:typedef struct HWND hwnd; / 消息发向的窗口的句柄 UINT message;/ 主消息的标识值 WPARAM wParam;/ 附消息值,其具体含义依赖于主消息值 LPARAM lParam;/ 附消息值,其具体含义依赖于主消息值 DWORD time;/ 消息放入消息队列中的时间 POINT pt;/ 消息放入消息队列时的鼠标坐标 MSG, *PMSG;上述结构中的主消息表明了消息的

38、类型,例如,是键盘消息还是鼠标消息等,附消息的含义则依赖于主消息值,例如,如果主消息是键盘消息,那么附消息中则存储了是键盘的哪个具体键的信息。事实上,GetMessage函数还可以过滤消息,它的第二个参数是用来指定从哪个窗口的消息队列中获取消息,其他窗口的消息将被过滤掉。如果该参数为NULL,则GetMessage从该应用程序线程的所有窗口的消息队列中获取消息。第三个和第四个参数是用来过滤MSG结构中主消息值的,主消息值在wMsgFilterMin和wMsgFilterMax之外的消息将被过滤掉。如果这两个参数为0,则表示接收所有消息。特别地,当且仅当GetMessage函数在获取到WM_QU

39、IT消息后,将返回0值,于是程序退出消息循环。TranslateMessage函数的作用是把虚拟键消息转换到字符消息,以满足键盘输入的需要。DispatchMessage函数所完成的工作是把当前的消息发送到对应的窗口过程中去。2消息处理用于消息处理的函数又叫窗口过程,在这个函数中,不同的消息将用switch语句分配到不同的处理程序中去。Windows的消息处理函数都有一个确定的统一方式,即这种函数的参数个数和类型以及其返回值的类型都有明确的规定。在HelloWin.c中,WinProc函数明确处理了3个消息,分别是WM_CREATE(创建窗口消息)、WM_PAINT(窗口重画消息)、WM_DE

40、STROY(销毁窗口消息)。事实上,应用程序发送到窗口的消息远远不止以上这几条,像WM_SIZE、WM_MINIMIZE、WM_MOVE等这样经常使用的消息就有好几十条。为了减轻编程的负担,Windows的API提供了DefWindowProc函数来处理这些最常用的消息,调用这个函数后,这些消息将按照系统默认的方式得到处理。因此,在switch语句中,只需明确处理那些有必要进行特别响应的消息,把其余的消息交给DefWindowProc函数来处理,即将消息的控制交由Windows进行默认处理,这是一种明智的选择。 3结束消息循环 当用户按Alt+F4键或单击窗口右上角的“退出”按钮,系统就向应用

41、程序发送一条WM_DESTROY的消息。在处理此消息时,调用了PostQuitMessage函数,该函数会向窗口的消息队列中发送一条WM_QUIT消息。在消息循环中,GetMessage函数一旦检索到这条消息,就会返回FALSE,从而结束消息循环,随后程序也结束。1.2.5 WM_PAINT消息WM_PAINT是Win32的图形和文本编程中经常使用到的消息。当窗口客户区的一部分或全部变成“无效”时,则必须 “刷新”重绘,此时将向程序发出此消息。那么客户区怎么会“无效”呢?在最初窗口创建时,整个客户区都是“无效”的,因为窗口上还没有绘制任何东西。所以,在创建窗口时,会发出第一个WM_PAINT消

42、息。在HelloWin.c程序中,由于在注册窗口时,指定了wndclass.style的风格为CS_VREDRAW和 CS_HREDRAW,这表明只要窗口的高度或宽度发生变化,就将使整个窗口“无效”,从而发出WM_PAINT消息,使得系统重画整个窗口。当窗口最小化再恢复为以前的大小时,Windows将令窗口“无效”,并发出WM_PAINT消息使系统重画整个窗口。当窗口移至与另一窗口有重叠被遮挡时,Windows也将窗口视为“无效”,发出WM_PAINT消息以便刷新窗口。在窗口过程函数WndProc中,WM_PAINT消息处理通常总是从BeginPaint函数开始,而从EndPaint函数结束。

43、BeginPaint函数用来返回指定窗口句柄的设备描述表句柄,设备描述表用来将程序与计算机外部输出设备连接起来。hdc定义的是句柄HDC变量,DrawText等GDI函数都需要通过这样的HDC句柄来绘制图形和文本。EndPaint用来释放设备描述表句柄,并使先前无效区域变为有效,从而使Windows不再发送WM_PAINT消息。PAINTSTRUCT是“绘图信息结构”,BeginPaint和EndPaint函数都需要PAINTSTRUCT结构变量作为自己的参数。需要说明的是,BeginPaint和EndPaint函数必须成对出现,所有GDI函数的调用也应在这两个函数之间进行。DrawText函

44、数用来在参考矩形内使用指定的格式来绘制文本,它的函数原型如下:int DrawText( HDC hDC, / 绘制设备的句柄 LPCTSTR lpString, / 要绘制的文本 int nCount, / 文本的字符个数 LPRECT lpRect, / 参考矩形 UINT uFormat / 文本绘制格式);其中,当nCount为1时,表示lpString指定的是以“0”为结尾的字符串,并自动计算该字符串的字符个数。lpRect是一个指向RECT类型的“矩形”结构指针,该“矩形”结构含有left、top、right和bottom 4个LONG域。为了能在窗口客户区中间绘制文本,该函数的l

45、pRect被填为RECT变量rc的指针,它通过调用GetClientRect函数,获取hwnd窗口的客户区大小。同时,指定uFormat格式为DT_SINGLELINE(单行输出)、DT_CENTER(水平居中)和DT_VCENTER(垂直居中)。1.2.6 Windows基本数据类型在前面示例和函数原型中,有一些“奇怪”的数据类型,如前面的HINSTANCE和LPSTR等,事实上,很多这样的数据类型只是一些基本数据类型的别名,以方便不同风格的程序员使用。表1.1列出了一些在Windows编程中常用的基本数据类型;表1.2列出了常用的预定义句柄,它们的类型均为void *,即一个32位指针。表

46、1.1 Windows编程中常用的基本数据类型Windows所用的数据类型对应的基本数据类型说明BOOLbool布尔值BSTRunsigned short *32位字符指针BYTEunsigned char8位无符号整数COLORREFunsigned long用作颜色值的32位值DWORDunsigned long32位无符号整数,段地址和相关的偏移地址LONGlong32位带符号整数LPARAMlong作为参数传递给窗口过程或回调函数的32位值LPCSTRconst char *指向字符串常量的32位指针LPSTRchar *指向字符串的32位指针LPVOIDvoid *指向未定义类型的3

47、2位指针LRESULTlong来自窗口过程或回调函数的32位返回值UINTunsigned int32位无符号整数WORDunsigned short16位无符号整数WPARAMunsigned int当做参数传递给窗口过程或回调函数的32位值说明:l 这些基本数据类型都是用大写字符来表示的,以与一般C/C+基本数据类型相 区别。l 若数据类型的前缀是P或LP,则表示该类型是一个指针或长指针数据类型;若数据类型的前缀是U,则表示是无符号数据类型。l Windows还提供一些宏来处理上述基本数据类型。例如,LOBYTE和HIBYTE分别用来获取16位数值中的低位和高位字节,LOWORD和HIWO

48、RD分别用来获取32位数值中的低位字和高位字,MAKEWORD是将两个16位无符号值结合成一个32位无符号值。表1.2 Windows编程中常用的句柄类型句柄类型说明HBITMAP保存位图信息的内存域的句柄HBRUSH画刷句柄续表句柄类型说明HCURSOR鼠标光标句柄HDC设备描述表句柄HFONT字体句柄HICON图标句柄HINSTANCE应用程序的实例句柄HMENU菜单句柄HPALETTE颜色调色板句柄HPEN在设备上画图时用于指明线型的笔的句柄HWND窗口句柄1.3 C+的“类”事实上,前面的Ex_HelloMsg和Ex_HelloWin都是基于Win32 API的C/C+应用程序。显然,

49、随着应用程序的复杂性增加,C/C+应用程序代码也必然变得复杂。为了简化上述编程,Visual C+设计了一套基础类库(Microsoft Foundation Class Library,MFC)。MFC把Windows编程规范中的大多数内容封装成为各种类,使程序员从繁杂的编程中解脱出来,提高了编程和代码效率。那么,什么是“类”呢?这里就先来讨论它。1.3.1 类和对象1类的定义如同C语言的“结构”类型,“类”也是一种复合的数据类型,只不过要更为复杂一些。在C+中,一个独立“类”的声明格式一般如下:class / 声明部分类体private:public:protected:;/ 实现部分其中

50、,class是类声明的关键字,class的后面是要声明的类名。类中的数据和函数都是类的成员,分别称为数据成员和成员函数。数据成员用来描述类状态等的属性,由于数据成员常用变量来定义,所以有时又将这样的数据成员称为成员变量。成员函数是用来对数据成员进行操作的,又称为方法。注意:类体中最后一个花括号后面的分号“;”不能省略。类中关键字public、private和protected声明了类中的成员与类外之间的关系,称为访问权限。对于public成员来说,它们是公有的,可以在类外访问。对于private成员来说,它们是私有的,不能在类外访问,数据成员只能由类中的函数所使用,成员函数只允许在类中调用。而

51、对于protected成员来说,它们是受保护的,具有半公开性质,可在类中或其子类中访问(后面还会讨论)。成员函数既可以在类中进行定义,也可先在类中声明函数原型,然后在类外定义,这种定义又称为成员函数的实现。需要说明的是,成员函数在类外实现时,必须用作用域运算符“:”来告知编译系统该函数所属的类。即: :( )函数体 例如,下面的类的定义示例代码:class CStuscore public:float Average(void); / 求平均成绩:在类中声明char * getName()/ 获取姓名:直接在类中完成return strName;char * getNo()/ 获取学号:直接在

52、类中完成return strStuNO; private:charstrName12;/ 姓名charstrStuNO9;/ 学号 private:floatfScore3;/ 三门课程成绩;float CStuscore:Average(void)/ 在类体外部定义return (float)(fScore0 + fScore1 + fScore2)/3.0);本例中用关键词class声明了名为CStuscore的类。在类的声明中,描述学生的姓名、学号与三门课程成绩的数据strName、strStuNo、fScore用private定义为私有数据成员。这表明数据成员strName、strSt

53、uNo、fScore只能在类中使用,而不能在类外使用。对学生成绩信息进行处理的函数Average、getName和getNo用关键词public声明成公有成员函数,这样就可在外部程序中通过getName和getNo函数分别来获取学生姓名和学号数据,通过Average函数获取学生的平均成绩。可见,类中的私有数据成员只能通过公有接口函数(像getName和getNo函数)来访问,从而保证数据的安全性。类CStuscore中,成员函数getName和getNo的声明和定义是在类体中同时进行的,而函数Average是在类体中声明,在类体外实现的,注意函数Average实现中函数名前面一定要用域作用符“

54、:”指明该函数所属的类。习惯上,往往将类的声明和实现分开来编写代码,并将类的声明保存在.h文件中,而将类的实现保存在同名的.cpp文件中,这也是Visual C+的一种规范。2对象的定义和初始化同变量一样,类声明后,就可以定义该类的对象。类的对象也有好几种定义方式。但由于“类”比任何数据类型都要复杂得多,为了提高程序的可读性,真正将“类”当成一个密闭、“封装”的盒子(接口),在程序中应尽量使用对象的声明之后定义方式,即按下列格式进行: ;其中,类名是已声明过的类的标识符,对象名可以有一个或多个,多个时要用逗号隔开。被定义的对象既可以是一个普通对象,也可以是一个数组对象或指针对象。例如:CStu

55、score one, *Stu, Stus2;这时,one是类CStuscore的一个普通对象,Stu和Stus分别是该类的一个指针对象和对象数组。若对象是一个指针,则还可像指针变量那样进行初始化,例如:CStuscore *two = &one;可见,在程序中,对象的使用和变量是一样的,只是对象还有成员的访问等手段。3对象成员的访问一个对象的成员就是该对象的类所定义的数据成员和成员函数。访问对象的成员变量和成员函数与访问一般结构的变量的方法是一样的。对于普通对象,其访问格式如下:.()例如,one.getName()用来调用对象one中的成员函数getName,Stus0.getNo()用来

56、调用对象数组元素Stus0中的成员函数getNo。要注意,由于类的封装性,每个成员均有声明的访问属性,一个类对象只能访问该类的公有型成员,而对于私有型成员则不能访问,例如getName和getNo等公有成员可以由对象通过上述方式来访问,但strName、strStuNo、fScore等私有成员不能被对象来访问。若对象是一个指针,则对象的成员访问格式如下:-()“-”是另一个表示成员的运算符,它与“.”运算符的区别是:“-”用来表示指向对象的指针的成员,而“.”用来表示一般对象的成员。需要说明的是,下面的两种表示是等价的(对于成员函数也适用):-(*).例如,(*two).getName()与two-g

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

copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!