Reputation: 139
Delphi declaration of external c++ dll function.
Const dllname = 'NavServer.dll';
function FindNavMeshPath(MapID : integer; StartX : Single ; StartY : Single ; StartZ : Single ; EndX : Single ; EndY : Single ; EndZ : Single): PansiChar; stdcall; external dllname;
extern "C" NAVSERVER_API const char* __stdcall FindNavMeshPath(const int MapID, const float StartX, const float StartY, const float StartZ, const float EndX, const float EndY, const float EndZ)
{ //const char*
const char* str1;
if (pathSize > 0)
{
std::stringstream ss;
for (int i = 0;i < pathSize; i++)
{
ss << i;
}
ss << "dadsaassaasd";
std::string tmp;
ss >> tmp;
str1 = tmp.c_str();
//ss.str();
return str1;
///tmp.data();
}
else
{
return "Path return no points.";
}
c++ dll function, This all works as intended until a specific length of str1 then Delphi returns garbage. I am needing to return potentially very long strings. Any suggestions would be appreciated thanks !
Upvotes: 0
Views: 635
Reputation: 595402
The problem is that the DLL function is returning a pointer to data that is freed automatically when the function exits, thus a dangling pointer is returned to the caller (ie, Delphi). Any use of that pointer by the caller is thus undefined behavior.
The DLL needs to allocate memory dynamically for the returned pointer, and then export a second function that the caller can use to free the memory when done using it, eg:
const
dllname = 'NavServer.dll';
function FindNavMeshPath(MapID : integer; StartX : Single; StartY : Single; StartZ : Single; EndX : Single; EndY : Single; EndZ : Single): PAnsiChar; stdcall; external dllname;
procedure FreeMeshPath(Path: PAnsiChar); stdcall; external dllname;
var
Path: PAnsiChar;
begin
Path := FindNavMeshPath(...);
if Path <> nil then
try
...
finally
FreeMeshPath(Path);
end;
end;
extern "C" NAVSERVER_API const char* __stdcall FindNavMeshPath(const int MapID, const float StartX, const float StartY, const float StartZ, const float EndX, const float EndY, const float EndZ)
{
char* str1;
if (pathSize > 0)
{
std::ostringstream oss;
for (int i = 0;i < pathSize; i++)
{
oss << i;
}
oss << "dadsaassaasd";
std::string tmp = oss.str();
size_t needed = tmp.size() + 1;
str1 = new(nothrow) char[needed];
if (str1)
std::copy_n(tmp.c_str(), needed, str1);
}
else
{
static const char *errorMsg = "Path return no points.";
static const size_t needed = std::strlen(errorMsg) + 1;
str1 = new(nothrow) char[needed];
if (str1)
std::copy_n(errorMsg, needed, str1);
}
return str1;
}
extern "C" NAVSERVER_API void __stdcall FreeMeshPath(char *Path)
{
delete[] Path;
}
Alternatively, have the DLL use an OS-provided memory manager, like LocalAlloc()
or CoTaskMemAlloc()
, so that the caller can then free the returned memory directly using the appropriate ...Free()
function, eg:
const
dllname = 'NavServer.dll';
function FindNavMeshPath(MapID : integer; StartX : Single; StartY : Single; StartZ : Single; EndX : Single; EndY : Single; EndZ : Single): PAnsiChar; stdcall; external dllname;
var
Path: PAnsiChar;
begin
Path := FindNavMeshPath(...);
if Path <> nil then
try
...
finally
LocalFree(Path);
end;
end;
extern "C" NAVSERVER_API const char* __stdcall FindNavMeshPath(const int MapID, const float StartX, const float StartY, const float StartZ, const float EndX, const float EndY, const float EndZ)
{
char* str1;
if (pathSize > 0)
{
std::ostringstream oss;
for (int i = 0;i < pathSize; i++)
{
oss << i;
}
oss << "dadsaassaasd";
std::string tmp = oss.str();
size_t needed = tmp.size() + 1;
str1 = static_cast<char*>(LocalAlloc(LMEM_FIXED, needed));
if (str1)
std::copy_n(tmp.c_str(), needed, str1);
}
else
{
static const char *errorMsg = "Path return no points.";
static const size_t needed = std::strlen(errorMsg) + 1;
str1 = static_cast<char*>(LocalAlloc(LMEM_FIXED, needed));
if (str1)
std::copy_n(errorMsg, needed, str1);
}
return str1;
}
Alternatively, make the caller allocate its own memory buffer that is passed into the DLL to be filled in, and then the caller can free the buffer when done using it. This typically requires the caller to call the DLL function twice - once to calculate the necessary buffer size, and then again to fill the buffer after it has been allocated, eg:
const
dllname = 'NavServer.dll';
function FindNavMeshPath(MapID : integer; StartX : Single; StartY : Single; StartZ : Single; EndX : Single; EndY : Single; EndZ : Single; PathBuffer: PAnsiChar; PathBufferSize: integer): integer; stdcall; external dllname;
var
Path: AnsiString;
Size: Integer;
begin
Size := FindNavMeshPath(..., nil, 0);
if Size <> -1 then
begin
SetLength(Path, Size-1);
FindNavMeshPath(..., PAnsiChar(Path), Size);
...
end;
end;
extern "C" NAVSERVER_API int __stdcall FindNavMeshPath(const int MapID, const float StartX, const float StartY, const float StartZ, const float EndX, const float EndY, const float EndZ, char *PathBuffer, int PathBufferSize)
{
if (pathSize > 0)
{
std::ostringstream oss;
for (int i = 0;i < pathSize; i++)
{
oss << i;
}
oss << "dadsaassaasd";
std::string tmp = oss.str();
size_t needed = tmp.size() + 1;
if (!PathBuffer)
return needed;
if (PathBufferSize < needed)
return -1;
std::copy_n(tmp.c_str(), needed, PathBuffer);
return needed-1;
}
else
{
static const char *errorMsg = "Path return no points.";
static const size_t needed = std::strlen(errorMsg) + 1;
if (!PathBuffer)
return needed;
if (PathBufferSize < needed)
return -1;
std::copy_n(errorMsg, needed, PathBuffer);
return needed-1;
}
}
Upvotes: 2