2

我如何从 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.
4

1 回答 1

6

您有三个主要选择。

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

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

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

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

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

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

看起来像这样:

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

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

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

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

返回一个 COM 字符串

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

procedure GetString(out str: WideString); stdcall;

此处有更多详细信息:为什么不能将 WideString 用作互操作的函数返回值?

于 2015-10-30T10:40:16.123 回答