【问题标题】:Passing PChar from the Delphi DLL从 Delphi DLL 传递 PChar
【发布时间】:2015-10-30 10:21:23
【问题描述】:

如何从 DLL 传递 pchar? dll 必须与其他应用程序兼容,而不仅仅是 delphi。 帮助中写道,将指针传递给局部变量是危险的,如果我们将此变量设为全局,代码将不是线程安全的。

我们可以安全地传递一个宽字符串,但在这种情况下,dll 将与其他(非 Delphi)应用程序不兼容。

{code in dll}
       function Test:pchar;
        var
          str:string;
        begin
           str:='Some string';
           result:=pchar(str); // wrong way, may be UB. 
        end;


        {code in dll}

        var
          str:string // global variable

        function Test:pchar;
        begin
          str:='Some string';
          result:=pchar(str); // code not threadsafe
        end;

        {code in dll}
        function Test:WideString;
        var
          str:WideString;
        begin
           str:='Some string';
           result:=str; // will works ONLY with Delphi apps :( 
        end;

:(

how do experienced programmers?



var
  Form1: TForm1;
  function Test(out p:pchar):Integer;stdcall; external 'project2';
  procedure FinalizeStr(P:Pointer);stdcall; external 'project2';

implementation

{$R *.dfm}

function StrFromDll:string;
var
  p:pchar;
begin
  try
    setstring(result,P, test(p));
  finally
    finalizestr(cardinal(p));
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  showmessage(strfromdll);
end;


{code in dll}


library Project2;

uses
  fastmm4,fastmm4messages,
  SysUtils,
  Classes;

{$R *.res}

function Test(out p:pchar):Integer;stdcall;
var
  str:string;
  len:integer;
begin
  str:='Hello, i am a string from the dll?!#@%!?'+inttostr(hinstance);  // only for example
  len:=length(str);
  p:=allocmem(len+1);
  StrPLCopy(P,str,len);
  result:=len;
end;

procedure FinalizeStr(P:Pointer);stdcall;
begin
  FreeMem(P);
end;

exports Test,FinalizeStr;
end.

【问题讨论】:

    标签: delphi dll pchar


    【解决方案1】:

    您有三个主要选择。

    让调用者分配被调用者填充的内存

    function GetString(Buffer: PWideChar; BufferLen: Integer): Integer; stdcall;
    

    调用者必须知道要分配多少内存。您可以通过安排函数返回复制的字符数来做到这一点,或者,如果Buffernil,则需要缓冲区的大小。所以调用者可能会这样调用这个函数:

    var
      str: string;
    ....
    SetLength(str, GetString(nil, 0) - 1);
    GetString(PChar(str), Length(str) + 1);
    

    -1+1 用于处理空终止符。

    让被调用者分配内存,并导出释放器

    看起来像这样:

    function GetString: PWideChar; stdcall;
    function Free(P: Pointer); stdcall;
    

    被调用者从其内部堆中分配内存。但是被调用者还必须导出一个释放内存的函数。调用顺序如下所示:

    var
      P: PWideChar;    
      str: string;
    ....
    P := GetString();
    str := P;
    Free(P);
    

    对此的一个转折是分配一个共享堆,例如 COM 堆。这样你就不需要导出一个释放器,因为调用者可以在没有你帮助的情况下获得 COM 堆释放器。

    返回一个 COM 字符串

    COM BSTR 是在共享 COM 堆之外分配的,任何 Windows 开发环境都可以使用这些对象。 Delphi 将这些包装为WideString。不过,一个转折是 Delphi ABI 与其他工具不同,您不能使用 WideString 作为函数返回值并与其他工具互操作。相反,您应该使用 out 参数。

    procedure GetString(out str: WideString); stdcall;
    

    更多详情:Why can a WideString not be used as a function return value for interop?

    【讨论】:

    • 您删除了对这个答案的接受。有什么具体原因吗?
    猜你喜欢
    • 1970-01-01
    • 2019-08-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-19
    • 2012-08-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多