dyn. Einbinden von DLL's zur Laufszeit? (C++ oder C#.net)

Peacemaker

Lt. Commander
Mitglied seit
12.08.2002
Beiträge
137
Renomée
0
hi Leute

Ich habe folgendes Problem... Ich habe ein kleines grafisches Simulation-Programm in C#.net welches derzeit 2 Simulationsmodis besitzt. Diese Modis sind im Hauptprogramm fix integriert.

Nun möchte ich diese Modis irgendwie von der Hauptanwendung lösen (als DLL?). Grund: das sollte wie ein Plug-in System funktionieren.

So stelle ich mir das vor: Alle DLLs (Modis) besitzen (gleichnamige?) Klassen und public Funktionen -> Damit ich jede DLL über ein und dem selben Aufruf in der Hauptanwendung nutzen kann.

Anwendung: Ich möchte in der Hauptanwendung wärend der Laufzeit eine Auswahl von diesen .DLLs haben (bzw. eine Bezeichnung) von denen ich immer exakt eine auswählen kann. Anschließend möchte ich mit ein und dem selben Aufruf die Simulation starten. Nach der Simulation möchte ich eine andere DLL auswählen können und diese wieder starten... etc

Für Hinweise, Hilfen, Lösungen, neue Ideen?, etc... für C#.net und/oder C++ (ich hatte eh schon lange vor das Programm in C++ umzuschreiben) wäre ich sehr dankbar! :) Lösungen für beide Sprachen wären natürlich am besten :D
 
Peacemaker schrieb:
aus diesem Posting

So stelle ich mir das vor: Alle DLLs (Modis) besitzen (gleichnamige?) Klassen und public Funktionen -> Damit ich jede DLL über ein und dem selben Aufruf in der Hauptanwendung nutzen kann.

Das wird nicht gehen, da die Funktionen und Klassen über ihre Namen unterschieden werden. Wenn du jetzt zwei vollkommen unterschiedlichen Funktionen den gleichen Namen gibst, weiß der Compiler/Linker nicht, welche nun gemeint ist. Du wirst die Funktionen/Klassen also schon unterschiedlich benennen müssen.
 
PuckPoltergeist schrieb:
aus diesem Posting

Das wird nicht gehen, da die Funktionen und Klassen über ihre Namen unterschieden werden. Wenn du jetzt zwei vollkommen unterschiedlichen Funktionen den gleichen Namen gibst, weiß der Compiler/Linker nicht, welche nun gemeint ist. Du wirst die Funktionen/Klassen also schon unterschiedlich benennen müssen.

Danke für die Antwort :)

Namenskonflikte zur Laufzeit: Das ist schon klar wenn ich alle DLLs gleichzeitig nutze... aber da ich in dem Absatz von den DLLs geschrieben habe von denen immer nur eine einzeln eingebungen werden sollte, gibt es ja keine anderen DLLs mit denen diese im Namenskonflikt stehen kann. Natürlich weiß ich nicht ob das dynamische "wechseln" von DLLs überhaupt realisierbar ist (deshalb frag ich ja hier ;)).

Beispiel 1.dll und 2.dll haben beide gleiche Klassennamen und Funktionen.
- 1.DLL wird eingebungen und genutzt
- 1.DLL wird wieder gelöst oder was auch immer
- 2.DLL wird eingebungen und genutzt
- 2.DLL wird wieder gelöst oder was auch immer
- ...

Namenskonflikte zur Programierung: Klarerweise muss ich beide dlls in seperaten Projekten entwickeln damit es eben diese Namenskonflikte nicht gibt (ich denke das hast du gemeint)... Aber Plugins sollten ja auch extern programmiert sein, sonst kann ja sonst niemand ohne sourcecode plugins (Simulations-Modis) dazumachen.
 
Mit der Klasse System.Reflection.Assembly kannst Du lt. MSDN Assemblies zur Laufzeit laden. Mit Reflection sollte es dann auch kein Problem sein, Code in diesen Assemblies aufzurufen.
 
Peacemaker schrieb:
aus diesem Posting

Danke für die Antwort :)

Namenskonflikte zur Laufzeit:

