Ich versuche, eine nicht verwaltete C-DLL zum Laden von Bilddaten in eine C# -Anwendung zu verwenden. Die Bibliothek hat eine ziemlich einfache Schnittstelle, in der Sie eine Struktur übergeben, die drei Rückrufe enthält, einen, um die Größe des Bildes zu erhalten, einen, der jede Pixelreihe empfängt, und schließlich einen, der aufgerufen wird, wenn der Ladevorgang abgeschlossen ist. Wie dies (C# verwaltet Definition):Fixieren eines Delegaten innerhalb einer Struktur vor der Übergabe an nicht verwalteten Code
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct st_ImageProtocol
{
public st_ImageProtocol_done Done;
public st_ImageProtocol_setSize SetSize;
public st_ImageProtocol_sendLine SendLine;
}
Die Typen Start st_ImageProtocol sind delgates:
public delegate int st_ImageProtocol_sendLine(System.IntPtr localData, int rowNumber, System.IntPtr pixelData);
Mit der Test-Datei, die ich die SetSize verwenden sollte einmal aufgerufen werden, dann wird die SendLine erhalten 200 Mal aufgerufen (einmal für jede Pixelreihe im Bild), wird schließlich der Fertig-Callback ausgelöst. Was tatsächlich passiert ist, dass die SendLine 19 Mal aufgerufen wird und dann eine AccessViolationException ausgelöst wird, die behauptet, dass die Bibliothek versucht hat, auf geschützten Speicher zuzugreifen.
Ich habe Zugriff auf den Code der C-Bibliothek (obwohl ich die Funktionalität nicht ändern kann) und während der Schleife, wo es die SendLine-Methode aufruft es keinen neuen Speicher zuweist oder freigibt, so meine Annahme ist, dass die Delegate selbst ist das Problem und ich muss es anheften, bevor ich es übergebe (ich habe derzeit keinen Code innerhalb des Delegaten selbst, außer einem Zähler, um zu sehen, wie oft er aufgerufen wird, also bezweifle ich, dass ich irgendetwas auf der verwalteten Seite breche). Das Problem ist, dass ich nicht weiß, wie ich das machen soll; Die Methode, die ich verwendet habe, um die Strukturen in nicht verwaltetem Raum zu deklarieren, funktioniert nicht mit Delegaten (Marshal.AllocHGlobal()) und ich kann keine andere geeignete Methode finden. Die Delegaten selbst sind statische Felder in der Program-Klasse, daher sollten sie nicht als Garbage Collected betrachtet werden, aber ich nehme an, dass die Runtime sie verschieben kann.
This blog entry by Chris Brumme sagt, dass die Delegierten müssen nicht vor dem in den nicht verwalteten Code übergeben gepinnt werden wird:
Klar, dass die nicht verwalteten Funktionszeiger auf eine feste Adresse beziehen. Es wäre eine Katastrophe, wenn der GC das verlagern würde! Dies führt dazu, dass viele Anwendungen ein Pinning-Handle für den Delegaten erstellen. Das ist völlig unnötig. Der nicht verwaltete Funktionszeiger verweist auf einen systemeigenen Code-Stub, den wir dynamisch erzeugen, um den Übergang & Marshalling durchzuführen. Dieser Stub existiert in einem festen Speicher außerhalb des GC-Heaps.
Aber ich weiß nicht, ob dies gilt, wenn der Delegat Teil einer Struktur ist. Es bedeutet jedoch, dass es möglich ist, sie manuell zu pinnen, und ich bin daran interessiert, wie ich dies oder irgendeinen besseren Vorschlag machen kann, warum eine Schleife 19 mal laufen würde und plötzlich scheitern würde.
Danke.
Edited Johans Fragen ... Antwort
Der Code, der die Struktur ordnet wie folgt:
_sendLineFunc = new st_ImageProtocol_sendLine(protocolSendLineStub);
_imageProtocol = new st_ImageProtocol()
{
//Set some other properties...
SendLine = _sendLineFunc
};
int protocolSize = Marshal.SizeOf(_imageProtocol);
_imageProtocolPtr = Marshal.AllocHGlobal(protocolSize);
Marshal.StructureToPtr(_imageProtocol, _imageProtocolPtr, true);
Wo die _sendLineFunc und die _imageProtocol Variablen beide statischen Felder des Programms sind Klasse. Wenn ich die Interna dieses richtig verstehe, bedeutet das, dass ich einen nicht verwalteten Zeiger an eine Kopie der _imageProtocol-Variable in die C-Bibliothek übergebe, aber diese Kopie enthält einen Verweis auf die statische _sendLineFunc. Dies sollte bedeuten, dass die Kopie nicht vom GC berührt wird - da sie nicht verwaltet wird - und der Delegat nicht erfasst wird, da er noch im Bereich ist (statisch).
Die struct tatsächlich in die Bibliothek als Rückgabewert von einem anderen Callback übergeben werden, aber als Zeiger:
private static IntPtr beginCallback(IntPtr localData, en_ImageType imageType)
{
return _imageProtocolPtr;
}
Grundsätzlich gibt es einen anderen struct-Typen, der die Bilddateinamen und die Funktionszeiger auf diesen Rückruf hält Die Bibliothek ermittelt, welcher Bildtyp in der Datei gespeichert ist, und verwendet diesen Rückruf, um die richtige Protokollstruktur für den angegebenen Typ anzufordern. Mein Dateinamen Struktur wird in der gleichen Weise wie das Protokoll einer oben angegebenen und verwaltet werden, so ist es wahrscheinlich die gleichen Fehler enthält, aber da dieser Delegierte nur einmal aufgerufen wird und rief schnell Ich habe keine Probleme mit ihm noch nicht gehabt.
Edited für ihre Antworten
Danke an alle zu aktualisieren, aber nach ein paar weiteren Tagen auf dem Problem und machen keine Fortschritte Ausgaben habe ich beschlossen, es beiseite zu legen. Falls jemand interessiert ich ein Tool für die Nutzer der Lightwave 3D-Rendering-Anwendung und ein nettes Feature die Möglichkeit, die verschiedenen Bildformate anzuzeigen schreiben versucht hat, die Lichtwellenstützen (einige davon sind ziemlich exotische) gewesen wäre. Ich dachte, dass der beste Weg dies zu tun wäre, einen C# -Wrapper für die Plugin-Architektur zu schreiben, die Lightwave für die Bildbearbeitung verwendet, so dass ich ihren Code verwenden könnte, um die Dateien tatsächlich zu laden. Leider nach einer Reihe von Plugins, gegen meine Lösung versuchen, hatte ich eine Vielzahl von Fehlern, die ich nicht verstehen konnte oder beheben und meine Vermutung ist, dass die Lichtwelle nicht die Methoden in üblichen Weise auf den Plugins nicht nennen, wahrscheinlich um die Sicherheit zu verbessern von laufendem externen Code (wilder Stich in der Dunkelheit, gebe ich zu). Vorläufig werde ich die Bildfunktion fallen lassen und wenn ich mich entscheide, sie wiederherzustellen, werde ich sie auf andere Weise angehen.
Nochmals vielen Dank, habe ich viel gelernt durch diesen Prozess, auch wenn ich nicht das Ergebnis bekommen habe ich wollte.
Danke für die Fragen - Ich habe meine Frage mit einigen weiteren Details bearbeitet –