Seite 1 von 2

[gelöst für Plugins] DLLs sollen in ein Unterverzeichnis

Verfasst: 17. August 2007 16:52
von FaS
Ich möchte diverse externe von meinem Programm benötigte Bibliotheken in einem Unterverzeichnis "bin" verstauen.

Der Normalfall ist, dass Qt-Bibliotheken im Programmverzeichnis liegen müssen, Qt-Plugin-Bibliotheken in einem Unterverzeichnis mit Namen des Plugins usw., das ist mir zu dumm. Daher möchte ich entweder diese Unterverzeichnisse wegrationalisieren oder am besten alles in einem gemeinsamen Unterordner "bin" liegen haben, auch nicht-Qt-libs.

Würde gern wissen wie das geht, ich hoffe es reicht, irgendwelche Angaben in der .pro-Datei zu ändern, z.B. Suchpfade zu setzen o.ä., in der Doku hab ich nichts gefunden.

MfG,
FaS

Verfasst: 17. August 2007 21:56
von Dawn
Also spontan fallen mir da 3 möglichkeiten ein:

1. die unsaubere aber einfache lösung
dein bin verzeichnis in die PATH umgebungsvariable unter windows (entsprechendes für die anderen OS) eintragen. Dann sucht das Betriebssystem beim startet deines Programms in diesem (und anderen) ordnern nach den libraries und wird fündig.

2. die sauberere aber kompliziertere lösung
du musst die libraries und die benötigten funktionen per hand laden.

3. die wohl sauberste und auch einfachste lösung
dein programm direkt mit in den bin ordner packen und dann beim starten das arbeitsverzeichnis auf ../ ändern.

Verfasst: 18. August 2007 00:05
von FaS
Danke Dawn für deine Tipps, ich würde das Problem aber jetzt gerne von der Ursache her betrachten. Zunächst mal deine Vorschläge:
1. PATH - ist wirklich nicht sehr schön und verlangt eine Installation.
2. Funktionen manuell laden - wenn das so aufwendig ist wie es sich anhört..
3. Programm im bin-Ordner - ich würde es schon gerne im Stammordner haben, wie soll es der Benutzer sonst ausführen. Eine Verknüpfung im Programmordner ist unschön und würde den Speicherort des Programms bindend machen.

Also, wo steht geschrieben, dass z.B. Qt-Plugin-DLLs in einem Unterordner mit dem Namen des Plugins liegen müssen, das wird doch sicherlich beim linken in die Anwendung gemeißelt, kann man das nicht ändern? Im erzeugten makefile konnte ich diesbezüglich nichts direkt finden..
Speziell geht es mir um die QCA-(Crypto-)API, seine DLL akzeptiert der im Programmordner, aber einige Funktionen werden als Plugins zur Verfügung gestellt und liegen in <Qt-Dir>/plugins/crypto, ich fürchte dass die Haupt-Bibliothek explizit dort nach ihnen sucht, also in einem Unterordner namens crypto - mann was haben die sich nur dabei gedacht, ist doch krank: die Haupt-DLL ist ja auch nicht in diesem Ordner.. Wenn dies der Fall sein sollte gibts wohl keine Lösung? Was nimmt er als Ausgangsort für die Suche, den Ordner wo die Anwendung liegt, oder das Arbeitsverzeichnis? Kann man die Suchpfade ggf. zur Laufzeit ändern? Vielleicht sogar auch für alle anderen Bibliotheken?

Hoffe es gibt ne Lösung..

Verfasst: 18. August 2007 08:05
von Volker
Bei PlugIns bist Du nicht auf den Ordner festgelegt, wenn Du sie mit dem QPlugInLoader lädst. Also auch quasi manuell, aber ist evtl. einfacher als mit z.B. LoadLibrary

Code: Alles auswählen

	QDir pluginsDir = QDir(qApp->applicationDirPath()+QDir::separator()+"MeinPlugInDir");
	QStringList pluginFileNames;	
	foreach (QString fileName, pluginsDir.entryList(QDir::Files)) 
	{
		if (QLibrary::isLibrary(fileName))
		{
			QPluginLoader loader(pluginsDir.absoluteFilePath(fileName));
			QObject *plugin = loader.instance();
			if (plugin) 
			{				
				PlugInInterface *meinPlugIn= qobject_cast<PlugInInterface *>(meinPlugin);
				if (meinPlugIn)
					m_meinePlugIns.push_back(meinPlugIn);
				else
					plugin->deleteLater();
			}
			else
				QMessageBox::warning(0, tr("Error"), tr("Error loading plugin %1\n").arg(fileName)+loader.errorString());
		}
	}	

