C语言功能强大的一个重要原因是因为具有丰富的库函数,例如字符串处理有string库,数学处理有math库。微软公司对程序库的支持又更进一步,提供了丰富的动态链接库。下面以微软操作系统内嵌的纸牌链接库为例写一个简单的小程序。
由于我们已经较为理解Windows应用程序的框架了,因此这次拓展练习将所有的代码由手工完成一个最简洁的框架。
在WinLearn中创建新的Win32 Application,项目名叫做WinStep6。在选项中注意选择简单的应用程序。
图1 创建简单应用程序
初始的代码应该是这样的:
我们首先在WinMain中注册一个窗口类。窗口类的代码可以从早期的WinStep1中的MyRegistClass函数中复制过来:
注意其中删除了和资源相关的代码。
然后通过刚注册的WinStep6窗口类,生成纸牌的主窗口。相应的代码可以从WinStep1中的InitInstance函数中复制过来。
向主函数中增加主循环代码。主循环代码可以从WinStep1中的WinMain复制过来。
向程序中添加消息处理函数MainProc。消息处理函数可以由WinStep1中的MainWndProc复制并改编而来。因为我们不用处理菜单和快捷键,因此将相关的代码删除了。剩下的框架如下:
将一些变量的声明,函数的声明补充完整,这个最简单的框架就可以运行了。
我们自定义的框架程序运行结果:
在这个最简框架基础上,我们来做一个简单的纸牌显示和操作的演示。
微软提供的纸盘链接库叫做cards.dll,通常在Windows\System32目录下。若是找不到该链接库,可以从网络或者网络课堂下载。
动态链接库(.dll)和常见的静态链接库(.lib,例如math.lib)类似,都存放了函数的二进制代码。但是动态链接库要多一些步骤。首先是程序执行时根据需要加载动态链接库,和程序不再需要库函数时从内存中卸载动态链接库以节约内存开销。这两个函数分别是LoadLibrary和FreeLibrary。在刚才的最简框架基础上添加对cards的打开和关闭:
程序片段34 动态链接库的加载
程序片段35 动态链接库的释放
然后需要从动态链接库中获得函数的入口。
微软的cards.dll提供了5个函数来绘制纸牌,和大多数静态链接库不同,微软的cards.dll没有提供头文件,因此需要我们手工建立这5个函数的声明。一般而言,我们应该为cards.dll的函数专门建立一个cards.h头文件,但是为了简洁起见,在WinStep6中,仅仅将这几个头文件放置在WinStep6.cpp开头的位置。
并且在函数声明后面立即为这5个函数建立全局函数指针备用:
注意这5个typedef定义了一种函数指针类型,举例而言,第一行的函数定义如下:
这行定义了一个接受两个整型指针作为形参并且返回BOOL类型(其实BOOL就是int类型)的函数指针,凡是这样的函数指针都可以叫做pfcdtInit类型。
因此下面的全局变量定义中,使用pfcdtInit定义了一个函数指针:
在应用程序设计中,这样的书写方式很常见,希望同学们能够模仿并书写自己的函数指针定义。
在主函数中,等动态链接库加载成功后,便可以从动态链接库中获得这5个函数的入口地址(即给前述5个指针变量赋值)
在正式绘图前,需要知道所绘制的一张完整的纸牌的大小(所占用的像素)。这使用cdtInit函数可以获得。
因为在绘图中会反复用到纸牌的大小,因此将这大小数据以全局变量形式存放:
并且在主函数中获得牌的大小:
下面在WM_PAINT中绘制我们的第一张牌:红桃K。注意为了后面的绘图,下面的代码中增加了一个矩形结构体RECT,并且在绘图代码WM_PAINT中增加了获得窗口位置大小的信息的代码。
现在程序可以运行并且绘制一张图片了。
绘图函数cdtDraw的参数很直接:hdc表示在哪个窗口绘图,20,20表示左上角20,20坐标位置;50表示第50号牌。cards每张牌均有一个牌号,若定义花色为suit(03),点数为face(012),则有公式card = suit + face * 4例如红桃K的卡号为2+12×4=50。卡号53~68为各种牌的背面。type为显示类型,type=0时表示显示正面,这时候卡号card只能是0~51,当type=1时表示显示牌背面,这时候卡号card只能是53~68.使用其他卡号将绘制错误的牌。