21.1 关于库的基础知识
(1)两种LIB库——都是链接时才用,链接完就不再使用了。属开发期的产物。
LIB库 |
区别 |
对象库 (也叫静态链接库) |
①是多个obj文件能过Lib.exe组合成一个.lib文件。包含了实际执行代码、符号表等。链接时被加到exe文件中。 ②只需要使用#pragma comment(lib,"XXXX.lib")或在编译器中设置相关选项。并包含头文件到进相应的文件中。 |
导入库 (导入动态库时使用) |
①只包含函数的地址和符号表等,确保程序静态载入动态链接库。②引入方式与静态链接库一样,只需要使用#pragma comment(lib,"XXXX.lib")或在编译器中设置相关选项,并按照头文件函数接口的声明调用函数就可以了。 ③如果是动态载入,则不需要该导入库了。但要通过LoadLibrary调入DLL文件,并手工调用GetProcAddress获得对应函数。 |
(2)静态链接库和动态链接库的特点
名称 |
特点 |
|
静态链接库 |
缺点 |
①多个程序使用相同库函数时,要存多份相同的代码到各个exe中,显然浪费空间。 ②如果某个函数有错或更新算法,则所有用到此函数的exe要重新编译一遍,升级麻烦。 ③多个exe运行时,要载入相同的代码,浪费内存。 |
优点 |
①仅在链接时使用,链接完后,可执行文件可脱离库文件单独存在 ②代码的访问速度快 |
|
动态链接库 |
特点 |
①程序运行时被载入,且内存中只保留一份代码,这份代码是通过分页机制被映射到不同进程的地址空间。但数据段仍是多份的,会被映射到不同的物理内存中,有多少个程序,就会产生多少份的数据段 ②动态链接库与可执行文件的不同,仅在文件头的属性位不同而己,exe文件的一些特征,动态链接库中也有。如动态链接库也可以使用各种资源。 ③动态链接库是被映射到其他应用程序的地址空间的,与应用程序是“一体”的。它所拥有的资源也可被应用程序使用。它的任何操作都代表应用程序进行,当在库中打开文件、分配内存和创建窗口,这些都为应用程序所拥的。 ④不能独立于应用程序而单独运行。 |
(3)库的入口点和退出点
int WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved)
fdwReason |
含义 |
DLL_PROCESS_ATTACH |
①当动态链接链被映射到程序进程的地址空间时,相当于初始化信号 ②在进程的整个生命周期内,只会用这个参数调用一次。如果以后的线程通过调用LoadLibraryEx函数,只会递增DLL的使用计数,而不再用这个参数去调用DllMain。 ③返回值TRUE,表明初始化成功。返回FALSE,系统会终止整个进程的运行。 ④hInstance为动态链接库的模块实例句柄(注意不是“宿主”的实例句柄),获得该句柄的唯一途径是在入口函数被调用时,保存这个参数,否则运行时没有其他方法可获取了。 |
DLL_PROCESS_DETACH |
①表示动态链接库将被卸载,给库提供一个自清理的机会 ②同样,进程的整个生命期内,只会用这个参数调用函数一次。 ③注意,DLL能够阻止进程的终止。例如,当收到DLL_PROCESS_DETACH时,让程序进入一个无限循环。只有当每个DLL都处理完该通知后,操作系统才会中止进程。 |
DLL_THREAD_ATTACH |
①表示应用程序创建了一个新的线程,系统会向当前进程中的所有DLL发送该通知。只有系统处理后这个通知,系统才允许新线程开始执行它的线程函数。 ②如果程序频繁的创建创建和结束线程,会以该参数和DLL_THREAD_DETACH频繁地调用该函数。 ③系统只会新的线程调用该函数,如果系统己经存的线程则不会再次收到该通知。 ④主线程比较特殊,他不会调用以该参数去调用DllMain。 |
DLL_THREAD_DETACH |
①线程将要结束时,会以该参数调用DllMain。注意,此时的线程还没结束,甚到还可以发送线程消息。但不应该再使用PostMessage,因为该线程可能在消息被收取取之前就消失了。 ②如果调用TerminateThread终止线程时,那么不会收到该通知。 ③如果DLL被撤消时,仍有线程在运行,那么就不为任何线程调用该函数。 ④该通知能阻止线程的中止 |
(4)动态链接链的调用
①静态调用:
#include "..\\EdrLib\\EdrLib.h" //包含动态库的头文件 #pragma comment(lib,"..\\Debug\\EdrLib.lib") //告诉编译器DLL相对应的lib文件所在路径和文件名
★特点:使用方便,像使用自己内部函数一样的使用DLL中的函数。但装入DLL过程中任何错误,应用程序没有任何机会完成应变的措施,因为它根本没来得及被装入执行。
②动态调用
//注意,这里可以不包含动态库的头文件 typedef int (* lpAddFun)(int ,int);//定义一个动态库里的AddNew的函数指针。 lpAddFun addFun; //函数指针 hDll = LoadLibrary("EdrLib.dll");//动态加载DLL模块句柄 addFun = (lpAddFun)GetProcAddress(hDll, "AddNew");//得到函数的地址 …… addFun(2,3); …… if(hDll) FreeLibrary(hDll);//释放已经加载的DLL模块。
★特点:因为事先不需要知道动态库文件名和函数名,所以也就不再需要根据导入库中的dll文件名和函数名写入exe文件头的导入表中,所以就不再需要#include动态库的头文件和.lib导入库。需要使用动态库时才由应用程序通过LoadLibrary装入,如果动态库不存在会返回NULL,这时错误是可控的。
21.2 动态链接库导出函数名称问题
(1)DLL导出函数名称的关系图
(2)用模块定义文件导出C\C++文件中的函数——可让编译器不改动函数名。
①新建模块定义文件,内容如下:(文件名如“EdrLib.def”)
;模块定义文件里的注释是用分号的 LIBRARY EdrLib ;EdrLib为动态链接库的名称 EXPORTS EdrCenterTextA @1 ;@1,为函数的编序号 EdrCenterTextW @2
②设置链接时依赖文件
方法:“项目属性”→“配置属性”→“链接器”→“输入”→“模块定义文件”→输入“EdrLib.def”(不包含引号)
(3)VC 中默认调用是 __cdecl 方式,Windows API 使用 __stdcall 调用方式。在 DLL 导出函数中,为了跟 Windows API 保持一致,一般使用 __stdcall方式。如果调用约定仍采用C默认的方式不变,则按模块定义文件或_declspec(dllexport)导出都能使C文件(注意,不是C++文件)的函数名保持不变。但如果调用约定发生改变时,即使使用extern "C",编译后的函数名还是会发生改变。例如上面我们加入_stdcall关键字说明调用约定。
【EdrTest程序】
EdrLib.dll链接库
//Edrlib.h
/*-------------------------------------------------------- EDRLIB.H header file --------------------------------------------------------*/ #pragma once; #include <windows.h> #ifdef _cplusplus #ifdef API_EXPORT #define EXPORT extern "C" __declspec(dllexport) //当头文件供动态库本身使用时 #else #define EXPORT extern "C" __declspec(dllimport) //当头文件供调用库的程序使用时 #endif #else #ifdef API_EXPORT #define EXPORT __declspec(dllexport) //当头文件供动态库本身使用时 #else #define EXPORT __declspec(dllimport) //当头文件供调用库的程序使用时 #endif #endif EXPORT BOOL CALLBACK EdrCenterTextA(HDC, PRECT, PCSTR); EXPORT BOOL CALLBACK EdrCenterTextW(HDC, PRECT, PCWSTR); #ifdef UNICODE #define EdrCenterText EdrCenterTextW #else #define EdrCenterText EdrCenterTextA #endif
//EdrLib.c
/*-------------------------------------------------------- EDRLIB.C —— Easy Drawing Routine Library module (c) Charles Petzold,1998 --------------------------------------------------------*/ #include <Windows.h> #define API_EXPORT #include "EdrLib.h" //入口和退出点 int WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved) { return TRUE; } EXPORT BOOL CALLBACK EdrCenterTextA(HDC hdc, PRECT prc, PCSTR pString) { int iLength; SIZE size; iLength = lstrlenA(pString); GetTextExtentPoint32A(hdc, pString, iLength, &size); return TextOutA(hdc, (prc->right - prc->left - size.cx) / 2, (prc->bottom - prc->top - size.cy) / 2, pString,iLength); } EXPORT BOOL CALLBACK EdrCenterTextW(HDC hdc, PRECT prc, PCWSTR pString) { int iLength; SIZE size; iLength = lstrlenW(pString); GetTextExtentPoint32W(hdc, pString, iLength, &size); return TextOutW(hdc, (prc->right - prc->left - size.cx) / 2, (prc->bottom - prc->top - size.cy) / 2, pString, iLength); }
//EdrTest测试文件
/*------------------------------------------------------------ EDRTEST.C -- Program using EDRLIB dynamic-link library (c) Charles Petzold, 1998 ------------------------------------------------------------*/ #include <windows.h> //-----------------------静态调用(隐式链接)------------------- //#include "..\\EdrLib\\EdrLib.h" //#pragma comment(lib,"..\\Debug\\EdrLib.lib") //动态链接库的导入库 //-----------------------动态调用(显式链接)-------------------- #ifdef UNICODE typedef BOOL (CALLBACK *PEDRCENTERTEXT)(HDC, PRECT, PCWSTR); #define EdrCenterText "EdrCenterTextW" #else typedef BOOL (CALLBACK *PEDRCENTERTEXT)(HDC, PRECT, PCSTR); #define EdrCenterText "EdrCenterTextA" #endif //--------------------------------------------------------- LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("StrProg") ; 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, // window class name TEXT ("DLL Demonstration Program"), // window caption WS_OVERLAPPEDWINDOW, // window style CW_USEDEFAULT, // initial x position CW_USEDEFAULT, // initial y position CW_USEDEFAULT, // initial x size CW_USEDEFAULT, // initial y size NULL, // parent window handle NULL, // window menu handle hInstance, // program instance handle NULL) ; // creation parameters ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc ; PAINTSTRUCT ps ; RECT rect ; static HANDLE hDll; //供动态调用DLL使用 static PEDRCENTERTEXT pEdrCenterText; //供动态调用DLL使用 switch (message) { case WM_CREATE: //动态调用(显式链接) hDll = LoadLibrary(TEXT("EdrLib.dll")); if (hDll) pEdrCenterText = (PEDRCENTERTEXT)GetProcAddress(hDll, EdrCenterText); return 0 ; case WM_PAINT: hdc = BeginPaint (hwnd, &ps) ; GetClientRect (hwnd, &rect) ; //静态调用(隐式链接) //EdrCenterText(hdc, &rect, TEXT("This string was displayed by a DLL")); //动态调用(显式链接) pEdrCenterText(hdc, &rect, TEXT("This string was displayed by a DLL")); EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY: if (hDll) FreeLibrary(hDll); PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; }
21.3 在DLL中共享内存
(1)动态链接库存在于不同进程的地址空间,它们代表“宿主”程序工作,其代码段会被不同进程共享,但数据段会被映射到不同的物理内存,也就是这些数据是不能共享的,但有时我们需要数据在程序间共享,怎么办?
(2)创建数据共享区的步骤。
①声明并创建的一个数据区——表现在dll文件中会出现一个自定义的myshared数据区
#pragma data_seg(“myshared”) int iTotal =0; //一定要初始化,否则被编译到未初始化区,而不是myshared区 WCHAR szString[MAX_STRING]={‘\0’}; //一定要初始化。 #pragma data_seg(); //数据区定义结束
②将数据区设为可读、可写和可共享的属性
#pragma comment(linker, "/SECTION:myshared,RWS")
【StrProg程序】
效果图
//DLL动态链接库代码
/*------------------------------------------------------ STRLIB.H header file -------------------------------------------------------*/ #pragma once #include <Windows.h> #ifdef __cplusplus #ifdef API_EXPORT #define EXPORT extern "C" __declspec(dllexport) #else #define EXPORT extern "C" __declspec(dllimport) #endif #else #ifdef API_EXPORT #define EXPORT __declspec(dllexport) #else #define EXPORT __declspec(dllimport) #endif #endif //字符串的最大行数和字符长度 #define MAX_STRINGS 256 #define MAX_LENGTH 63 //用于显示字符串的函数(回调函数,由调用者决定如何显示) /* 函数指针名称:GETSTRCB,其中CB表示CALLBACK 第1个参数:为指向要显示的字符串的指针 第2个参数:指定自定义的数据结构的指针,用来决定如何显示或处理字符串 */ typedef BOOL(CALLBACK* GETSTRCB)(PCTSTR, PVOID); //每个函数都有两个版本:ANSI和UNICODE EXPORT BOOL CALLBACK AddStringA(PCSTR); EXPORT BOOL CALLBACK AddStringW(PCWSTR); EXPORT BOOL CALLBACK DeleteStringA(PCSTR); EXPORT BOOL CALLBACK DeleteStringW(PCWSTR); EXPORT int CALLBACK GetStringA(GETSTRCB,PVOID); EXPORT int CALLBACK GetStringW(GETSTRCB, PVOID); //根据UNICODE标识符的情况来使用相应版本的函数 #ifdef UNICODE #define AddString AddStringW #define DeleteString DeleteStringW #define GetStrings GetStringW #else #define AddString AddStringA #define DeleteString DeleteStringA #define GetStrings GetStringA #endif
//StrLib.c
/*----------------------------------------------------------- STRLIB.C —— Library module for STRPPROG program (c)Charles Petzold,1998 -----------------------------------------------------------*/ #define API_EXPORT #include <Windows.h> #include "StrLib.h" //创建DLL共享内存,会在DLL文件中创新一个新的数据区,名为"myshared"区,就像 //一般的文件有代码区、常量区等。 //(需要链接选项: /SECTION:myshared,RWS) #pragma data_seg(".myshared") //共享数据区定义开始 int iTotal = 0; //必须初始化,否则分配在未初始化数据区中,该变量用于存在当前的字符串(串数)(不是每个字符串的字符个数) WCHAR szStrings[MAX_STRINGS][MAX_LENGTH + 1] = { '\0' }; //必须初始化,MAX_STRINGS行,MAXLENGTH+1列 #pragma data_seg() //共享数据区定义结束 #pragma comment (linker,"/SECTION:.myshared,RWS") //设为可读写、可共享属性 //#pragma comment(linker,"/DEF:strLib.def" ) int WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved) { return TRUE; } EXPORT BOOL CALLBACK AddStringA(PCSTR pStringIn) { BOOL bReturn=FALSE; int iLength; PWSTR pWideStr; //将字符串转为UNICODE编码,并调用AddStringW函数 iLength = MultiByteToWideChar(CP_ACP, 0, pStringIn, -1, NULL, 0); //返回转化后的字符个数(含\0) pWideStr = malloc(iLength*sizeof(WCHAR)); //课本这里有误,因为返回的是字符个数,而不是缓冲区大小 MultiByteToWideChar(CP_ACP, 0, pStringIn, -1, pWideStr, iLength); //iLength缓冲最多能存储的字符数 bReturn = AddStringW(pWideStr); free(pWideStr); return bReturn; } EXPORT BOOL CALLBACK AddStringW(PCWSTR pStringIn) { PWSTR pString; int i, iLength; if (iTotal == MAX_STRINGS - 1) //如果当前字符串的个数己经达到最大,则不再增加 return TRUE; //为字符串分配内存,复制字符串并转为大写 if ((iLength = wcslen(pStringIn)) == 0) //长度不含\0 return FALSE; pString = (PWSTR)malloc(sizeof(WCHAR)*(1 + iLength)); wcscpy_s(pString, iLength + 1, pStringIn); _wcsupr_s(pString, iLength+1); //将字符串转为大写 //比较输入的字符串与DLL共享区中各字符串的大写,并按字母表的顺序进行排序 for ( i=iTotal; i >0; i--) { if (wcscmp(pString, szStrings[i - 1]) > 0) //从最后面往前比较,如果pString较大,就跳出 break; //如果pString较小,则szStrings[i-1]后移,腾出位置出来。 wcscpy_s(szStrings[i], wcslen(szStrings[i-1])+1, szStrings[i-1]); } wcscpy_s(szStrings[i], iLength + 1,pString); //将pStringIn加入到数组中合适的位置 iTotal++; free(pString); pString = NULL; return TRUE; } EXPORT BOOL CALLBACK DeleteStringA(PCSTR pStringIn) { BOOL bReturn; int iLength; PWSTR pWideStr; //将字符串转为UNICODE编码,并调用DeleteStringW函数 iLength = MultiByteToWideChar(CP_ACP, 0, pStringIn, -1, NULL, 0); //返回转化后的字节总数(含\0) pWideStr = malloc(iLength*sizeof(WCHAR)); MultiByteToWideChar(CP_ACP, 0, pStringIn, -1, pWideStr, iLength); bReturn = DeleteStringW(pWideStr); free(pWideStr); return bReturn; } EXPORT BOOL CALLBACK DeleteStringW(PCWSTR pStringIn) { int i, j; if (0 == wcslen(pStringIn)) return FALSE; for (i = 0; i < iTotal; i++) { //查找pStringIn字符串,在数组中的位置(_wcsicmp不区分大小写) if (_wcsicmp(szStrings[i], pStringIn) == 0) break; } //i返回索引位置 if (i == iTotal) //如果找不到,则返回 return FALSE; //将i后面的元素往前移 for (j =i; j< iTotal;j++) { wcscpy_s(szStrings[j],wcslen(szStrings[j+1])+1,szStrings[j+1]); } szStrings[iTotal--][0] = '\0'; return TRUE; } //其中pfnGetStrCallBack为回调函数,用于用户自己决定如果显示或处理字符串,pParam为附加参数 EXPORT int CALLBACK GetStringA(GETSTRCB pfnGetStrCallBack, PVOID pParam) { BOOL bReturn; int i, iLength; PSTR pAnsiStr; //枚举各串字符串 for (i = 0; i < iTotal; i++) { //转化为UNICODE iLength = WideCharToMultiByte(CP_ACP, 0, szStrings[i], -1, NULL, 0, NULL, NULL); pAnsiStr = malloc(iLength); WideCharToMultiByte(CP_ACP, 0, szStrings[i], -1, pAnsiStr, iLength, NULL, NULL); //调用回调函数,并传入附回参数 bReturn = pfnGetStrCallBack((PCTSTR)pAnsiStr, pParam); free(pAnsiStr); if (bReturn == FALSE) //如果错误,返回错误的行号 return i + 1; } return iTotal; } //其中pfnGetStrCallBack为回调函数,用于用户自己决定如果显示或处理字符串,pParam为附加参数 EXPORT int CALLBACK GetStringW(GETSTRCB pfnGetStrCallBack, PVOID pParam) { BOOL bReturn; int i; for (i = 0; i < iTotal; i++) //枚举各串字符串,并在回调函数中进行处理。 { bReturn = pfnGetStrCallBack(szStrings[i], pParam); if (bReturn == FALSE) //如果错误,返回错误的行号 return i + 1; } return bReturn; }
//StrProg测试程序
/*------------------------------------------------------------ STRPROG.C -- Program using STRLIB dynamic-link library (c) Charles Petzold, 1998 ------------------------------------------------------------*/ #include <windows.h> #include "resource.h" #include "..\\StrLib\\StrLib.h" #pragma comment(lib,"..\\debug\\Strlib.lib") //回调函数的参数(结构体) //将DLL共享区中若干行和列来显示,其中每行高度yIncr(cyChar), //每列宽度xIncr(cxChar*MAXLENGTH) typedef struct { HDC hdc; int xText; //当前的输出位置(x坐标) int yText; //当前的输出位置(y坐标) int xStart;//字符串输出的起始x坐标 int yStart;//字符串输出的起始y坐标 int xIncr; //cxChar * MAX_LENGTH,每列的宽度 int yIncr; //每次输出时,每行的高度(cyChar) int xMax; //xIncr*(1+cxClient/xIncr); 最大的列数 int yMax; //cyChar*(cyClient /cyChar-1);最大的行数 }CBPARAM; LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; TCHAR szAppName[] = TEXT("StrProg"); TCHAR szString[MAX_LENGTH + 1]; 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 = szAppName; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"), szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, // window class name TEXT ("DLL Demonstration Program"), // window caption WS_OVERLAPPEDWINDOW, // window style CW_USEDEFAULT, // initial x position CW_USEDEFAULT, // initial y position CW_USEDEFAULT, // initial x size CW_USEDEFAULT, // initial y size NULL, // parent window handle NULL, // window menu handle hInstance, // program instance handle NULL) ; // creation parameters ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } //显示字符串的回调函数,GetString函数来回调使用 BOOL CALLBACK GetStrCallBack(PCTSTR pString, CBPARAM* pcbp) { //每成若干行和列来显示DLL共享区中的每一条字符串。 TextOut(pcbp->hdc, pcbp->xText, pcbp->yText, pString, lstrlen(pString)); //下一行的位置 pcbp->yText += pcbp->yIncr; if (pcbp->yText >pcbp->yMax) //如果该列己经显示满了,计算新的行和列 { pcbp->yText = pcbp->yStart; //新的一行的位置,回到起始行 pcbp->xText += pcbp->xIncr; //新的一列的位置 if (pcbp->xText > pcbp->xMax) //如果列数太多(超过了显示区域范围了), //则不显示忽略该字符串 return FALSE; } return TRUE; } BOOL CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_INITDIALOG: return TRUE; case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: GetDlgItemText(hDlg, IDC_STRING, szString, MAX_LENGTH); EndDialog(hDlg, TRUE); return TRUE; case IDCANCEL: EndDialog(hDlg, FALSE); return TRUE; } } return FALSE; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static HINSTANCE hInst; HDC hdc ; PAINTSTRUCT ps ; static int cxClient, cyClient,cxChar,cyChar; CBPARAM cbparam; static UINT iDataChangeMsg; switch (message) { case WM_CREATE: hInst = ((LPCREATESTRUCT)lParam)->hInstance; cxChar = LOWORD(GetDialogBaseUnits()); cyChar = HIWORD(GetDialogBaseUnits()); //注册消息,但数据发生变化时,进行广播通知所有实例。 iDataChangeMsg = RegisterWindowMessage(TEXT("StrProgDataChange")); return 0 ; case WM_SIZE: cxClient = LOWORD(lParam); cyClient = HIWORD(lParam); return 0; case WM_COMMAND: switch (LOWORD(wParam)) { case IDM_ENTER: if (DialogBox(hInst, TEXT("EnterDlg"), hwnd, &DlgProc)) { if (AddString(szString)) PostMessage(HWND_BROADCAST, iDataChangeMsg, 0, 0); else MessageBeep(0); } break; case IDM_DELETE: if (DialogBox(hInst, TEXT("DeleteDlg"), hwnd, &DlgProc)) { if (DeleteString(szString)) PostMessage(HWND_BROADCAST, iDataChangeMsg, 0, 0); //广播通知各个实例 else MessageBeep(0); } break; } break; case WM_PAINT: hdc = BeginPaint (hwnd, &ps) ; cbparam.hdc = hdc; cbparam.xText = cbparam.xStart = cxChar; cbparam.yText = cbparam.yStart = cyChar; cbparam.xIncr = cxChar*MAX_LENGTH; //每列的宽度 cbparam.yIncr = cyChar; //每行的高度 //cxClient/cbparam.xIncr表示总列数(取整数部分),加1能保 //证当总列数不满客户区宽度时,剩余的客户区宽度里也要充分利用,断续 //输出(当然,这将导致部分文字被剪裁,也就是最后一列字符串不完整了。 cbparam.xMax = cbparam.xIncr*(1 + cxClient / cbparam.xIncr); cbparam.yMax = cyChar*(cyClient / cyChar -1 ); GetStrings((GETSTRCB)GetStrCallBack, (PVOID)&cbparam); EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; default: if (message == iDataChangeMsg) InvalidateRect(hwnd, NULL, TRUE); break; } return DefWindowProc (hwnd, message, wParam, lParam) ; }
//resource.h
//{{NO_DEPENDENCIES}} // Microsoft Visual C++ 生成的包含文件。 // 供 StrProg.rc 使用 // #define IDC_STRING 1001 #define IDM_ENTER 40001 #define IDM_DELETE 40002 #define IDC_STATIC -1 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 103 #define _APS_NEXT_COMMAND_VALUE 40005 #define _APS_NEXT_CONTROL_VALUE 1002 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif
//strProg.rc
// Microsoft Visual C++ generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "winres.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // 中文(简体,中国) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS) LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE BEGIN "resource.h\0" END 2 TEXTINCLUDE BEGIN "#include ""winres.h""\r\n" "\0" END 3 TEXTINCLUDE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Menu // STRPROG MENU BEGIN MENUITEM "&Enter!", IDM_ENTER MENUITEM "&Delete!", IDM_DELETE END ///////////////////////////////////////////////////////////////////////////// // // Dialog // ENTERDLG DIALOGEX 0, 0, 227, 78 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Enter" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN DEFPUSHBUTTON "确定",IDOK,46,46,50,14 PUSHBUTTON "取消",IDCANCEL,130,46,50,14 LTEXT "&Enter:",IDC_STATIC,27,20,21,8 EDITTEXT IDC_STRING,61,18,144,14,ES_AUTOHSCROLL END DELETEDLG DIALOGEX 0, 0, 221, 60 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Delete" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN DEFPUSHBUTTON "确定",IDOK,46,39,50,14 PUSHBUTTON "取消",IDCANCEL,130,39,50,14 LTEXT "&Enter:",IDC_STATIC,22,12,21,8 EDITTEXT IDC_STRING,56,10,144,14,ES_AUTOHSCROLL END ///////////////////////////////////////////////////////////////////////////// // // DESIGNINFO // #ifdef APSTUDIO_INVOKED GUIDELINES DESIGNINFO BEGIN "ENTERDLG", DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 220 TOPMARGIN, 7 BOTTOMMARGIN, 71 END "DELETEDLG", DIALOG BEGIN END END #endif // APSTUDIO_INVOKED #endif // 中文(简体,中国) resources ///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED
21.4 关于动态链接库的其他问题
(1)动态库不接收消息,但可以调用GetMessage或PeekMessage从消息队列中获取消息,这些消息是来自“宿主”程序的消息。
(2)动态链接库的静态载入方法——以调用Rectangle为例
默认情况下,程序自动加入了GDI32.LIB(导入库)这一选项(可以在项目属性中看到GDI32.LIB、USER32.LIB、Kernel32.LIB等附加依赖项)。当正常调用Rectangle时,程序会从GDI32.LIB中找到该函数地址,并提供给调用程序,即采用静态载入(静态载入或叫隐式载入动态链接库的方法)。
(3)动态链接库的动态载入方法——适用于没用导入库的DLL。(Rectangle另类调用方法)
//回调函数 typedef BOOL(WINAPI* PFNRECTANGLE)(HDC, int, int, int, int); HANDLE hLibrary; PFNRECTANGLE pfnRectangle; hLibrary = LoadLibrary(TEXT("GDI32.DLL")); //动态载入DLL //获取Rectangle函数的地址 pfnRectangle = (PFNRECTANGLE)GetProcAddress(hLibrary, TEXT("Rectangle")); pfnRectangle(hdc, xLeft, yTop, xRight, yBottom); //绘图 FreeLibrary(hLibrary); //释放动态链接库
(4)资源库
【ShowBit程序】
效果图
//BitLib.Dll
/*------------------------------------------------------------------------------- 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; }
//resource.h
//{{NO_DEPENDENCIES}} // Microsoft Visual C++ 生成的包含文件。 // 供 BitLib.rc 使用 // // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 110 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif
//BitLib.rc
// Microsoft Visual C++ generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "winres.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // 中文(简体,中国) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS) LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE BEGIN "resource.h\0" END 2 TEXTINCLUDE BEGIN "#include ""winres.h""\r\n" "\0" END 3 TEXTINCLUDE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Bitmap // 1 BITMAP "BITMAP1.BMP" 2 BITMAP "BITMAP2.BMP" 3 BITMAP "BITMAP3.BMP" 4 BITMAP "BITMAP4.BMP" 5 BITMAP "BITMAP5.BMP" 6 BITMAP "BITMAP6.BMP" 7 BITMAP "BITMAP7.BMP" 8 BITMAP "BITMAP8.BMP" 9 BITMAP "BITMAP9.BMP" #endif // 中文(简体,中国) resources ///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED
//ShowBit测试程序
/*------------------------------------------------------------ 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("ShowBit"); 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, // window class name TEXT ("Show Bitmaps from BITLIB(Press Key)"), // window caption WS_OVERLAPPEDWINDOW, // window style CW_USEDEFAULT, // initial x position CW_USEDEFAULT, // initial y position CW_USEDEFAULT, // initial x size CW_USEDEFAULT, // initial y size NULL, // parent window handle NULL, // window menu handle hInstance, // program instance handle NULL) ; // creation parameters 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) { BITMAP bm; HDC hMemDC; GetObject(hBitmap, sizeof(BITMAP), &bm); hMemDC = CreateCompatibleDC(hdc); SelectObject(hMemDC, hBitmap); BitBlt(hdc,xStart, yStart,bm.bmWidth,bm.bmHeight ,hMemDC,0,0,SRCCOPY); DeleteDC(hMemDC); } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc ; PAINTSTRUCT ps ; static HINSTANCE hLibrary; static int iCurrentIndex; HBITMAP hBitmap; switch (message) { case WM_CREATE: hLibrary = LoadLibrary(TEXT("BitLib.dll")); if (NULL==hLibrary) { MessageBox(hwnd, TEXT("Can't load BitLib.dll"), szAppName, 0); return -1; } return 0 ; case WM_CHAR: if (hLibrary) { iCurrentIndex++; iCurrentIndex %= 9; InvalidateRect(hwnd, NULL, TRUE); } return 0; case WM_PAINT: hdc = BeginPaint (hwnd, &ps) ; if (hLibrary) { //第1个参数为hLibrary,表示从动态库中加载资源 hBitmap = LoadBitmap(hLibrary, MAKEINTRESOURCE(iCurrentIndex+1)); 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) ; }