C ++での簡単なデモ呼び出しDelphi DLLが必要

私はC ++でうまく動作しませんが、今はDelphi DLLを呼び出してDLLに文字列を渡し、新しい文字列を返す関数をビルドする必要があります。

ここに私のデルファイのDLLコードです:

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 ++で簡単なコードを作成して、結果フォームのhello関数を呼び出して助けてくれます。

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));
...

Delphiで同じDLLを使用する必要がある場合、同じ種類のコードをPascalに非常に簡単に変換できます。

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はあまり効率的ではありませんが、それはOSによって管理される必要があるためです。一般にRTLのメモリマネージャからメモリを割り当てる方が良いです。呼び出し元が以前に割り当てたメモリの割り当てを解除するためにDLLから余分な関数をエクスポートするだけです。たとえば、GetMem()とFreeMem()、またはStrAlloc()とStrDispose()を使用します。
追加された 著者 Remy Lebeau,
@TOndrejはい、それは時には非常に不便かもしれません。たとえば、返される文字列を計算するのが高価な場合や破壊的な場合は、2回繰り返す必要があります。私は共通のアロケータの大ファンです。それは本当に人生を非常に簡単にします。あなたはコードのボリュームから、より簡単にコードを見ることができます。
追加された 著者 David Heffernan,
@remy早すぎる最適化
追加された 著者 David Heffernan,
DLL関数は、Windows APIによって通常行われるように、呼び出し元に必要なバッファサイズを通知するために実装できます。呼び出し元は関数を2回呼び出します。最初はnilバッファを使用し、必要なバッファサイズを受け取ると、バッファが十分に割り当てられ、2回目に呼び出されます。
追加された 著者 Ondrej Kelle,