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