2014-09-22 90 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