Tipps für die Verwendung einer C-Bibliothek von C#
Ich habe eine library in C, die ich verwenden möchte von C#.
Von dem, was ich zusammengetragen aus dem internet, eine Idee ist, wickeln Sie es in eine C++ - dll, und DllImport dass.
Das problem ist, dass die Funktion, die ich anrufen möchte hat eine ziemlich versaute parameter eingestellt: unter anderem eine Referenz auf eine Funktion (die ein .NET-Funktion), und ein paar von arrays (einige schreiben, einige Lesen).
int lmdif(int (*fcn)(int, int, double*, double*, int*),
int m, int n, double* x, double ftol, double xtol, double gtol,
int maxfev, double epsfcn, double factor)
Gegebenen und einer Schnittstelle wie dieser, was sind die Gemeinheiten sollte ich Ausschau halten? (Und Lösungen zu, bitte)
Warum nicht Sie...
- Rewrite in C#? Ich begann, aber es ist schon maschinell übersetzt aus FORTRAN, und ich weiß nicht, wie viel coding-Sachen, die ich nicht verstehen kann.
- Verwenden eine vorhandene .NET-Bibliothek? Ich versuche es jetzt, aber die Ergebnisse sind nicht genau das gleiche
- Kompilieren in C++? Ich denke über es, aber es sieht aus wie eine Menge von Schmerzen
- Angriff === verwenden?
- Interessante Frage übrigens! Es gibt viele Möglichkeiten, um eine Schnittstelle mit C-Bibliotheken. An deiner Stelle würde ich vielleicht gemacht haben, eine Netzwerk-Schnittstelle zwischen einem C-Programm die Bibliothek und Ihre .NET-Programm. Aber es kommt auf die performance-Anforderungen für das Instanz. Ich hatte noch nie gehört, über Reflexion.emittieren, cool zu wissen. @leppie
- Soweit ich weiß ist die fortran-dlls eine C-Schnittstelle und zugänglich sein sollte, ohne eine Cpp-wrapper. Aber ich bin nicht sicher, ob der Funktionszeiger funktionieren kann.
- es ist tatsächlich buchstäblich ein
.lib
war ich unter dem Eindruck, den ich nicht erreichen kann, die direkt von C#. - das ist richtig, wenn es eine lib ohne zugehörige dll, die Sie nicht darauf zugreifen können. Aber Sie sind in der Lage zur Bindung der lib in eine dll unter Verwendung einer def-Datei zu exportieren, die erforderlichen Methoden, ohne Ihren Quellcode neu kompilieren.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Nur 'böse' - parameter wird der Funktion ein pointer. Aber zum Glück .NET behandelt Sie ziemlich gut über die Delegierten.
Das problem ist nur mit der Aufrufkonvention. In C# ist es nur strahlt ein Typ (iirc
stdcall
), in der Erwägung, dass der C-code erwartencdecl
. Das Letztgenannte problem kann behandelt werden, auf eine IL-Ebene, obwohl (oder mitReflection.Emit
).Hier ist ein code das tut es über
Reflection.Emit
(dies ist zu helfen, zu verstehen, was Pseudo-Attribut gesetzt werden muss, auf der DelegatInvoke
- Methode).Microsoft-Dokumentation für Marschall.GetFunctionPointerForDelegate hier:
"Wandelt ein Abgeordneter in einen Funktionszeiger, der callable von nicht verwaltetem code."
Dies ist, wie ich in der Regel interagieren mit C-DLLs aus C#:
Keine Notwendigkeit, wickeln Sie Sie in C++, und Sie erhalten die benötigten Aufruf-Konvention.
stdcall
Aufrufkonvention, nichtcdecl
.Für das Protokoll, ich habe die meisten der Weg, um zu bekommen, diese funktioniert, dann landete Sie ziehen an den entsprechenden C-Dateien in das C++ Projekt (da war ich im Kampf mit Kompatibilitätsproblemen im code, den ich gar nicht brauche).
Hier sind einige Tipps, die ich auf dem Weg abgeholt, die hilfreich sein können für Menschen geschieht, die auf diese Frage:
Marshalling-arrays
In C gibt es keinen Unterschied zwischen einem Zeiger auf ein double (
double*
) und ein array von doubles (double*
). Wenn Sie kommen, um interop, Sie müssen in der Lage sein, um keine Verwechslungen. Ich brauchte das übergeben von arrays vondouble
, so dass die Signatur könnte wie folgt Aussehen:C muss der zusätzliche Informationen, wie lang das array ist, die Sie zu Durchlaufen haben, als separate parameter. Die Anordnung muss auch wissen, wo diese Größe ist, so geben Sie die
SizeParamIndex
- gibt die Indexposition mit der Basis null, die Größe-parameter in der Liste Parameter.Geben Sie auch die Richtung, in die das array soll übergeben werden. In diesem Beispiel
x
übergeben inFoo
, die 'zurück'y
.Aufrufkonvention
Braucht man eigentlich nicht zu verstehen, die feineren details von dem, was dies bedeutet (in anderen Worten, ich nicht), Sie müssen nur wissen, dass unterschiedliche Aufrufkonventionen vorhanden ist, und, dass Sie müssen match auf beiden Seiten. Der C# - Standard ist
StdCall
, der C-Standard istCdecl
. Dies bedeutet, dass Sie nur explizit angeben müssen, die Aufruf-Konvention, wenn es vom Standardwert abweicht, auf denen je Seite du stehst, es zu benutzen.Dies ist besonders haarig im Falle eines Rückrufs. Wenn wir vorbei Rückruf
C
ausC#
wir beabsichtigen für das callback aufgerufen wird, mitStdCall
, aber wenn wir es übergeben, wir sind mitCdecl
. Diese Ergebnisse in der folgenden Signaturen (siehe diese Frage für den Zusammenhang):Verpackung der Rückruf
Offensichtlich, aber es war nicht sofort klar für mich:
.def-Datei
Alle Funktionen, die Sie verfügbar machen möchten, aus C++ - Projekt (zu
DllImport
ed), müssen Abbildung in derModDef.def
- Datei, deren Inhalt würde wie folgt Aussehen:extern "C"
Wenn Sie möchten, verwenden Sie C-Funktionen aus C++, müssen Sie deklarieren Sie als
extern "C"
. Wenn Sie inklusive einer header-Datei von C-Funktionen, die Sie gehen wie folgt aus:Vorkompilierte Header
Andere Sache, die ich tun musste, um zu vermeiden, Zusammenstellung Fehler war
Right-click -> Properties -> C/C++ -> Precompiled Headers
- und set -Precompiled header
zu Vorkompilierte Header Nicht Verwenden für jedes 'C' - Datei.Es wurde eine MSDN-Artikel Jahren, die reulted in der InteropSignatureToolkit.
Dieses kleine tool ist immer noch nützlich, um die marshall-C-Schnittstellen. Kopieren und Vergangenheit-interface-code in der ""SigImp Übersetzung Sniplet" und beobachten Sie die Ergebnisse.
Das Ergebnis ist die folgende, aber ich habe keine Ahnung, wie der Delegat verwendet wird, oder ob es funktioniert. Also, wenn es funktioniert, fügen Sie einige Kommentare.
Hier ist eine Möglichkeit, senden Sie eine MENGE von numerischen Daten, um Ihre C-Funktion über einen StringBuilder. Nur werfen Ihre zahlen in einem StringBuilder-Objekt, während die Einstellung Trennzeichen an die gewünschte Position et voilá:
- Und C-Seite, Holen die StringBuilder-Objekt als char*: