《Windows程序设计》读书笔二十一 动态链接库

来源:互联网 发布:c语言主函数作用 编辑:程序博客网 时间:2024/05/20 17:59

第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){intiLength;SIZEsize;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){intiLength;SIZEsize;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");HWNDhwnd;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){HDChdc;PAINTSTRUCT ps;RECTrect;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_LENGTH63// 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 AddStringAddStringW#define DeleteStringDeleteStringW#define GetStringsGetStringsW#else#define AddStringAddStringA#define DeleteStringDeleteStringA#define GetStringsGetStringsA#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")intiTotal = 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){BOOLbReturn;intiBufSize;PWSTRpWideStr;// convert string to UNICODE and call AddStringWiBufSize = 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){PWSTRpString;inti, iLength;if (iTotal == MAX_STRINGS - 1)return FALSE;if ((iLength = wcslen(pStringIn) == 0))return FALSE;// allocate memory for storing string, copy it , convert to uppercasepString = malloc(sizeof(WCHAR) * (1 + iLength));wcscpy(pString, pStringIn);_wcsupr(pString);// alphabetize the stringsfor (i = iTotal; i > 0; i--){if (wcscmp(pString, szStrings[i - 1] )>= 0)//insert sort algorithmbreak;wcscpy(szStrings[i], szStrings[i - 1]);}wcscpy(szStrings[i], pString);iTotal++;free(pString);return TRUE;}EXPORT BOOL CALLBACK DeleteStringA(PCSTR pStringIn){BOOLbReturn;intiBufSize;PWSTRpWideStr;// convert string to UNICODE and call DeleteStringWiBufSize = 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 actionif (i == iTotal)return FALSE;// else adjust list downwardfor (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){BOOLbReturn;inti, iLength;PSTRpAnsiStr;for (i = 0; i < iTotal; i++){// convert string from UNICODEiLength = 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 functionbReturn = pfnGetStrCallBack(pAnsiStr, pParam);if (bReturn == FALSE)return i + 1;free(pAnsiStr);}return iTotal;}EXPORT int CALLBACK GetStringsW(GETSTRCB pfnGetStrCallBack, PVOID pParam){BOOLbReturn;inti;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{HDChdc;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){HWNDhwnd;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 HINSTANCEhInst;static intcxChar, cyChar, cxClient, cyClient;static UINTiDataChangeMsg;CBPARAMcbparam;HDChdc;PAINTSTRUCTps;TEXTMETRICtm;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 changesiDataChangeMsg = 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);elseMessageBeep(0);}break;case IDM_DELETE:if (DialogBox(hInst, TEXT("DeleteDlg"), hwnd, &DlgProc)){if (DeleteString(szString))PostMessage(HWND_BROADCAST, iDataChangeMsg, 0, 0);elseMessageBeep(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")intiTotal = 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){HWNDhwnd;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){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);}//define the Window Procedure WndProc    LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){static HINSTANCEhLibrary;static intiCurrent = 1;HBITMAPhBitmap;HDChdc;PAINTSTRUCTps;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释放动态库的实力句柄

阅读全文
0 0
原创粉丝点击