21.2 关于动态链接库的其他话题

来源:互联网 发布:鼎域名邸房价走势 编辑:程序博客网 时间:2024/05/21 16:57

摘录于《Windows程序(第5版,珍藏版).CHarles.Petzold 著》P974

        我前面提到过动态库模块不接收消息。但是,一个库模块可以调用 GetMessage 和 PeekMessage。库模块用这些函数从消息队列中拿到的消息实际上时调用该库函数的程序的消息。一般来说,库为调用它的程序服务——这个规则适用于库模块调用的大部分 Windows 函数。

        一个动态链接库可以从库文件或从调用该库的程序文件中加载资源(如图标、字符串和位图)。负责加载资源的函数需要一个实例句柄。如果动态链接库使用它自己的实例句柄(在动态库初始化时传入),则动态库可以从自己的文件中获取资源。如果要加载调用程序的 .EXE 文件中的资源,则动态库函数需要调用该函数的程序的句柄。

        在动态库中注册窗口类和创建窗口会有点棘手。无论是窗口类结构,或是 CreateWindow 函数,都需要一个实例句柄。虽然你可以使用动态库模块的实例句柄来创建窗口类和窗口,但当动态库创建窗口类和窗口,最好使用调用程序的实例句柄

        由于模态对话框的消息来自于程序外的消息循环,因此你可以在动态库里通过调用 DialogBox 创建一个模态对话框。相应的实例句柄可以使用动态库的句柄,DialogBox 的 hwndParent 参数可以设置为 NULL。

21.2.1  没有导入函数的动态链接库

        与其让 Windows 在程序第一次加载进内存时执行动态链接,不如在程序运行时把运行程序和库模块链接起来。例如,通常可按以下方式调用 Rectangle 函数:

Rectangle(hdc, xLeft, yTop, xRight, yBottom);
之所以可以如此是因为调用时这个程序已经和 GDI32.LIB 导入库链接起来了,而后者提供了 Rectangle 函数的地址。

        还可以用另一种非常迂回的方法调用 Rectangle 函数。先用 typedef 为 Rectangle 函数定义一个函数类型:

typedef BOOL (WINAPI * PFNRECT) (HDC, int, int, int, int);
然后定义两个变量:

HANDLE hLibrary;PFNRECT pfnRectangle;
现在,把 hLibrary 设置为动态库的句柄,把 pfnRectangle 设置为 Rectangle 函数的地址:

hLibrary = LoadLibrary(TEXT("GDI32.DLL"));pfnRectangle = (PFNRECT) GetProcAddress (hLibrary, TEXT("Rectangle"));
如果库文件没有找到或发生了其他错误,LoadLibrary 函数就返回 NULL。现在可以调用这个函数,然后释放动态库:

pfnRectangle (hdc, xLeft, yTop, xRight, yBottom);FreeLibrary(hLibrary);

        尽管这种运行时动态链接技术对于 Rectangle 这个函数没有太大的意义,但这种技术对于在运行前不知道库模块名称的情况是非常方便的。

        前面的代码使用了 LoadLibrary 和 FreeLibrary 函数。Windows 会为所有库模块维护一个“引用计数”。LoadLibrary 让引用计数递增。当 Windows 加载使用某个动态库的任何程序时,该动态库的引用计数也会递增。FreeLibrary 让引用计数减 1。终止一个使用该动态库的程序的案例也会使动态库的引用计数减 1。当引用计数降到 0,Windows 会把这个动态库从内存中卸载,因为它不再需要了

21.2.2  资源库

        动态链接库中任何一个能够被其他 Windows 程序或动态库引用的函数必须被导出。但是动态库也不一定非导出函数不可。这样的动态库里有什么呢?答案是资源。

        假设程序需要大量位图。一般情况下,可以在程序的资源脚本里列举这些资源,然后通过 LoadBitmap 函数把这些资源加载到内存。但你可能需要创建几组不同的位图,每一组位图针对 Windows 中常用的某种主要的显示分辨率。在这种情况下,把这些不同分辨率的位图放入不同的文件会更合理一些,因为一个用户只要用到磁盘上的一组位图就可以了。这些文件便称为资源库。

       图 21-5 显示了如何创建一个叫做 BITLIB.DLL 的、含有 9 个位图的资源库文件。BITLIB.RC 列举所有的位图文件,并且赋给每个文件一个数值。为了创建 BITLIB.DLL,你需要分别叫做 BITMAP1.BMP,BITMAP2.BMP 等的 9 个位图。你可以使用本书附带光盘上的位图文件,或使用 Visual C++ 自己创建。这些位图分别被赋予了 1~9 的数值 ID。

