来自DLL的Delphi接口

3
使用Delphi XE。
当尝试从DLL中动态访问Delphi接口对象时,如果尝试动态访问而不是静态访问,则会失败。
在dll中的接口单元实现了一个函数来返回接口的实例。当静态链接时,在进入函数时结果为nil,一切正常。当动态加载时,结果为非nil,因此在执行结果赋值时,IntFCopy代码看到它为非nil,因此在赋值之前尝试释放它,这会导致异常。
任何见解都将不胜感激。
该DLL包括testinterfaceload\_u并导出testInt:
library testinterfaceload;

uses
  SimpleShareMem,
  SysUtils,
  Classes,
  testinterfaceload_u in 'testinterfaceload_u.pas';

{$R *.res}
exports testInt;

begin
end.

testinterfaceload_u是定义接口和简单类实现的单元:

unit testinterfaceload_u;

interface

type ITestInt = interface
 procedure Test;
end;

{this function returns an instance of the interface}
function testInt : ITestInt; stdcall; export;

type

TTestInt = class(TInterfacedObject,ITestInt)
 procedure Test;
end;



implementation

function testInt : ITestInt;
begin
//debugger shows result as non-nil ITestInt even before this assignment, when dynamic
  result := TTestInt.Create;  
end;

procedure TTestInt.Test;
var
  i : integer;
begin
  i := 0;
end;



end.

这是一个控制台应用程序,它加载 DLL 并调用 testInt 函数以返回接口:

program testload_console;

{$APPTYPE CONSOLE}

uses

SysUtils,
  Windows,
  testinterfaceload_u in 'testinterfaceload_u.pas';

type
  TTestInt = function() : ITestInt;

var
   TestInt: TTestInt;
   NewTestInt : ITestInt;
   DLLHandle: THandle;
begin
  DLLHandle := LoadLibrary('testinterfaceload.dll');
  if (DLLHandle < HINSTANCE_ERROR) then
       raise Exception.Create('testinterfaceload.dll can not be loaded or not found. ' +     SysErrorMessage(GetLastError));
  @TestInt := GetProcAddress(DLLHandle, 'testInt');
  try
    if Assigned(TestInt) then
      NewTestInt := TestInt;
  except on e:Exception do
    WriteLn(Output,e.Message);
  end;
end.

为什么使用显式的LoadLibrary而不是隐式链接? - undefined
我在使用InstallAware时遇到了一个问题,尝试运行我的一个调用另一个dll的dll。如果加载了第二个dll,InstallAware似乎在卸载这些库时会出错,因此我正在尝试在第一个dll中处理它的生命周期。 - undefined
1
如果 (DLLHandle < HINSTANCE_ERROR) 那么就是错误的。在出错的情况下,LoadLibrary 返回 0(即 NULL)。此外,不需要使用 export,它只会造成混淆。请将其删除。 - undefined
4个回答

5

您的接口定义应包含GUID,每个函数都需要“stdcall”声明。如果没有它,您可能会遇到问题。

type ITestInt = interface
  ['{AA286610-E3E1-4E6F-B631-F54BC6B31150}']
  procedure Test; stdcall
end;

5

TTestInt需要在导入DLL的代码中声明为stdcall


嗯,有趣,我之前不知道这个。如果你没有异议的话,能给一个关于这方面的文档链接吗?我的目标是学到新东西,而不仅仅是空洞的争论。 - undefined
1
@Downvoter 这里是链接:点击这里:对于字符串、动态数组、方法指针或变体结果,效果与将函数结果声明为在已声明参数之后的附加var参数相同。换句话说,调用者传递了一个额外的32位指针,该指针指向一个变量,用于返回函数结果。 - undefined
谢谢,值得了解这个“理论”部分。 - undefined

3
尝试一下:尝试在dll的testInt函数和控制台应用程序中的TTestInt函数类型的声明中添加调用方法(stdcall、pascal等)。

stdcall在TTestInt函数类型上。不幸的是,它没有解决我的InstallAware问题。 - undefined
那显然是另外一回事。一旦你将调用约定排好,上面你呈现的代码就没问题了。调用约定不匹配完美地解释了“调试器在这个赋值之前就显示非空的 ITestInt 结果,当时是动态的”。 - undefined

1

死灵贴模式开启

function testInt : ITestInt;
begin
//debugger shows result as non-nil ITestInt even before this assignment, when dynamic
  result := TTestInt.Create;  
end;

应该是这样的一个过程。
procedure testInt(out intf: ITestInt); stdcall;
begin
 intf := TTestInt.Create;  
end;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接