Qwindows.dll Never Found In .Exe Dir
When I copy my app to a vanilla machine, I'm getting the "could not load platform plugin 'windows'" error.
Now, there are plenty of resources for fixing that, and I have the purple links to prove I've tried most of them. Here's the layout of the folder holding the application:
Here's the weird part: on my development box, Qt was installed (using the binaries installer on the site) into C:/Qt. If I recreate the full directory hierarchy to the platforms folder from my development machine on the vanilla machine and drop the qwindows.dll in there, the application will run fine. That is, if I create C:/Qt/5.2.1/msvc2012_64/plugins/platforms and put qwindows.dll in there, the app runs. No additional dependencies added, no nothing else: I only create the folders and move the qwindows.dll!
Why would my application not be looking for qwindows.dll in its own directory? I'm stumped.
Hi, and welcome to the Qt Dev Net!
What happens if, on your development machine, you rename C:\Qt to something else? Are you still able to launch your application?
Thanks for the welcome and the reply! If I rename the Qt directory on my development machine, I can no longer run the application on my development machine, even though I've also created a platforms folder where the application executable is located and put qwindows.dll in there. I get the error mentioned above about not finding the windows platform. Why would that be?
[quote]If I rename the Qt directory on my development machine, I can no longer run the application on my development machine[/quote]That means there's something in the Qt folder that you forgot to copy.
I notice that you have libEGL.dll and libGLESv2.dll, but not d3dcompiler_XX.dll. Copy d3dcompiler_XX.dll and see how it goes.
If it still fails, try the "quick and dirty" deployment at http://qt-project.org/wiki/Deploy_an_Application_on_Windows (this technique ensures that you can't possibly miss anything)
The weird part is that even with the quick and dirty deployment, I get the error. However, it seems like the only thing it's looking for in the Qt directory is the qwindows.dll. I know this because if I rename my Qt directory back to C:\Qt and run my quick and dirty deployed app, it runs (as expected). Then, if I rename C:\Qt\5.2.1\msvc2012_64\plugins\platforms\ to platforms.hide, my application gives me the platforms error again. If I then rename C:\Qt\5.2.1\msvc2012_64\plugins\platforms.hide back to platforms and then hide the non-platform Qt DLLs by renaming C:\Qt\5.2.1\msvc2012_64\bin to bin.hide, my quick and dirty deployed application runs fine.
Hi, just a guess, but the culprit ought be some .dll (as JKSH says: you forgot to copy).
However because you get that infamous "could not load platform plugin ‘windows" message box, agreed it points to qwindows.dll.
But try to copy some of the other .dlls in the platforms directory, like qmininal.dll... Maybe the absence of some of the other .dlls also trigger the same messagebox.
Also to get a clearer picture of exactly what .dlls are loaded when your app has been launched, you can use Profiling in Dependency Walker or ListDlls from sysinternals.com.
EDIT: (I just read your first post again, you mention just by removing or installing qwindows.dll on the vanilla machine triggers the error, hmmm, no other dll)
Perhaps instead you have a call to QCoreApplication::libraryPaths() or QCoreApplication::addLibraryPath() in your main()? Calling those guys removes Qt's default lookup of the platforms directory next to your .exe file, i.e. that could explain your malaise.
Thanks for the reply hskoglund. I've followed the Quick and Dirty deployment mentioned above, so I should have every Qt base dll as well as every plugin dll (including platforms) in my release directory or the appropriate subfolders.
I diff'd the output from Dependency Walker's profiling of a run of my app when it succeeds (with the Qt directory intact) and when it fails (with the Qt directory renamed). They're identical up to the point where the app goes to load qwindows.dll, which it looks for in the Qt install directory; at this point the failing version creates an error message box. Frustratingly, I don't get any kind of error message about trying to load a library and it failing.
I went and added QT_DEBUG_PLUGINS to the environment variables when running and I get output like so:
QFactoryLoader::QFactoryLoader() checking directory path "C:/Qt/5.2.1/msvc2012_64/plugins/styles" ...
QFactoryLoader::QFactoryLoader() checking directory path "C:/Qt/5.2.1/msvc2012_64/plugins/platforms" ...
QFactoryLoader::QFactoryLoader() looking at "C:/Qt/5.2.1/msvc2012_64/plugins/platforms/qwindows.dll"
QFactoryLoader::QFactoryLoader() checking directory path "C:/Qt/5.2.1/msvc2012_64/plugins/accessible" ...
QFactoryLoader::QFactoryLoader() looking at "C:/Qt/5.2.1/msvc2012_64/plugins/accessible/qtaccessiblequick.dll"
QFactoryLoader::QFactoryLoader() checking directory path "C:/Users/User Name/Documents/Project.Name/Release/release/accessible" ...
QFactoryLoader::QFactoryLoader() looking at "C:/Users/User Name/Documents/Project.Name/Release/release/accessible/qtaccessiblequick.dll"
Notice how the styles and platforms subdirectories are only checked for in the install directory, but accessible is checked in both the install directory and the application's directory. I think this is a clue, but I'm not sure why it's happening. The loop in QFactoryLoader::update() looks like it should be checking all directories every time.
Also one last thing: from what I can tell after looking at the source, neither QCoreApplication::libraryPaths() nor QCoreApplication::addLibraryPath() affects the default library lookup in the current directory. The former returns what has been set or, if nothing is set, sets the default paths (install directory/plugins and application directory). The latter calls libraryPaths() to "prime the pump" before prepending the additional library path, so nothing is overwritten. Maybe you're thinking of QCoreApplication::setLibraryPaths()? That one's definitely destructive.
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!
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)