Entwickler-Ecke

Basistechnologien - Dynamischer DllImport


xuxu81 - Sa 28.04.18 15:49
Titel: Dynamischer DllImport
Hallo zusammen,

für mein Projekt wird eine DLL benötigt. Derzeit liegt sie im selben Ordner wie die Anwendung, es soll aber auch die Möglichkeit geboten werden, sie auszulagern (z.B. in einem Unterordner). Mir ist der genaue Pfad also erst zur Laufzeit bekannt, leider erfordert DllImport aber einen konstanten Wert (String).

Weiß zufällig jemand eine elegante Möglichkeit, das ganze dynamisch einzubinden? Oder eine Alternative zu DllImport (bestenfalls mit einem Link zu einem Beispiel was ihr als Praxistest empfehlen könnt)?

Vorab schon mal vielen lieben Dank für eure Zeit und Mühe. :)


Ralf Jansen - Sa 28.04.18 17:27

Entweder du ergänzt den Suchpfad für Dlls via SetDllDirectory [https://www.pinvoke.net/default.aspx/kernel32.setdlldirectory] so das DllImport auch dieses Verzeichnis durchsucht wenn es versucht die Dll zu laden oder du lädst die entsprechende Dll vorab per LoadLibrary [https://www.pinvoke.net/default.aspx/kernel32.LoadLibrary]. DllImport benutzt die dann einfach wenn die schon im Speicher ist.


xuxu81 - Sa 28.04.18 17:44

Danke, hilfreicher Tipp! :)

Funktioniert aber nicht, wenn der Name der Bibliothek auch dynamisch ist, oder? Könnte möglicherweise etwas klappen, dass er einfach alle Bibliotheken innerhalb eines Verzeichnis importiert und ich dann direkt auf die Methoden darin zugreifen kann?

Im Grunde sowas wie

C#-Quelltext
1:
foreach (string dll in Directory.GetFiles(path, "*.dll")) Assembly.LoadFile(dll);                    

was aber nur für die Wrapper Klasse funktionieren würde, nicht für die eigentlichen Bibliotheken (unmanaged).

Moderiert von user profile iconTh69: Code- durch C#-Tags ersetzt


Ralf Jansen - Sa 28.04.18 18:28

Wenn bei dir alles dynamisch ist bleibt dann nur dynamischer Code und DllImport ist raus. Um noch halbwegs vernünftig zur Designzeit damit arbeiten zu können hilft Marshal.GetDelegateForFunctionPointer [https://msdn.microsoft.com/de-de/library/system.runtime.interopservices.marshal.getdelegateforfunctionpointer(v=vs.110).aspx]

Aber am besten erzählst du erstmal warum du willst was du da willst. Ich erkenne da gerade keinen sinnvollen Anwendungsfall. Ganz zu schwiegen von den Bauchschmerzen die es mir bereitet einfach mal alles was in einem Verzeichnis liegt in den Prozessraum zu laden.


xuxu81 - Sa 28.04.18 19:00

Also, im Grunde hatte ich für mein Projekt eine DLL und nach einiger Suche auch eine entsprechende Wrapper Klasse gefunden. Die Funktionalität war für meine Wünsche aber noch nicht vollständig, also habe ich beide entsprechend erweitert und möchte diese in naher Zukunft bei Github veröffentlichen.

Allerdings hat es mich schon immer gestört, wenn der Nutzer dann mit den statischen Dateinamen/Pfaden leben, oder entsprechend neu kompilieren muss, anstatt diese einfach nach seinen persönlichen Wünschen in sein Projekt einbinden zu können.

Vereinfacht ausgedrückt habe ich in der Wrapper Klasse derzeit

C#-Quelltext
1:
2:
3:
const String _dllimp="blabla.dll";
[DllImport(_dllimp, EntryPoint = "blabla_one", CallingConvention = CallingConvention.Cdecl)]
...

und hätte gerne

C#-Quelltext
1:
2:
3:
String _dllimp=GetFileAndPath();
[DllImport(_dllimp, EntryPoint = "blabla_one", CallingConvention = CallingConvention.Cdecl)]
...


Moderiert von user profile iconTh69: Code- durch C#-Tags ersetzt


Th69 - So 29.04.18 12:01

Ich selber habe noch nicht damit gearbeitet, aber du könntest das AppDomain.AssemblyResolve [https://msdn.microsoft.com/de-de/library/system.appdomain.assemblyresolve(v=vs.110).aspx]-Ereignis dazu benutzen (d.h. du checkst dadrin auf den vorher fest zugewiesenen DllImport-Namen und lädst dann dynamisch die Assembly).


Delete - So 29.04.18 12:49

- Nachträglich durch die Entwickler-Ecke gelöscht -


Ralf Jansen - So 29.04.18 13:00

user profile iconTh69 hat folgendes geschrieben Zum zitierten Posting springen:
Ich selber habe noch nicht damit gearbeitet, aber du könntest das AppDomain.AssemblyResolve [https://msdn.microsoft.com/de-de/library/system.appdomain.assemblyresolve(v=vs.110).aspx]-Ereignis dazu benutzen (d.h. du checkst dadrin auf den vorher fest zugewiesenen DllImport-Namen und lädst dann dynamisch die Assembly).

Das funktioniert auch für unmanaged Dlls und nicht nur für Assemblies?

user profile iconxuxu81 hat folgendes geschrieben Zum zitierten Posting springen:
Allerdings hat es mich schon immer gestört, wenn der Nutzer dann mit den statischen Dateinamen/Pfaden leben, oder entsprechend neu kompilieren muss, anstatt diese einfach nach seinen persönlichen Wünschen in sein Projekt einbinden zu können.


Ein weiterer simple Lösung wäre die irgendwo liegende Dll mit irgendeinem Namen einfach durch Code an eine Stelle zu kopieren und passend zu benennen von wo sie dann durch DLLImport geladen wird. Der kopierende Code kann dann irgendeine Konfiguration/Variablen benutzen die mit dem tatsächlichen Namen bzw. tatsächlichen Lagerort umgehen können.


Th69 - So 29.04.18 14:14

Ups, da war ich wohl noch nicht ganz wach. ;-) Du hast recht, das geht nur für managed DLLs (Assemblies) - also vergesst den Vorschlag.


xuxu81 - So 29.04.18 15:18

Hallo zusammen und vielen Dank für eure Antworten.

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Ich verwende sowas in Delphi. Das hier ist "mein erstes Mal" in C#. :D

Danke Dir! :) Hatte den Weg auch schon mehrfach bei meiner Googlesuche gesehen und werde es wohl auch auf die Art machen, wenn ich keine bessere Methode finde. ^^

user profile iconRalf Jansen hat folgendes geschrieben Zum zitierten Posting springen:
Ein weiterer simple Lösung wäre die irgendwo liegende Dll mit irgendeinem Namen einfach durch Code an eine Stelle zu kopieren und passend zu benennen von wo sie dann durch DLLImport geladen wird. Der kopierende Code kann dann irgendeine Konfiguration/Variablen benutzen die mit dem tatsächlichen Namen bzw. tatsächlichen Lagerort umgehen können.

Ich bin nicht sicher, ob ich Dich richtig verstanden habe: Meinst Du die Pfade/Namen im Code statisch zu belassen, dann vor dem Importieren in die eigentliche Anwendung die Bibliotheken zu suchen, an die statisch vorgesehenen Orte zu kopieren und dann mit diesen zu arbeiten und ggf. beim Schließen der App die temporären Kopien wieder zu löschen..?

Könnte ein funktionierendes Workaround sein.