Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/json/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 保持pinvoke方法的活力_C#_Pinvoke_Dllimport - Fatal编程技术网

C# 保持pinvoke方法的活力

C# 保持pinvoke方法的活力,c#,pinvoke,dllimport,C#,Pinvoke,Dllimport,这是我的C代码: LIBRARY_API bool __cdecl Initialize(void (*FirstScanForDevicesDoneFunc)(void)); 下面是使用此DLL的C#PINvoke代码: [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void FirstScanForDevicesDoneFunc(); [DllImport("Nati

这是我的C代码:

    LIBRARY_API bool __cdecl Initialize(void (*FirstScanForDevicesDoneFunc)(void));
下面是使用此DLL的C#PINvoke代码:

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

    [DllImport("Native.dll", CallingConvention=CallingConvention.Cdecl)]
    public static extern bool Initialize(FirstScanForDevicesDoneFunc);
当我这样调用此方法时:

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

    static public void FirstScanForDevicesDone() {
        //do something
    }
在Init()的末尾,当回调到来时,我得到NullReferenceException。因此,我使用线程来保持它,一切正常,但我不满意此解决方案:

    static public bool Working = true;

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

            Initialize(FirstScanForDevicesDone);
            while (Working)
            {
                Thread.Sleep(1000);
            }
        }).Start();
    }
有没有更复杂的方法让它继续工作

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

不,线程实际上并没有解决问题。它只是看起来像这样,这是测试调试构建和使用调试器的副作用。在您将发布版本发布给客户之后,它仍然会以完全相同的方式崩溃。当然是最糟糕的失败。中解释了线程解决问题的原因

您需要解决真正的问题,您正在将委托对象传递给本机代码,但是在Init()方法完成后,就不再有对该对象的可见引用。垃圾收集器无法窥视本机代码内部以查看其使用情况。因此,下一次垃圾收集将销毁对象,当本机代码在此之后进行回调时,kaboom。请注意,通常情况下,调试器助手会对此发出措辞强烈的警告,请确保您没有通过射击messenger来解决问题

正确的解决方法是:

   static FirstScanForDevicesDoneFunc callbackDelegate;

   static public void Init() {
        callbackDelegate = new FirstScanForDevicesDoneFunc(FirstScanForDeviceDone);
        Initialize(callbackDelegate);
   }

callbackDelegate变量确保GC始终可以看到对对象的引用。当本机代码无法再进行回调时,将其设置回null。通常不会。

我们首先需要了解问题所在。为什么要出现
NullReferenceException
?本机代码在做什么?何时调用回调?为本机代码定义托管回调的标准方法是使用Marshal.GetFunctionPointerForDelegate方法。@AlexFarber您之前的注释似乎有效:保留回调函数的静态变量。这意味着回调将在最后被收集。我现在觉得自己很笨。这里不需要
GetFunctionPointerForDelegate
。谢谢-这是一个很好的简单解决方案。我觉得自己很愚蠢,因为我以前没有注意到这一点。