Reputation: 247
I have the following C++ function that exports a struct with a char*
field in it, but the string value is not as expected when used in Delphi, although it is null-terminated.
typedef struct _MyStruct{
char* name;
// other fields
}MyStruct,*PMyStruct;
extern "C" __declspec(dllexport) __cdecl MyTestStr(PMyStruct _PMyStruct)
{
std::string str = "string";
std::vector<char> cstr(str.begin(), str.end);
cstr.push_back('\0');//null-terminated
_PMyStruct->name = cstr.data();
}
type
PMyStruct = ^MyStruct;
MyStruct= record
name : PAnsiChar;
// other fields
end;
procedure MyTestStr(_PMyStruct: PMyStruct); cdecl; external 'mytest.dll' name 'MyTestStr';
procedure TestMyRec();
var
_MyStruct: MyStruct;
begin
MyTestStr(@_MyStruct);
showmessage(_MyStruct.name);
// here the result is just 'YYYYYYYYYYYYYYYYYY' not 'string'
end;
Upvotes: 0
Views: 904
Reputation: 62531
Change the definition of _MyStruct::name
to const char *
, and just assign the literal to it.
Note that names starting _
followed by an uppercase letter are reserved for the implementation, so your whole program has undefined behaviour.
You don't need to typedef struct
.
struct MyStruct
{
const char* name; // mutable pointer to constant char(s)
// other fields
};
using PMyStruct = * MyStruct;
extern "C" __declspec(dllexport) __cdecl void MyTestStr(PMyStruct pMyStruct)
{
pMyStruct->name = "string";
}
In the general case, it is inadvisable to pass owning pointers across dll boundaries. Instead the caller should allocate, and the function copy into that allocation. This is the pattern used across the Win32Api. You either return the size, or take int *
parameters to write the sizes into
C++ Dll
extern "C" __declspec(dllexport) __cdecl void MyTestStr(PMyStruct pMyStruct = nullptr, int * firstname_size = nullptr, int * lastname_size = nullptr)
{
if (pMyStruct)
{
std::strncpy(pMyStruct->firstname, "string", pMyStruct->firstname_len);
std::strncpy(pMyStruct->lastname, "other string", pMyStruct->lastname_len);
}
if (firstname_size) { *firstname_size = 7; }
if (lastname_size) { *lastname_size = 13; }
}
Delphi exe
type
PInteger = ^Integer;
PMyStruct = ^MyStruct;
MyStruct= record
firstname : PAnsiChar;
firstname_len : Integer;
lastname : PAnsiChar;
lastname_len : Integer;
// other fields
end;
procedure MyTestStr(pMyStruct: PMyStruct; firstname_len : PInteger; lastname_len : PInteger); cdecl; external 'mytest.dll' name 'MyTestStr';
procedure TestMyRec();
var
myStruct: MyStruct;
begin
// If you don't know how much memory you will need, you have to ask
MyTestStr(nil, @myStruct.firstname_len, @myStruct.lastname_len);
GetMem(myStruct.firstname, myStruct.firstname_len);
GetMem(myStruct.lastname, myStruct.lastname_len);
MyTestStr(@myStruct);
// Use myStruct
FreeMem(myStruct.firstname);
FreeMem(myStruct.lastname);
end;
Upvotes: 0
Reputation: 80107
_PMyStruct->name=cstr.data();
just makes pointer onto string body. But after function call local object std::string
should be disposed. So you have got pointer to some memory address with unpredictable contents, this might cause AV if memory does not belong to application more.
Seems you have to allocate memory and call function that copies needed data into this memory address. Free this memory when needed.
Upvotes: 1