2012-11-01 71 views
5

我目前正在寫一個軟件在Visual Studio 2012與RFID卡通信。 我得到了一個用Delphi編寫的DLL來處理與讀卡器的通信。C#應用程序與非託管DLL凍結整個系統

問題是:我的軟件在裝有VS2012的機器上運行良好。在其他系統上,它凍結了自己或整個系統。 我在配置x32和x64的Win XP/7/8上試過了。 我正在使用.NET 4.0。

連接到閱讀器後,軟件啓動一個backgroundWorker,該閱讀器使用命令在閱讀器射頻場中盤點卡片(以200毫秒的速率)輪詢閱讀器。美國的崩潰發生在約。讀取器連接10到20秒後。下面是代碼:

[DllImport("tempConnect.dll", CallingConvention = CallingConvention.StdCall)] 
private static extern int inventory(int maxlen, [In] ref int count, 
             IntPtr UIDs, UInt32 HFOffTime); 
public String getCardID() 
    { 
     if (isConnectet()) 
     { 
      IntPtr UIDs = IntPtr.Zero; 
      int len = 2 * 8; 
      Byte[] zero = new Byte[len]; 
      UIDs = Marshal.AllocHGlobal(len); 
      Thread.Sleep(50); 
      Marshal.Copy(zero, 0, UIDs, len); 
      int count = 0; 
      int erg; 
      String ret; 
      try 
      { 
       erg = inventory(len, ref count, UIDs, 50); 
      } 
      catch (ExternalException) // this doesn't catch anything (iI have set <legacyCorruptedStateExceptionsPolicy enabled="true"/>) 
      { 
       return "\0"; 
      } 
      finally 
      { 
       ret = Marshal.PtrToStringAnsi(UIDs, len); 
       IntPtr rslt = LocalFree(UIDs); 
       GC.Collect(); 
      } 
      if (erg == 0) 
       return ret; 
      else 
       return zero.ToString(); 
     } 
     else 
      return "\0"; 
    } 

的DLL是用Delphi編寫,代碼DLL命令是:

function inventory (maxlen: Integer; var count: Integer; 
        UIDs: PByteArray; HFOffTime: Cardinal = 50): Integer; STDCALL; 

我覺得有可能是內存泄漏的地方,但我不知道如何找到它......


編輯:

我在上面的代碼中添加了一些想法(顯式GC.Collect(),try-catch-finally),但它仍然不起作用。

下面是代碼,調用getCardID():

的操作,運行每200ms:

if (!bgw_inventory.IsBusy) 
    bgw_inventory.RunWorkerAsync(); 

異步BackgroundWorker的作用:

private void bgw_inventory_DoWork(object sender, DoWorkEventArgs e) 
    { 
      if (bgw_inventory.CancellationPending) 
     { 
      e.Cancel = true; 
      return; 
     } 
     else 
     { 
      String UID = reader.getCardID(); 
      if (bgw_inventory.CancellationPending) 
      { 
       e.Cancel = true; 
       return; 
      } 
      if (UID.Length == 16 && UID.IndexOf("\0") == -1) 
      { 
       setCardId(UID); 
       if (!allCards.ContainsKey(UID)) 
       { 
        allCards.Add(UID, new Card(UID)); 
       } 
       if (readCardActive || deActivateCardActive || activateCardActive) 
       { 
        if (lastActionCard != UID) 
         actionCard = UID; 
        else 
         setWorkingStatus("OK", Color.FromArgb(203, 218, 138)); 
       } 
      } 
      else 
      { 
       setCardId("none"); 
       if (readCardActive || deActivateCardActive || activateCardActive) 
        setWorkingStatus("waiting for next card", Color.Yellow); 
      } 
     } 
    } 

編輯

Ti現在我已經在代碼上做了一些小修改(上面的更新)。現在只有應用程序。在「tempConnect.dll」處與0xC00000FD(堆棧溢出)崩潰。 這不會發生在安裝了VS2012的系統上,或者如果我使用本機Delphi的DLL! 有沒有人有任何其他想法?


編輯

現在我所做的DLL記錄它的堆棧大小,並發現了一些奇怪的: 如果這就是所謂的從我的C#PROGRAMM調查,堆棧大小是不斷變化的上下。 如果我從一個自然的Deplhi程序做同樣的工作,stacksize是不變的! 所以我會做進一步調查,但我沒有真正的想法,我有什麼搜索...

+0

只有一個疑問,我不認爲它與你的問題有關,而是在調用inventory()函數後分析代碼,你正在檢查UIDS是否爲空。這個結構表明UIDS的值可能在'inventory'函數內設置爲null。如果是這樣的話 - 你不應該通過參考找到UIDS嗎? – 2012-11-01 18:18:49

+0

在調用Marshal.FreeHGlobal()之前,如果在最內層的try-catch處理程序中引發異常,則當前代碼可能會泄漏。我建議在這個處理程序中添加一個'finally'子句,其中調用Marshal.FreeHGlobal()'完成。這樣你可以確保分配的內存被釋放,即使出現了問題。我還建議在catch子句中記錄錯誤,因爲此時你不能分辨'zero.ToString()'的返回值是否是'inventory()'返回的合法值,或者出錯了。 – 2012-11-01 18:42:55

+0

請問您可以發佈調用'getCardID()'的代碼嗎?我問這個問題的原因只是爲了驗證inventory()不可能被多個線程同時調用。如果它不是線程安全的,那可能是您遇到凍結的原因。乾杯! – 2012-11-01 19:15:43

回答

1

我有點關心如何使用該Marshal對象。當你擔心內存泄漏時,它似乎經常分配內存,但我沒有看到它明確地釋放內存。垃圾收集器應該(操作詞)正在處理這個問題,但是你說你自己有一些非託管代碼。發佈的信息很難說明非託管代碼的起始位置。

檢查出this question尋找.NET本身內存泄漏的一些好技術 - 這會給你提供大量關於如何在代碼管理端使用內存的信息(也就是你可以直接使用的部分控制)。使用帶有斷點的Windows性能監視器來關注系統的整體健康狀況。如果.NET似乎表現得很好,但是WPM顯示出一些尖銳的尖峯,它可能在非託管代碼中。除此之外,你不能真正控制任何東西,所以現在可能是時候回到文檔了。

相關問題