一、学习背景
现在我们要学的是Windows32的编程,与DOS不同的是,实现的目标是一个具有你所需要的功能的“窗口”。这个“窗口”时时刻刻都在与操作系统之间,通过一个称之为“消息队列”的东西传送数据。因此处理好各种消息便成了Win32编程的核心。
SDK编程是相对于MFC来说的,也就是用C和C++来调用WindowsAPI,API是操作系统的东西,其他语言(如VB)也可以调用。因此,以前也有人将这种编程称为API编程。当然,这儿对C或C++进行了一些扩展,所谓扩展,就是增加了许多宏定义、模板,让你看了之后大吃一惊,真不知“何方神圣”倒底有何神通。
如果你C或C++还不会的话,请先学习C语言和C++语言,否则,你再刻苦也等于看电影,也许你看懂了但你肯定不会演电影。学习窗口编程之前,你要知道几个概念。
- 实例(instance): 这是C++的内容,“类”相当于结构体的数据类型,用“类”这种类型去定义一个变量(如果是指针,则假定已经得到了空间),这个变量就是那个“类”的“实例”。它和C语言中的变量是一回事,不过只有用“类”定义的变量才称为“实例”。
- 句柄(handle):这是C语言的内容,和文件句柄一样,它实际上是一个整数,用来标识是哪一个实例,也就是实例的标识符。通过句柄可以操作这个实例。
- 回调(CALLBACK):这其实是C语言中的函数指针,函数指针一般在大项目搭框架时常常使用。也就是你设计好要得到什么,把接口定义好,具体怎么实现完全交给别人,别人只要把函数名传给你就OK。这个函数名就是地址,不过取这种地址的指针在定义上与普通指针有些不同。在C语言的相关教程中有“函数指针”的详细解说和例子。
二、搭建编程环境
既可以使用较老的VC++6.0也可以使用较新的Visual Studio 2017,但VS的体量大,占用系统资源多,许多功能现阶段难以用到,故本文使用较老的VC。
2.1 VC开发环境的搭建
(1)打开VC后,单击菜单栏中【文件(F)】--【新建(N)】--【工程】--Win32 Application,填入工程名,本文以Test为例,单击【完成】结束创建,如图1-1和1-2所示;
图1-1 新建工程1
图1-2 新建工程2
(2)再在工程中添加源代码文件,击菜单栏中【文件(F)】--【新建(N)】--【文件】--C/C++ Header File,填入名称,勾选添加到工程,如图1-3所示。
图1-3 源代码文件的创建
2.2 Visual Studio开发环境搭建
(1)【文件】——【新建】——【项目】——【Visual c++】——【Win32项目】,如图所示;
图1-4 VS下构建编程环境1
图1-5 VS下构建编程环境2
图1-6 VS下构建编程环境3
(2)再创建一个控制台应用,以下步骤同2.1,不再赘述。
三、第一个程序
3.1 MessageBox函数
MessageBox函数用于显示一个模态对话框,其中包含一个系统图标、一个按钮和一个简短的应用于应用程序的消息,如状态或者错误的信息。下面详细介绍该函数:
1 int WINAPI MessageBox( 2 _In_opt_ HWND hWnd, 3 _In_opt_ LPCTSTR lpText, 4 _In_opt_ LPCTSTR lpCaption, 5 _In_ UINT uType 6 );
注释:_In_表示该参数是输入的,_opt_说明该参数是可选参数。
参数含义如表1-1所示:
表1-1 MessageBox函数参数含义
参数 | 含义 |
Hwnd |
1、该消息框的父窗口句柄; 2、如果此参数为NULL,则该消息框没有父窗口。 |
lpText | 消息框的内容 |
lpCaption | 消息框的标题 |
uType |
1、指定一个决定对话框内容和行为的位标志集; 2、此参数可以通过下列标志或标志组合,来显示消息框中的按钮以及图标。 |
表1-2 uType参数定义解析
按钮 | 含义 |
MB_OK | 默认值,显示 “确认” 按钮 |
MB_YESNO | 显示 “是” 与 “否” 两个按钮 |
MB_ABORTRETRYIGNORE | 显示 “中止” , “重试” 和 “跳过” 三个按钮 |
MB_YESNOCANCEL | 显示 “是” , “否” , “取消” 三个按钮 |
MB_RETRYCANCEL | 显示 “重试” 和 “取消” 两个按钮 |
MB_OKCANCEL | 显示 “确认”,“取消”两个按钮 |
其他可参见这里。
MessageBox是有返回值的,返回值为用户点击的按钮。
1 #define IDOK 1 2 #define IDCANCEL 2 3 #define IDABORT 3 4 #define IDRETRY 4 5 #define IDIGNORE 5 6 #define IDYES 6 7 #define IDNO 7
3.2 WinMain()函数
WinMain()函数是窗口程序的入口函数,在这个函数中你就可以调用各种API函数来完成你的目标。类似于普通c语言源文件中的main(),Windows编程中以WinMain为进入点。具体运行步骤如下:
①一般是先调用RegisterClassEx()函数用当前窗口句柄去向操作系统申请(或称登录)将要创建一个什么样的窗口,申请成功后,②再调用CreateWindowEx()函数创建一个窗口对象,这仅仅是一个外观,③还要调用ShowWindow()函数设置初期表示,即最大或最小或普通等。④最后还要调用UpdateWindow()函数向窗口传送WM_PAINT消息来画出窗口里面的内容。窗口创建完后,这是一个“静止”的窗口,⑤因此还要在WinMain()函数的最后添加消息循环,最后才return。WinMain()函数完了之后,⑥还要再编写一个“窗口消息处理”函数。上面讲了一大堆API函数的调用也许有点昏头,但那些全是固定的,基本上不要编程。在你理解之后,只要修改少量参数便可,真正要编程的是这个“窗口消息处理”函数。
下面我们从简单入手,先不创建窗口,而只是调用一下通用对话框。主要是了解WinMain()函数。
1 #include <windows.h> 2 3 int APIENTRY WinMain(HINSTANCE hInstance, //当前窗口实例句柄 4 HINSTANCE hPrevInstance, //前一个实例句柄,Win32下为NULL 5 LPSTR lpCmdLine, //命令行参数字符 6 int nCmdShow //窗口的显示方式 7 ) 8 { 9 MessageBox(NULL,TEXT("Hello World!"),TEXT("我的程序"), MB_OK); //通用对话框 10 11 return 0; //函数返回值 12 }
程序运行效果如图所示: 图1-4 程序运行效果 代码解释:第1行为加入Windows编程的头文件,第二行空出一行,使得头文件声明与代码行分出层次与空隙是一种良好的程序设计习惯,增强了代码的可读性。第3-7行属于调用消息框所必需的。下面进行简单解释,初学者知道其为调用消息框所必需的即可,读者不懂也没有关系。
(1)APIENTRY不是返回类型,这是为编译器准备的一个宏定义,告诉编译器将当前函数编译成64位机也能用的长调用。为什么要这样一个开关呢?VisualC++要兼容C语言、C++语言、16位的Windows版本等,同时还要为将来扩展为64位机作准备。虽然它和CALLBACK是同一宏定义,但编译时还是略有区别。
(2)WinMain()函数在.Net上变成了_tWinMain(),这也是为了兼容而定义的宏定义。
(3)HINSTANCE是实例句柄,象这样用H或h开头的东西(如HDC)都是句柄,其值是整数。如果你硬写成int型,编译器会分不清,因此出编译错误。
(4)LPSTR是UNICODE型的字符串指针。
(5)hInstance是当前窗口的实例句柄,hPrevInstance是16位机上用于识别当前窗口是否重复打开,当hPrevInstance不为NULL时为前一个窗口实例句柄,在32位机上hPrevInstance恒为NULL。
(6)lpCmdLine是命令行传来的参数。
(7)nCmdShow是初始显示方式,最大/最小/正常/隐藏等等。
(8)MessageBox()函数是Win32的API函数,调用通用对话框。这不是我们要学习的窗口编程。
而9行的消息框中返回一个整数值,,该值指示用户单击了哪个按钮。如果想获取该值,可修改上述代码如下。
1 #include <windows.h> 2 3 int APIENTRY WinMain(HINSTANCE hInstance, //当前窗口实例句柄 4 HINSTANCE hPrevInstance, //前一个实例句柄,Win32下为NULL 5 LPSTR lpCmdLine, //命令行参数字符 6 int nCmdShow //窗口的显示方式 7 ) 8 { 9 int ret=MessageBox(NULL,TEXT("你满18岁了嘛?"),TEXT("判断是否成年"), MB_YESNO); //通用对话框 10 11 if(ret==IDYES) 12 { 13 MessageBox(NULL,TEXT("你成年啦!"),TEXT("判断结果"),MB_OK); 14 } 15 else 16 { 17 MessageBox(NULL,TEXT("你还没有成年!"),TEXT("判断结果"),MB_OK); 18 } 19 20 return 0; //函数返回值 21 }
如果想同时显示表1-2和表1-3的内容,可以使用|(或运算符),比如:
MessageBox(NULL, TEXT("Hello World"), TEXT("hello"),MB_OKCANCEL| MB_ICONQUESTION);