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

3 つの主なオプションがあります。

呼び出し元に、呼び出し先が設定するメモリを割り当てさせる

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

呼び出し元は、割り当てるメモリの量を知っている必要があります。Bufferこれを行うには、関数がコピーされた文字数、またはnil必要なバッファーのサイズを返すように調整します。したがって、呼び出し元はこの関数を次のように呼び出すことができます。

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

-1+1は、null ターミネータを処理するためのものです。

呼び出し先にメモリを割り当てさせ、デロケーターをエクスポートします

それは次のようになります。

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 に答える