Verfasst: 18. August 2007 08:47
von isifloh
Hallo

den Suchpfad für die plugins kannst du mittels dieser Methoden von
QApplication (geerbt von QCoreApplication) manipulieren.

removeLibraryPath
ibraryPaths
setLibraryPaths
addLibraryPath

Danach werden die plugins auch automatisch aus einem andere Verzeichnis geladen und ein händisches Laden mittels QPluginLoader ist nicht notwendig.

mfg

Verfasst: 18. August 2007 15:32
von FaS
Volker: Hört sich gut an, damit könnte ich wohl erreichen, dass die Plugins sogar kein Unterverzeichnis mehr benötigen. Aber was ist mit PlugInInterface und m_meinePlugIns? Ist letzteres einfach nur eine Liste, deren Elemente am Ende deleted werden müssen? Und woher bekomme ich das PlugInInterface?..

isifloh: Danke auch dir, damit kann ich wenigstens einen gemeinsamen Unterordner für die Plugin-Ordner beliebig wählen.

Verfasst: 19. August 2007 09:50
von Volker
Das PlugInInterface ist die Schnittstelle zu Deinen PlugIns, oder geht's dir nur um PlugIns die schon bei Qt mitgeliefert werden?

Guck Dir einfach mal das PlugIn Howto in der Qt Doku an:
http://doc.trolltech.com/4.3/plugins-howto.html
Da würde dann z.B. dem von mir angegebenen PlugInInterface das FilterInterface entsprechen.

Bzgl. des Deletes: Normalerweise leitet die implementierte Klasse ja von QObject ab, so dass Du mit setParent auch ein Parent setzen kannst und dich dann nicht unbedingt selbst um das löschen kümmern musst. Wenn du das nicht machst, musst du aber ja irgendwo das ganze wieder freigeben, daher die Liste.

Du kannst aber natürlich auch irgendwo anders die Referenz speichern. War nur ein Beispiel.

Verfasst: 19. August 2007 16:35
von FaS
Volker hat geschrieben:Das PlugInInterface ist die Schnittstelle zu Deinen PlugIns, oder geht's dir nur um PlugIns die schon bei Qt mitgeliefert werden?
Jain. Es ist nicht von Qt, aber auch nicht von mir. Aber vielleicht benötige ich später Qt-Plugins. Es gibt eine Erweiterung die nennt sich "Qt Cryptographic Architecture" (QCA), diese hat Plugins erstellt und ins Qt-Verzeichnis kopiert, und um diese geht es mir momentan.
Hab mir mal den Quellcode angeschaut, über

Code: Alles auswählen

QDir dir(libpath.filePath(PLUGIN_SUBDIR));
wird ein Ordner innerhalb eines library-paths ausgesucht und nach bereits geladen- und ist-library-Tests wird mit

Code: Alles auswählen

ProviderItem *i = ProviderItem::load(fname);
das Plugin geladen, anschließend folgen Versions-, Name-bereits-vorhanden-Tests usw.; die load()-Funktion benutzt auch lauter interne Klassen. Außerdem wird das Plugin dann in einer eigenen Liste gespeichert. Würde ich es hinbekommen es irgendwie zuvor selbst zu laden, würde er es anschließend selbst wahrscheinlich nocheinmal laden. Denke da gibts keie Möglichkeit außer

Code: Alles auswählen

#define PLUGIN_SUBDIR "crypto"
im QCA-Code zu ändern und das Teil neu zu kompilieren, aber das ist unschön und mit Qt-eigenen Plugins später könnte ich das ja auch nicht so einfach machen.
Ich denke das Hinzufügen eines neuen Library-Suchpfades ist erstmal die einfachste Möglichkeit, auch wenn es den Nachteil hat, dass DLLs nun im Programmverzeichnis UND einem frei wählbaren Unterordner liegen - eigentlich wollte ich nur eines von beiden.

Verfasst: 20. August 2007 14:43
von RHBaum
removeLibraryPath
ibraryPaths
setLibraryPaths
addLibraryPath
wobei die dir auch nur bei reinen "QTPlugins" helfen, also die standard plugins die QT mitbringt oder eigene Plugins der QT Plugin Typen / treiber.

