読者です 読者をやめる 読者になる 読者になる

アンマネージ関数に渡したdelegateの中で、さらにアンマネージ関数にdelegateを渡すと落ちる

言葉では説明しにくいので、すごく端折ったコードで書くとこんな感じです。即席で書いているので間違っていたらすみません。

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が一枚噛んでいるようではあるのですが、じゃあどう直せばいいのかと言われるとさっぱり分からず、困り果てています。


どなたかご存知の方はご教授ください・・・