2011-12-30 6 views
16

私はC++/CLIデリゲートを試しています(.NETリファレンスライブラリを作成しようとしています)。関数ポインタ(System.AccessViolationException)としてC++/CLIデリゲート

C++/CLIでデリゲートを定義してから、C#でデリゲートのインスタンスを作成し、関数ポインタ経由でアンマネージC++を通じてデリゲートのインスタンスを呼び出します。これはすべて期待通りに機能します。

コードは

using System; 

namespace TestProgram 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      Library.Test.MessageDelegate messageDelegate = new Library.Test.MessageDelegate(Message); 
      Library.Test test = new Library.Test(messageDelegate); 
      test.WriteMessage(); 
      Console.Read(); 
     } 

     static void Message() 
     { 
      Console.WriteLine(1024); 
     } 
    } 
} 

次の私のマネージC++ファイル(Managed.cpp)

#include "Unmanaged.hpp" 
#include <string> 

namespace Library 
{ 
    using namespace System; 
    using namespace System::Runtime::InteropServices; 

    public ref class Test 
    { 
    public: 
     delegate void MessageDelegate(); 
    internal: 
     MessageDelegate^ Message; 
     void* delegatePointer; 

    public: 
     Test(MessageDelegate^ messageDelegate) 
     { 
      delegatePointer = (void*)Marshal::GetFunctionPointerForDelegate(messageDelegate).ToPointer(); 
     } 

     void WriteMessage() 
     { 
      Unmanaged::WriteMessage(delegatePointer); 
     } 
    }; 
} 

そして、私のアンマネージドC++ファイル(Unmanaged.cppを)この(最初に私のC#)を説明するために、

#include "Unmanaged.hpp" 

namespace Unmanaged 
{ 
    typedef void (*WriteMessageType)(); 
    WriteMessageType WriteMessageFunc; 

    void WriteMessage(void* Function) 
    { 
     WriteMessageType WriteMessageFunc = (WriteMessageType)(Function); 
     WriteMessageFunc(); 
    } 
} 

このコードはすべて期待どおりに動作し、メソッド()がdelへの関数ポインタによって呼び出されるため、出力は "1024"です。 egateメソッド。

私の問題は、引数を持つデリゲートを使って同じメソッドを適用しようとすると発生します。デリゲートvoid MessageDelegate(int number);次のように

私のコードは(C#)となりました:私は実行すると

#include "Unmanaged.hpp" 

namespace Unmanaged 
{ 
    typedef void (*WriteMessageType)(int number); 
    WriteMessageType WriteMessageFunc; 

    void WriteMessage(void* Function, int number) 
    { 
     WriteMessageType WriteMessageFunc = (WriteMessageType)(Function); 
     WriteMessageFunc(number); 
    } 
} 

#include "Unmanaged.hpp" 
#include <string> 

namespace Library 
{ 
    using namespace System; 
    using namespace System::Runtime::InteropServices; 

    public ref class Test 
    { 
    public: 
     delegate void MessageDelegate(int number); 
    internal: 
     MessageDelegate^ Message; 
     void* delegatePointer; 

    public: 
     Test(MessageDelegate^ messageDelegate) 
     { 
      delegatePointer = (void*)Marshal::GetFunctionPointerForDelegate(messageDelegate).ToPointer(); 
     } 

     void WriteMessage(int number) 
     { 
      Unmanaged::WriteMessage(delegatePointer, number); 
     } 
    }; 
} 

そして、私のアンマネージドC++ファイル:

using System; 

namespace AddProgram 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      Library.Test.MessageDelegate messageDelegate = new Library.Test.MessageDelegate(Message); 
      Library.Test test = new Library.Test(messageDelegate); 
      test.WriteMessage(1024); 
      Console.Read(); 
     } 

     static void Message(int number) 
     { 
      Console.WriteLine(number); 
     } 
    } 
} 

私はC++のファイルを管理しますプログラムに次のエラーが表示されます:

未処理のライブラリ "System.AccessViolationException"の未処理の例外が発生しました。

追加情報:保護されたメモリを読み書きしようとしました。これはしばしば、他のメモリが壊れていることを示します。

ちなみに、コンソールウィンドウには1024が表示されますが、ランダムなint(〜1000000)が続き、エラーが表示されます。

私はこのエラーが発生する理由のいくつかを想像し始めることができますが、わかりませんし、見つけにくいです。なぜこのエラーが出るのか誰にでも教えてもらえれば、それを修正するために何ができるのでしょうか、私は大いに感謝します。

+0

よく質問された質問 –

答えて

11
void WriteMessage(void* Function, int number) 

void *としての関数ポインタの受け渡しはかなり悪い考えです。これはコンパイラが何か間違っていることをチェックするのを防ぎます。コンパイラがこの特定のケースでそれを検出することはできませんが、何か間違っています。デリゲートは、__stdcall呼び出し規約を使用する関数ポインタとしてマーシャリングされます。実際の関数ポインタは、ネイティブコードのデフォルトである__cdecl呼び出し規約を使用します。これにより、スタック上のコールの不均衡が発生します。

[UnmanagedFunctionPointer] attributeをデリゲート宣言に適用して修正するには、CallingConvention :: Cdeclを指定します。

+0

ありがとうございます。 – xcvd

+4

まだスタックにプッシュされているものがないので、何の違いもありません。 –

1

デリゲートから作成された関数ポインタはガベージコレクタには表示されず、到達可能性分析時にはカウントされません。 the documentationから

:デリゲートが収集されている場合は

You must manually keep the delegate from being collected by the garbage collector from managed code. The garbage collector does not track reference [sic] to unmanaged code.

、関数ポインタはダングリング残っている、とあなたのプログラムがひどく動作します。アクセス違反は、可能性の高い結果の1つですが、唯一の可能性ではありません。ネイティブ/マネージトランポリンを含むメモリが他のデータ用に再利用された場合、CPUはそれを命令として解釈しようとする可能性があります。

解決策は、たとえば、C++/CLI gcrootクラス(.NET GCHandleを中心に薄いラッパー)を使用してデリゲートを到達可能にすることです。

+0

私はGCHandleを使用して問題を解決できませんでした。デリゲートにGCHandleを割り当てても同じエラーが発生するので、ガベージコレクタの問題ではないと思います。 – xcvd

+0

また、/ cliでUnmanaged.cppをコンパイルするとすぐに問題を解決する必要があります。 – xcvd

関連する問題