
本文共 24378 字,大约阅读时间需要 81 分钟。
第21章 动态链接库
2.1 关于库的基本知识
Windows程序是可执行文件,通常创建一个或多个窗口,并使用消息循环来接收用户的输入。
动态库一般不能直接执行,而且他们一般也不接收消息。他是包含许多函数的独立文件,这些函数可以被应用程序和其他DLL调用以完成特定的工作。
动态链接发生在运行时,静态链接发生在编译时。
常见动态库KERNEL32.dll, USER32.dll GDI32.dll 等
动态库为许多应用程序提供函数和资源。扩展名(dll) 可通过LoadLibrary或LoardLibraryEx函数加载。
动态库相比静态库节省磁盘空间(静态链接每个应用都要链接源码,造成资源浪费),当运行多个应用实例的时候动态库节约内存。此外修改了动态库不必重新编译所有使用了库中函数的应用程序。(静态库修改需要重新编译)
21.1.1 库:一词多义
对象库object library 扩展名为.LIB 在link过程中被添加到exe中。 例如LIBC.LIB
导入库是一种特殊形式的对象库。导入库的扩展名也是.LIB 用来解析源代码中库函数的调用。导入库不包含任何代码。他们只给连接器提供信息,以建立.exe文件中用于动态链接的重新定位。例如 KERNEL32.lib USER32.lib GDI32.lib
对象库和导入库仅在程序开发时被使用,而动态库则是在运行时使用。 动态库必须在应用程序EXE目录或者系统目录下。否则必须制定详细的绝对路径加载。
21.1.2 一个简单的DLL
EDRLIB.dll (Easy Drawing Routines)
在VS2013中先创建一个Win32 Project命名为 EDRTEST 选择empty project
然后在Solution上右键Add new project 选择Win32 Project 选择DLL 命名为EDRLIB 选择empty project。添加完成的工程如下
首先在EDRLIBproject中添加EDRLIB.c 和EDRLIB.h文件。代码如下
EDRLIB.h
/* EDRLIB.H header file*/#ifndef _EDRLIB_H_#define _EDRLIB_H_#ifdef __cplusplus#define EXPORT extern "C" __declspec (dllexport)#else#define EXPORT __declspec (dllexport)#endifEXPORT BOOL CALLBACK EdrCenterTextA(HDC, PRECT, PCSTR);EXPORT BOOL CALLBACK EdrCenterTextW(HDC, PRECT, PCWSTR);#ifdef UNICODE#define EdrCenterText EdrCenterTextW#else#define EdrCenterText EdrCenterTextA#endif#endif // _EDRLIB_H_
EDRLIB.c
/* EDRLIB.c -- Easy Drawing Routine Library module (c) Charles Petzold, 1998*/#include <windows.h>#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);}
dllmain 通常用于初始化和逆初始化 默认返回TRUE
extern "c" 表示若头文件被C++编译器编译时,按照C语言的方式编译,防止C++编译器对函数名进行调整而导致链接失败。
21.1.3 库的入口和退出点
若库需要实例句柄(例如创建DialogBox)则应该把hInstance作为全局变量来保存。 最后一个是保留参数
fdwReason参数是4个值中的一个,用来说明Windows调用dllMain的原因。
fdwReason 为 DLL_PROCESS_ATTACH表明动态库被映射到一个进程的地址空间。相当于初始化信号 如果初始化成功范围非0值。 返回0表示windows无法运行该程序
fdwReason 为 DLL_PROCESS_DETACH时,意味着这个进程不再需要DLL了。这给库一个自清理的机会。
fdwReason 为 DLL_THREAD_ATTACH 调用时,意味着一个关联的进程创建了一个新的线程。 当该线程终止时,windows会以DLL_THREAD_DETACH调用dllmain
DllMain以参数DLL_THREAD_DETACH被调用时,线程仍然存在。他甚至可以在此过程中发送线程消息。但他不应该再使用PostMessage,因为该线程可能在消息被收取之前就消失了。
21.1.4 测试程序
EDRTEST.c
/* EDRTEST.c -- Program using EDRLIB dynamic-link library*/#include <windows.h> #include "edrlib.h"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; //The window Class wndClass.style = CS_HREDRAW | CS_VREDRAW; wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class. 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; //Register the Window Class to the Windows System. if (!RegisterClass(&wndClass)) { MessageBox(NULL, TEXT("This program require Windows NT!"), szAppName, MB_ICONERROR); return 0; } //This function will generate an WM_CREATE message. 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); //This function will generate a WM_PAINT message. /* The message loop for this program. if received the WM_QUIT message, the function will return 0.*/ while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam;}//define the Window Procedure WndProc LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){ HDC hdc; PAINTSTRUCT ps; RECT rect; switch (message) //get the message { case WM_PAINT: hdc = BeginPaint(hwnd, &ps); GetClientRect(hwnd, &rect); EdrCenterText(hdc, &rect, TEXT("This string was displayed by a DLL")); EndPaint(hwnd, &ps); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam);}
1. 右键EDRTEST设置为Active Project
2. 设置依赖关系 Project->Project dependencies. 勾选EDRLIB
表示EDRLIB如果有必要会在EDRTEST之前重新生成。
3. 确保最后生成的DLL和EXE在同一个目录VS2013默认在Solution的Release或者Debug下。不需要修改
4. 在ProjectSetting中,设置EDRTEST的语言为UNICODE (默认不需要修改)
5. 在VS2013中除了设置ProjectDependency以外还需要设定Reference关系。(Dependency只影响编译)
在EDRTEST的Project Setting中 Common Properties中添加References 为 EDRLIB(这部非常关键,否则编译的时候会报告link错误)这样连接器就会自动加载EDRLIB.lib的导入库从而在运行时动态链接EDRLIB.dll文件。
*否则需要自己写Loardlibrary加载dll并自己申明函数指针调用。并且在恰当的时候Freelibrary
运行结果如下。
DLL做的所有事情都代表应用程序的行为。例如所有他分配的内存由应用程序拥有,他所创建的任何窗口都属于应用程序,他所打开的任何文件也属于应用程序。多个进程可以共享DLL中相同的代码,然而由DLL维护的数据对每个进程是不一样的。每个进程都有自己的,供DLL使用数据的地址空间。
21.1.5 在DLL中共享内存
windows默认对于dll的内存访问是进程隔离的(进程间对于同一个DLL不共享内存)
STRPROG程序和STRLIB的例子
STRLIB 中的3个导出函数使用共享内存 用来存储和排序256个字符串。放在共享内存中
EXPORT BOOL CALLBACK AddString (pStringIn) //添加字符串
EXPORT BOOL CALLBACK DeleteString (pStringIn) //删除字符串
EXPORT int CALLBACK GetStrings(pfnGetStrCallBack, pParam) //获取字符串 参数是回调函数
回调函数原型
EXPORT BOOL CALLBACK GetStrCallBack(PSTR pString, PVOID pParam) 每对应一个字符串调用一次call back函数直到返回FALSE。
项目名字为STRPROG
STRLIB.h
/* STRLIB.H header file*/#ifdef __cplusplus#define EXPORT extern "C" __declspec (dllexport)#else#define EXPORT __declspec (dllexport)#endif// the maximum number of strings STRLIB will store and their lengths#define MAX_STRINGS 256#define MAX_LENGTH 63// the callback function type definition uses generic stringstypedef BOOL(CALLBACK * GETSTRCB) (PCTSTR, PVOID);// each function has ANSI and UNICODE versionsEXPORT BOOL CALLBACK AddStringA (PCSTR);EXPORT BOOL CALLBACK AddStringW (PCWSTR);EXPORT BOOL CALLBACK DeleteStringA (PCSTR);EXPORT BOOL CALLBACK DeleteStringW (PCWSTR);EXPORT int CALLBACK GetStringsA (GETSTRCB, PVOID);EXPORT int CALLBACK GetStringsW (GETSTRCB, PVOID);// use the correct version depending on the UNICODE identifier#ifdef UNICODE#define AddString AddStringW#define DeleteString DeleteStringW#define GetStrings GetStringsW#else#define AddString AddStringA#define DeleteString DeleteStringA#define GetStrings GetStringsA#endif
STRLIB.c
/* STRLIB.c -- Library module for STRPROG program (c) Charles Petzold, 1998*/#define _CRT_SECURE_NO_WARNINGS#include <windows.h>#include <wchar.h> // for wide-character string functions#include "strlib.h"// shared memory section (requires /SECTION:shared, RWS in link options)#pragma data_seg ("shared")int iTotal = 0;WCHAR szStrings[MAX_STRINGS][MAX_LENGTH + 1] = { '\0' };#pragma data_seg ()#pragma comment(linker, "/SECTION:shared,RWS")int WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved){ return TRUE;}EXPORT BOOL CALLBACK AddStringA(PCSTR pStringIn){ BOOL bReturn; int iBufSize; PWSTR pWideStr; // convert string to UNICODE and call AddStringW iBufSize = MultiByteToWideChar(CP_ACP, 0, pStringIn, -1, NULL, 0); pWideStr = malloc(iBufSize); MultiByteToWideChar(CP_ACP, 0, pStringIn, -1, pWideStr, iBufSize); bReturn = AddStringW(pWideStr); free(pWideStr); return bReturn;}EXPORT BOOL CALLBACK AddStringW(PCWSTR pStringIn){ PWSTR pString; int i, iLength; if (iTotal == MAX_STRINGS - 1) return FALSE; if ((iLength = wcslen(pStringIn) == 0)) return FALSE; // allocate memory for storing string, copy it , convert to uppercase pString = malloc(sizeof(WCHAR) * (1 + iLength)); wcscpy(pString, pStringIn); _wcsupr(pString); // alphabetize the strings for (i = iTotal; i > 0; i--) { if (wcscmp(pString, szStrings[i - 1] )>= 0) //insert sort algorithm break; wcscpy(szStrings[i], szStrings[i - 1]); } wcscpy(szStrings[i], pString); iTotal++; free(pString); return TRUE;}EXPORT BOOL CALLBACK DeleteStringA(PCSTR pStringIn){ BOOL bReturn; int iBufSize; PWSTR pWideStr; // convert string to UNICODE and call DeleteStringW iBufSize = MultiByteToWideChar(CP_ACP, 0, pStringIn, -1, NULL, 0); pWideStr = malloc(iBufSize); MultiByteToWideChar(CP_ACP, 0, pStringIn, -1, pWideStr, iBufSize); 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++) { if (_wcsicmp(szStrings[i], pStringIn) == 0) break; } // If given string not in list, return without taking action if (i == iTotal) return FALSE; // else adjust list downward for (j = i; j < iTotal; j++) wcscpy(szStrings[j], szStrings[j + 1]); szStrings[iTotal--][0] = '\0'; return TRUE;}EXPORT int CALLBACK GetStringsA(GETSTRCB pfnGetStrCallBack, PVOID pParam){ BOOL bReturn; int i, iLength; PSTR pAnsiStr; for (i = 0; i < iTotal; i++) { // convert string from 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); // call callback function bReturn = pfnGetStrCallBack(pAnsiStr, pParam); if (bReturn == FALSE) return i + 1; free(pAnsiStr); } return iTotal;}EXPORT int CALLBACK GetStringsW(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 iTotal;}
STRPROG.c
/* STRPROG.c - Program using STRLIB dynamic-link library (c) Charles Petzold, 1998*/#include <windows.h>#include "strlib.h"#include "resource.h"typedef struct{ HDC hdc; int xText; int yText; int xStart; int yStart; int xIncr; int yIncr; int xMax; int yMax;}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; //The window Class wndClass.style = CS_HREDRAW | CS_VREDRAW; wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class. 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; //Register the Window Class to the Windows System. if (!RegisterClass(&wndClass)) { MessageBox(NULL, TEXT("This program require Windows NT!"), szAppName, MB_ICONERROR); return 0; } //This function will generate an WM_CREATE message. 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); //This function will generate a WM_PAINT message. /* The message loop for this program. if received the WM_QUIT message, the function will return 0.*/ while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam;}BOOL CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam){ switch (message) { case WM_INITDIALOG: SendDlgItemMessage(hDlg, IDC_STRING, EM_LIMITTEXT, MAX_LENGTH, 0); return TRUE; case WM_COMMAND: switch (wParam) { case IDOK: GetDlgItemText(hDlg, IDC_STRING, szString, MAX_LENGTH); EndDialog(hDlg, TRUE); return TRUE; case IDCANCEL: EndDialog(hDlg, FALSE); return TRUE; } } return FALSE;}BOOL CALLBACK GetStrCallBack(PTSTR pString, CBPARAM * pcbp){ TextOut(pcbp->hdc, pcbp->xText, pcbp->yText, pString, lstrlen(pString)); if ((pcbp->yText += pcbp->yIncr) > pcbp->yMax) { pcbp->yText = pcbp->yStart; if ((pcbp->xText += pcbp->xIncr) > pcbp->xMax) return FALSE; } return TRUE;}//define the Window Procedure WndProc LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){ static HINSTANCE hInst; static int cxChar, cyChar, cxClient, cyClient; static UINT iDataChangeMsg; CBPARAM cbparam; HDC hdc; PAINTSTRUCT ps; TEXTMETRIC tm; switch (message) //get the message { case WM_CREATE: hInst = ((LPCREATESTRUCT)lParam)->hInstance; hdc = GetDC(hwnd); GetTextMetrics(hdc, &tm); cxChar = (int)tm.tmAveCharWidth; cyChar = (int)(tm.tmHeight + tm.tmExternalLeading); ReleaseDC(hwnd, hdc); //Register message for notifying instances of data changes iDataChangeMsg = RegisterWindowMessage(TEXT("StrProgDataChange")); return 0; case WM_COMMAND: switch (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; } return 0; case WM_SIZE: cxClient = (int)LOWORD(lParam); cyClient = (int)HIWORD(lParam); return 0; 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; 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 Developer Studio generated include file.// Used by StrProg.rc//#define IDC_STRING 1000#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 104#define _APS_NEXT_COMMAND_VALUE 40003#define _APS_NEXT_CONTROL_VALUE 1001#define _APS_NEXT_SYMED_VALUE 101#endif#endif
strprog.rc
//Microsoft Developer Studio generated resource script.//#include "resource.h"#define APSTUDIO_READONLY_SYMBOLS///// Generated from the TEXTINCLUDE 2 resource.//#include "afxres.h"/#undef APSTUDIO_READONLY_SYMBOLS/// English (U.S.) resources#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)#ifdef _WIN32LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US#pragma code_page(1252)#endif //_WIN32#ifdef APSTUDIO_INVOKED///// TEXTINCLUDE//1 TEXTINCLUDE DISCARDABLE BEGIN "resource.h\0"END2 TEXTINCLUDE DISCARDABLE BEGIN "#include ""afxres.h""\r\n" "\0"END3 TEXTINCLUDE DISCARDABLE BEGIN "\r\n" "\0"END#endif // APSTUDIO_INVOKED///// Dialog//ENTERDLG DIALOG DISCARDABLE 20, 20, 186, 47STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENUCAPTION "Enter"FONT 8, "MS Sans Serif"BEGIN LTEXT "&Enter:",IDC_STATIC,7,7,26,9 EDITTEXT IDC_STRING,31,7,148,12,ES_AUTOHSCROLL DEFPUSHBUTTON "OK",IDOK,32,26,50,14 PUSHBUTTON "Cancel",IDCANCEL,104,26,50,14ENDDELETEDLG DIALOG DISCARDABLE 20, 20, 186, 47STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENUCAPTION "Delete"FONT 8, "MS Sans Serif"BEGIN LTEXT "&Delete:",IDC_STATIC,7,7,26,9 EDITTEXT IDC_STRING,31,7,148,12,ES_AUTOHSCROLL DEFPUSHBUTTON "OK",IDOK,32,26,50,14 PUSHBUTTON "Cancel",IDCANCEL,104,26,50,14END///// Menu//STRPROG MENU DISCARDABLE BEGIN MENUITEM "&Enter!", IDM_ENTER MENUITEM "&Delete!", IDM_DELETEEND///// DESIGNINFO//#ifdef APSTUDIO_INVOKEDGUIDELINES DESIGNINFO DISCARDABLE BEGIN "ENTERDLG", DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 179 TOPMARGIN, 7 BOTTOMMARGIN, 40 END "DELETEDLG", DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 179 TOPMARGIN, 7 BOTTOMMARGIN, 40 ENDEND#endif // APSTUDIO_INVOKED#endif // English (U.S.) resources/#ifndef APSTUDIO_INVOKED///// Generated from the TEXTINCLUDE 3 resource.///#endif // not APSTUDIO_INVOKED
运行结果如下
开了两个窗口分别加入string, 两个窗口的string分别都可以显示出来。而且都可以对其进行增删操作。如果所有调用此dll的进程都退出,则共享内存的空间会被释放
注:经过测试发现如果将两个dll和exe分别放置在不同的目录下,发现两个strprog之间不能通过dll共享内存了。怀疑是windows操作系统加载了两份dll?
难道windows操作系统是根据目录文件名的方式来判断dll是否已经加载了?后续深入了解崽揭晓。
这里在调试的时候遇到了窗口无法显示的问题CreateWindow会调用默认主窗口函数并发送几条消息,必须返回TRUE才能正常创建窗口。
若使用自定义消息的时候要特别注意返回值的问题。默认交给DefWindowProc来返回即可
windows在win32进程的地址空间构造了一堵墙。通常情况下,一个地址空间的数据是私有的,其他进程看不见。STRLIB表明该程序允许在其实例之间共享其数据。
使用共享内存需要用到VS的编译器的扩展命令和link参数
// shared memory section (requires /SECTION:shared, RWS in link options)#pragma data_seg ("shared")int iTotal = 0;WCHAR szStrings[MAX_STRINGS][MAX_LENGTH + 1] = { '\0' };#pragma data_seg ()#pragma comment(linker, "/SECTION:shared,RWS")
最重要的是要将所有数据初始化,否则会编译器会将其放在未初始化的区中,而不是shared区
/SECTION:shared,RWS 表明该区具有可读,可写和可共享的属性。 该区的数据运行被所有STRLIB的实力所共享。
采用共享是在应用程序中共享数据的最方便的方式。如果要动态分配共享内存的空间,则应该考虑使用文件映像对象。参考 MSDN的 File Mapping
21.2 关于动态链接库的其他话题
动态库本身不接收消息。但是一个库模块可以调用GetMessage和PeekMessage。库模块调用这些函数从消息队列拿到的消息,其实是调用该库函数的程序的消息。
动态库可以从库文件或者调用他的应用程序文件中加载资源。需要自己的句柄或者应用程序的实例句柄。
在动态库中创建窗口CreateWindow需要一个实例句柄,因为窗口消息仍然是通过应用程序发送的,最好使用应用程序的实例句柄。
由于模态对话框的消息来自程序外的消息循环,因此你可以在动态库中调用DialogBox创建模态对话框。相应的实力句柄可以使用动态库自身的句柄。dialogBox的hwndParent设定为NULL
21.2.1 没有导入函数的动态链接库
GDI函数可以直接调用是因为在程序运行时已经执行了动态链接 GDI32.lib
也可以自己控制何时加载动态库。
typedef BOOL (WINAPI * PFNRECT)(HDC, int, int, int, int);
HANDLE hLibrary
PFNRECT pfnRectangle
hLibrary = LoadLIbrary(TEXT("GDI32.DLL));
pfnRectangle = (PFNRECT) GetProcAddress(hLibrary, TEXT("Rectangle"));
然后就可以调用pfnRectangle了
使用完毕以后调用
FreeLibrary(hLibrary);
Windows使用引用计数机制,调用LoadLibrary会增加1 , FreeLibrary 会减1. 如果某动态库的引用计数器为0.windows会把该库从内存中卸载。
21.2.2 资源库
提供资源给其他程序引用。
ShotBit例子
如果没有导出函数的dll库不会创建导入库 lib 。因此不应该让应用程序依赖于dll 否则链接时候会需要导入库。
BITLIB.c
/* 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
//Microsoft Developer Studio generated resource script.//#include "resource.h"#define APSTUDIO_READONLY_SYMBOLS///// Generated from the TEXTINCLUDE 2 resource.//#include "afxres.h"/#undef APSTUDIO_READONLY_SYMBOLS/// English (U.S.) resources#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)#ifdef _WIN32LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US#pragma code_page(1252)#endif //_WIN32#ifdef APSTUDIO_INVOKED///// TEXTINCLUDE//1 TEXTINCLUDE DISCARDABLE BEGIN "resource.h\0"END2 TEXTINCLUDE DISCARDABLE BEGIN "#include ""afxres.h""\r\n" "\0"END3 TEXTINCLUDE DISCARDABLE BEGIN "\r\n" "\0"END#endif // APSTUDIO_INVOKED///// 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"#endif // English (U.S.) resources/#ifndef APSTUDIO_INVOKED///// Generated from the TEXTINCLUDE 3 resource.///#endif // not APSTUDIO_INVOKED
SHOWBIT.c
/* 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; //The window Class wndClass.style = CS_HREDRAW | CS_VREDRAW; wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class. 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; //Register the Window Class to the Windows System. if (!RegisterClass(&wndClass)) { MessageBox(NULL, TEXT("This program require Windows NT!"), szAppName, MB_ICONERROR); return 0; } //This function will generate an WM_CREATE message. 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); //This function will generate a WM_PAINT message. /* The message loop for this program. if received the WM_QUIT message, the function will return 0.*/ 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; POINT pt; 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);}//define the Window Procedure WndProc 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) //get the message { case WM_CREATE: if ((hLibrary = LoadLibrary(TEXT("BITLIB.DLL"))) == NULL) { MessageBox(hwnd, TEXT("Cant't load BITLIB.DLL"), szAppName, 0); return -1; } 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消息中加载库,若失败则程序直接退出。
在处理WM_DESTROY消息中调用FreeLibrary释放动态库的实力句柄
发表评论
最新留言
关于作者
