i don't work well with C++ but now i need build a function that call Delphi DLL and pass a string to DLL and get return new string.
here is my Delphi DLL code:
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.
Anyone can help me create simple code in C++ to call and get result form hello function, thank for help.
You are trying to concat a PWideChar to a String literal and return it as another PWideChar. That will not work as-is. You should not be returning a PWideChar anyway. That leads to memory management nightmares. A better design is to let the caller pass a buffer into the DLL to fill in instead, eg:
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.
Then, given this C++ declaration::
int __stdcall hello(wchar_t* name, wchar_t* buffer, int buflen);
You can call it all kinds of different ways, depending on your needs:
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));
...
The same kind of code can be translated to Pascal very easily if you ever need to use the same DLL in 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;
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?
Remy's approach is good so long as the caller knows how big a buffer to allocate. An alternative approach is to allocate memory in the DLL and have the caller free the memory. This only works if both parties use the same allocator. An example of a shared allocator is the COM allocator and COM BSTR
of course uses this. In Delphi a BSTR
maps to WideString which gives us the following approach.
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);
Obviously in this simple example, the required buffer size for the concatenated string is simple to calculate. But if the actual function in the DLL was more complex then this approach would become more obviously advantageous.