4

多くの実験の結果、FreePascalでコンパイルされたDLLのPCharをDelphiでコンパイルされたEXEと交換する方法を見つけました。私はDLLとEXEの両方のソースコードを担当していますが、一方はFreePascalに、もう一方はDelphiにある必要があります。私のソリューションには、DLLの次のメソッドが含まれます。

function GetAString(): PChar;
var aString: string;
begin
  aString := 'My String';
  result := StrAlloc(length(aString) + 1);
  StrPCopy(result, aString); 
end;

procedure FreeString(aString: PChar);
begin
  StrDispose(aString); 
end;

また、Delphi EXEから、GetAStringメソッドを呼び出すには、GetAStringメソッドを呼び出し、PCharを実際のDelphi文字列に保存して、FreeStringメソッドを呼び出す必要があります。

これは、FreePascalDLLからDelphiEXEを使用して文字列を交換するための最良の方法ですか?DelphiからFreeStringの呼び出しを回避できますか?

そして最後に、それが正しい解決策である場合、デフォルトでDelphi 2010とWideStringでどのように動作しますか?FreePascalでもWidePCharを強制する必要がありますか?

4

3 に答える 3

7

FreeString呼び出しを使用せずにDLLとDelphiアプリケーション間で文字列を交換する1つの方法は、呼び出し元のアプリケーションから文字列バッファをPCharとして取得し、DLLのバッファを埋めることです。これが、呼び出し元のアプリケーションと文字列を交換する必要がある場合のWindowsAPI関数の動作です。

これを行うために、呼び出し元のアプリケーションは文字列バッファーを作成し、そのバッファーを参照するPCharをバッファーサイズとともにDLL関数に送信します。バッファサイズがDLLがアプリケーションに送信する必要のある実際の文字列よりも小さい場合、DLL関数は、バッファに必要な実際のサイズを呼び出し元のアプリケーションに送信できます。

デフォルトではDelphi2010とWideStringでどのように動作しますか:FreePascalでもWidePCharを強制する必要がありますか?

Delphi2009およびDelphi2010では、PCharはPWideCharと同じです。以前のバージョンのDelphiでは、そして私が知る限り、FreePascalではPCharはPAnsiCharと同じです。したがって、DLLからPCharを返す場合、コードはDelphi2010で正しく機能しません。明示的にPAnsiCharまたはPWideCharを使用する必要があります。再びWindowsAPI関数をたどることができます。これらは、多くのAPI関数の2つのバージョンを提供します。1つは名前にサフィックスとしてW文字が付いたWideCharをサポートし、もう1つは名前にサフィックスとしてA文字が付いたANSIサポートを備えています。

DLL関数の宣言は次のようになります。

function AStringFuncW(Buffer: PWideChar; var BufferSize: Integer): Boolean;

function AStringFuncA(Buffer: PAnsiChar; var BufferSize: Integer): Boolean;

編集:

サンプルコードは次のとおりです。

1-widecharのDLL関数は次のようになります。

function AStringFuncW(Buffer: PWideChar; var BufferSize: Integer): Boolean; stdcall;
var
  MyOutputStr : WideString;
begin
  Result := False;

  // Calculate your output string here.
  MyOutputStr := 'This is a sample output';

  // Check if buffer is assigned, and its given length is enough
  if Assigned(Buffer) and (BufferSize >= Length(MyOutputStr) + 1) then
  begin
    //Copy output string into buffer
    StrPCopy(Buffer,MyOutputStr);
    Result := True;
  end;
  //Return actual size of output string.
  BufferSize := Length(MyOutputStr) + 1;
end;

AnsiCharバージョンの場合、AnsiStringとPAnsiCharで同じコードを使用するか、ANSI文字列パラメーターをUnicodeに変換し、AStringFuncA関数内でAStringFuncWを呼び出してから、戻り文字列をAStringFuncWからPAnsiCharに変換できます。

2-DLLクライアントが使用するインターフェイスユニットでこれらの関数を定義する方法は次のとおりです。

unit TestDLLIntf;

interface
const
  TestDll = 'Test.dll';

  function AStringFuncW(Buffer: PWideChar; var BufferSize: Integer): Boolean; stdcall;
  function AStringFuncA(Buffer: PWideChar; var BufferSize: Integer): Boolean; stdcall;
  function AStringFunc(Buffer: PWideChar; var BufferSize: Integer): Boolean; stdcall;

implementation