Nein, ich meine Namenskonflikte zur Compilezeit. Der Linker muss die Namen ja beim Zusammenfügen auflösen. Offenbar hast du eine falsche Vorstellung von dynamischen Bibliotheken. Die ermöglichen dir nicht sowas, was du hier als plugin-System beschreibst. Sobald dein Programm gestartet wird, werden auch sämtliche Bibliotheken geladen, gegen die du gelinkt hast.
 
PuckPoltergeist schrieb:
aus diesem Posting

Nein, ich meine Namenskonflikte zur Compilezeit. Der Linker muss die Namen ja beim Zusammenfügen auflösen. Offenbar hast du eine falsche Vorstellung von dynamischen Bibliotheken. Die ermöglichen dir nicht sowas, was du hier als plugin-System beschreibst. Sobald dein Programm gestartet wird, werden auch sämtliche Bibliotheken geladen, gegen die du gelinkt hast.

wenn ich von dynamischen Bibliotheken eine Ahnung hätte, hätte ich hier nicht gefragt ;)

also wenn ich alle dlls fix einprogrammieren muss, dann kann ich das vergessen...

Das mit den Reflextions werde ich mal in der MSDN nachlesen, danke :)

(warum ich auf diese ganze dll idee gekommen bin ist der Proton Texteditor... der hat seine Plugins auch als DLLs eingebunden mit gleichen Namen wenn ich mich richtig erinnere... ist schon eine weile her)
 
Peacemaker schrieb:
aus diesem Posting

also wenn ich alle dlls fix einprogrammieren muss, dann kann ich das vergessen...

Es ist eh sauberer, die Klassen/Funktionen unterschiedlich zu benennen. Das macht das Programmieren deutlich einfacher, weil es die Verwechslungsgefahr reduziert.

(warum ich auf diese ganze dll idee gekommen bin ist der Proton Texteditor... der hat seine Plugins auch als DLLs eingebunden mit gleichen Namen wenn ich mich richtig erinnere... ist schon eine weile her)

Kann ich mir nur schwer vorstellen.
 
Sorry, Puck, aber ich denke, das das durchaus geht, was der thread Ersteller meint.

So wie ich es verstanden habe, ist seine exe nciht abhängig von den dlls, also nicht statisch gegen diese gelinkt (also der linker wurde nicht mit der "dll.lib" aufgerufen). Statt dessen kann er zur Laufzeit mittels LoadLibrary und GetProcAddress genaus da erreichen, was er will. Er stellt letzlich nur ein interface zur Verfügung und mit einbinden einer dll, wirden die Funktionen eingeklinkt. Das kann man sogar so lösen, daß man alle dlls gleichzeitig laden kann. Zumindest funktioniert dies unter C. (Oder ist das grad der Haken des ganzen, daß das unter c++/c# nicht mehr so geht?)

Evtl hilft das: http://www.codeproject.com/csharp/dyninvok.asp?print=true
 
Zuletzt bearbeitet:
danke für den Link, der hilft mir weiter :)

Damit werde ich wohl nächste Woche (im Urlaub :D) zu tun haben...
 
PrakashP schrieb:
aus diesem Posting

Sorry, Puck, aber ich denke, das das durchaus geht, was der thread Ersteller meint.

