2014-09-22 114 views
2

这里是我的C代码:保持PInvoked方法活着

LIBRARY_API bool __cdecl Initialize(void (*FirstScanForDevicesDoneFunc)(void)); 

,这里是C#的PInvoke代码与此DLL工作:

[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()回调时我得到NullRefer enceException。所以我用螺纹保持它和一切工作正常,但我不满意这个解决方案:

static public bool Working = true; 

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

      Initialize(FirstScanForDevicesDone); 
      while (Working) 
      { 
       Thread.Sleep(1000); 
      } 
     }).Start(); 
    } 

有没有一种更复杂的方法来保持这方面的工作?

+0

我们首先需要了解问题。为什么你有'NullReferenceException'?本地代码在做什么?什么时候回叫? – 2014-09-22 11:29:54

+1

为本地代码定义托管回调的标准方法是使用Marshal.GetFunctionPointerForDelegate方法。 – 2014-09-22 11:40:40

+0

@AlexFarber它看起来你以前的评论工作:保持回调函数的静态变量。这意味着回调正在收集。我现在感到很蠢。 – 2014-09-22 11:46:38

回答

10

所以我用螺纹保持它和一切工作正常

没有,该线程实际上并没有解决问题。它只有看起来像,它是测试Debug版本和使用调试器的副作用。将发布版本发布给客户后,它仍会以完全相同的方式崩溃。当然最糟糕的一种失败。为什么它似乎像线程解决它在this post解释。

您需要修复真正的问题,即将委托对象传递给本机代码,但在Init()方法完成后,不再有可见的对象引用。垃圾收集器无法在本机代码内部窥视以查看它正在使用中。所以下一个垃圾回收会在本机代码在回调之后进行回调时破坏对象kaboom。请注意,您通常会从调试助手那里得到措辞强烈的警告,请确保您没有通过拍摄信使解决问题。

适当解决方法是:

static FirstScanForDevicesDoneFunc callbackDelegate; 

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

callbackDelegate变量确保GC总能看到的对象的引用。当本机代码无法再回调时,您将其设置为空。通常从不。

+0

谢谢 - 这是一个伟大而简单的解决方案。我感到很蠢,以前没有注意到这一点。 – 2014-09-22 11:52:21