DLL、BPL与字符串传递

时间:2010-01-26 14:50:30  来源:第二电脑网  作者:第二电脑网

  第二电脑网导读:或返回值的情况下。凡是用DELPHI开发过DLL的,都知道DELPHI的DLL向导生成的代码中,在DLL Project Source一开头就有一段长长的“关于DLL内存管理的重要说明”,其内容大致是说:如果在DLL的exports函数中使用string类型作为参数或返回值的,必须在DLL的Uses段和你的应用程序的Uses段的最前面加入ShareMem,它会使二者共同使用BORLNDMM.DLL进行内存管理,才能保证string类型的...
  正文:

在我开发基于动态代理的轻量容器过程中,动态装入外部的客户自定义接口/类/组件功能是一个必要的组成部分。对于应该选择用DLL还是BPL来作为自定义组件的实现方式一直不能确定。在反复的试验过程中,发现了一些其中的技术细节,特别是在用字符串类型作为参数或返回值的情况下。

凡是用Delphi开发过DLL的,都知道DELPHI的DLL向导生成的代码中,在DLL Project Source一开头就有一段长长的“关于DLL内存管理的重要说明”,其内容大致是说:如果在DLL的exports函数中使用string类型作为参数或返回值的,必须在DLL的Uses段和你的应用程序的Uses段的最前面加入ShareMem,它会使二者共同使用BORLNDMM.DLL进行内存管理,才能保证string类型的内存分配/释放正确。

这是因为string类型在DELPHI内部,由编译器为其提供了动态内存分配/释放机制和引用计数机制,这才能使得string类型可以像一般简单类型一样地使用,而不用像在C/C++中那样麻烦地考虑内存管理和防止内存泄漏。但这同时也带了像DLL中的这样的问题:如果不使用ShareMem的话,就有可能发生在一处分配的内存被错误地在另一处释放,最终导致讨厌的Access Violation。

对于简单的函数调用,用DLL+ShareMem就可以实现了,但是如果涉及到接口和类的时候就不行了。



考虑下面这个简单的接口及实现。


//---------------------------------------------------------
//  定义在接口单元
{$M+}
IDemoIntf = interface
['{5F3C4D61-B885-41B6-B43B-C4725DF5D901}']
Function  GetHello( nID : Integer ) : String; StdCall;
End;
{$M-}

//---------------------------------------------------------
//  定义在实现单元
type
TDemoImpl = class(TInterfacedObject, IDemoIntf)
protected
{ Protected declarations }
Function  GetHello( nID : Integer ) : String; StdCall;
end;

Procedure CompRegister( aIntfReg : TMRegisterIntfEvent ); Cdecl;

implementation

Procedure CompRegister( aIntfReg : TMRegisterIntfEvent );
Begin
  aIntfReg( IDemoIntf, TypeInfo( IDemoIntf ), TDemoImpl );
  End;
  
  { TDemoImpl }
  
  function TDemoImpl.GetHello(nID: Integer): String;
  begin
    Result := 'Hello ' + IntToStr( nID );
  end;
  
  

首先来看DLL版的实现。创建一个DLL Project,然后把上述两个单元加入,并将CompRegister函数Exports出来。关于这个CompRegister函数要作一下简单说明:

这个CompRegister就是一个注册入口,容器将该DLL调用入立即执行这个注册函数,而用户组件包必须实现这个注册函数,并在其中向容器注册用户实现的接口/类等。TMRegisterIntfEvent是一个方法类型,容器调用注册函数时通过参数将它的注册方法引用传给这个注册函数供它调用。

下面是对应的用DUnit实现的单元测试程序。


procedure TTestCaseDLLPackageLoader.Setup;
Var
funcInit : TMFuncCompRegister;
begin
  hPkg := LoadLibrary( 'demopkg.dll' );
  funcInit := TMFuncCompRegister( GetProcAddress( hPkg, 'CompRegister' ) );
  funcInit( GMIntfReg.RegisterIntf );
end;

procedure TTestCaseDLLPackageLoader.TearDown;
begin
  GMIntfReg.UnregisterIntf( IDemoIntf );
  FreeLibrary( hPkg );
end;

procedure TTestCaseDLLPackageLoader.TestLoader;
Var
f : IDemoIntf;
begin
  f := GMIntfReg.GetIntfInstance( IDemoIntf ) As IDemoIntf;
  Check( f.GetHello( 10 ) = 'Hello 10' );
end;

这个测试很简单:首先在初始化(Setup方法[1])载入DLL,然后调用CompRegister注册接口和实现类。在测试函数 TestLoader中,通过接口注册管理器(GMIntfReg,由容器提供)的GetIntfInstance方法取得接口实例。这个方法的内部实现原理就是通过接口的GUID找到对应的类类型,然后创建一个实例,再通过Supports函数转成接口引用。调用接口方法GetHello并用DUnit 的Check函数进行检查。最后在清理过程(TearDown方法[1])中删除接口注册信息并释放DLL。
DLL、BPL与字符串传递》由第二电脑网原创提供,转载请注明:http://www.002pc.com/master/College/Language/Delphi/2010-01-26/12441.html


关键字:

关于《DLL、BPL与字符串传递》文章的评论

站内搜索: 高级搜索

热门搜索: Windows style 系统 tr IP QQ CPU 安装 function 注册 if td