/*---------------------------------------------------------------BITLIB.C -- Code entry point for BITLIB dynamic-link library(c) Charles Petzold, 1998---------------------------------------------------------------*/#include <Windows.h>int WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved){return TRUE;}
BITLIB.RC (excerpts)// Microsoft Visual C++ 生成的资源脚本。//#include "resource.h"///////////////////////////////////////////////////////////////////////////////// Bitmap//1                       BITMAP  DISCARDABLE     "bitmap1.bmp"2                       BITMAP  DISCARDABLE     "bitmap2.bmp"3                       BITMAP  DISCARDABLE     "bitmap3.bmp"4                       BITMAP  DISCARDABLE     "bitmap4.bmp"5                       BITMAP  DISCARDABLE     "bitmap5.bmp"6                       BITMAP  DISCARDABLE     "bitmap6.bmp"7                       BITMAP  DISCARDABLE     "bitmap7.bmp"8                       BITMAP  DISCARDABLE     "bitmap8.bmp"9                       BITMAP  DISCARDABLE     "bitmap9.bmp"

        在一个叫 SHOWBIT 的客户区中创建 BITLIB 项目。和以前一样,在另一个名为 SHOWBIT 的项目中创建如图 21-6 所示的 SHOWBIT 程序。但是,不要让 SHOWBIT 依赖于 BITLIB;否则,链接过程将需要一个 BITLIB.LIB 文件,而这个文件并没有创建,因为 BITLIB 没有导出函数。取而代之的做法是,把各个程序分别设为当前项目(Active Project),然后依次生成 BITLIB 和 SHOWBIT。

        SHOWBIT.C 从 BITLIB 中读入位图资源并且把它们显示在自己的客户区。按键盘上的任意键可以循环显示这些位图。

/*---------------------------------------------------------SHOWBIT.C -- Shows bitmaps in BITLIB dynamic-link library(c) Charles Petzold, 1998----------------------------------------------------------*/#include <Windows.h>LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);TCHAR szAppName[] = TEXT("StrProg");int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow){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 = szAppName;if (!RegisterClass(&wndclass)){MessageBox(NULL, TEXT("This program requires Windows NT!"),szAppName, MB_ICONERROR);return 0;}hwnd = CreateWindow(szAppName, TEXT("Show Bitmaps from BITLIB (press Key)"),WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL);if (!hwnd)return 0;ShowWindow(hwnd, iCmdShow);UpdateWindow(hwnd);while (GetMessage(&msg, NULL, 0, 0)){TranslateMessage(&msg);DispatchMessage(&msg);}return msg.wParam;}void DrawBitmap(HDC hdc, int xStart, int yStart, HBITMAP hBitmap){BITMAPbm;HDChMemDC;POINTpt;hMemDC = CreateCompatibleDC(hdc);SelectObject(hMemDC, hBitmap);GetObject(hBitmap, sizeof(BITMAP), &bm);pt.x = bm.bmWidth;pt.y = bm.bmHeight;BitBlt(hdc, xStart, yStart, pt.x, pt.y, hMemDC, 0, 0, SRCCOPY);DeleteDC(hMemDC);}LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){static HINSTANCE hLibrary;static int iCurrent = 1;HBITMAP hBitmap;HDC hdc;PAINTSTRUCT ps;switch (message){case WM_CREATE:if ((hLibrary = LoadLibrary(TEXT("BITLIB.DLL"))) == NULL){MessageBox(hwnd, TEXT("Can't load BITLIB.DLL"),szAppName, 0);}return 0;case WM_CHAR:if (hLibrary){iCurrent++;InvalidateRect(hwnd, NULL, TRUE);}return 0;case WM_PAINT:hdc = BeginPaint(hwnd, &ps);if (hLibrary){hBitmap = LoadBitmap(hLibrary, MAKEINTRESOURCE(iCurrent));if (!hBitmap){iCurrent = 1;hBitmap = LoadBitmap(hLibrary, MAKEINTRESOURCE(iCurrent));}if (hBitmap){DrawBitmap(hdc, 0, 0, hBitmap);DeleteObject(hBitmap);}}EndPaint(hwnd, &ps);return 0;case WM_DESTROY:if (hLibrary)FreeLibrary(hLibrary);PostQuitMessage(0);return 0;}return DefWindowProc(hwnd, message, wParam, lParam);}

        在处理 WM_CREATE 消息的过程中,SHOWBIT 取得 BITLIB.DLL 的句柄:

if ((hLibrary = LoadLibrary(TEXT("BITLIB.DLL"))) == NULL)
如果 BITLIB.DLL 和 SHOWBIT.EXE 不在同一目录,Windows 会按照本章前面讨论的方式进行搜索。如果 LoadLibrary 返回 NULL,SHOWBIT 将显示一个消息框,报告错误并从 WM_CREATE 消息中返回 -1。这会导致 CreateWindow 向 WinMain 返回 NULL。随后,程序终止运行。

        SHOWBIT 可以通过以库句柄和位图编号为参数调用 LoadBitmap 而获取位图的句柄:

hBitmap = LoadBitmap(hLibrary, MAKEINTRESOURCE(iCurrent));
如果对应于 iCurrent 的位图无效,或没有足够的内存以加载位图,则函数返回错误。

        在处理 WM_DESTROY 消息时,SHOWBIT 会释放动态库:

FreeLibrary(hLibrary);
当最后一个 SHOWBIT 的实例结束时,BITLIB.DLL 的引用计数降到 0,它所占用的内存将被释放。正如你所见,这是一个简单的实现剪贴画程序的方法——该剪贴画程序可以把事先产生的位图(或图元文件或增强型图元文件)加载到剪贴板,供其他程序使用。

0 0