So wie ich es verstanden habe, ist seine exe nciht abhängig von den dlls, also nicht statisch gegen diese gelinkt (also der linker wurde nicht mit der "dll.lib" aufgerufen). Statt dessen kann er zur Laufzeit mittels LoadLibrary und GetProcAddress genaus da erreichen, was er will. Er stellt letzlich nur ein interface zur Verfügung und mit einbinden einer dll, wirden die Funktionen eingeklinkt. Das kann man sogar so lösen, daß man alle dlls gleichzeitig laden kann. Zumindest funktioniert dies unter C. (Oder ist das grad der Haken des ganzen, daß das unter c++/c# nicht mehr so geht?)

Ich wusste schon immer, dass MS nur Schund baut, aber soweit war mir das auch nicht bekannt. Naja, jedem wie er es braucht. Da wundert es mich nicht, dass man bei MS selber nicht mehr weiß, was der eigenen Code tut. ;D
 
PuckPoltergeist schrieb:
aus diesem Posting

Ich wusste schon immer, dass MS nur Schund baut, aber soweit war mir das auch nicht bekannt. Naja, jedem wie er es braucht. Da wundert es mich nicht, dass man bei MS selber nicht mehr weiß, was der eigenen Code tut. ;D
Muss man diesen Post verstehen? Was gibt's gegen die beiden API-Funktionen LoadLibrary() und GetProcAddress() einzuwenden? *noahnung*
 
TiKu schrieb:
aus diesem Posting

Muss man diesen Post verstehen? Was gibt's gegen die beiden API-Funktionen LoadLibrary() und GetProcAddress() einzuwenden? *noahnung*

Das öffnet dem Chaos Tür und Tor. Somit lassen sich ja dutzende verschiedener Funktionen erzeugen, die aber alle den gleichen Namen tragen. Da brauch man sich dann auch nicht wundern, wenn man irgendwann den Überblick verliert, und die falsche Funktion aufruft.
 
Ich glaube, Du verstehst hier einfach etwas grundlegend falsch.
Mit LoadLibrary() lädst Du eine DLL in den Speicher. Mit GetProcAddress() holst Du Dir die Adresse einer von dieser DLL exportierten Funktion. Innerhalb einer DLL kannst Du *nicht* mehreren Funktionen/Klassen den selben Namen geben. Aber DLL-übergreifend existiert diese Einschränkung logischerweise nicht, denn dann bräuchtest Du eine globale Organisation, die nur damit beschäftigt ist, Programmierern Funktionsnamen zuzuweisen, die sie verwenden könne, ohne mit den Namen anderer Programmierer zu kollidieren.
Jede COM-DLL enthält bspw. eine Function namens DllRegisterServer (oder so ähnlich).

Hier mal ein Codeschnippsel wie das mit LoadLibrary funktioniert:
Code:
	typedef HANDLE HTHEME;
	typedef HRESULT (__stdcall *CloseThemeDataFn)(HTHEME);
	typedef HRESULT (__stdcall *GetThemePartSizeFn)(HTHEME, HDC, int, int, RECT*, UINT, SIZE*);
	typedef BOOL (__stdcall *IsAppThemedFn)(VOID);
	typedef HTHEME (__stdcall *OpenThemeDataFn)(HWND, LPCWSTR);
	CloseThemeDataFn pfnCloseThemeData = NULL;
	GetThemePartSizeFn pfnGetThemePartSize = NULL;
	IsAppThemedFn pfnIsAppThemed = NULL;
	OpenThemeDataFn pfnOpenThemeData = NULL;

	// now retrieve the addresses
	HMODULE hModUXTheme = ::LoadLibrary(_T("uxtheme.dll"));
	if(hModUXTheme) {
		pfnCloseThemeData = (CloseThemeDataFn) GetProcAddress(hModUXTheme, "CloseThemeData");
		pfnGetThemePartSize = (GetThemePartSizeFn) GetProcAddress(hModUXTheme, "GetThemePartSize");
		pfnIsAppThemed = (IsAppThemedFn) GetProcAddress(hModUXTheme, "IsAppThemed");
		pfnOpenThemeData = (OpenThemeDataFn) GetProcAddress(hModUXTheme, "OpenThemeData");
	}
 
TiKu schrieb:
aus diesem Posting

Ich glaube, Du verstehst hier einfach etwas grundlegend falsch.

Ich habe das schon richtig verstande, und war auch nie davon ausgegangen, dass mehrere Funktionen gleichen Namens in der selben dll funktionieren würden. Es reicht doch aber schon, wenn du das über verschiedene dlls machen kannst. Du passt einmal nicht auf, und arbeitest auf einmal mit der falschen dll. Dann wunderst du dich, wieso dein Programm blödsinn macht. Meiner Meinung nach, ist das eine ideale Möglichkeit, um Strukturierung zu umgehen bzw. zu vermeiden.
Ein ähnlicher Mechanismus ist mir unter Unix nicht bekannt, und ich hoffe, dass auch niemand auf die Idee kommt, sowas mal zu implementieren. Es ist schon ganz sinnvoll, wenn die Symbole schon zur Compilezeit mit ihren Adressen verknüpft werden
 
Ähm, LoadLibrary() und GetProcAddress verwendet man auch unter Windows eher selten. Ansonsten werden meines Wissens auch unter Windows die Symbole beim Kompilieren mit ihren Adressen verknüpft.

Normalerweise läuft das unter Windows so ab:
Du "includest" den Header, in dem die Funktion (bspw. void ILFree(LPITEMIDLIST)) definiert ist und schreibst im Quellcode dann einfach ILFree(pidl); Da ist nix mit LoadLibrary() und GetProcAddress() und trotzdem musst Du nicht statisch auf die shell32 linken.
Wenn Dein Programm aber mit verschiedenen Versionen ein und derselben DLL laufen soll (wenn ich mir die lib-Konflikte unter Linux so anschaue, dann glaube ich Dir, dass Linux sowas nicht kennt), können LoadLibrary() und GetProcAddress() ganz nützlich sein. Bspw. exportiert die comctl32.dll erst ab *grübel* Version 4.71 oder so die Funktion DllGetVersion(). Mit LoadLibrary() und GetProcAddress() kann man jetzt erstmal prüfen ob die Funktion exportiert wird, bevor man darauf zugreift.
 
TiKu schrieb:
aus diesem Posting

Ähm, LoadLibrary() und GetProcAddress verwendet man auch unter Windows eher selten. Ansonsten werden meines Wissens auch unter Windows die Symbole beim Kompilieren mit ihren Adressen verknüpft.

Aber bestimmt nicht bei dem von dir beschriebenen Mechanismus. Geht ja auch schlecht, wenn die Symbole zur Compilezeit nicht bekannt sind.

Normalerweise läuft das unter Windows so ab:
Du "includest" den Header, in dem die Funktion (bspw. void ILFree(LPITEMIDLIST)) definiert ist und schreibst im Quellcode dann einfach ILFree(pidl); Da ist nix mit LoadLibrary() und GetProcAddress() und trotzdem musst Du nicht statisch auf die shell32 linken.

Also wenn ich dich richtig verstanden habe, hat das nix mit statisch oder dynamisch linken zu tun, sondern ist immer dynamisch. Ist halt nur eine besondere Form des dynamischen Linkens, bei dem die Symbole zur Compilezeit nicht bekannt sein müssen.

Wenn Dein Programm aber mit verschiedenen Versionen ein und derselben DLL laufen soll (wenn ich mir die lib-Konflikte unter Linux so anschaue, dann glaube ich Dir, dass Linux sowas nicht kennt),

Auf welche lib-Konflikte spielst du hier an? ???

können LoadLibrary() und GetProcAddress() ganz nützlich sein. Bspw. exportiert die comctl32.dll erst ab *grübel* Version 4.71 oder so die Funktion DllGetVersion(). Mit LoadLibrary() und GetProcAddress() kann man jetzt erstmal prüfen ob die Funktion exportiert wird, bevor man darauf zugreift.

Soll heißen, ich kann mit neueren Programmen auf ältere Bibliotheken zugreifen, sofern ich selber teste, ob die gewünscht Funktionalität geboten wird? Da erschließt sich mir der Nutzen nicht so ganz.
 
@Puck

Ich verstehe auch nicht, wo du so ganz dein Problem sieht. "Dasselbe" gibt es auch unter der Unix (oder zumidest Linux) Welt. Guck mal nach dlopen... (oder gleich in den vdr code um das mal in der Anwendung zu sehen; hier geht das auf jeden Fall auch mit c++ code.)

Ich finde diese Art des Einbindens von dlls/libs gerade praktisch, weil damit eine executable nicht abhängig von einer library sein muß. Die exe kann nachwievor ausgeführt werden und evtl etwas sinvolles machen. Ich verweise insbesondere auf multimedia player, die nur weil eine lib nicht gefunden wird noch entsprechend anderer Formate abspielen könnte, statt wie das in der Linux Welt üblich ist, daß mal wieder gar nichts geht. Ich bin ja ein großer Linux Fan, aber das nervt mich wirklich an, daß alles zur compile time gelinkt wird und damit das Produkt entsprechend unflexibel und Versionsabhänig wird.
 
PuckPoltergeist schrieb:
aus diesem Posting

Also wenn ich dich richtig verstanden habe, hat das nix mit statisch oder dynamisch linken zu tun, sondern ist immer dynamisch
Jupp. Statisch linken geht (quasi als 3. Möglichkeit) aber auch.
PuckPoltergeist schrieb:
aus diesem Posting

Auf welche lib-Konflikte spielst du hier an? ???
Nen Kumpel hatte letztens erst Probleme mit der libaspell. Programm x brauchte ne neuere Version als Programm y, aber y kam mit der neueren Version nicht klar. Ich selbst hatte auch schon ab und an solche Probleme - vorzugsweise mit Bibliotheken aus dem GTK-Dunstkreis. Dass solche lib-Konflikte die Ausnahme sind, ist mir schon klar. Ich hätte vll. nen ;) dahinter setzen sollen.:)
PuckPoltergeist schrieb:
aus diesem Posting

