0

次の方法で C++ dll があります。

//C++ dll method (external)
GetServerInterface(ServerInterface* ppIF /*[OUT]*/)
{
//The method will set ppIF
}

//ServerInterface is defined as:
typedef void *  ServerInterface;

C# プロジェクトから dll にアクセスするために、C++/CLI プロジェクトを作成し、マネージド クラスを次のように宣言しました。

public ref class ComWrapperManager
{
//
//
ServerInterface _serverInterface;
void Connect();
//
//
}

以下に示すように、Connect() メソッドを使用して GetServerInterface を呼び出します。最初の呼び出しは機能しますが、2 番目の呼び出しは機能しません。誰かが理由を説明できますか?そのポインターをマネージ クラスのメンバー変数として永続化する必要があります。これを行うより良い方法はありますか?

    void Connect()
    {
    ServerInterface localServerInterface;
    GetServerInterface(&localServerInterface); //THIS WORKS

    GetServerInterface(&_serverInterface); //THIS DOESNT

    //Error 1   error C2664: 'ServerInterface ' : 
    //cannot convert parameter 1 from //'cli::interior_ptr<Type>' 
    //to 'ServerInterface *'


    }
4

2 に答える 2

7

管理対象オブジェクトのメンバーへのポインターを渡しています。このようなポインターは特別で、内部ポインターと呼ばれます。それらはガベージ コレクターによって追跡され、GC がヒープを圧縮するときに管理対象オブジェクトが移動されると、ポインター値が変更されます。

問題は、そのポインターをアンマネージ コードに渡していることです。GC は、ネイティブ コードが使用しているポインター値のコピーを変更できません。別のスレッドがガベージ コレクションをトリガーすると、ネイティブ コードが実行され、ポインターが逆参照されると、災害が発生します。オブジェクトは元のアドレスには存在しません。ものすごく悪い。また、発生する可能性が非常に低いため、診断が非常に困難です。

コンパイラは、あなたがこの間違いを犯していることを確認できます。そしてC2664に文句を言います。

回避策は、GC によって移動されないメモリ位置に格納されているポインターを渡すことです。このような場所は非常に簡単に見つけることができ、ローカル変数が条件を満たしています。スタックに格納され、移動されることはありません。したがって、代わりに次のようにします。

void Connect()
{
    ServerInterface temp;
    GetServerInterface(&temp);
    this->_serverInterface = temp;
    // etc..
}

クラスメンバーを割り当てることを忘れないでください。

于 2013-09-09T12:33:00.043 に答える
1

2 つ目の方法を実行できない理由は次のとおり_serverInterfaceです。マネージ クラスの一部である void ポインターです。ガベージ コレクターが何をするか考えてみてください...管理対象オブジェクトをメモリ内で自由に移動できるため、void ポインターのアドレスは刻々と変化する可能性があります。したがって、そのアドレスを使用することは有効ではありません。

これには 2 つの解決策があります。

  1. お気づきのように、スタック変数のアドレスをアンマネージ メソッドに渡すことができます。マネージ オブジェクトとは異なり、ガベージ コレクターが処理を行ってもスタックは移動しないため、アドレスは変更されません。次に、スタック変数に格納されたデータを取得して、それをクラス フィールドにコピーできます。これは、そのアドレスを扱っていないため、正常に機能します。
  2. 他の回答者が指摘したように、管理対象オブジェクトをメモリにロックできます。移動できなくなったら、void ポインター フィールドのアドレスを問題なく取得できます。(彼は、C++/CLI 構文を探しているところに C# 構文を示しています。私はチェックするコンパイラではありませんが、C++/CLI 構文は同じではないと思います。)

2 つの解決策のうち、私はあなたが既に実装している #1 を好みます。解決策 #2 は、ガベージ コレクターが再配置しようとしている領域の中央に、移動できないメモリのブロックを導入します。選択肢が与えられたとしても、ガベージ コレクターの邪魔にならないようにします。

于 2013-09-09T11:32:25.743 に答える