function AStringFuncW; external TestDll name 'AStringFuncW';
function AStringFuncA; external TestDll name 'AStringFuncA';
{$IFDEF UNICODE}
 function AStringFunc; external TestDll name 'AStringFuncW';
{$ELSE}
 function AStringFunc; external TestDll name 'AStringFuncA';
{$ENDIF}

end.

上記のコードでは、AStringFuncW関数とAStringFuncA関数の両方が外部関数として宣言されています。AStringFunc関数は、Delphi 2009〜2010のWideCharバージョンを参照し、他のバージョンのAnsiCharバージョンを参照します。

3-ここでは、DLLクライアントが関数をどのように使用できるかを確認できます。

procedure TForm1.Button1Click(Sender: TObject);
var
  Str : string;
  Size : Integer;
begin
  // Retrieve required buffer size
  AStringFunc(nil,Size);
  // Set buffer
  SetLength(Str,Size);
  // Retrieve output string from DLL function.
  if AStringFunc(PChar(Str),Size) then
    ShowMessage(Str);
end;

上記のコードでは、クライアントアプリケーションは最初にAStringFuncから実際の出力サイズを取得し、次に文字列バッファを設定し、DLLから出力文字列を取得します。AStringFuncは、コンパイラがUnicodeをサポートしているかどうかに応じて、DLL内のAStringFuncAまたはAStringFuncWのいずれかを参照するため、DelphiのUnicodeバージョンと非Unicodeバージョンの両方で同じコードが機能することに注意してください。

于 2010-04-14T15:52:36.170 に答える
2

渡すデータによっては、代わりにWideStringsを使用できる場合があります。これらはWindowsヒープに割り当てられるため、DLLに割り当ててEXEで解放すると、両方とも同じメモリマネージャーを経由します。

次のような関数宣言を使用できるはずです。

procedure GetAString(var Result: WideString); stdcall;

また、DelphiとFreePascalはどちらも、割り当てと解放を自動的に処理します。WideStringは、Unicode対応のDelphiでもUnicode以前のものと同じであるため、そのために変更する必要はありません。

于 2010-04-14T16:31:45.160 に答える
2

EXEおよびDLLでメモリを割り当て/割り当て解除するための経験則は、次のとおりです。メモリを割り当てるモジュールは、同じメモリの割り当てを解除する責任があります。あなたの例では、メモリの割り当てと割り当て解除を行うのはDLLなので、正しいです。

一般的な規則から1つの例外があります。Delphiと最新のFreePascalバージョンはどちらも、WideStringsをBSTR(COMで使用される文字列データ型)として実装しています。WideStringは、Delphi(またはFree Pascal)メモリマネージャではなく、Windowsによって割り当てられて解放されます。したがって、EXEとDLLの間でWideString引数を渡すためにPWideCharを使用する必要はありません。WideStringsは直接渡すことができます。


更新しました:

次のコードをテストしました。

Free Pascal(バージョン2.2.4)DLL:

library TestLib1;

{$mode objfpc}{$H+}

{$IFDEF WINDOWS}{$R TestLib1.rc}{$ENDIF}

procedure GetAStringProc(var S: WideString); stdcall;
begin
  S:= '12345';
end;

function GetAStringFunc: WideString; stdcall;
begin
  Result:= '12345';
end;

exports
  GetAStringProc, GetAStringFunc;

begin
end.

Delphi 2009 EXE(メインフォーム):

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    S: WideString;
  end;

var
  Form1: TForm1;

type
  TGetAStringProc = procedure(var S: WideString); stdcall;
  TGetAStringFunc = function: WideString; stdcall;

var
  GetAStringProc: TGetAStringProc;
  GetAStringFunc: TGetAStringFunc;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
    GetAStringProc(S);
    Caption:= S;
    S:= '';
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
    S:= GetAStringFunc;
    Caption:= S;
    S:= '';
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  LibHandle: THandle;

begin
  LibHandle:= LoadLibrary('TestLib1.dll');
  if LibHandle <> 0 then begin
    @GetAStringProc:= GetProcAddress(LibHandle, 'GetAStringProc');
    @GetAStringFunc:= GetProcAddress(LibHandle, 'GetAStringFunc');
  end;
  if not Assigned(GetAStringProc) or not Assigned(GetAStringFunc) then
      ShowMessage('Error!');
end;

end.

関数GetAStringFuncがアクセス違反につながる間、プロシージャGetAStringProcは正常に機能するように見えました。DelphiとFreePascalは、文字列型の結果を異なる方法で返すようです。

于 2010-04-14T16:40:16.120 に答える