保持PInvoked方法活着

Mik*_*e G 2 c# pinvoke dllimport

这是我的C代码:

    LIBRARY_API bool __cdecl Initialize(void (*FirstScanForDevicesDoneFunc)(void));
Run Code Online (Sandbox Code Playgroud)

这是使用此DLL的C#PINvoke代码:

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void FirstScanForDevicesDoneFunc();

    [DllImport("Native.dll", CallingConvention=CallingConvention.Cdecl)]
    public static extern bool Initialize(FirstScanForDevicesDoneFunc);
Run Code Online (Sandbox Code Playgroud)

当我这样调用这个方法时:

    static public void Init() {
        Initialize(FirstScanForDevicesDone);
    }

    static public void FirstScanForDevicesDone() {
        //do something
    }
Run Code Online (Sandbox Code Playgroud)

Init()回调结束时,我得到NullReferenceException.所以我使用Thread来保持它并且一切正常,但我对这个解决方案不满意:

    static public bool Working = true;

    static public void Init() {
        new Thread(() =>
        {
            Thread.CurrentThread.IsBackground = true;

            Initialize(FirstScanForDevicesDone);
            while (Working)
            {
                Thread.Sleep(1000);
            }
        }).Start();
    }
Run Code Online (Sandbox Code Playgroud)

是否有更复杂的方法来保持这种工作?

Han*_*ant 10

所以我使用Thread来保持它,一切正常

不,该线程实际上没有解决问题.它只是看起来像,测试Debug构建和使用调试器的副作用.将Release版本发布给客户后,它仍会以完全相同的方式崩溃.最糟糕的当然是失败.在这篇文章中解释了为什么线程似乎解决了它.

您需要解决实际问题,您将委托对象传递给本机代码,但在Init()方法完成后,对该对象不再有可见的引用.垃圾收集器无法查看本机代码以查看其中的使用情况.因此,当本机代码在此之后进行回调时,下一个垃圾收集将破坏对象kaboom.请注意,您通常会从调试器助手那里得到一个措辞强烈的警告,确保您没有通过拍摄信使来解决问题.

正确的解决方法是:

   static FirstScanForDevicesDoneFunc callbackDelegate;

   static public void Init() {
        callbackDelegate = new FirstScanForDevicesDoneFunc(FirstScanForDeviceDone);
        Initialize(callbackDelegate);
   }
Run Code Online (Sandbox Code Playgroud)

所述callbackDelegate变量确保GC总能看到的对象的引用.当本机代码不能再进行回调时,将其设置为null.通常从不.