2012-11-01 12 views
5

Attualmente sto scrivendo un software in Visual Studio 2012 per la comunicazione con le schede RFID. Ho una DLL scritta in Delphi per gestire la comunicazione con il lettore di schede.L'applicazione C# con DLL non gestita blocca l'intero sistema

Il problema è: il mio software funziona correttamente su macchine che hanno installato VS2012. Su altri sistemi si congela o l'intero sistema. L'ho provato su Win XP/7/8 con configurazione x32 e x64. Sto usando .NET 4.0.

Dopo il collegamento al lettore, il software avvia un backgroundWorker, che esegue il polling (a una velocità di 200ms) del lettore con un comando per l'inventario delle carte nel campo RF dei lettori. Lo schianto avviene di solito ca. 10 a 20 secondi dopo la connessione del lettore. Ecco il codice:

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

La DLL è scritto in Delphi, il comando di codice DLL è:

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

Penso che ci può essere una perdita di memoria da qualche parte, ma non ho idea come trovarlo ...


EDIT:

Ho aggiunto alcune idee (esplicito GC.Collect(), try-catch-finally) al mio codice sopra, ma ancora non funziona.

Ecco il codice, che chiama getCardID():

L'azione, che viene eseguito ogni 200 ms:

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

Async BackgroundWorker fa:

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 ora ho fatto alcune piccole rielaborazioni (aggiornamenti sopra) al codice. Ora solo l'app. si arresta in modo anomalo con 0xC00000FD (overflow dello stack) in "tempConnect.dll". Questo non accade sui sistemi con VS2012 installato o se uso la DLL con Delphi nativo! Qualcuno ha altre idee?


EDIT

Ora ho fatto la DLL di registrazione è stacksize e hanno trovato qualcosa di strano: Se si chiama e interrogato dal mio C# Programma, lo stacksize sta cambiando continuamente su e giù. Se faccio lo stesso da un programma Deplhi naturale, lo stacksize è costante! Quindi farò ulteriori indagini, ma non ho davvero idea, cosa devo cercare ...

+0

Un solo dubbio, non penso sia collegato al tuo problema, ma analizzando il codice dopo aver chiamato la funzione 'inventory()', stai verificando se UIDS non è nullo. Questo costrutto suggerisce che il valore di UIDS potrebbe essere impostato su null all'interno della funzione 'inventory'. Se è così, non dovresti passare l'UIDS per riferimento? –

+0

Il codice corrente potrebbe perdere se è stata generata un'eccezione all'interno del gestore try-catch più interno, prima che venga raggiunta la chiamata a "Marshal.FreeHGlobal()". Suggerirei di aggiungere a questo gestore una clausola 'finally' in cui è stata eseguita la chiamata a' Marshal.FreeHGlobal() '. In questo modo si garantisce che la memoria allocata venga liberata anche se qualcosa è andato storto. Suggerirei anche di registrare l'errore all'interno della clausola catch, dal momento che in questo momento non si può sapere se un valore di ritorno di 'zero.ToString()' è un valore legittimo restituito da 'inventory()', o qualcosa è andato storto . –

+0

Potresti per favore postare il codice che sta chiamando 'getCardID()'? Il motivo per cui lo chiedo è solo per verificare che non vi è alcuna possibilità che inventory() possa essere chiamato contemporaneamente da più di un thread. Se non è progettato per essere thread-safe, questo potrebbe essere un motivo per i blocchi che stai riscontrando. Saluti! –

risposta

1

Sono un po 'preoccupato di come stai usando quell'oggetto Marshal. Come temete con la perdita di memoria, sembra che stia allocando memoria abbastanza spesso ma non lo vedo mai esplicitamente rilasciandolo. Il netturbino dovrebbe essere il (parola operativa) a prendersi cura di questo, ma tu dici di avere qualche codice non gestito nel mix. È difficile con le informazioni pubblicate dire dove inizia il codice non gestito.

Dai un'occhiata a this question per alcune buone tecniche per trovare perdite di memoria in .NET stesso - questo ti darà un sacco di informazioni su come la memoria viene utilizzata nella parte gestita del tuo codice (cioè, la parte che puoi direttamente controllo). Utilizzare Windows Performance Monitor con i punti di interruzione per tenere d'occhio lo stato generale del sistema. Se sembra che .NET si stia comportando, ma WPM mostra alcuni picchi di punta, è probabilmente nel codice non gestito. Non si può controllare veramente nulla se non il proprio uso lì, quindi sarebbe probabilmente il momento di tornare alla documentazione a quel punto.