从C ++调用Delphi DLL函数

时间:2019-07-09 14:41:49

标签: c++ delphi

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

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

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

error: cannot convert 'GetLastErrText_funtype {aka wchar_t* (__attribute__((__stdcall__)) *)()}' to 'BSTR {aka wchar_t*}' in initialization

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;
}

如何解决此代码?


更新(阅读雷米·勒博的评论后):

在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;
}

1 个答案:

答案 0 :(得分: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代码实际上未返回原始的BSTR指针,就像您的C ++代码所期望的那样。实际上,它返回的是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包装器,它本身并不是 actual 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;