《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释放动态库的实力句柄
- 《Windows程序设计》读书笔二十一 动态链接库
- 《Windows程序设计》读书笔十一 对话框
- 《Windows程序设计》读书笔二 Unicode简介
- Windows程序设计-动态链接库
- windows程序设计之动态链接库
- windows程序设计学习笔记--动态链接库
- 《Windows程序设计》读书笔二十三 尝试互联网
- Windows 32位汇编语言程序设计教程 之动态链接库
- Windows程序设计__孙鑫C++Lesson19《动态链接库》
- Windows程序设计学习——动态链接库
- 《Windows程序设计》读书笔四 文本输出
- 《Windows程序设计》读书笔五 绘图基础
- 《Windows程序设计》读书笔六 键盘
- 《Windows程序设计》读书笔七 鼠标
- 《Windows程序设计》读书笔八 计时器
- 《Windows程序设计》读书笔十二 剪贴板
- 孙鑫-MFC笔记十一--动态链接库
- windows动态链接库
- Android 异步加载图片,使用LruCache和SD卡或手机缓存,效果非常的流畅
- 【OPNET小问题】统计量显示收发数据数量不匹配以及如何导出统计量数据
- Android Service与Activity之间通信的几种方式
- 8月19号CCPC——The Designer(未解答)
- spring中@param和mybatis中@param使用区别
- 《Windows程序设计》读书笔二十一 动态链接库
- Android开源库集锦
- 问题2
- SQL Server 配置管理器无法启动
- java基础学习总结——对象转型
- Select2下拉框总结
- python类和对象(7)
- Android 应用程序之间内容分享详解(二)
- Windows系统kernel32.dll文件出问题的原因及其解决方案