Tatsächlich habe ich eine C ++ (funktionierende) DLL erhalten, die ich in mein C # -Projekt importieren möchte, um dessen Funktionen aufzurufen.
Es funktioniert, wenn ich den vollständigen Pfad zur DLL wie folgt spezifiziere:
string str = "C:\\Users\\userName\\AppData\\Local\\myLibFolder\\myDLL.dll";
[DllImport(str, CallingConvention = CallingConvention.Cdecl)]
public static extern int DLLFunction(int Number1, int Number2);
Das Problem ist, dass es sich um ein installierbares Projekt handelt, sodass der Ordner des Benutzers nicht derselbe ist (z. B. Pierre, Paul, Jack, Mama, Papa, ...), je nachdem, auf welchem Computer / in welcher Sitzung er ausgeführt wird.
Ich möchte, dass mein Code etwas allgemeiner ist, wie folgt:
/*
goes right to the temp folder of the user
"C:\\Users\\userName\\AppData\\Local\\temp"
then go to parent folder
"C:\\Users\\userName\\AppData\\Local"
and finally go to the DLL's folder
"C:\\Users\\userName\\AppData\\Local\\temp\\myLibFolder"
*/
string str = Path.GetTempPath() + "..\\myLibFolder\\myDLL.dll";
[DllImport(str, CallingConvention = CallingConvention.Cdecl)]
public static extern int DLLFunction(int Number1, int Number2);
Die große Sache ist, dass "DllImport" einen "const string" -Parameter für das DLL-Verzeichnis wünscht.
Meine Frage lautet also: Was könnte in diesem Fall getan werden?
Antworten:
Im Gegensatz zu den Vorschlägen einiger anderer Antworten ist die Verwendung des
DllImport
Attributs immer noch der richtige Ansatz.Ich verstehe ehrlich gesagt nicht, warum Sie nicht wie alle anderen auf der Welt einen relativen Pfad zu Ihrer DLL angeben können . Ja, der Pfad, in dem Ihre Anwendung installiert wird, unterscheidet sich auf den Computern verschiedener Personen. Dies ist jedoch im Grunde eine universelle Regel für die Bereitstellung. Der
DllImport
Mechanismus ist in diesem Sinne ausgelegt.Tatsächlich ist es nicht einmal das
DllImport
, was damit umgeht. Es sind die nativen Win32-DLL-Laderegeln, die die Dinge regeln, unabhängig davon, ob Sie die handlichen verwalteten Wrapper verwenden (der P / Invoke-Marshaller ruft nur aufLoadLibrary
). Diese Regeln werden hier sehr detailliert aufgeführt , aber die wichtigsten sind hier auszugsweise:Sofern Sie Ihre DLL nicht wie eine System-DLL benennen (was Sie unter keinen Umständen tun sollten), wird die Standardsuchreihenfolge in dem Verzeichnis angezeigt, aus dem Ihre Anwendung geladen wurde. Wenn Sie die DLL während der Installation dort platzieren, wird sie gefunden. Alle komplizierten Probleme verschwinden, wenn Sie nur relative Pfade verwenden.
Einfach schreiben:
Wenn dies jedoch aus irgendeinem Grund nicht funktioniert und Sie die Anwendung zwingen müssen, in einem anderen Verzeichnis nach der DLL zu suchen, können Sie den Standardsuchpfad mithilfe der
SetDllDirectory
Funktion ändern .Beachten Sie, dass gemäß der Dokumentation:
Solange Sie diese Funktion aufrufen, bevor Sie die aus der DLL importierte Funktion zum ersten Mal aufrufen, können Sie den Standardsuchpfad zum Suchen von DLLs ändern. Der Vorteil ist natürlich, dass Sie dieser Funktion, die zur Laufzeit berechnet wird, einen dynamischen Wert übergeben können. Dies ist mit dem
DllImport
Attribut nicht möglich. Daher verwenden Sie dort immer noch einen relativen Pfad (nur den Namen der DLL) und verlassen sich auf die neue Suchreihenfolge, um ihn für Sie zu finden.Sie müssen diese Funktion aufrufen. Die Erklärung sieht folgendermaßen aus:
quelle
.dll
und andere Systeme fügen die entsprechende Erweiterung unter Mono hinzu (z.so
. B. unter Linux). Dies kann hilfreich sein, wenn die Portabilität ein Problem darstellt.SetDllDirectory
. Sie können auch einfach ändernEnvironment.CurrentDirectory
und alle relativen Pfade werden von diesem Pfad ausgewertet!AddDllDirectory
auf der anderen Seite ...DllImport
mehr als nur ein Wrapper istLoadLibrary
. Es berücksichtigt auch das Verzeichnis der Assembly, in der dieextern
Methode definiert ist . DieDllImport
Suchpfade können zusätzlich mit eingeschränkt werdenDefaultDllImportSearchPath
.Noch besser als Rans Vorschlag
GetProcAddress
, einfach die Funktionen aufzurufen,LoadLibrary
bevor dieDllImport
Funktionen aufgerufen werden (nur mit einem Dateinamen ohne Pfad), und das geladene Modul wird automatisch verwendet.Ich habe diese Methode verwendet, um zur Laufzeit zu wählen, ob eine native 32-Bit- oder 64-Bit-DLL geladen werden soll, ohne eine Reihe von P / Invoke-d-Funktionen ändern zu müssen. Stecken Sie den Ladecode in einen statischen Konstruktor für den Typ, der die importierten Funktionen hat, und alles wird gut funktionieren.
quelle
FunctionLoader
Code aufrufen .Wenn Sie eine DLL-Datei benötigen, die sich nicht im Pfad oder am Speicherort der Anwendung befindet, können Sie dies meines Erachtens nicht tun, da
DllImport
es sich um ein Attribut handelt und Attribute nur Metadaten sind, die für Typen, Mitglieder und andere festgelegt sind Sprachelemente.Eine Alternative, die Ihnen dabei helfen kann, das zu erreichen, was Sie meiner Meinung nach versuchen, besteht darin, das native
LoadLibrary
über P / Invoke zu verwenden, um eine DLL aus dem von Ihnen benötigten Pfad zu laden und dannGetProcAddress
einen Verweis auf die von Ihnen benötigte Funktion zu erhalten von dieser .dll. Verwenden Sie diese dann, um einen Delegaten zu erstellen, den Sie aufrufen können.Um die Verwendung zu vereinfachen, können Sie diesen Delegaten dann auf ein Feld in Ihrer Klasse festlegen, sodass die Verwendung so aussieht, als würde eine Mitgliedsmethode aufgerufen.
BEARBEITEN
Hier ist ein Code-Snippet, das funktioniert und zeigt, was ich meinte.
Hinweis: Ich habe mich nicht um die Verwendung gekümmert
FreeLibrary
, daher ist dieser Code nicht vollständig. In einer realen Anwendung sollten Sie darauf achten, die geladenen Module freizugeben, um einen Speicherverlust zu vermeiden.quelle
Solange Sie das Verzeichnis kennen, in dem sich Ihre C ++ - Bibliotheken zur Laufzeit befinden, sollte dies einfach sein. Ich kann deutlich sehen, dass dies in Ihrem Code der Fall ist. Sie befinden
myDll.dll
sich immyLibFolder
Verzeichnis des temporären Ordners des aktuellen Benutzers.Jetzt können Sie die DllImport-Anweisung mit einer const-Zeichenfolge wie unten gezeigt weiter verwenden:
Fügen Sie zur Laufzeit vor dem Aufrufen der
DLLFunction
Funktion (in der C ++ - Bibliothek vorhanden) diese Codezeile in C # -Code hinzu:Dadurch wird die CLR lediglich angewiesen, im Verzeichnispfad, den Sie zur Laufzeit Ihres Programms erhalten haben, nach nicht verwalteten C ++ - Bibliotheken zu suchen.
Directory.SetCurrentDirectory
call setzt das aktuelle Arbeitsverzeichnis der Anwendung auf das angegebene Verzeichnis. Wenn IhrmyDLL.dll
am Pfad vorhanden ist, der durch denassemblyProbeDirectory
Pfad dargestellt wird, wird er geladen und die gewünschte Funktion wird über p / invoke aufgerufen.quelle
Legen Sie den DLL-Pfad in der Konfigurationsdatei fest
Gehen Sie wie folgt vor, bevor Sie die DLL in Ihrer App aufrufen
Rufen Sie dann die DLL auf und Sie können wie unten verwenden
quelle
DllImport funktioniert ohne den angegebenen vollständigen Pfad einwandfrei, solange sich die DLL irgendwo im Systempfad befindet. Möglicherweise können Sie den Ordner des Benutzers vorübergehend zum Pfad hinzufügen.
quelle
Wenn alles fehlschlägt, legen Sie einfach die DLL in den
windows\system32
Ordner. Der Compiler wird es finden. Geben Sie die DLL zu laden aus mit:DllImport("user32.dll"...
, SatzEntryPoint = "my_unmanaged_function"
Ihre gewünschte nicht verwaltete Funktion zu C # app zu importieren:Quelle und noch mehr
DllImport
Beispiele: http://msdn.microsoft.com/en-us/library/aa288468(v=vs.71).aspxquelle