Qwindows.dll Never Found In .Exe Dir
-
Hi. just a quick reply re. QCoreApplication::libraryPaths(), it actually removes the default library lookup if you call it in main.cpp before you do QApplication a(argc, argv).
(I've been through Qt's source code a lot this easter weekend, reason is I'm writing another blog post "on my blog":http://tripleboot.org about all the nooks and crannies about plugin loading...)
-
[quote author="hskoglund" date="1398188965"]Hi. just a quick reply re. QCoreApplication::libraryPaths(), it actually removes the default library lookup if you call it in main.cpp before you do QApplication a(argc, argv).[/quote]Good find. That doesn't sound intuitive though! I wonder if it's intentional (and if so, why)...
-
(This might be off topic but QCoreApplication::libraryPaths() behavior is quite interesting)
bq. I wonder if it’s intentional (and if so, why)…
Finally managed to wrap my head around this:
QCoreApplication::libraryPaths() is a utility function with an intentional side effect: it's responsible for loading the paths needed for locating plugins like qwindows.dll. And as an optimization (sensible I think) it caches the result, so that this extra work is only done once. The other similar functions, for example addLibraryPath(), always call libraryPaths() first to make sure the paths are all setup properly.
It tries to load 4 different paths:
- the hardwired 'plugpath' written into Qt5Core.dll by the installation.
- qt.conf's plugin=path
- the path to the app's .exe file
- env. variable QT_PLUGIN_PATH=
The unexpected behavior occurs because for path 2) and 3) to be loaded ok, QApplication's constructor (i.e. QApplication a(argc, argv); in main.cpp) must have been called first.
Normally this is not a problem since the first call to libraryPaths() is in QFactoryLoader::update() as a part of QApplication's constructor, i.e. the self variable in QCoreApplication has been set.
But if you're trying to be clever and invoking libraryPaths() or addLibraryPath() in main.cpp before QApplication's constructor is called, then self is still nullptr, and only path 1) and 4) are set.
So proper behavior of libraryPaths() depends on two different variables, and by calling it prematurely one of them is not yet kosher :-( Perhaps the docs should warn about this.
-
hskoglund: That's very valuable information. Thanks so much for investigating and sharing!
So, BKewl: Did you call QCoreApplication::libraryPaths() or similar before constructing your QCoreApplication? If so, the solution would be to change the order.
-
So I think I finally found the root of the problem (thank you hskoglund for all of your detective work--looking forward to your blog post!).
I've been calling QApplication::setStyle before calling the QApplication constructor (as recommended by the documentation). I found out I was passing a string for a style whose support was not compiled in; in this case, QStyleFactory::create uses QFactoryLoader to try and find a matching style dll in the styles subdirectory. Well, QFactoryLoader calls QApplication::libraryPaths (surprise!) which results in the situation hskoglund mentioned above and so the libraryPaths are missing the application path.
The next question is, if you look at my debug output above: why is the "/accessible" subdirectory correctly searched later on? The answer here is that it looks like the trolls were trying to anticipate someone calling libraryPaths() before constructing QApplication and so they put a corrective function in QCoreApplication::init called (appropriately enough) appendApplicationPathToLibraryPaths. As expected, this method appends the application path to the end of the library paths if the library paths have already been initialized and are missing the application path.
And the final question is: if during construction of QApplication the application path is appended to libraryPaths, why do the platforms still fail to load? The answer here is: because they're loaded in QCoreApplication::init just before the libraryPaths are updated. QCoreApplication::init calls QCoreApplicationPrivate::createEventDispatcher a few lines above calling appendApplicationPathToLibraryPaths. If you trace it down, you'll find that QGuiApplicationPrivate::createEventDispatcher calls createPlatformIntegration, which looks for the platform dlls.
So, while QApplication will correctly add back in the application path to its libraryPaths during its construction, loading of the platforms is unfortunately immune to this fix and so will fail if you have a call to libraryPaths() somewhere before constructing QApplication.
-
Aha! I was trying today to understand why, starting with "/accessible", the app subdirectory is somehow magically resurrected.
Thanks to you grokking this, it's because of that extra band-aid call in QCoreApplication::init: d->appendApplicationPathToLibraryPaths(). Saved me some hours!About QApplication::setStyle(QString), I just checked out the docs, and as you say, they're recommending you do it before QApplication's constructor. Which means this is a bug waiting to strike you, not on your development PC, where everything is fine and dandy thanks to the hardwired installation directory, but on a PC you deploy to.
It seems an easy solution would be to just insert an extra call to appendApplicationPathToLibraryPaths() before createEventDispatcher(). (An extra call is harmless since the function always checks if the app directory is already in there.)
Submit this issue with setStyle() as a bugreport?
-
[quote author="hskoglund" date="1398363478"]Submit this issue with setStyle() as a bugreport?[/quote]I think so. :)
Again, great detective work, guys!
-
"Bug Report":https://bugreports.qt-project.org/browse/QTBUG-38598
-
Good! I meant to do it but laziness etc.. :-)
BTW, I see that also calling QStyleFactory::keys() releases the bug, because one could argue that as long as you call setStyle() with a kosher string the bug will not strike, but keys() is 100% to do it.
I'm pretty certain the bug exists on Mac on Linuxes as well, I'll check that and post a comment to your bugreport.
-
Finally got around to finish that "blog post":http://www.tripleboot.org/?p=536
Did you know that there are actually 7 (!!) different ways to specify where to find qwindows.dll (i.e. 6 ways + the default application path)