简体   繁体   中英

Delphi and c++ struct

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;

_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.

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;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM