关于dll动态加载和静态加载的困惑,麻烦各位高手给我解下惑以下说明,估计大家都熟悉:>>动态链接库简介
>>动态链接库(DLL,即 “Dynamic-Link Library”)是一个能够被应用程序和其它的DLL调用的过程和函数的集合体,它里面包含的是公共代码或资源。DLL是Windows的基石,所有的Win32 API函数都包含在DLL中。 >>使用DLL有许多优点: >>1、一个DLL可以提供给不同的程序使用,如果有多个程序使用相同的DLL,也只需将DLL在内存中装载一次,这样就节省了内存开销。 >>......最近写代码发现个问题,这个优点1,现在看来是有点问题。照理说,一个dll,无论通过什么方式加载(静态、动态),在内存中,都应该只加载一次,卸载时,如果有一个加载的程序没有卸载dll,这个dll都应该仍然在内存中,直到所有使用者卸载了dll,但是我写了段代码,发现事实却不是这样。代码很简单,主程序exe,加载了动态库:dll3、dll4,这两个dll都加载了dll2,并在窗体中调用dll2的一个函数显示一个公用窗体,为简单,dll3、dll4就直接用静态连接的方式加载了dll2,结果发现,当dll3、dll4的窗体都打开了dll2中的公用窗体,然后卸载dll3或dll4中的一个(卸载时会调用dll2中的释放窗体函数去释放窗体),主程序马上崩溃,另外一个现象是,dll3、dll4两个窗体打开,然后dll4个调用dll2中函数打开公用窗体,然后卸载dll4,这时在dll3的窗体中,调用dll2的函数去打开公用窗体,会报地址错误。手头没有其他工具,就用360->高级->系统进程状态功能,查看这个exe加载dll的情况,一看,发现了奇怪现象,加载dll3(其中静态加载dll2),结果360显示,dll2被加载了两次,然后再加载dll4(其中静态加载dll2),dll2没有再次被加载,卸载dll4,发现,exe进程中的两个dll2都被卸载了,也就是说,虽然dll3仍然在内存中,但dll3静态加载的dll2,却已经被从内存中卸载了,所以才有了上面两个现象:1、此时在dll3中调用dll2的函数,报地址错误,当然了,dll都卸载了;2、如果卸载dll4之前,dll3已经调用dll2中的函数打开了窗体,那么在卸载dll4后,主程序崩溃。解决方法很简单,就是用在dll3、dll4中LoadLibrary动态加载dll2,这时用360工具去看进程加载dll的情况,就完全和前面说的一样,dll3、dll4加载了dll2,内存中只看到dll2加载了一次,卸载dll3、dll4中的一个,dll2仍然在内存中,直到所有加载dll2的dll全部从内存卸载,dll2才从内存中卸载。这个现象怎么解释?静态加载dll为什么会有这样的问题?麻烦各位高手给我解下惑。我用的是delphi6。 以下为代码:Project1.dll代码(其中Unit1就是一个form,没代码):
library Project1;uses
SysUtils, Unit1 in 'Unit1.pas' {Form1};{$R *.res}type
Tt = record
form: TForm1;
h: THandle;
end;
var
a:array of Tt;function ShowA(h: THandle):THandle; stdcall;
var
i: integer;
begin
i := length(a);
setlength(a, i + 1);
a[i].form := TForm1.Create(nil);
a[i].form.Caption := IntToStr(i);
a[i].form.Show ;
a[i].h := h;
Result := a[i].form.Handle ;
end;function FreeA():boolean; stdcall;
var
i: integer;
begin
for i := 0 to length(a) - 1 do
begin
a[i].form.Free ;
end;
setlength(a, 0);
Result := true;
end;exports
ShowA, FreeA;begin
end.
Project3.dll代码:
library Project3;uses
SysUtils,
windows,
Dialogs,
Classes;
type
TShowA = function (h: THandle):THandle;stdcall;
TFreeA = function:boolean;stdcall;{$R *.res}
function ShowA3(h: THandle):THandle;stdcall;external 'C:\Program Files\Borland\Delphi6\Projects\dlltest\Project1.dll' name 'ShowA';
function FreeA3():boolean;stdcall;external 'C:\Program Files\Borland\Delphi6\Projects\dlltest\Project1.dll' name 'FreeA';// 动态加载Project1.dll调用请启用以下代码
//var
// h3: THandle ;
// ShowA3: TShowA;
// FreeA3: TFreeA;function ShowA(h: THandle):THandle; stdcall;
begin
Result := ShowA3(h);
end;function FreeA():boolean; stdcall;
begin
Result := FreeA3;
end;procedure LoadA(); stdcall;
begin
// 动态加载Project1.dll调用请启用以下代码
// h3 := LoadLibrary('C:\Program Files\Borland\Delphi6\Projects\dlltest\Project1.dll');
// if h3 <> 0 then
// begin
// @ShowA3 := GetProcAddress(h3, 'ShowA');
// @FreeA3 := GetProcAddress(h3, 'FreeA');
// if (@ShowA3 = nil) or (@FreeA3 = nil) then
// begin
// FreeLibrary(h3);
// ShowMessage('err');
// end;
// end;
end;procedure UnLoadA(); stdcall;
begin
// 动态加载Project1.dll调用请启用以下代码
// FreeLibrary(h3);
end;
exports
ShowA, FreeA, LoadA, UnLoadA;begin
end.
Project4.dll代码:
library Project4;uses
SysUtils,
windows,
Dialogs,
Classes;type
TShowA = function (h: THandle):THandle;stdcall;
TFreeA = function:boolean;stdcall;{$R *.res}
//C:\Program Files\Borland\Delphi6\Projects\dlltest\
function ShowA4(h: THandle):THandle;stdcall;external 'Project1.dll' name 'ShowA';
function FreeA4():boolean;stdcall;external 'Project1.dll' name 'FreeA';// 动态加载Project1.dll调用请启用以下代码
//var
// h4: THandle ;
// ShowA4: TShowA;
// FreeA4: TFreeA;function ShowA(h: THandle):THandle; stdcall;
begin
Result := ShowA4(h);
end;function FreeA():boolean; stdcall;
begin
Result := FreeA4;
end;procedure LoadA(); stdcall;
begin
// 动态加载Project1.dll调用请启用以下代码
// h4 := LoadLibrary('C:\Program Files\Borland\Delphi6\Projects\dlltest\Project1.dll');
// if h4 <> 0 then
// begin
// @ShowA4 := GetProcAddress(h4, 'ShowA');
// @FreeA4 := GetProcAddress(h4, 'FreeA');
// if (@ShowA4 = nil) or (@FreeA4 = nil) then
// begin
// FreeLibrary(h4);
// ShowMessage('err');
// end;
// end;
end;procedure UnLoadA(); stdcall;
begin
// 动态加载Project1.dll调用请启用以下代码
// FreeLibrary(h4);
end;
exports
ShowA, FreeA, LoadA, UnLoadA;
begin
end.
Project2.exe主要代码:
type
TShowA = function (h: THandle):THandle;stdcall;
TFreeA = function:boolean;stdcall;
TLoadA = procedure;stdcall;
TUnLoadA = procedure;stdcall;var
Form2: TForm2; h3: THandle;
LoadA3: TLoadA;
UnLoadA3: TLoadA;
ShowA3: TShowA;
FreeA3: TFreeA; h4: THandle;
LoadA4: TLoadA;
UnLoadA4: TLoadA;
ShowA4: TShowA;
FreeA4: TFreeA; hh3: array of THandle = nil;
hh4: array of THandle = nil;
implementation////////////////////////////////////////////
// 加载dll3 //
////////////////////////////////////////////
procedure TForm2.btn1Click(Sender: TObject);
begin
h3 := LoadLibrary('C:\Program Files\Borland\Delphi6\Projects\dlltest\Project3.dll');
if h3 <> 0 then
begin
@ShowA3 := GetProcAddress(h3, 'ShowA');
@FreeA3 := GetProcAddress(h3, 'FreeA');
@LoadA3 := GetProcAddress(h3, 'LoadA');
@UnLoadA3 := GetProcAddress(h3, 'UnLoadA');
if (@ShowA3 = nil) or (@FreeA3 = nil) then
begin
FreeLibrary(h3);
ShowMessage('err');
end
else
LoadA3;
end;
end; ////////////////////////////////////////////
// 调用dll3中ShowA //
////////////////////////////////////////////
procedure TForm2.btn2Click(Sender: TObject);
var
l: integer;
begin
l := length(hh3);
setlength(hh3,l + 1);
hh3[l] := ShowA3(self.Handle);
end;////////////////////////////////////////////
// 调用dll3中FreeA //
////////////////////////////////////////////
procedure TForm2.btn3Click(Sender: TObject);
begin
if FreeA3 then ShowMessage('ok');
end;////////////////////////////////////////////
// 调用dll3中卸载dll1函数,动态加载时调用 //
////////////////////////////////////////////
procedure TForm2.btn9Click(Sender: TObject);
begin
UnLoadA3;
end;////////////////////////////////////////////
// 卸载dll3 //
////////////////////////////////////////////
procedure TForm2.btn7Click(Sender: TObject);
begin
FreeLibrary(h3);
end;////////////////////////////////////////////
// 加载dll4 //
////////////////////////////////////////////
procedure TForm2.btn4Click(Sender: TObject);
begin
h4 := LoadLibrary('C:\Program Files\Borland\Delphi6\Projects\dlltest\Project4.dll');
if h4 <> 0 then
begin
@ShowA4 := GetProcAddress(h4, 'ShowA');
@FreeA4 := GetProcAddress(h4, 'FreeA');
@LoadA4 := GetProcAddress(h4, 'LoadA');
@UnLoadA4 := GetProcAddress(h4, 'UnLoadA');
if (@ShowA4 = nil) or (@FreeA4 = nil) then
begin
FreeLibrary(h4);
ShowMessage('err');
end
else
LoadA4;
end;
end;////////////////////////////////////////////
// 调用dll4中ShowA //
////////////////////////////////////////////
procedure TForm2.btn5Click(Sender: TObject);
var
l: integer;
begin
l := length(hh4);
setlength(hh4,l + 1);
hh4[l] := ShowA4(self.Handle);
end;////////////////////////////////////////////
// 调用dll4中FreeA //
////////////////////////////////////////////
procedure TForm2.btn6Click(Sender: TObject);
begin
if FreeA4 then ShowMessage('ok');
end;////////////////////////////////////////////
// 调用dll4中卸载dll1函数,动态加载时调用 //
////////////////////////////////////////////
procedure TForm2.btn10Click(Sender: TObject);
begin
UnLoadA4;
end;////////////////////////////////////////////
// 卸载dll4 //
////////////////////////////////////////////
procedure TForm2.btn8Click(Sender: TObject);
begin
FreeLibrary(h4);
end;
编译出1个exe,3个dll,dll3、dll4加载dll1是静态加载的,运行exe:按这个顺序点击按钮就会出现地址错误:加载dll3、加载dll4、卸载dll4、dll3中ShowA。按这个顺序点击按钮,exe会崩溃:加载dll3、加载dll4、dll3中ShowA、dll4中ShowA、dll4中FreeA、卸载dll4,然后显示出“dll3中ShowA”按钮创建的窗体。问题的本质是:
dll3、dll4用静态加载dll1,dll1会被两次加载进内存,但是卸载时dll3、dll4其中一个时,却把两个dll1全部卸载了。
动态加载不存在这个问题。
静态加载如果完全不写路径,dll1在系统搜索路径,运行结果和动态加载一样,如果静态加载dll1使用相对路径(比如:dll\project1.dll),同样存在这个问题。代码已经在delphi5、delphi6下验证有这个问题。
>>动态链接库(DLL,即 “Dynamic-Link Library”)是一个能够被应用程序和其它的DLL调用的过程和函数的集合体,它里面包含的是公共代码或资源。DLL是Windows的基石,所有的Win32 API函数都包含在DLL中。 >>使用DLL有许多优点: >>1、一个DLL可以提供给不同的程序使用,如果有多个程序使用相同的DLL,也只需将DLL在内存中装载一次,这样就节省了内存开销。 >>......最近写代码发现个问题,这个优点1,现在看来是有点问题。照理说,一个dll,无论通过什么方式加载(静态、动态),在内存中,都应该只加载一次,卸载时,如果有一个加载的程序没有卸载dll,这个dll都应该仍然在内存中,直到所有使用者卸载了dll,但是我写了段代码,发现事实却不是这样。代码很简单,主程序exe,加载了动态库:dll3、dll4,这两个dll都加载了dll2,并在窗体中调用dll2的一个函数显示一个公用窗体,为简单,dll3、dll4就直接用静态连接的方式加载了dll2,结果发现,当dll3、dll4的窗体都打开了dll2中的公用窗体,然后卸载dll3或dll4中的一个(卸载时会调用dll2中的释放窗体函数去释放窗体),主程序马上崩溃,另外一个现象是,dll3、dll4两个窗体打开,然后dll4个调用dll2中函数打开公用窗体,然后卸载dll4,这时在dll3的窗体中,调用dll2的函数去打开公用窗体,会报地址错误。手头没有其他工具,就用360->高级->系统进程状态功能,查看这个exe加载dll的情况,一看,发现了奇怪现象,加载dll3(其中静态加载dll2),结果360显示,dll2被加载了两次,然后再加载dll4(其中静态加载dll2),dll2没有再次被加载,卸载dll4,发现,exe进程中的两个dll2都被卸载了,也就是说,虽然dll3仍然在内存中,但dll3静态加载的dll2,却已经被从内存中卸载了,所以才有了上面两个现象:1、此时在dll3中调用dll2的函数,报地址错误,当然了,dll都卸载了;2、如果卸载dll4之前,dll3已经调用dll2中的函数打开了窗体,那么在卸载dll4后,主程序崩溃。解决方法很简单,就是用在dll3、dll4中LoadLibrary动态加载dll2,这时用360工具去看进程加载dll的情况,就完全和前面说的一样,dll3、dll4加载了dll2,内存中只看到dll2加载了一次,卸载dll3、dll4中的一个,dll2仍然在内存中,直到所有加载dll2的dll全部从内存卸载,dll2才从内存中卸载。这个现象怎么解释?静态加载dll为什么会有这样的问题?麻烦各位高手给我解下惑。我用的是delphi6。 以下为代码:Project1.dll代码(其中Unit1就是一个form,没代码):
library Project1;uses
SysUtils, Unit1 in 'Unit1.pas' {Form1};{$R *.res}type
Tt = record
form: TForm1;
h: THandle;
end;
var
a:array of Tt;function ShowA(h: THandle):THandle; stdcall;
var
i: integer;
begin
i := length(a);
setlength(a, i + 1);
a[i].form := TForm1.Create(nil);
a[i].form.Caption := IntToStr(i);
a[i].form.Show ;
a[i].h := h;
Result := a[i].form.Handle ;
end;function FreeA():boolean; stdcall;
var
i: integer;
begin
for i := 0 to length(a) - 1 do
begin
a[i].form.Free ;
end;
setlength(a, 0);
Result := true;
end;exports
ShowA, FreeA;begin
end.
Project3.dll代码:
library Project3;uses
SysUtils,
windows,
Dialogs,
Classes;
type
TShowA = function (h: THandle):THandle;stdcall;
TFreeA = function:boolean;stdcall;{$R *.res}
function ShowA3(h: THandle):THandle;stdcall;external 'C:\Program Files\Borland\Delphi6\Projects\dlltest\Project1.dll' name 'ShowA';
function FreeA3():boolean;stdcall;external 'C:\Program Files\Borland\Delphi6\Projects\dlltest\Project1.dll' name 'FreeA';// 动态加载Project1.dll调用请启用以下代码
//var
// h3: THandle ;
// ShowA3: TShowA;
// FreeA3: TFreeA;function ShowA(h: THandle):THandle; stdcall;
begin
Result := ShowA3(h);
end;function FreeA():boolean; stdcall;
begin
Result := FreeA3;
end;procedure LoadA(); stdcall;
begin
// 动态加载Project1.dll调用请启用以下代码
// h3 := LoadLibrary('C:\Program Files\Borland\Delphi6\Projects\dlltest\Project1.dll');
// if h3 <> 0 then
// begin
// @ShowA3 := GetProcAddress(h3, 'ShowA');
// @FreeA3 := GetProcAddress(h3, 'FreeA');
// if (@ShowA3 = nil) or (@FreeA3 = nil) then
// begin
// FreeLibrary(h3);
// ShowMessage('err');
// end;
// end;
end;procedure UnLoadA(); stdcall;
begin
// 动态加载Project1.dll调用请启用以下代码
// FreeLibrary(h3);
end;
exports
ShowA, FreeA, LoadA, UnLoadA;begin
end.
Project4.dll代码:
library Project4;uses
SysUtils,
windows,
Dialogs,
Classes;type
TShowA = function (h: THandle):THandle;stdcall;
TFreeA = function:boolean;stdcall;{$R *.res}
//C:\Program Files\Borland\Delphi6\Projects\dlltest\
function ShowA4(h: THandle):THandle;stdcall;external 'Project1.dll' name 'ShowA';
function FreeA4():boolean;stdcall;external 'Project1.dll' name 'FreeA';// 动态加载Project1.dll调用请启用以下代码
//var
// h4: THandle ;
// ShowA4: TShowA;
// FreeA4: TFreeA;function ShowA(h: THandle):THandle; stdcall;
begin
Result := ShowA4(h);
end;function FreeA():boolean; stdcall;
begin
Result := FreeA4;
end;procedure LoadA(); stdcall;
begin
// 动态加载Project1.dll调用请启用以下代码
// h4 := LoadLibrary('C:\Program Files\Borland\Delphi6\Projects\dlltest\Project1.dll');
// if h4 <> 0 then
// begin
// @ShowA4 := GetProcAddress(h4, 'ShowA');
// @FreeA4 := GetProcAddress(h4, 'FreeA');
// if (@ShowA4 = nil) or (@FreeA4 = nil) then
// begin
// FreeLibrary(h4);
// ShowMessage('err');
// end;
// end;
end;procedure UnLoadA(); stdcall;
begin
// 动态加载Project1.dll调用请启用以下代码
// FreeLibrary(h4);
end;
exports
ShowA, FreeA, LoadA, UnLoadA;
begin
end.
Project2.exe主要代码:
type
TShowA = function (h: THandle):THandle;stdcall;
TFreeA = function:boolean;stdcall;
TLoadA = procedure;stdcall;
TUnLoadA = procedure;stdcall;var
Form2: TForm2; h3: THandle;
LoadA3: TLoadA;
UnLoadA3: TLoadA;
ShowA3: TShowA;
FreeA3: TFreeA; h4: THandle;
LoadA4: TLoadA;
UnLoadA4: TLoadA;
ShowA4: TShowA;
FreeA4: TFreeA; hh3: array of THandle = nil;
hh4: array of THandle = nil;
implementation////////////////////////////////////////////
// 加载dll3 //
////////////////////////////////////////////
procedure TForm2.btn1Click(Sender: TObject);
begin
h3 := LoadLibrary('C:\Program Files\Borland\Delphi6\Projects\dlltest\Project3.dll');
if h3 <> 0 then
begin
@ShowA3 := GetProcAddress(h3, 'ShowA');
@FreeA3 := GetProcAddress(h3, 'FreeA');
@LoadA3 := GetProcAddress(h3, 'LoadA');
@UnLoadA3 := GetProcAddress(h3, 'UnLoadA');
if (@ShowA3 = nil) or (@FreeA3 = nil) then
begin
FreeLibrary(h3);
ShowMessage('err');
end
else
LoadA3;
end;
end; ////////////////////////////////////////////
// 调用dll3中ShowA //
////////////////////////////////////////////
procedure TForm2.btn2Click(Sender: TObject);
var
l: integer;
begin
l := length(hh3);
setlength(hh3,l + 1);
hh3[l] := ShowA3(self.Handle);
end;////////////////////////////////////////////
// 调用dll3中FreeA //
////////////////////////////////////////////
procedure TForm2.btn3Click(Sender: TObject);
begin
if FreeA3 then ShowMessage('ok');
end;////////////////////////////////////////////
// 调用dll3中卸载dll1函数,动态加载时调用 //
////////////////////////////////////////////
procedure TForm2.btn9Click(Sender: TObject);
begin
UnLoadA3;
end;////////////////////////////////////////////
// 卸载dll3 //
////////////////////////////////////////////
procedure TForm2.btn7Click(Sender: TObject);
begin
FreeLibrary(h3);
end;////////////////////////////////////////////
// 加载dll4 //
////////////////////////////////////////////
procedure TForm2.btn4Click(Sender: TObject);
begin
h4 := LoadLibrary('C:\Program Files\Borland\Delphi6\Projects\dlltest\Project4.dll');
if h4 <> 0 then
begin
@ShowA4 := GetProcAddress(h4, 'ShowA');
@FreeA4 := GetProcAddress(h4, 'FreeA');
@LoadA4 := GetProcAddress(h4, 'LoadA');
@UnLoadA4 := GetProcAddress(h4, 'UnLoadA');
if (@ShowA4 = nil) or (@FreeA4 = nil) then
begin
FreeLibrary(h4);
ShowMessage('err');
end
else
LoadA4;
end;
end;////////////////////////////////////////////
// 调用dll4中ShowA //
////////////////////////////////////////////
procedure TForm2.btn5Click(Sender: TObject);
var
l: integer;
begin
l := length(hh4);
setlength(hh4,l + 1);
hh4[l] := ShowA4(self.Handle);
end;////////////////////////////////////////////
// 调用dll4中FreeA //
////////////////////////////////////////////
procedure TForm2.btn6Click(Sender: TObject);
begin
if FreeA4 then ShowMessage('ok');
end;////////////////////////////////////////////
// 调用dll4中卸载dll1函数,动态加载时调用 //
////////////////////////////////////////////
procedure TForm2.btn10Click(Sender: TObject);
begin
UnLoadA4;
end;////////////////////////////////////////////
// 卸载dll4 //
////////////////////////////////////////////
procedure TForm2.btn8Click(Sender: TObject);
begin
FreeLibrary(h4);
end;
编译出1个exe,3个dll,dll3、dll4加载dll1是静态加载的,运行exe:按这个顺序点击按钮就会出现地址错误:加载dll3、加载dll4、卸载dll4、dll3中ShowA。按这个顺序点击按钮,exe会崩溃:加载dll3、加载dll4、dll3中ShowA、dll4中ShowA、dll4中FreeA、卸载dll4,然后显示出“dll3中ShowA”按钮创建的窗体。问题的本质是:
dll3、dll4用静态加载dll1,dll1会被两次加载进内存,但是卸载时dll3、dll4其中一个时,却把两个dll1全部卸载了。
动态加载不存在这个问题。
静态加载如果完全不写路径,dll1在系统搜索路径,运行结果和动态加载一样,如果静态加载dll1使用相对路径(比如:dll\project1.dll),同样存在这个问题。代码已经在delphi5、delphi6下验证有这个问题。
解决方案 »
- 调用PDF控件的问题,顶者有分!
- FastMM怎么老抓不准泄露的代码位置呢,急等
- !!急!!在别人电脑上打开表出错,但是在我的电脑上运行是好的!
- ****************************诸位(北京)的兄弟,有熟悉Windows Media服务器的吗?我这儿有个项目**************************
- 用什么控件显示数据库?(记录前带复选框,记录可以被选中,修改,删除,添加)
- 怎样实现通过Delphi程序在Oracle数据库中创建表空间和用户啊?
- 怎么在’query‘重量需使用select?
- c语言的crc16算法 delphi 实现
- delphi中如何在一个窗口中显示另一个窗体。。50分
- 如何获得并修改本机的IP,网关与DNS??一百九十分!!!!!
- delphi控制excel单元格的格式
- RegisterServiceProcess(GetCurrentProcessId,0);
原因可能是在dll中使用VCL控件了吧.
俺 一般用dll只是用来实现一些function或者procedure(不涉及到VCL控件)
要用到VCL时,基本上用的是package
一个导入表中带path另一个不带,先unload不带path的会导致它静态引用的dll也被unload