1

アンマネージ C++ ライブラリがあります。.NET アプリケーションの機能を公開したいと考えています。処理方法がわからない特定の関数が1つあります。

typedef void (free_fn*) (void*); void put (void *data, free_fn deallocation_function);

アイデアは、動的に割り当てられたバッファーを関数に渡し、割り当て解除関数を提供することです。ライブラリはデータを非同期的に処理し、後でデータが不要になったときにバッファを解放します。

ボイド *p = malloc (100); ... バッファを埋める... put (p, free);

この種のものを .NET アプリケーションに公開するにはどうすればよいですか?

4

7 に答える 7

5

これを行うときは十分に注意してください。.NETは、実際には、オブジェクトをアンマネージルーチンへの途中で固定し、途中で固定を解除することを望んでいます。アンマネージコードが途中で固定されたポインタ値を保持している場合、メモリが移動されるか、ガベージコレクションされるか、またはその両方が発生する可能性が非常に高くなります。

これは、関数ポインタにマーシャリングされたデリゲートの場合に特に当てはまります(これについては信頼してください。マーシャリングされたデリゲートがガベージコレクションされていることがわかりました。Microsoftの担当者に確認してもらいました)。この問題の最終的な解決策は、一意のトランザクションIDとペアになっている静的テーブルにデリゲートのコピーを隠し、呼び出されたときにトランザクションIDを介してテーブル内のデリゲートを検索して実行するアンマネージ関数を作成することです。それは醜いです、そして私が別の選択肢を持っていたら、私はそれを使ったでしょう。

この場合、これを行うための最良の方法は次のとおりです。アンマネージコードはset itを使用し、モデルを忘れるため、APIをよりチャンクにする必要があります。アンマネージルーチンを介してメモリを割り当てるラッパーをマネージC++で作成し、データをそのラッパーにコピーしてから、アンマネージデアロケーターへのポインターとともに渡します。

于 2009-01-22T21:37:02.447 に答える
2

一般に、ライブラリの .NET コンシューマーは、動的に作成された配列を関数に渡しません。私の知る限り、.NET のすべてのコンテナーはガベージ コレクションされています。

とにかく、アンマネージ コードのマネージ ラッパーを作成する必要があります。これには多くのチュートリアルと記事がありますが、ここから始めましょう

管理されていないコードの .NET ラッパーを作成するとき、.NET ですべての関数にアクセスできるようにすることよりも、機能を維持することに集中したいことがわかりました。あなたの例では、マネージ ラッパーで配列をアンマネージ メモリにコピーし、ライブラリ内で必要な操作を実行する方がよい場合があります。この方法では、.NET ランタイムのガベージ コレクションを回避するために、マネージ メモリのピン留めや、マネージ メモリからアンマネージ メモリへのマーシャリングを行う必要がありません。ただし、マネージ ラッパーの実装方法は、その関数の目的によって異なります。

本当にこの関数を .NET の関数に実装したい場合は、.NET の Marshal クラスを調べて、アンマネージ コードでマネージ メモリを制御する必要があります。

コールバック関数については、最初にマネージ コードで割り当てることができる .NET デリゲートを作成する必要があります。次に、管理されていないバージョンの put 関数によって呼び出される、ライ​​ブラリ内部の管理されていない無料の関数を作成する必要があります。このアンマネージド フリー関数は、ユーザーが割り当てた場合、マネージド デリゲートの呼び出しを担当します。

于 2009-01-22T21:08:08.620 に答える
1

ほとんどの回答は、マネージド バッファからアンマネージド バッファにデータをコピーする必要があることを示唆しています。どのように正確にそれをしますか?以下の実装はOKですか?

void managed_put (byte data_ __gc[], size_t size_)
{
    //  Pin the data
    byte __pin *tmp_data = &data_[0];

    //  Copy data to the unmanaged buffer.
    void *data = malloc (size_);
    memcpy (data, (byte*) tmp_data, size_);

    //  Forward the call
    put (data, size_, free);
}
于 2009-01-22T23:24:03.120 に答える
1

アンマネージ コードで割り当てを解除しようとすると、狂気への最短ルートのように見えるため、マネージ バッファーを固定したくないことは間違いありません。この部分を完全に管理されたコードで書き直すことができない場合、最善の策は、ラッパーでデータのコピーを作成するか、管理された世界からバッファ管理を完全に隠すことです。

根性 (およびマゾヒスティックなスタミナ) がある場合は、ラッパーでバッファーを固定し、バッファーの固定を解除するマネージ関数のマーシャリングされたデリゲートを渡すことができます。しかし、私はそれをお勧めしません。マネージ ラッパーをいくつか実行しなければならなかった経験から、マネージ コードの一部を書き直す必要があるとしても、アンマネージ機能を最小限に公開することの価値を学びました。その境界を越えることは、東ドイツから西ドイツに行くのと同じくらい簡単で、パフォーマンスのヒットは言うまでもありません。

于 2009-01-22T21:51:15.697 に答える
1

以前の投稿者の一部は、廃止された MC++ を使用しています。C++/CLI は、はるかに洗練されたソリューションです。

相互運用のための最良の手法は、明示的ではなく暗黙的な相互運用です。これについてまだ誰もコメントしていないと思います。ただし、マネージド <-> ネイティブから型をマーシャリングする機能が提供されます。型定義または構造レイアウトを変更しても、重大な変更は発生しません (明示的な相互運用が行われます)。

このウィキペディアの記事は、いくつかの相違点を文書化しており、詳細情報の出発点として適しています。

P/Invoke (明示的および暗黙的)

また、サイトmarshal-as.netには、この新しい方法に関するいくつかの例と情報があります (ここでも、ネイティブ構造体が再定義されてもコードが壊れないため、より理想的です)。

于 2009-02-21T03:44:09.947 に答える
0

関数自体のマネージド ラッパー (マネージド関数を渡したい場合はアンマネージド ラッパー) が必要です。または、アンマネージ関数ポインターをマネージ ワールドの不透明なハンドルとして扱います。

于 2009-01-22T20:33:33.713 に答える
0

あなたはそれが非同期であると述べたので、私はこのようにします。.Net 公開関数はデータのみを受け取り、デリゲートは受け取りません。コードは、ピン留めされたデータと関数ポインターを関数に渡し、データのピン留めを解除するだけです。これにより、メモリのクリーンアップは GC に任せられますが、非同期部分が完了するまでメモリがクリーンアップされないようにします。

于 2009-01-22T23:03:23.943 に答える