📄 21. 动态链接库.txt
字号:
EndPaint (hwnd, &ps) ;
return 0 ;
case WM_DESTROY:
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
注意,为了定义EdrCenterText函数,EDRTEST.C包括EDRLIB.H表头文件,此函数将在WM_PAINT消息处理期间呼叫。
在编译此程序之前,您可能希望做以下几件事。首先,在「Project」菜单选择「Select Active Project」。这时您将看到「EDRLIB」和「EDRTEST」,选择「EDRTEST」。在重新编译此工作空间时,您真正要重新编译的是程序。另外,在「Project」菜单中,选择「Dependencies」,在「Select Project To Modify」清单方块中选择「EDRTEST」。在「Dependent On The Following Project(s)」列表选中「EDRLIB」。此操作的意思是:EDRTEST需要EDRLIB动态链接库。以后每次重新编译EDRTEST时,如果必要的话,都将在编译和连结EDRTEST之前重新重新编译EDRLIB。
从「Project」菜单选择「Settings」,单击「General」标签。当您在左边的窗格中选择「EDRLIB」或者「EDRTEST」项目时,如果设定为「Win32 Release」,则显示在右边窗格中的「Intermediate Files」和「Output Files」将位于RELEASE目录;如果设定为「Win32 Debug」,则位于DEBUG目录。如果不是,请按此修改。这样可确保EDRLIB.DLL与EDRTEST.EXE在同一个目录中,而且程序在使用DLL时也不会产生问题。
在「Project Setting」对话框中依然选中「EDRTEST」,单击「C/C++」页面标签。按本书的惯例,在「Preprocessor Definitions」中,将「UNICODE」添加到Debug设定。
现在您就可以在「Debug」或「Release」设定中重新编译EDRTEST.EXE了。必要时,Visual C++将首先编译和连结EDRLIB。RELEASE和DEBUG目录都包含EDRLIB.LIB(引用链接库)和EDRLIB.DLL。当Developer Studio连结EDRTEST时,将自动包含引用链接库。
了解EDRTEST.EXE文件中不包含EdrCenterText程序代码很重要。事实上,要证明执行了EDRLIB.DLL文件和EdrCenterText函数很简单:执行EDRTEST.EXE需要EDRLIB.DLL。
执行EDRTEST.EXE时,Windows按外部链接库模块执行固定的函数。其中许多函数都在一般Windows动态链接库中。但Windows也看到程序从EDRLIB呼叫了函数,因此Windows将EDRLIB.DLL文件加载到内存,然后呼叫EDRLIB的初始化例程。EDRTEST呼叫EdrCenterText函数是动态链接到EDRLIB中函数的。
在EDRTEST.C原始码文件中包含EDRLIB.H与包含WINDOWS.H类似。连结EDRLIB.LIB与连结Windows引用链接库(例如USER32.LIB)类似。当您的程序执行时,它连结EDLIB.DLL的方式与连结USER32.DLL的方式相同。恭喜您!您已经扩展了Windows的功能!
在继续之前,我还要对动态链接库多说明一些:
首先,虽然我们将DLL作为Windows的延伸,但它也是您的应用程序的延伸。DLL所完成的每件工作对于应用程序来说都是应用程序所交代要完成的。例如,应用程序拥有DLL配置的全部内存、DLL建立的全部窗口以及DLL打开的所有文件。多个应用程序可以同时使用同一个DLL,但在Windows下,这些应用程序不会相互影响。
多个程序能够共享一个动态链接库中相同的程序代码。但是,DLL为每个程序所储存的数据都不同。每个程序都为DLL所使用的全部数据配置了自己的地址空间。我们将在下以节看到,共享内存需要额外的工作。
在DLL中共享内存
令人兴奋的是,Windows能够将同时使用同一个动态链接库的应用程序分开。不过,有时却不太令人满意。您可能希望写一个DLL,其中包含能够被不同应用程序或者同一个程序的不同例程所共享的内存。这包括使用共享内存。共享内存实际上是一种内存映像文件。
让我们测试一下,这项工作是如何在程序STRPROG(「字符串程序(string program)」)和动态链接库STRLIB(「字符串链接库(string library)」)中完成的。STRLIB有三个输出函数被STRPROG呼叫,我们只对此感兴趣,STRLIB中的一个函数使用了在STRPROG定义的callback函数。
STRLIB是一个动态链接库模块,它储存并排序了最多256个字符串。在STRLIB中,这些字符串均为大写,并由共享内存维护。利用STRLIB的三个函数,STRPROG能够添加字符串、删除字符串以及从STRLIB获得目前的所有字符串。STRPROG测试程序有两个菜单项(「Enter」和「Delete」),这两个菜单项将启动不同的对话框来添加或删除字符串。STRPROG在其显示区域列出目前储存在STRLIB中的所有字符串。
下面这个函数在STRLIB定义,它将一个字符串添加到STRLIB的共享内存。
EXPORT BOOL CALLBACK AddString (pStringIn)
参数pStringIn是字符串的指针。字符串在AddString函数中变成大写。如果在STRLIB的列表中有一个相同的字符串,那么此函数将添加一个字符串的复本。如果成功,AddString传回「TRUE」(非0),否则传回「FALSE」(0)。如果字符串的长度为0,或者不能配置储存字符串的内存,或者已经储存了256个字符串,则传回值将都是FALSE。
STRLIB函数从STRLIB的共享内存中删除一个字符串。
EXPORT BOOL CALLBACK DeleteString (pStringIn)
另外,参数pStringIn是一个字符串指针。如果有多个相同内容字符串,则删除第一个。如果成功,那么DeleteString传回「TRUE」(非0),否则传回「FALSE」(0)。传回「FALSE」表示字符串长度为0,或者找不到相同内容的字符串。
STRLIB函数使用了呼叫程序中的一个callback函数,以便列出目前储存在STRLIB共享内存中的字符串:
EXPORT int CALLBACK GetStrings (pfnGetStrCallBack, pParam)
在呼叫程序中,callback函数必须像下面这样定义:
EXPORT BOOL CALLBACK GetStrCallBack (PSTR pString, PVOID pParam)
GetStrings的参数pfnGetStrCallBack指向callback函数。直到callback函数传回「FALSE」(0),GetStrings将为每个字符串都呼叫一次GetStrCallBack。GetStrings传回传递给callback函数的字符串数。pParam参数是一个远程指针,指向程序写作者定义的数据。
当然,此程序可以编译成Unicode程序,或者在STRLIB的支持下,编译成Unicode和非Unicode应用程序。与EDRLIB一样,所有的函数都有「A」和「W」两种版本。在内部,STRLIB以Unicode储存所有的字符串。如果非Unicode程序使用了STRLIB(也就是说,程序将呼叫AddStringA、DeleteStringA和GetStringsA),字符串将在Unicode和非Unicode之间转换。
与STRPROG和STRLIB项目相关的工作空间名为STRPROG。此文件按EDRTEST工作空间的方式组合。程序21-3显示了建立STRLIB.DLL动态链接库所必须的两个文件。
程序21-3 STRLI
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 strings
typedef BOOL (CALLBACK * GETSTRCB) (PCTSTR, PVOID) ;
// Each function has ANSI and Unicode versions
EXPORT 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
----------------------------------------------------------------------------*/
#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 iLength ;
PWSTR pWideStr ;
// Convert string to Unicode and call AddStringW
iLength = MultiByteToWideChar (CP_ACP, 0, pStringIn, -1, NULL, 0) ;
pWideStr = malloc (iLength) ;
MultiByteToWideChar (CP_ACP, 0, pStringIn, -1, pWideStr, 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 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)
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 iLength ;
PWSTR pWideStr ;
// Convert string to Unicode and call DeleteStringW
iLength = MultiByteToWideChar (CP_ACP, 0, pStringIn, -1, NULL, 0) ;
pWideStr = malloc (iLength) ;
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++)
{
if (_wcsicmp (szStrings[i], pStringIn) == 0)
break ;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -