言葉では説明しにくいので、すごく端折ったコードで書くとこんな感じです。即席で書いているので間違っていたらすみません。
C++側の実装 (hoge.dll)
typedef void (_cdecl *CallbackA)(void); typedef int (_cdecl *CallbackB)(int a, int b); extern "C" __declspec(dllexport) void _cdecl Hoge(CallbackA callback) { callback(); } extern "C" __declspec(dllexport) void _cdecl Fuga(CallbackB callback) { printf("%d\n", callback(1, 2)); }
C#側の実装
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] delegate void CallbackA(); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] delegate int CallbackB(int a, int b); class Program { [DllImport("hoge.dll")] extern static void Hoge(CallbackA callback); [DllImport("hoge.dll")] extern static void Fuga(CallbackB callback); static void Main(string[] args) { for(int i=0; i<0xffff; i++) { Hoge( delegate(){ CallbackMethod(); } ); } } static void CallbackMethod() { Fuga( delegate(int a, int b){ return a.CompareTo(b); } ); } }
つまり、アンマネージ関数に渡したdelegateの中で、さらにアンマネージ関数にdelegateを渡して呼び出しています。なおここでは省略していますが、アンマネージ関数の実行中は渡したdelegateはGCHandleで動かないようにしています。
これはDebugモードで実行したときは何も起こらないのですが、Releaseモードで実行すると、しばらくは動作するものの、何度目かのCallbackMethodの呼び出しで
Process is terminated due to StackOverflowException.
と表示されて落ちます。StackOverflowというと再帰から抜けられなくなったなどが思い浮かびますが、一見それはなさそう。謎です。
ここで、CallbackMethodを以下のように書き換えると、1回目の呼び出しで落ちるようになります。エラーメッセージは同じです。
static void CallbackMethod() { Fuga( delegate{ Console.WriteLine("Fugafuga"); } ); GC.Collect(); // ここで死亡 }
よってまた例によってGCが一枚噛んでいるようではあるのですが、じゃあどう直せばいいのかと言われるとさっぱり分からず、困り果てています。
どなたかご存知の方はご教授ください・・・