3

文字列を返す関数をエクスポートする DLL を構築したいと考えています。この DLL は、他のプログラミング言語で動作するはずです!! これに対するあらゆる種類の厄介な解決策/ハックを見つけました。最善の方法は、関数が Pchar を返してから、同じ DLL に含まれる別の関数を呼び出して (ReleaseMemory と呼びましょう)、PChar 用に予約されたメモリを解放することです。

とにかく、最近 FastShareMem ライブラリを発見しました。ReleaseMemoryを呼び出さずに、私が望むことを正確に実行できると言われています。一方、FastMM は、DLL とアプリケーションの両方がメモリ マネージャーとして FastMM を使用するため、同じ AS LONG を実行しているようです。これにより、FastMM をユニバーサル DLL のメモリ マネージャーとして使用する機会が即座に失われます。右?

====================

FastShareMem ( http://www.codexterity.com/fastsharemem.htm )、Delphi 7、Windows XP 32 ビット、Windows 7 64 ビット

4

4 に答える 4

8

Delphi を返す場合、stringDelphi の文字列型を使用するプログラミング言語は他にないため、DLL は他のプログラミング言語では動作しません。タイプが同じでない場合、メモリをどのように割り当てるかは問題ではありません。テキストを操作している場合は、Windows API のモデルに従い、従来の文字ポインターを使用してください。

あなたが見つけた解決策 - ポインターを返し、DLL がメモリを解放する別の関数を提供する - はハックではなく、まったく厄介ではありません。これはまったく普通の解決策であり、あなたの DLL を使用している人は、それを見て目をつぶることはありません。API 関数は同様のモデルを使用します。FormatMessageつまり、文字列を割り当て、割り当てた文字列を で解放する必要があることを指定しますLocalFree

一貫性があり、DLL のコンシューマーが使用できる限り、どのメモリ マネージャーを使用してもかまいません。LocalAlloc1 つの方法として、 and LocalFree、 orなどの文字列の割り当てと解放を行う Windows API 関数を指定する方法がありSysAllocStringますSysFreeString。もう 1 つの方法は、何も割り当てないことです。呼び出し元が文字列を返す必要がある場合、呼び出し元はバッファーを提供し、その大きさを教えてくれます。バッファーが小さすぎる場合は、呼び出し元がバッファーを再割り当てして関数を再呼び出しできるように、必要なサイズを返します。その例については、 を参照してくださいGetLongPathName

FastSharemem は、Delphi のメモリ マネージャがどのように機能するかについて長い説明を提供し、プログラムでそのユニットを使用するだけですべての問題を回避できると述べています。ただし、上で述べたことを思い出してください。DLL のコンシューマは、使用するのと同じメモリ マネージャを使用できる必要があります。DLL のコンシューマが Delphi で記述されていない場合、FastSharemem ユニットを使用できません。FastSharemem は同種の Delphi 環境では問題ありませんが、混合環境で使用すると、他のメモリ マネージャーと同じように落とし穴があります。

于 2010-08-11T05:42:37.260 に答える
6

2 つの異なるシナリオを混在させています。

  1. Delphi DLL を使用する Delphi アプリケーション
  2. Delphi DLL を使用するすべてのアプリケーション

最初のシナリオでは、Delphi のバージョンを混在させるか、変なことをしない限り、メモリ マネージャは同じであり、コンパイラも同じです。これにより、メモリマネージャーを共有する方法があり、コンパイラは割り当て/割り当て解除を適切に処理できます。これは、FastMM と FastShareMem の両方が機能するシナリオであり、この 1 つだけです。

2 番目のシナリオでは、アプリケーションと DLL が異なるメモリ マネージャーを使用します。おそらく非常に異なるものであり、通常は 1 つを共有する方法はありません。このような状況では、割り当て解除関数を提供したとしても、DLL 内で割り当てられた PChar を決して返さないことが最善の方法です。呼び出し元には、コンパイラ/インタープリターの前に適切な割り当て解除ルーチンを呼び出す機会があります。COMはあなたが言ったように動作しますが、独自のメモリマネージャーを介してメモリの割り当て/割り当て解除を強制するため、安全です. プレーンな DLL で強制することはできないため、安全ではありません。

最善の方法は、呼び出し元の言語に十分な大きさのバッファーを渡し、そこに PChar を書き込むことです。もちろん、呼び出し元にバッファーのサイズを伝える何らかの方法が必要です。これが Windows 自体の仕組みであり、この選択を行ったのには十分な理由があります。

于 2010-08-11T16:16:43.033 に答える
3

私は FastSharemem の作成者で、2 セント相当の寄付をしたいと思っています。Rob の言うとおりです。FastSharemem は、モジュールがすべて Delphi で作成されることを想定しています。異なる言語のモジュール間でデータを渡すのは、特に文字列のような動的データの場合、注意が必要です。

これが、複雑なデータ構造を扱うときに Windows API を扱うのが面倒なことが多い理由です。また、Microsoft の COM (OLE) が独自のメモリ管理関数と特殊な型を提供する理由の 1 つでもあります。目標は、異なるソースからコンパイルされたモジュール間のバイナリ互換性です。

したがって、Windows は以前にそれを行っていたので、Windows が行う 2 つの方法のいずれかを使用できます。また:

1) C スタイルの API (PChars など) を公開し、細心の注意を払って API を指定します。クライアントが呼び出す必要があるメモリ割り当てルーチンを公開するか、クライアントに割り当てを行わせることができます。Windows API は、異なるタイミングで両方を実行します。クライアントは、モジュールと便利に通信するために SDK を必要とする場合もあります。また、stdcall 呼び出し規約を統一して使用することを忘れないでください。

