Потрібна проста демо-виклик Delphi DLL в C ++

Я не працюю добре з C + +, але тепер мені потрібно побудувати функцію, яка викликає Delphi DLL і передавати рядок для DLL і отримати повернення нового рядка.

тут мій код DLL для Delphi:

library testdll;
uses
  System.Classes,Winapi.Windows,System.SysUtils;
{$R *.res}

function hello(name : PWideChar):PWideChar;
var
rs:PWideChar;
begin
  rs:=PWideChar('Hello '+rs);
  Result:=rs;
end;

exports
hello;
begin
end.

Будь-яка людина може допомогти мені створити простий код в C + +, щоб зателефонувати і отримати результат форма Привіт функція, дякую за допомогу.

2

2 Відповіді

Ви намагаєтеся зв'язати PWideChar з літералом String і повернути його як інший PWideChar. Це не буде працювати як є. Ви не повинні повертати PWideChar. Це призводить до управління пам'яттю кошмарів. Кращий дизайн - дозволити абоненту передавати буфер в DLL, щоб заповнити замість нього, наприклад:

library testdll;

uses
  System.Classes,
  Winapi.Windows,
  System.SysUtils;

{$R *.res}

function hello(name, buffer : PWideChar; buflen: Integer): Integer; stdcall;
var
  rs: UnicodeString;
begin
  rs := 'Hello '+UnicodeString(name);
  if buffer = nil then
  begin
    Result := Length(rs) + 1;
  end else
  begin
    Result := Min(buflen, Length(rs));
    Move(rs[1], buffer^, Result * SizeOf(WideChar));
  end;
end;

exports
  hello;

begin
end.

Потім, враховуючи цю C ++ декларацію ::

int __stdcall hello(wchar_t* name, wchar_t* buffer, int buflen);

Ви можете викликати його різними способами, залежно від ваших потреб:

wchar_t str[256];
int len = hello(L"joe", str, 255);
str[len] = 0;
...

int len = hello(L"joe", NULL, 0);
wchar_t *str = new wchar_t[len];
len = hello(L"joe", str, len);
str[len] = 0;
...
delete[] str;

int len = hello(L"joe", NULL, 0);
std::wstring str(len-1);
str.resize(hello(L"joe", &str[0], len));
...

int len = hello(L"joe", NULL, 0);
UnicodeString str;
str.SetLength(len-1);
str.SetLength(hello(L"joe", str.c_str(), len));
...

Такий самий код можна легко перекласти на Паскаль, якщо вам знадобиться використовувати одну й ту ж DLL у Delphi:

function hello(name, buffer: PWideChar, buflen: Integer): Integer; stdcall; extern 'testdll.dll';


var
  str: array[0..255] of WideChar;
  len: Integer;
begin
  len := hello('joe', str, 255);
  str[len] := #0;
  ...
end;


var
  str; PWideChar
  len; Integer;
begin
  len := hello('joe', nil, 0);
  GetMem(str, len];
  len := hello('joe', str, len);
  str[len] := #0;
  ...
  FreeMem(str);
end;


var
  str; UnicodeString;
  len; Integer;
begin
  len := hello('joe', nil, 0);
  SetLength(str, len-1);
  SetLength(str, hello('joe', PWideChar(str), len));
  ...
end;
5
додано
Можливо. З іншого боку, функція повернення негативного значення для значення довжини служить тій же меті.
додано Автор Remy Lebeau, джерело
Більш послідовний спосіб (у будь-якому випадку з Windows API) полягає в тому, щоб використовувати повернене значення як індикатор успіху/помилки і використовувати параметр buflen (який повинен бути var), щоб вказати необхідну довжину буфера.
додано Автор Ondrej Kelle, джерело

Update It turns out that Delphi uses a non-standard calling convention for WideString return values. So the code below won't work. The basic concept is sound but you need to return BSTR or use an out parameter of type WideString. More details here: Why can a WideString not be used as a function return value for interop?


Підхід Ремі хороший до тих пір, поки абонент знає, наскільки великий буфер виділити. Альтернативний підхід полягає в тому, щоб виділити пам'ять в бібліотеці DLL і викликати вільну пам'ять. Це працює, лише якщо обидві сторони використовують один і той же розподільник. Прикладом спільного розподільника є розподільник COM і, звичайно, COM BSTR використовує це. У Delphi BSTR відображається в WideString, що дає нам наступний підхід.

Delphi

function concat(s1, s2: PWideChar): WideString; stdcall;
begin
  Result := s1 + s2;
end;

C ++

// DLL import
BSTR __stdcall concat(wchar_t *s1, wchar_t *s2);

BSTR bstr_res = concat(L"Wello, ", L"world!");
std::wstring res(bstr_res);
SysFreeString(bstr_res);

Очевидно, що в цьому простому прикладі необхідний розмір буфера для зв'язаного рядка простий для обчислення. Але якщо фактична функція в DLL була більш складною, то цей підхід стане більш очевидним.

3
додано
@David: BSTR/WideString не дуже ефективний, хоча частково тому, що він повинен управлятися ОС. Зазвичай краще виділяти пам'ять з менеджера пам'яті RTL. Просто експортуйте додаткову функцію з DLL, яку абонент може використовувати, щоб дозволити DLL звільнити пам'ять, яку вона раніше виділила. Наприклад, за допомогою GetMem() і FreeMem (), або StrAlloc() і StrDispose ().
додано Автор Remy Lebeau, джерело
@TOndrej Так, але це іноді може бути дуже незручно. Наприклад, якщо це дорого, або деструктивно, щоб обчислити рядок, що повертається, це робити двічі не добре. Я великий шанувальник загального розподільника. Це дійсно робить життя дуже легким. Ви можете бачити з томів коду, який простіше кодувати.
додано Автор David Heffernan, джерело
@remy, що вражає передчасною оптимізацією
додано Автор David Heffernan, джерело
Функція DLL може бути реалізована для інформування абонента про необхідний розмір буфера, як це зазвичай робиться Windows API. Викликає цю функцію двічі: спочатку з нульовим буфером, щоб отримати необхідний розмір буфера, тоді він буде виділяти достатньо великий буфер і викликати його вдруге.
додано Автор Ondrej Kelle, джерело
IT KPI C/С++ новым годом
IT KPI C/С++ новым годом
747 учасників

Чат обсуждения С/С++. - Вопросы "напишите за меня лабу" - это оффтоп. - Оффтоп, флуд, оскорбления и вбросы здесь не приняты. - За нарушение - предупреждение или mute на неделю. - За спам и рекламу - ban. Все чаты IT KPI: https://t.me/itkpi/1147