Soll heißen, ich kann mit neueren Programmen auf ältere Bibliotheken zugreifen, sofern ich selber teste, ob die gewünscht Funktionalität geboten wird? Da erschließt sich mir der Nutzen nicht so ganz.
Man kann die Funktionalität nutzen, wenn sie geboten wird und das Feature deaktivieren oder auf eine andere (suboptimale) Implementierung zurückgreifen wenn sie nicht geboten wird. Natürlich kann man auch einfach die Bibliothek aktualisieren, aber das kann andere Programme beeinflussen.

/edit: Achja, Dein Argument von wegen dass ein Vertipper dazu führen kann, dass die falsche Funktion geladen wird, ist wohl auch eher theoretischer Natur.;)
 
PrakashP schrieb:
aus diesem Posting

@Puck

Ich verstehe auch nicht, wo du so ganz dein Problem sieht. "Dasselbe" gibt es auch unter der Unix (oder zumidest Linux) Welt. Guck mal nach dlopen... (oder gleich in den vdr code um das mal in der Anwendung zu sehen; hier geht das auf jeden Fall auch mit c++ code.)

Man lernt nie aus. Wusste nicht, dass das hier auch funktioniert. *bäh*

Ich finde diese Art des Einbindens von dlls/libs gerade praktisch, weil damit eine executable nicht abhängig von einer library sein muß. Die exe kann nachwievor ausgeführt werden und evtl etwas sinvolles machen. Ich verweise insbesondere auf multimedia player, die nur weil eine lib nicht gefunden wird noch entsprechend anderer Formate abspielen könnte, statt wie das in der Linux Welt üblich ist, daß mal wieder gar nichts geht. Ich bin ja ein großer Linux Fan, aber das nervt mich wirklich an, daß alles zur compile time gelinkt wird und damit das Produkt entsprechend unflexibel und Versionsabhänig wird.

