Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Community
  • |
  • Diskussionsforum
32bit/64bit unmanaged DLL "importen" - wie?

Moderationshinweis von herbivore (11.06.2012 - 10:32:24):

Dies ist ein Thread, auf den aus der FAQ verwiesen wird. Bitte keine weitere Diskussion, sondern nur wichtige Ergänzungen und diese bitte knapp und präzise. Vielen Dank!

CaptainIglo
myCSharp.de - Member



Dabei seit:
Beiträge: 366
Herkunft: Feldkirch - Österreich

Themenstarter:

32bit/64bit unmanaged DLL "importen" - wie?

beantworten | zitieren | melden

Hallo,

habe hier ein Project in welchem ich eine externe C++-dll verwende, welche in der 32bit und 64bit Variante vorliegt (32bit geht unter 64bit nicht) und per DllImport verwednet wird.

Kann ich meiner Anwendung irgendwie sagen, dass sie anstatt "xyz.dll", je nach System "x32\xyz.dll" bzw. "x64\xyz.dll" lädt?

mfg
Capt.Iglo
private Nachricht | Beiträge des Benutzers
Timur Zanagar
myCSharp.de - Member

Avatar #avatar-3412.jpg


Dabei seit:
Beiträge: 1559

beantworten | zitieren | melden

Hallo CaptainIglo,

Wenn dein Projekt via AnyCPU kompiliert wird, dann IMHO garnicht. Du müsstest zwei Konfigurationen anlegen und via DllImport je nach Konfiguration die eine oder andere DLL miteinbinden.
private Nachricht | Beiträge des Benutzers
gfoidl
myCSharp.de - Team

Avatar #avatar-2894.jpg


Dabei seit:
Beiträge: 7540
Herkunft: Waidring

beantworten | zitieren | melden

Hallo zusammen,

nachfolgender Vorschlag bezieht sich auf unmanaged (od. native) DLLs.
Zitat
Kann ich meiner Anwendung irgendwie sagen, dass sie anstatt "xyz.dll", je nach System "x32\xyz.dll" bzw. "x64\xyz.dll" lädt?
Statt x32 wird üblicherweise x86 verwendet, aber das ändert nichts an der Lösung.

Die Suchreihenfolge für native DLLs ist:
Zitat von Dynamic-Link Library Search Order (Windows)
  1. The directory from which the application loaded.
  2. The system directory.
  3. The Windows directory.
  4. The current directory.
  5. The directories that are listed in the PATH environment variable. Note that this does not include the per-application path specified by the App Paths registry key. The App Paths key is not used when computing the DLL search path.
Davon können wir Punkt 4 und 5 beeinflussen. Punkt 4 ist aber nicht sicher, da nicht garantiert ist, dass dieses Verzeichnis während der Lebensdauer des Prozesses nicht geändert wird. D.h. wir müssen den Weg über die Umgebungsvariable PATH gehen.

Beispielsweise so:


string path = Environment.GetEnvironmentVariable("PATH") ?? string.Empty;
string dllDir = System.IO.Path.GetDirectoryName(typeof(NativeMethods).Assembly.Location);

if (Environment.Is64BitProcess)
	dllDir = System.IO.Path.Combine(dllDir, "x64");
else
	dllDir = System.IO.Path.Combine(dllDir, "x86");

path += ";" + dllDir;

Environment.SetEnvironmentVariable("PATH", path);
Da in Environment.SetEnvironmentVariable kein EnvironmentVariableTarget angegeben wurde, gilt diese Umgebungsvariable nur für den aktuellen Prozess.

Dieser Code sollte ausgeführt werden, bevor erstmals auf eine mit DllImport versehenen Methode zugegriffen wird. Z.B. in im Einstiegspunkt des Prozesses (Main) od. im statischen Konstruktor der (wie im Beispiel) NativeMethods-Klasse.

Das .net-Projekt ist mit der Konfiguration "Any CPU" zu erstellen.

Wenn das Ganze auch für NuGet verwendet werden soll, so ist es praktisch sich Microsoft.SqlServer.Compact als Vorlage zu nehmen. Die Script-Files im tools-Ordner sind entsprechend anzupassen.


