【问题标题】:Using C dll in delphi return nothing在delphi中使用C dll什么都不返回
【发布时间】:2012-09-28 22:57:55
【问题描述】:

我一直在尝试基于documentation 在 delphi 中重用 C dll 文件。

服务器运行良好,我可以访问和使用本地服务器上的数据库 java和php。

在 delphi 上,我使用了动态加载,并且在所有返回变量的函数上运行良好,但在返回接口的函数上失败了。

unit for library :
unit SQLDBC_C;

interface
uses windows, classes, sysutils;

type
  SQLDBC_IRuntime = interface
  end;

var
  getSDKVersion : function :Pchar; stdcall;
  ClientRuntime_GetClientRuntime: function (errorText:Pchar; errorTextSize:Integer) : SQLDBC_IRuntime; stdcall;

implementation

var
  libhandle : THandle;

procedure initLibrary;
begin
  libhandle := LoadLibrary('libSQLDBC_C.dll');
  if libhandle>=23 then begin
     @getSDKVersion:=GetProcAddress(libhandle,'getSDKVersion');
     @ClientRuntime_GetClientRuntime:=
        GetProcAddress(libhandle,'ClientRuntime_GetClientRuntime');
  end;
end;

initialization
begin
  initLibrary;
end;

finalization
begin
  if libhandle>=32 then
    FreeLibrary(libhandle);
end;

end.

这是测试程序:

procedure TForm1.Button1Click(Sender: TObject);
var
  err : array [0..200] of char;
  rt : SQLDBC_IRuntime;

begin
  Memo1.Clear;
  FillChar(err, sizeof(err), 0);
  Memo1.Lines.Add(getSDKVersion); //this function successed

  rt := ClientRuntime_GetClientRuntime(@err,200); 
  //this function had no return value, (rt always nil) but no error return at err variable
  if assigned(rt) then begin
    ......
  end;
end;

我已经阅读了geskillDan HackermaxRon 提出的类似问题,但它无法解决我的问题。

谁能告诉我这里出了什么问题?

【问题讨论】:

  • Delphi 的哪个版本?你确定它是stdcall吗?很可能是 cdecl。
  • 这让我想起了这个问题:stackoverflow.com/questions/9349530/…
  • @DavidHeffernan 感谢您的回复。 stdcall 和 cdecl 之间没有区别。我在 Windows XP 机器上,正在尝试 Delphi 6、7 和 XE2。
  • 确保您将 AnsiChar 用于 XE2。最好始终将它用于此库。尝试一个 C++ 程序并检查它是否有效。如果有,有什么不同?
  • 这就是问题所在。它适用于 C++、java 和 php。在 delphi 6、7 和 XE2 上,库成功加载了返回字符串和数字的函数,除了返回接口的所有函数。如果我使用接口它返回零。如果我使用指针,它会返回指针但会引发异常。

标签: delphi dll dllimport


【解决方案1】:

我无法测试它,因为我没有libSQLDBC_C.dll

问题是 already explained 。作为您的情况的解决方法,您可以在 Delphi ClientRuntime_GetClientRuntime 声明中返回一个指针

ClientRuntime_GetClientRuntime: function (errorText:Pchar;
                                errorTextSize:Integer): Pointer; stdcall;

并将其转换为SQLDBC_IRuntime 接口:

var
  err : array [0..200] of char;
  rt : SQLDBC_IRuntime;

begin
  Pointer(rt):= ClientRuntime_GetClientRuntime(@err,200); 

【讨论】:

  • 我已经做了一些试验并同意你在这里写的东西(见我的更新)。然而遗憾的是,Pointer 技巧在这种情况下毫无用处。
  • 确定类型转换需要一个 COM 接口(IUnknown 后代);但我想只有新手才能从 dll 导出指向 C++ 类的指针,在 SAP 产品中看到它很奇怪。
  • 这和从DLL中导出Delphi类是一样的错误。
  • @serg :使用指针的好主意。在我的情况下,不要尝试将指针转换为接口,否则你会得到一个异常。保持指针不变。谢谢。
  • 但是如果你把它作为一个指针,你怎么能在它上面调用方法呢?
【解决方案2】:

返回接口的 C++ 函数不容易映射到 Delphi 函数。 Delphi 中托管类型的返回值的调用约定与 C++ 使用的不匹配。

为了说明,我创建了一个简单的 C++ 测试 DLL 来导出这个函数:

extern "C" __declspec(dllexport) IUnknown* __stdcall GetInterface()
{
    CoInitialize(NULL);
    IUnknown* result;
    CoCreateInstance(CLSID_ActiveDesktop, NULL, CLSCTX_INPROC_SERVER, 
      IID_IUnknown, (void**) &result);
    return result;
}

将其映射到 Delphi 的明显方法如下:

function GetInterface: IUnknown; stdcall; external DLL name '_GetInterface@0';

然而,当我们调用这个函数时,它总是返回nil

解决方法完全按照 Serg 的建议:

function GetInterface: Pointer; stdcall; external DLL name '_GetInterface@0';

然后我们可以这样称呼它:

var
  intf: IUnknown;
....
Pointer(intf) := GetInterface;

当我们这样做时,intf 不是nil,我们可以很高兴地调用它的方法。

所以,我们在这里了解到的是,Delphi 不能轻易调用返回接口的外部函数,除非这些外部函数也在 Delphi 中实现。但至少我们有一个可行的解决方法。


不幸的是,这种解决方法对您没有立即用处。那是因为SQLDBC_IRuntime 是一个 C++ 类。它不是 COM 兼容接口。注意SQLDBC_IRuntime 没有实现IInterface。所以它不提供_AddRef_ReleaseQueryInterface。 Delphi 的接口支持基于IInterface 的可用性。这意味着你不能使用 Delphi 的SQLDBC_IRuntime

您需要创建一个 C++ 桥接 DLL,以 Delphi 可以调用的方式公开功能。例如,通过公开调用 SQLDBC_IRuntime 的 C++ 方法的普通旧函数。

【讨论】:

  • 它不返回零; Delphi 原型错误,所以返回值没有作为函数的结果出现。
  • @Serg 你能详细说明一下吗?我认为您对我的问题的链接不适用于此处。有许多返回 COM 接口的 Win32 API 函数。我的直觉是问题在于SQLDBC_IRuntime 根本不是 COM 接口。即使你能抓住它,你能拿它做什么?
  • 不,WinAPI 函数通常返回 HResult(双字),而不是接口;我可以详细说明,但我需要分析函数的参数堆栈。
  • @Serg 你可能是对的。你读过这个吗:support.smartbear.com/viewarticle/33100
  • 好链接,我对这个问题的想法完全一样。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-07
  • 1970-01-01
  • 2011-06-15
  • 2012-06-18
  • 2018-09-10
相关资源
最近更新 更多