Mag ja sein, dass damit das ganze deutlich flexibler wird, aber in meinen Augen lädt das geradezu zu chaotischer Programmierung ein. Damit lässt sich jedwedes Konzept, was man zu Beginn ja eigentlich erstellen sollte, mühelos untergraben.

edit: Ich sehe gerade (nach Sichtung der ersten Lektüre), dass sowas praktisch schon im Sprachstandard von C/C++ verankert ist. Also Kommando zurück zu meiner ersten Aussage, das geht sehr wohl. 8)
 
Zuletzt bearbeitet:
PuckPoltergeist schrieb:
Mag ja sein, dass damit das ganze deutlich flexibler wird, aber in meinen Augen lädt das geradezu zu chaotischer Programmierung ein. Damit lässt sich jedwedes Konzept, was man zu Beginn ja eigentlich erstellen sollte, mühelos untergraben.

Ich finde ja eher das Gegenteil ist der Fall. Dadurch daß man nicht mehr den Luxus hat mittels .a oder .la einen lib einzubinden, ist man gezwungen seine API/Interface beizubehalten, damit das reinladen der lib weiterhin klappt. Natürlich kann man mit dlopen und co viel Schabernack treiben, aber du kannst auch genau das simulieren, was das Einbinden einer lib mittels .la (bzw .lib unter win) passieren würde - nur halt nicht zur compile sondern zur runtime.

