【问题标题】:Call a Delphi DLL function from C++从 C++ 调用 Delphi DLL 函数
【发布时间】:2019-07-09 14:41:49
【问题描述】:

我已经用 Delphi 编写了一个 DLL,现在我需要用 C++ 代码测试这个 DLL 的功能。

我以前从未使用过 C++。我安装了 Code::Blocks 并尝试用 C++ 编写代码。

返回整数或布尔值的函数没有问题,但返回BSTR类型的函数有问题:

错误:无法在初始化中将 'GetLastErrText_funtype {aka wchar_t* (__attribute__((__stdcall__)) *)()}' 转换为 'BSTR {aka wchar_t*}'

Delphi函数头:

function GetLastErrText: BSTR; stdcall;

C++ 代码:

#include <iostream>
#include <cstdlib>
#include <windows.h>

using namespace std;

int main()
{
    HINSTANCE dll_module = LoadLibrary("test.dll");

    if(dll_module == NULL) { return -1; }

    FARPROC GetLastErrText = GetProcAddress(dll_module, "GetLastErrText");
    typedef BSTR (__stdcall* GetLastErrText_funtype)();

    std::cout << "\nGetLastErrText = " << (void*)GetLastErrText;

    if (GetLastErrText != 0) {
        BSTR bstr = (GetLastErrText_funtype) GetLastErrText();  // <<<< ERROR
        std::cout << "\nstr = " << bstr;
    }

    FreeLibrary(dll_module);

    std::cout << "\n";

    return 0;
}

如何修复此代码?


更新(阅读 Remy Lebeau 的评论后):

Delphi 中非常简化的代码

library test_ws;

uses
  System.SysUtils,
  Vcl.Dialogs;

function GetLastErrText: WideString; stdcall;
begin
  try
    Result := 'This is the result of GetLastErrText';
  except
    on E:Exception do
      ShowMessage('Delphi exception::GetLastErrText : ' + E.Message); // when call from C++ code : Out of memory
  end;
end;

exports
  GetLastErrText;

begin
end.

根据 C++ 代码:

#include <iostream>
#include <windows.h>

using namespace std;

int main()
{
    HMODULE lib = LoadLibrary("test_ws.dll");

    typedef BSTR (__stdcall *Func)();
    Func GetLastErrText = (Func) GetProcAddress(lib, "GetLastErrText");

    BSTR bstr = GetLastErrText(); // <<<--- Out of memory
    std::wcout << bstr;
    SysFreeString(bstr);

    return 0;
}

【问题讨论】:

  • 这是一个非常常见的错误,即让函数在互操作中返回字符串。那里有无穷无尽的例子。您需要重新考虑该功能的工作原理。想想内存管理。
  • 您可能想阅读这篇关于 DLL 的文章:rvelthuis.de/articles/articles-dlls.html

标签: c++ delphi


【解决方案1】:

您试图先调用 DLL 函数,然后将其返回值类型转换为函数指针。您不能将函数指针分配给 BSTR 指针,这是编译器所抱怨的。

在正确调用函数之前,您需要对函数指针进行类型转换。您正在尝试在函数调用中执行此操作,但要正确执行此操作,您需要这样做:

BSTR bstr = ((GetLastErrText_funtype)GetLastErrText)(); // <-- note the extra parenthesis around the cast!

否则,最好在第一次获取函数指针时将其强制转换,然后在需要时可以像普通函数一样调用它:

typedef BSTR (__stdcall* GetLastErrText_funtype)();
GetLastErrText_funtype GetLastErrText = (GetLastErrText_funtype) GetProcAddress(dll_module, "GetLastErrText");
...
BSTR bstr = GetLastErrText();

不要忘记在使用完 BSTR 后释放它(前提是 DLL 使用 SysAllocString...() 函数之一正确分配它):

BSTR bstr = GetLastErrText();
std::cout << "\nstr = "; // <-- std::cout does not support wchar_t data!
std::wcout << bstr;      // <-- use std::wcout instead...
SysFreeString(bstr);     // <-- free it!

更新:您的 DLL 的 Delphi 代码实际上并没有像您的 C++ 代码所期望的那样返回原始的 BSTR 指针。它实际上返回一个WideString,它是Delphi 中的托管类型,并且总是通过隐藏的输出参数从函数返回,您的C++ 代码不会填充该参数。这就是您的 C++ 代码失败的原因。见Why can a WideString not be used as a function return value for interop?。 C++ 端的正确函数签名必须看起来更像这样:

typedef void (__stdcall* GetLastErrText_funtype)(WideString&);

但是,WideStringBSTR包装器,它本身并不是实际 BSTR。您不能在 Delphi 和 C++Builder 编译器之外按原样使用 WideString

您需要重写 DLL 函数以使其与非 C++Builder 编译器更加兼容。例如:

function GetLastErrText: PWideChar; stdcall;
var
  RealResult: WideString absolute Result;
begin
  try
    Initialize(RealResult);
    RealResult := 'This is the result of GetLastErrText';
  except
    on E: Exception do
      ShowMessage('Delphi exception::GetLastErrText : ' + E.Message);
  end;
end; 

或者:

function GetLastErrText: PWideChar; stdcall;
begin
  try
    Result := nil;
    WideString(Result) := 'This is the result of GetLastErrText';
  except
    on E: Exception do
      ShowMessage('Delphi exception::GetLastErrText : ' + E.Message);
  end;
end; 

【讨论】:

  • 我添加了 Delphi 代码和固定的 C++ 代码。我仍然收到“内存不足”错误。如果你从 Delphi 程序中调用这个 DLL,它会正常工作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-06-23
  • 2015-07-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-26
相关资源
最近更新 更多