Managed DLLs (also .net-DLLs) sollten idealerweise mit der Konfiguration "Any CPU" erstellt werden, da für die CLR nur die Bittigkeit der EXE von Bedeutung ist. D.h. wenn die EXE 32bit ist, so wird von der CLR die DLL auch als 32bit behandelt. Für 64bit analog. Für fremde DLLs muss die Bittigkeit der EXE zu jener der DLLs passen, andernfalls erhält man zur Laufzeit eine BadImageFormatException. Siehe hierzu auch BadImageFormatException bei Verwendung einer Assembly.
Bei managed DLLs macht es keinen Sinn eine 32bit- und eine 64bit-Version anzubieten und diese zur Laufzeit entsprechend einzubinden, da bei managed DLLs Any CPU die bessere Wahl ist.

mfG Gü

Speedski, nativ, native, DLL, unmanaged, DllImport, 32bit, 64bit, 32/64, 32/64bit
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
private Nachricht | Beiträge des Benutzers
Mr. Bart Simpson
myCSharp.de - Member

Avatar #avatar-3273.gif


Dabei seit:
Beiträge: 502
Herkunft: Mittelfranken

beantworten | zitieren | melden

Nur als Ergänzung: Das Laden von nativen DLLs lässt sich auch mit einem anderen Ansatz lösen - ohne setzen von Path-Variablen etc.
Folgende Vorgehensweise ist dafür nötig:

a) Native Windows Calls einbinden


[DllImport("Kernel32.dll", SetLastError = true)]
public static extern IntPtr LoadLibraryEx(string dllFilePath, IntPtr hReservedNull, LoadLibraryFlags dwFlags);

[DllImport("Kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool FreeLibrary(IntPtr hModule);

[DllImport("Kernel32.dll", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);

b) den Pfad zur richtigen Dll bestimmen (ggf. abhängig von Environment.Is64BitProcess)
c) die DLL laden und den Zeiger darauf merken

 LoadLibraryEx("FullPathToLibrary.dll", IntPtr.Zero, LoadLibraryFlags.LOAD_WITH_ALTERED_SEARCH_PATH)
d) Für jede benötigte Funktion jeweils den Einsprungpunkt holen und einen entsprechenden Delegate darauf erezugen lassen

private static TDelegate GetFunctionDelegate<TDelegate>(IntPtr dllHandle, string functionName)
           where TDelegate : class
        {
            IntPtr function = NativeCalls.GetProcAddress(dllHandle, functionName);
            var funcPointer = Marshal.GetDelegateForFunctionPointer(function, typeof(TDelegate));
            return funcPointer as TDelegate;
        }

TDelegate muss dafür definiert sein als Delegate mit der Signatur der zu importierenden Funktion.
Am Ende kann die DLL dann auch noch mit FreeLibrary wieder freigegeben werden.

Vorteile hiervon:
  • Es wird alles dynamisch geladen
  • Der Pfad zur DLL kann frei angegeben werden (ohne PATH-Variable und andere Unwägbarkeiten)
  • Der Zeitpunkt des Ladens (und Entladens) der DLL kann exakt bestimmt werden
  • Es ist (wenn nötig) möglich die aufzurufenden Methoden dynamisch zu bestimmen (Zugriff via String, der den Namen enthält)
  • Verschiedene Funktionen mit gleicher Signatur können über den selben Delegate-Typ importiert werden (keine mehrfachen Importe via DllImport-Attribut nötig)


Nachteil: Mann muss einiges "von Hand lösen" - z.B. via Marshal.GetLastWin32Error() immer die nativen Calls überprüfen und das Ergebnis ggf. in eine ".net-konforme" Exception packen, was beim "normalen" DllImport schon automatisch mit dabei ist.

Bart Simpson
Praxis ist wenn alles funktioniert und keiner weiss warum.
Theorie ist wenn man alles weiss, aber nichts funktioniert.

Bei uns wird Theorie und Praxis vereint: Nichts funktioniert und keiner weiss warum...
private Nachricht | Beiträge des Benutzers