DLL动态链接库如何共享数据和如何创建纯资源DLL
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了DLL动态链接库如何共享数据和如何创建纯资源DLL相关的知识,希望对你有一定的参考价值。
参考技术A 一、DLL动态链接库如何共享数据上次有人问起一个关于DLL编写的问题:他创建了一个DLL动态链接库,里面写了一个鼠标钩子,用于监视鼠标左键的点击;另外创建了一个普通MFC应用程序,里面可以通过点击按钮加载这个DLL库并安装钩子。钩子的效果是:当用户在屏幕的任何地方点击鼠标左键,都会导致MFC应用程序里面的计数器的加一。可是,程序编写完后,问题出现了:当他在MFC应用程序窗口内单击鼠标左键,计数器能够成功自增;可是一旦在窗口之外的地方左击,则不能自增,即使他把那个MFC应用程序设置为了“顶级窗口”——即无论是否有焦点,始终在桌面最前端。
我开始也很不解,怀疑他是否正确创建了钩子。钩子(hook)有两种,即进程相关钩子,和全局钩子。进程相关钩子只对指定的进程起作用,例如:
SetWindowsHookEx(WH_GETMESSAGE,HookProc, hMod, dwThreadId);
而全局钩子则对所有进程起作用,例如:
SetWindowsHookEx(WH_GETMESSAGE,HookProc, hMod, NULL);
仔细查看他的代码后,发现钩子编写是正确的,将这种可能排除。
最后终于发现——这主要得益于网友Hc1的提醒,在这里说一声谢谢!——原来是在DLL工程中,有一个和计数器密切相关的变量i没有共享,也就是说,对于不同的进程,他所看到的i是不同的。我们知道,动态链接库最大的好处是不同的进程可以共享,节省内存。但是,这种共享是有条件的,也就是说,被允许共享的代码和变量才能真正被共享,否则默认的是不共享。所以在这里我们需要将i设置为共享:
#pragma data_seg(“.MyData”)
int i=0;
#pragma data_seg()
注意,被共享的变量一定要初始化,否则,编译器仍然会把它作为非共享变量。
最后,在DEF文件中添加如下语句:
SECTIONS
.MyData READ WRITE SHARED
“.MyData”表示这段共享数据的名字,可以任意,甚至是中文。
编译结束后,我们可以通过命令行运行dumpbin命令查看dll的导出情况,可以发现确实导出了数据段“.MyData”。
二、如何创建纯资源DLL
有时候,我们需要创建纯资源的DLL文件,用来储存一些资源,例如我们可以通过创建纯资源DLL来解决软件的国际化问题(DLL里面包含多国语言资源,就可以实现安装时选择用户所使用的语言这一个性化功能)。编写纯资源DLL要比编写一般的DLL简单的多,例如不需要编写DEF导出文件等。
首先新建一个Win32 DLL工程。然后用“新建”命令创建一个“Resource Script”文件,在出现的RS编辑窗口中,单击鼠标右键,就可以添加各种资源,并可以对资源进行编辑。接着,在通过“新建”创建一个Cpp文件。虽然,后面我们会看到,我们可以不对这个空白的Cpp源文件进行任何操作,但是我们仍然需要新建一个Cpp,原因我不太清除,是实验测试的时候发现的。最后,单击“Project”,再“Settings”,再“Link”,在“Project Options”编辑框里面,添加“/noentry”,以避免编译器自动为我们的资源DLL文件添加默认的_main或者DllMain入口函数。事实上,经过测试,不设置这个“noentry”,工程照样可以成功编译,也可以在其他工程中使用正常,但是大小却要增加很多,对于纯资源的DLL文件来说,这些增加的体积毫无必要,所以我们还是需要手动添加这么一个命令。
如何在在普通MFC应用程序工程中使用我们写好的纯资源DLL呢?首先,需要定义资源宏。例如,在纯资源的DLL里面有一张名为:IDB_BITMAP1的位图,那么一般VC编译器默认的资源宏定义是从100开始的,所以在纯资源DLL里面有这样的宏:
#define IDB_BITMAP1 101
这句宏定义是在Resource.h头文件当中的。
那么我们也要在MFC应用程序中定义同样的宏,否则无法加载这个位图。
然后,我们需要加载DLL文件(在这里,我们认为开始时创建的动态链接库为A.dll):
HINSTANCE hDll=LoadLibrary("A.dll");
if(hDll==NULL)
MessageBox(“Load Error!”);
return;
加载并测试是否成功,否则返回。
然后,我们就可以像在正常MFC应用程序里面添加资源一样加载DLL里面的资源了:
HBITMAP hBit=(HBITMAP)::LoadImage(hDll,
MAKEINTRESOURCE(IDB_BITMAP1),
IMAGE_BITMAP,
0, 0,
LR_DEFAULTCOLOR);
if(hBit==NULL)
MessageBox("hBit is NULL");
return;
CBitmap bitmap;
bitmap.Attach(hBit);
CClientDC dc(this);
CDC dcCom;
dcCom.CreateCompatibleDC(&dc);
dcCom.SelectObject(&bitmap);
dc.BitBlt(0, 0, 500, 500, &dcCom, 0, 0, SRCCOPY);
ReleaseDC(&dcCom);
当然,最后在使用完DLL之后,不要忘了卸载DLL:
FreeLibrary(hDll);
这样就完成了。本回答被提问者和网友采纳
动态数据库的啥是动态链接库?
DLL三个字母对于你来说一定很熟悉吧,它是Dynamic Link Library 的缩写形式,函数的可执行代码位于一个 DLL 中,该 DLL 包含一个或多个已被编译、链接并与使用它们的进程分开存储的函数。DLL 还有助于共享数据和资源。多个应用程序可同时访问内存中单个 DLL 副本的内容。
动态链接库是一个能够被应用程序和其它的DLL调用的过程和函数的集合体,它里面包含的是公共代码或资源。由于DLL代码使用了内存共享技术,在某些地方windows也给了DLL一些更高的权限,因而DLL中可以实现一些一般程序所不能实现的功能,如实现windows的HOOK、ISAPI等。
同时,DLL还为不同语言间代码共享提供了一条方便的途径。因而DLL在编程时应用较为广泛,本文将介绍如何在 Delphi 中建立和使用DLL。
从使用效果看,DLL和unit 很像,它们都可以被别的工程模块所调用,但二者在内部的实现机制上确存在着差别。如果一个程序模块中用uses语句引用了某个unit,编译程序在编译该模块时,便会连同unit一起编译,并把编译后的可执行代码链接到本程序模块中,这就是一个程序模块能够调用所引用unit中过程和函数的原因。
当同一个unit被多个工程所引用时,则每个工程中都含有该unit的可执行代码,当含有该unit的多个工程同时执行时,unit的可执行代码会随不同工程而多次被调入内存,造成内存资源的浪费。DLL则不同,它即使被某个工程调用,编译后仍是独立的。
也就是说编译后,一个DLL库形成一个单独的可执行文件,而不与任何其它的可执行文件连接在一起,因而DLL库并不从属于某个特定的工程,当多个工程调用同一个DLL库时只有第一个工程把DLL库调入内存,其余工程并不重复调入同一个DLL库到内存,而是到同一个共享内存区读取。并且,DLL的执行代码是在程序运行期间动态调入的,而不是如unit在程序运行时就与整个工程一起调入内存。这样便可消除unit带来的相同代码多处占用内存的弊病。Delphi中DLL库的建立
在Delphi环境中,编写一个DLL同编写一个一般的应用程序并没有太大的区别。事实上作为DLL主体的DLL函数的编写,除了在内存、资源的管理上有所不同外,并不需要其它特别的手段。
一般工程文件的格式为:
program 工程标题;
uses 子句;
程序体
而DLLs工程文件的格式为:
library 工程标题;
uses 子句;
exprots 子句; 1.一般工程文件的头标用program关键字,而DLL工程文件头标用library 关键字。不同的关键字通知编译器生成不同的可执行文件。用program关键字生成的是.exe文件,而用library关键字生成的是.dll文件;
2.假如DLL要输出供其它应用程序使用的函数或过程,则必须将这些函数或过程列在exports子句中。而这些函数或过程本身必须用export编译指令进行编译。 在Delphi主菜单file 中选new...项,在弹出的窗口中双击DLL图标,便会自动给出DLL源模块框架,如下:
Library project1;
...注释...
uses
SysUtils, Classes;
begin
end.
接下来便可在USES和begin之间加入想在该DLL中实现的过程和函数的定义,并用export和exprots保字把它们引出,以便别的模块引用,在begin和end之间加入初始化代码,初始化代码是用来对DLL变量初始化的。应注意,即便无初始化代码begin与end也不可省略,如下例:
library minmax;
function Min(X, Y: Integer): Integer; export;
begin
if X < Y then Min := X else Min := Y;
end;
function Max(X, Y: Integer): Integer; export;
begin
if X > Y then Max := X else Max := Y;
end;
exports
Min index 1,
Max index 2;
begin
end.
经编译后,并以minmax.DLL存盘后,一个DLL库文件便形成了。 访问DLL库有两种方式,一种是静态引用,另一种是动态引用。
用静态引用这种方法装入DLL要做两件事情:为DLL 库创建一个输入单元,以及用USES把输入单元连接到要使用DLL 函数的程序模块中。为DLL库创建的输入单元与普通的单元的区别仅在于:在它的接口处声明的过程、函数,并不在它的实现部分给出真正的实现代码,而是用external关键字把过程、函数的实现细节委托给外部DLL模块。
external命令的使用语法如下:
procedure /function 过程/函数名;external DLL模块名;
下面给出为上面创建的minmax.DLL库写的输入单元源文件testdll .pas,从中可看出输入单元与一般单元的一些差别,代码如下所示:
unit testdll;
interface
uses
function Min (X, Y: Integer): Integer;
function Max (X, Y: Integer): Integer;
implementation
function Min; external ‘minmax.DLL’;
function Max; external ‘minmax.DLL’;
end.
一个应用程序若想调用minmax.DLL中的函数,只须在其uses语句中加入testdll 单元即可。
动态装入DLL,要用到Windows的三个API函数。Loadlibrary、Freelibrary和GetprocAddress 。 loadlibrary函数用来装入DLL库,其调用格式如下:
function loadlobrary (DLLfileName:Pchar): THandle:
当不再需要一个DLL库时,应调用FreeLibrary函数将其释放,以空出宝贵的内存资源,其调用格式如下:
procedure FreeLibrary (Libmodule:THandle)
Libmodule 为由LoadLibrary调用得到的DLL库句柄。在用loadlobrary 函数装入某个DLL库和调用FreeLibrary释放该DLL库之间的程序段中, 可以使用该DLL库中的过程和函数,具体使用方法是:用GetprocAddress函数把DLL库中函数的地址传递给程序中某个函数变量,再用该变量实现DLL函数的调用。GetprocAddress函数声名如下
function GetprocAddress (Libmodule:THandle:procname:pchar):TFarProc:
如下例所示:
type
TTimeRec = record
Second: Integer;
Minute: Integer;
Hour: Integer;
end;
TGetTime = procedure(var Time: TTimeRec);
THandle = Integer;
var
Time: TTimeRec;
Handle: THandle;
GetTime: TGetTime;
...
begin
Handle := LoadLibrary('DATETIME.DLL');
if Handle <> 0 then
begin
@GetTime := GetProcAddress(Handle, 'GetTime');
if @GetTime <> nil then
begin
GetTime(Time);
with Time do
WriteLn('The time is ', Hour, ':', Minute, ':', Second);
end;
FreeLibrary(Handle);
end;
end;
在调用动态链接库时应注意, 所需动态链接库须与应用程序在同一目录或Windows System 目录下。
动态链接库是 Windows下程序组织的一种重要方式,使用动态链接库可以极大地保护用户在不同开发工具、不同时期所做的工作,提高编程效率。
动态链接提供了一种方法,使进程可以调用不属于其可执行代码的函数。函数的可执行代码位于一个 DLL 文件中,该 DLL 包含一个或多个已被编译、链接并与使用它们的进程分开存储的函数。DLL 还有助于共享数据和资源。多个应用程序可同时访问内存中单个 DLL 副本的内容。
使用动态链接库可以更为容易地将更新应用于各个模块,而不会影响该程序的其他部分。例如,您有一个大型网络游戏,如果把整个数百MB甚至数GB的游戏的代码都放在一个应用程序里,日后的修改工作将会十分费时,而如果把不同功能的代码分别放在数个动态链接库中,您无需重新生成或安装整个程序就可以应用更新。 参考技术B 动态链接库(Dynamic Link Library,缩写为 DLL),是微软公司在微软Windows操作系统中,实现共享函数库概念的一种方式。这些库函数的扩展名是 ”.dll"、".ocx"(包含ActiveX控制的库)或者 ".drv"(旧式的系统驱动程序)。它并不是可执行的文件,而是能向多个程序提供公用代码函数和数据的封装库,程序所需要的一些函数和数据被事先封装在DLL文件中,当程序运行时可以主程序需要调用这些DLL动态链接库,使进程可以调用不属于其可执行代码的函数。函数的可执行代码位于一个 DLL 文件中,该 DLL 包含一个或多个已被编译、链接并与使用它们的进程分开存储的函数,同时有助于共享数据和资源,多个应用程序可同时访问内存中单个 DLL 副本的内容,使用动态链接库可以更为容易地将更新应用于各个模块,而不会影响该程序的其他部分。例如,您有一个大型网络游戏,如果把整个数百MB甚至数GB的游戏的代码都放在一个应用程序里,日后的修改工作将会十分费时,而如果把不同功能的代码分别放在数个动态链接库中,您无需重新生成或安装整个程序就可以应用更新。
以上是关于DLL动态链接库如何共享数据和如何创建纯资源DLL的主要内容,如果未能解决你的问题,请参考以下文章