Hast Du ne eigene Plugin engine, musst dich wirklich haendisch drum kuemmern.
1. PATH - ist wirklich nicht sehr schön und verlangt eine Installation.
PATH ist eine umgebungsvariable ....
damit wird die beim starten des prozesses vom elternprozess (Linux meistens die shell, unter windows der explorer die cmd shell oder weas sonst auch immer) geerbt, besser gesagt kopiert.

mit getenv kann man die auslesen und mit setenv lesen. Was du mit setenv schreibst, gilt aber nur fuer deinen prozess, sprich andere prozesse werden davon nicht beeinflusst und beim neustarten ist alles wieder weg.

Das bietet sich geradezu an fuer lokale aenderungen ....


Ciao ...

Verfasst: 20. August 2007 18:14
von FaS
RHBaum hat geschrieben:
PATH ist eine umgebungsvariable ....
Ja und d.h. dass man VOR dem Starten des Programms diese Variable setzen muss -> Installation oder Startscript. Wenn man das in main() mit setenv setzen würde, wäre es doch längst zu spät da er vorher das Vorhandensein der DLLs prüft oder nicht? Glaube ich hatte das aber mal probiert und setenv kannte er nicht, nur getenv... Hab grad Win neu installiert und kanns noch nicht testen...

Verfasst: 21. August 2007 12:39
von RHBaum
Ja und d.h. dass man VOR dem Starten des Programms diese Variable setzen muss
nicht unbedingt ....
du musst nur vor dem laden der plugins dazukommen, die Path variable anzupassen.
dann kannst deinen pluginpfad irgendwo in irgendwelchen settings stehen haben, nach dem starten des programms und lesen der settings wird der lokale $PATH angepasst (getenv / setenv), und danach die plugins geladen, und voiala, die plugins werden aus deinem Verzeichniss geladen und die anderen Applikationen kriegen davon gar nix mit, und du brauchst auch kein Script vorher ....

BTW, die kindprozesse erben die umgebungsvariablen natuerlich ...

Ciao ...

Verfasst: 21. August 2007 19:19
von Dawn
ich glaube er redet von den QT DLLs die nunmal VOR dem Programmstart geladen werden. Da kann man mit getenv nichts erreichen

Verfasst: 22. August 2007 01:52
von FaS
Genau, die qt-plugin-libs (derzeit sowieso nur eine) konnte ich ja bereits dank isifloh erfolgreich in einen gemeinsamen Unterordner verschieben mit

Code: Alles auswählen

QCoreApplication::addLibraryPath( "bin" );
Das Problem war jetzt nur dass es 2 Orte gibt in denen DLLs auftauchen, der bin-Unterodner (Plugins) und der Programmordner (alle anderen DLLs).
Plugins können nicht im Programmverzeichnis liegen, da sie ihren eigenen Unterordner verlangen (dumme dumme dumme Idee) und die anderen DLLs können nicht in einem Unterodner liegen, da dieser wärend dem Laden der Anwendung dem System nicht als Bibiliotheks-Pfad bekannt ist.
Eine mögliche Lösung die ich - vielleicht - akzeptieren würde wäre noch ein nicht-Qt-Starterprogramm welches systemabhängig geschrieben werden müsste ähnlich einer Verknüpfung, nur hab ich dazu kein Bock und würde ich es unter win mit g++/mingw compilieren bräuchte ich trotzdem die mingwm10.dll, lohnt sich also nicht wirklich.
Also ich werde es wohl so belassen wie es ist. Vielen Dank an alle die geholfen haben,
FaS

Verfasst: 22. August 2007 11:05
von RHBaum
ich glaube er redet von den QT DLLs die nunmal VOR dem Programmstart geladen werden.
Dll's werden nie "vor dem Start" geladen :-) sondern immer dynamisch zur laufzeit. sagt der name eigentlich scho

Die frage ist nur, ob man an den zeitpunkt vor den dynamsichen laden rankommt, bzw. ob man sich da ned mehr probleme einhandeln kann.