Dies hätte auch andere positive Nebeneffekte: Stell dir vor dein Prog ist von Dutzend libs abhängig, die aber großenteils nciht essentiell sind. Nun könntest du die essentiellen zur compile Zeit linken und den Rest bei Bedarf nachladen - ergo viel kürzere Start Zeiten und auch weniger Speicherverbrauch. Gerade unter Linux ist das Auflösen von library Abhängigkeiten und das Einbinden derer sehr ineffizeint geregelt. Man vergleiche etwa Startzeiten von Firefox unter Win und unter Linux.
 
PuckPoltergeist schrieb:
aus diesem Posting

Mag ja sein, dass damit das ganze deutlich flexibler wird, aber in meinen Augen lädt das geradezu zu chaotischer Programmierung ein. Damit lässt sich jedwedes Konzept, was man zu Beginn ja eigentlich erstellen sollte, mühelos untergraben.
Hey, man kann *alles* zweckentfremden. Man kann mit C++ total kranken Code schreiben, aber ist deshalb C++ für die Tonne?;)
 
Ok, ich gebe mich geschlagen. Ihr habt mich langsam überzeugt. ;)

Wenn man sich das nochmal genauer durch den Kopf gehen lässt, ist das Konzept wirklich gar nicht so verkehrt.

Jetzt mal die Frage an PrakashP: Wie alt/neu ist das Konzept von dlopen eigentlich? Wird ja anscheinend nicht sonderlich genutzt.

Ansonst bleibe ich aber bei der Meinung, dass dieses Konzept eine erheblich höhere Disziplin bei der Programmierung erfordert. Macht man beim herkömmlichen linken Blödsinn, fliegt einem das während des Compilierens um die Ohren. Baut man beim linken zur Laufzeit Mist, knallt es auch erst zur Laufzeit. Schlimmstenfalls passiert das erst dann, wenn man das Programm schon raus gebracht hat.
 
PuckPoltergeist schrieb:
Jetzt mal die Frage an PrakashP: Wie alt/neu ist das Konzept von dlopen eigentlich? Wird ja anscheinend nicht sonderlich genutzt.

Sehr neu scheint es nicht, denn wie ich gerade lese ist es doch für C und nciht C++ gemacht, aber mit eineigen Krücken geht es auch mit C++ scheint es. (Habe den vdr code nciht so weit auseinander genommen.)

Leider wird das nciht so häufig genutzt, denn ein -llib mit #include "lib.h" geht "etwas" schneller von der Hand als ne struct mit den Funktionen zu schreiben (falls dies wie so häufig dem Prog nicht als header beiliegt) und ne Routine zum Laden das ganzen. Und der Mensch ist halt von Natur aus faul...

Ein weiter Problem (darum das lange Laden bei compile-time-gelinkten libs), das man umschiffen könnte bei dyn Nachladen: Man kann nicht explizit aussuchen, welche Symbole exportiert werden. Es gibt anders als bei win kein dllexport. Erst bei den c++ Geschichten gibt es seit neuestem die visibility Sachen. Mnannn könnte zwar noch per hand alle nicht benötigten Symbole strippen, aber macht das jemand? Also wird der ganze Mist aufgelöst und reingeladen, den man wahrscheinlich gar nicht braucht.
 
Zuletzt bearbeitet:
PrakashP schrieb:
aus diesem Posting

Sehr neu scheint es nicht, denn wie ich gerade lese ist es doch für C und nciht C++ gemacht, aber mit eineigen Krücken geht es auch mit C++ scheint es. (Habe den vdr code nciht so weit auseinander genommen.)

Also ich habe hier gerade ein paar Dokumente zur Hand, welche diese Funktion mit C++ beschreiben. So viel Aufwand ist das gar nicht.

Leider wird das nciht so häufig genutzt, denn ein -llib mit #include "lib.h" geht "etwas" schneller von der Hand als ne struct mit den Funktionen zu schreiben (falls dies wie so häufig dem Prog nicht als header beiliegt) und ne Routine zum Laden das ganzen. Und der Mensch ist halt von Natur aus faul...

Was der Bauer nicht kennt... In der Windowswelt muss es wohl doch etwas verbreiteter sein.
 
Zurück
Oben Unten