c++ dll 将字符串返回到 delphi

c++ dll returning string to delphi

Delphi 外部 c++ dll 函数的声明。

       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 函数,这一切都按预期工作,直到 str1 的特定长度然后 Delphi returns 垃圾。我需要 return 可能很长的字符串。如有任何建议,我们将不胜感激!

问题是 DLL 函数返回一个指向函数退出时自动释放的数据的指针,因此 悬空指针 返回给调用者(即 Delphi).因此,调用者对该指针的任何使用都是 未定义行为

DLL 需要为返回的指针动态分配内存,然后导出第二个函数,调用者可以使用它来释放内存,例如:

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;
}

或者,让 DLL 使用 OS 提供的内存管理器,如 LocalAlloc()CoTaskMemAlloc(),这样调用者就可以使用适当的 ...Free()函数,例如:

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;
}

或者,让调用者分配自己的内存缓冲区,该缓冲区被传递到要填充的 DLL 中,然后调用者可以在使用完缓冲区后释放缓冲区。这通常需要调用者调用 DLL 函数两次 - 一次计算必要的缓冲区大小,然后在分配缓冲区后再次填充缓冲区,例如:

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;
    }
}