2012-11-01 13 views
5

Ich schreibe gerade eine Software in Visual Studio 2012 für die Kommunikation mit RFID-Karten. Ich habe eine DLL in Delphi geschrieben, um die Kommunikation mit dem Kartenleser zu handhaben.C# -Anwendung mit unmanaged DLL friert ganzes System ein

Das Problem ist: Meine Software läuft gut auf Maschinen, die VS2012 installiert haben. Auf anderen Systemen friert es sich selbst oder das ganze System ein. Ich habe es auf Win XP/7/8 mit x32 und x64-Konfiguration versucht. Ich benutze .NET 4.0.

Nach dem Anschluss an den Leser startet die Software einen backgroundWorker, der den Reader mit einem Befehl zur Inventur von Karten im Leser-RF-Feld abfragt (bei 200ms Geschwindigkeit). Der Absturz passiert normalerweise ca. 10 bis 20 Sekunden nach dem Anschluss des Readers. Hier ist der Code:

[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"; 
    } 

Die DLL in Delphi geschrieben wird, wird der Code DLL Befehl lautet:

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

Ich denke, es irgendwo ein Speicherverlust sein kann, aber ich habe keine Ahnung, wie es zu finden ...


EDIT:

Ich habe einige Ideen (explizit GC.Collect(), try-catch-finally) zu meinem obigen Code hinzugefügt, aber es funktioniert immer noch nicht.

Hier ist der Code, der getCardID() Aufrufe:

Die Aktion, das alle 200ms läuft:

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

Async Background tut:

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); 
      } 
     } 
    } 

EDIT

Ti ll jetzt habe ich ein paar kleine Verbesserungen (Updates oben) am Code vorgenommen. Jetzt nur die App. stürzt mit 0xC00000FD (Stack overflow) bei "tempConnect.dll" ab. Dies passiert nicht auf Systemen mit VS2012 installiert oder wenn ich die DLL mit nativen Delphi verwenden! Hat jemand noch andere Ideen?


EDIT

Jetzt habe ich die DLL es ist stack und fand seltsam etwas Anmeldung: Wenn es und von meinem C# Programm abgefragt genannt wird, wird der stack kontinuierlich nach oben und unten zu verändern. Wenn ich dasselbe von einem natürlichen Depli-Programm mache, ist die Stackgröße konstant! Also werde ich weitere Untersuchungen machen, aber ich habe keine wirkliche Idee, wonach ich suchen muss ...

+0

Nur ein Zweifel, ich glaube nicht, dass es mit Ihrem Problem zusammenhängt, aber indem Sie den Code nach Aufruf der 'inventory()' Funktion analysieren, prüfen Sie, ob UIDS nicht null ist. Dieses Konstrukt schlägt vor, dass der Wert von UIDS innerhalb der Funktion 'inventory' auf null gesetzt werden sollte. Wenn das der Fall ist - sollten Sie nicht die UIDS als Referenz verwenden? –

+0

Der aktuelle Code kann undicht werden, wenn eine Ausnahme im innersten try-catch-Handler ausgelöst wird, bevor der Aufruf von 'Marshal.FreeHGlobal()' erreicht wird. Ich würde vorschlagen, diesem Handler eine 'finally'-Klausel hinzuzufügen, in der der Aufruf von' Marshal.FreeHGlobal() 'erfolgt. Auf diese Weise stellen Sie sicher, dass der zugewiesene Speicher freigegeben wird, selbst wenn etwas schief gelaufen ist. Ich würde auch vorschlagen, den Fehler in der catch-Klausel zu protokollieren, da in diesem Moment nicht festgestellt werden kann, ob ein Rückgabewert von 'zero.ToString()' ein berechtigter Wert ist, der von 'inventory()' zurückgegeben wurde, oder etwas schief gelaufen ist . –

+0

Könnten Sie bitte den Code posten, der 'getCardID()' aufruft? Der Grund, warum ich das frage, ist nur zu überprüfen, dass es keine Möglichkeit gibt, dass inventory() gleichzeitig von mehr als einem Thread aufgerufen werden kann. Wenn es nicht thread-sicher ist, könnte das ein Grund für das Einfrieren sein, das Sie erfahren. Prost! –

Antwort

1

Ich bin ein wenig besorgt darüber, wie das Marshal Objekt verwendet wird. Wie Sie mit dem Speicherleck fürchten, scheint es, Speicher ziemlich häufig zuzuordnen, aber ich sehe es nicht ausdrücklich, es freizugeben. Der Müllsammler sollte (operatives Wort) dafür sorgen, aber Sie sagen, Sie haben einige unmanaged Code in der Mischung. Es ist schwierig mit den veröffentlichten Informationen zu sagen, wo der nicht verwaltete Code beginnt.

Überprüfen Sie this question für einige gute Techniken zum Auffinden von Speicherlecks in .NET selbst - das wird Ihnen eine Menge Informationen darüber geben, wie Speicher im verwalteten Ende Ihres Codes verwendet wird (das heißt, den Teil, den Sie direkt können Steuerung). Verwenden Sie den Windows-Systemmonitor mit Haltepunkten, um den allgemeinen Zustand des Systems im Auge zu behalten. Wenn .NET sich verhält, aber WPM einige scharfe Spitzen zeigt, ist es wahrscheinlich im nicht verwalteten Code. Sie können wirklich nichts als Ihre Verwendung dort kontrollieren, daher wäre es wahrscheinlich an der Zeit, zu diesem Zeitpunkt zu der Dokumentation zurückzukehren.