Das hauptproblem bei dem auch von M$ benutzen prinzip mit der statischen BindeLib ist, das da das dynamische laden der dlls meist uber statische klassenmember (also quasi global) und damit vor dem main() erfolgt.
QCoreApplication::addLibraryPath( "bin" );
wenn das funktioniert, dann werden die Dlls definitv danach (nach diesem aufruf) geladen ....
Plugins können nicht im Programmverzeichnis liegen, da sie ihren eigenen Unterordner verlangen (dumme dumme dumme Idee)
Was darauf hindeutet, das QT den lademechanismus fuer Dlls, zumindest fuer die eigenen plugins, von Windows aushebelt.
Bei loadlibrary ist definiert, das windows zuerst im verzeichnis des binaries sucht ....
Die QT wird beim QT-plugin-laden also irgendwie selber pfade auswerten und die dlls nur mit vollstaendigen pfad versuchen zu laden, was eine manipulation von der $PATH sowieso sinnlos macht.
Glaub hier versuchen die QT zu manipulieren waere ein weg mit zu viel aufwand ...
Das Problem war jetzt nur dass es 2 Orte gibt in denen DLLs auftauchen, der bin-Unterodner (Plugins) und der Programmordner (alle anderen DLLs).
du rufst doch
QCoreApplication::addLibraryPath( "bin" ); auf ?

ermittel doch mal den absoluten pfad deiner anwendung, und adde den auch als libraryPfad ueber die funktion ?
QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath());
wuerde mich wundern wenn die QT expliziet das dir des binaries als Lib pfad unterbindet ....

Ich mach mit den QT plugins ned viel, wir schreiben mehr eigene Plugins wo die schnittstellen QT frei sein muessen (nicht jeder unserer Zulieferer soll sich die QT kaufen) .... deshalb kenn ich mich auch mehr mit so generellen Dll probs aus, ned so mit dem QT Plugin spezifischen zeugs ...

Ciao ...

Verfasst: 22. August 2007 16:45
von FaS
RHBaum hat geschrieben:wenn das funktioniert, dann werden die Dlls definitv danach (nach diesem aufruf) geladen ....
Ja, die Plugins, das funktioniert nur mit den Plugins: wie weiter oben schon geschrieben hab ich mir den Lademechanismus des QCA-Plugins, um das es mir ging angesehen: Der sucht alle seine DLLs in <alle QCoreApplication::libraryPaths()>/PLUGIN_SUBDIR/*, wobei PLUGIN_SUBDIR ein #define für "crypto" ist.
RHBaum hat geschrieben:du rufst doch
QCoreApplication::addLibraryPath( "bin" ); auf ?

ermittel doch mal den absoluten pfad deiner anwendung, und adde den auch als libraryPfad ueber die funktion ?
Der sucht die Qt-Plugins mit Hilfe meines addLibraryPath() ja nicht direkt in bin, sondern in bin/<pluginname>/*. Der Pfad meiner Anwendung ist da standardmäßig schon drin, aber dann hätte ich dort doch lauter <pluginname>-Unterverzeichnisse; ein gemeinsames Unterverzeichnisse für diese Plugin-Verzeichnisse ist also die einzige Möglichkeit, wenn ich die Plugins nicht manuell ändern will.

-----
RHBaum hat geschrieben:deshalb kenn ich mich auch mehr mit so generellen Dll probs aus, ned so mit dem QT Plugin spezifischen zeugs ...
Ist doch toll, Qt-Zeugs ist erledigt, jetzt müssen nur noch alle anderen (nicht-Qt-Plugin-) DLLs in das bin-Verzeichnis ;-)
RHBaum hat geschrieben:Das hauptproblem bei dem auch von M$ benutzen prinzip mit der statischen BindeLib ist, das da das dynamische laden der dlls meist uber statische klassenmember (also quasi global) und damit vor dem main() erfolgt.
Hmm hört sich interessant an, dachte mir da mach ich das mal nach, statische Variablen in main.cpp müssten ja zuerst ausgewertet werden wenn main.cpp zuerst dem Linker übergeben wird oder nicht? Jedenfalls hab ich folgendes an den Anfang geschrieben:

Code: Alles auswählen

#include <cstdlib>

class PathChanger
{
  public:
    PathChanger();
};
PathChanger::PathChanger()
{
  putenv( "PATH=bin" );
}
static PathChanger pathChanger;
#include <cstdlib> brauchte ich für putenv, musste ich also zuvor angeben, dadurch wird wohl die mingwm10.dll immer im Programmverzeichnis liegen müssen (falls sie da schon geladen wird)?
Naja das ganze funktioniert für die ganzen DLLs jedenfalls nicht, vielleicht hast du noch ein Tipp?
Wenn ich im System eine Umgebungsvariable "bin" zu $PATH hinzufüge funktioniert das ganze, eine relative Angabe ist also in Ordnung.