3

TStringListDLL の内部を埋めたい。私のアプローチはメモリ管理のドキュメントに関して間違っているようですが、それは機能し、エラーや AV を引き起こしません。

そのコードがOKなら、誰か教えてもらえますか? 一般に、DLL でクラスを埋める方法がわかりません。

programm EXE

function MyClass_Create: IMyClass; stdcall; external ...

var
  _myClass_DLL: IMyClass; //shared interface in exe and dll

procedure FillList;
var
  list: TStringList;      
begin
  list := TStringList.Create(true); //memory allocated in EXE
  try
    _myClass_DLL.FillList(list);  //memory allocated in DLL???
    ShowMessage(list.Text);
  finally
    list.Free; //memory freed in EXE, frees also TObject created in DLL
  end;
end;

DLL コード:

library DLL

TMyClass = class(TInterfacedObject, IMyClass)
public
  procedure FillList(aList: TStringList);
end;

procedure TMyClass.FillList(aList: TStringList);
begin
  aList.AddObject('Text1', TObject.Create); //memory allocation in DLL?
  aList.AddObject('Text2', TObject.Create); //memory allocation in DLL?
end;

BORLNDMM.DLL やその他の ShareMem ユニットは使用していません。

編集:呼び出しを
に拡張しました。また、TObject は DLL で作成され、EXE で解放されますが、クラッシュしません。aList.Add()aList.AddObject()

回答:
以下の受け入れられた回答のコメントについては、exe と dll が同じ Delphi バージョンでコンパイルされ、仮想メソッドのみが呼び出されるため、そのコードは正しいです。

結論:
仮想メソッドまたはインターフェイスが使用されている限り、メモリ管理に問題はありません。つまり、オブジェクトがどこで作成または解放されるかは問題ではありません。

4

4 に答える 4

4

モジュールの境界を越えてクラスを渡したい場合は、ランタイムパッケージを使用してRTL/VCLにリンクする必要があります。TStringListこれが、DLLのクラスがEXEのクラスとまったく同じであることを確認する唯一の方法です。それが現在のアプローチの根本的な問題です。一方、ランタイムパッケージを使用してRTLに既にリンクしている場合は、問題ありません。

ランタイムパッケージを使用したくない場合は、インターフェイスを完全に再設計する必要があります。モジュールの境界を越えてクラスを渡すのをやめる必要があります。インターフェイスは使用できますが、クラスは使用できません。また、メモリの割り当てを制御して、メモリを割り当てたモジュールで常にメモリの割り当てが解除されるようにする必要があります。または、を使い始めShareMemます。

于 2012-08-02T13:38:30.353 に答える
2

このタイプの機能のために、そして共有メモリフリー、パッケージフリーを維持するために、私はdllの列挙子メソッドでコールバックを使用します。これは、たとえばウィンドウからフォントを取得する方法です。これが私が言及しているもののモックアップです:

type
  TMyClass = class
  private
    FList: TStringList;  // obv you need to construct this

  public
    function EnumListItem(s: string): integer;
  end;

function TMyClass.EnumListItem(s: string): integer;
begin
  FList.Add(s);
end;

procedure TMyClass.FillList;
begin
  _myClass_DLL.FillList(@EnumListItem);
  ShowMessage(FList.Text);
end;

これは、出発点を提供するためだけのものです。DLL側では、関数ポインタを使用してプログラムにコールバックし、一度に1つの文字列を渡します。

于 2012-08-02T20:00:15.507 に答える
1

BPL に真剣に反対する場合は、DLL とインターフェイスの COM 規則に固執することをお勧めします。

特に、COM には TStream のようなインターフェイスがあります。また、VCL には TStreamAdapter クラスがあり、COM IStream と VCL TStream の間で変換を行います。

そうすれば、DLL はデータ ストリームを作成し、それを COM IStream にラップして exe に渡す必要があります。EXE は逆変換し、TStream から文字列リストを埋めます。

より高速でローテクなアプローチは、Windows API 関数のようにメモリ バッファーを使用することです。彼らはそれを感じるか、より大きなサイズのバッファを要求するプログラム エラーを返します。それなら、関数を 2 回呼び出して、バッファー サイズを取得し、実際の作業を行います。また、PChar のようなポインター型を混在させたり、PAnsiChar または PWideChar にしたり、間違ったバッファー サイズを渡したりすると、コンパイラーからのセーフティ ネットがなく、メモリが破損するだけです。しかし、それは COM IStream よりも高速です。

おそらく、メモリを解放しない特別な種類のデストラクタを持つ COM 対応の Buffer オブジェクトを作成しますが、DLL バックグラウンド アイドル メモリ再収集スレッドへの参照を渡します。したがって、メインの EXe で不要になった場合は、遅かれ早かれ DLL 自体で解放されます。TStream ほど快適に使用することはできませんが、少なくともヒープ マネージャーが機能しなくなることはありません。

于 2012-08-02T15:31:14.167 に答える
0

dll から文字列リストを取得する最も簡単な方法は次のとおりです。tStringList を作成し、それを DLL 内に入力してから、テキストを return として渡す必要があります。

Dll ライブラリから:

function getDocuments(customer,depot:Pchar):Pchar;export;
var
 s:TstringList;
begin
 S:=TStringList.Create;
 S.Add('Row 1 '+customer);
 S.Add('Row 2 '+depot);
 S.Add('Row 3 ');
 S.Add('Row 4 ');
 S.Add('Row 5 ');
 Result:=pchar(s.Text);
 S.Free;
end;

エグゼから

function GetDLLExternalDocuments(customer,depot:pchar;out fList:TStringList):Word;
var GetDocumentsDLLExport:TGetDocumentsDLLExport;
var s:String;
var HandleDllExport :Thandle;
begin

  HandleDllExport := LoadLibrary('my_dll_library.dll');

  if HandleDllExport <> 0 then
   begin
    @GetDocumentsDLLExport := GetProcAddress(HandleDllExport, 'getDocuments');
    if @GetDocumentsDLLExport <> nil then
      begin
        s:=GetDocumentsDLLExport(cliente,impianto);
        fList.Text:=S;
        result:=0;
      end;


    FreeLibrary(HandleDllExport);
    HandleDllExport:=0;

   end;

end;

使用法:

procedure TfMain.Button1Click(Sender: TObject);
var
 S:tStringList;
begin
  S := tStringList.create;
  GetDLLExternalDocuments('123456','AAAAA',S);
  Showmessage(S.Text);
  s.Free; 
end;
于 2016-09-10T16:47:07.623 に答える