または、

2) COM 型を使用してデータを送受信します。Delphi は、優れた、ほぼ透過的な COM サポートを備えています。たとえば、文字列の場合、COM の BSTR (Delphi では WideString) を使用できます。

お役に立てれば。

于 2010-08-12T10:27:37.647 に答える
2

何が起こるかは基本的にこれです。個別にコンパイルされたすべてのコード (DLL または EXE) には、メモリ マネージャーと呼ばれる、システムからメモリを割り当てて管理する独自のコードが含まれています。簡単に言えば、そのコードが初期化されると、システムから大きなメモリ ブロックが割り当てられます。その後、GetMem を実行したり、文字列や配列などを割り当てたりするときに、メモリ マネージャーはその大きなブロックの一部を使用済みとしてマークします。それらを FreeMem/deallocate すると、それらは未使用としてマークされます。

ここで、EXE と DLL があり、どちらも独自のメモリ マネージャーを持っているとします。EXE は DLL プロシージャを呼び出し、DLL は文字列 (PChar) を割り当て、グランド メモリ ブロックの一部を使用済みとしてマークします。次に、ポインターを EXE に返します。EXE はそれを使用し、後で解放することを決定します。EXE はそれ自身のメモリ マネージャへのポインタを与え、それを解放するように要求しますが、それは EXE のグランド メモリ ブロックからでもありません! EXE のメモリ マネージャは、他のユーザーのメモリを「解放」する方法を知りません。

そのため、DllReleaseString() を呼び出して、借用したメモリ ポインタを DLL に返し、DLL 自身の内部メモリ マネージャがそれを解放できるようにする必要があります。

ここで、共有メモリ マネージャーが行うことは、相互に接続することです。DLL 内のメモリ マネージャーと EXE 内のメモリ マネージャーは互いに通信する方法を知っており、DLL のメモリ ポインターを EXE のメモリ マネージャーに渡すと、それが DLL からのものであることを認識し、DLL メモリ マネージャーに解放させます。もちろん、これは DLL と EXE メモリ マネージャの両方が同じメモリ マネージャ コードから構築されている場合にのみ可能です (そうでなければ、お互いを認識できません!)。DLL メモリ マネージャが共有メモリ マネージャで、EXE メモリ マネージャが別のものである場合、DLL メモリ マネージャは EXE にメモリの解放を「要求」できず、EXE メモリ マネージャは試行さえしません (共有ではありません)。

したがって、DLL をユニバーサルにしたい場合は、メモリ マネージャーが相互に通信することに依存することはできません。DLL は、別のメモリ マネージャーに依存する EXE または DLL と共に使用される可能性があり、まったく別の言語で記述されている可能性があります。プロジェクトのすべての部分を制御し、どこでも同じマネージャーを明示的に設定できる場合にのみ、メモリ マネージャーを共有できます。

于 2010-08-11T07:16:09.290 に答える