Вызов DLL-функции Delphi из C ++ - PullRequest
0 голосов
/ 09 июля 2019

Я написал DLL с Delphi, и теперь мне нужно протестировать функцию этой DLL из кода C ++.

Я никогда раньше не использовал 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 Ответ

1 голос
/ 09 июля 2019

Вы пытаетесь сначала вызвать функцию 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!

ОБНОВЛЕНИЕ : код Delphi вашей DLL фактически не возвращает необработанный указатель BSTR, как ожидает ваш код C ++. Фактически он возвращает WideString, который является управляемым типом в Delphi, и всегда возвращается из функции через скрытый выходной параметр, который ваш код C ++ не заполняет. Вот почему ваш код C ++ терпит неудачу. См. Почему WideString не может использоваться как возвращаемое функцией значение для взаимодействия? . Правильная сигнатура функции на стороне C ++ должна выглядеть примерно так:

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

Однако WideString - это обертка для BSTR, сама она не является фактическим BSTR. Вы не можете использовать WideString как есть вне компиляторов Delphi и C ++ Builder.

Вам нужно переписать функцию 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; 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...