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 ...
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? –
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 . –
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! –