PDA

View Full Version : Plugin can load or not depending on whether my app is launched from Qt Creator or not



agarny
2nd July 2011, 10:23
Hi, I am having an unexplained behaviour using Qt 4.7.3 and Qt Creator 2.2.1 on Windows 7 (everything works fine on Ubuntu 10.10 and Mac OS X 10.6 using the same versions of Qt and Qt Creator).

Basically, my application relies on various plugins being loaded upon starting my application. Now, for some weird reason, there is one out of 10 plugins that doesn't load when running my application from outside Qt Creator while if I start my application from within Qt Creator, then the plugin loads fine! This is the case whether I build a debug or release version of my application.

As I said, everything works fine on Linux and Mac OS X, so I am really puzzled as why that particular plugin doesn't load. Why that plugin?!

I know it might be a very long shot, but is there something trivial that I might have overlooked?

Here is some code that I use (before actually loading the plugin) to retrieve some information about the plugin itself:


PluginInfo Plugin::info(const QString &pPluginFileName)
{
// Return the information associated to the plugin

PluginInfo pluginInfo;

typedef PluginInfo (*PluginInfoFunc)();

PluginInfoFunc pluginInfoFunc = (PluginInfoFunc) QLibrary::resolve(pPluginFileName,
QString(name(pPluginFileName)+"PluginInfo").toLatin1().constData());

if (pluginInfoFunc)
// The plugin information function was found, so we can extract the
// information we are after

pluginInfo = pluginInfoFunc();
else
{
// The plugin information funciton couldn't be found which means that
// we are not dealing with an OpenCOR plugin

pluginInfo.type = PluginInfo::Undefined;

if (!name(pPluginFileName).compare("CellMLModelRepository")) {
QLibrary lib(pPluginFileName);

if (lib.load())
QMessageBox::information(0, QString("Info"), "Can load...");
else
QMessageBox::information(0, QString("Info"), QString("Can NOT load... [%1 | %2]").arg(pPluginFileName,
QFileInfo(pPluginFileName).exists()?"YES":"NO"));
}
}So, what happens is that I am trying to resolve a function which should be in my plugin. In all cases, it works but for that CellMLModelRepository plugin of mine. Having added some debug code, if the function cannot be found, I try to load the plugin using QLibrary which here fails for my CellMLModelRepository plugin even though QFile::exists() does report that the file exists...

caduel
2nd July 2011, 19:19
Have you tried to output QLibary::errorString() ?
This helps alot in tracking down such things :-)

Maybe the plugin is not found at all, or some environment stuff (like QT_PLUGIN_PATH) is not set?

HTH

agarny
2nd July 2011, 19:38
Yes, indeed... :) However, QLibrary::errorString() only tells me that "the specified module could not be found". Yet, QFile::exists() tells me that my plugin does exist... and as mentioned in my original message, it works fine from within Qt Creator. As for QT_PLUGIN_PATH, I will have a look (didn't know about that one), but still I can't understand why my other plugins would load fine since they are in the exact same directory...

ChrisW67
2nd July 2011, 22:33
Your library is failing to load because either:

The library file itself does not exist.
The library file depends on something that cannot be found when the environment is not being controlled by Qt Creator.
The library file exists but is not a dynamically loadable library.

If you are saying that 1 does not apply then my money is on option 2. Given that you are afflicted with Windows then the problem is that whatever you are depending on is not in the system PATH. If it runs in the IDE then this quite likely a Qt library, but it could anything else you have added to the IDE run-time PATH.

agarny
3rd July 2011, 03:20
#1 definitely doesn't apply indeed. Same with #3. So, it leaves #2.

Ok, something which for some reason I hadn't mentioned before is the fact that the plugin in question (CellMLModelRepository) requires another of my plugins (Core). Now, the way I am loading my plugins is by checking my plugin folder for files with, on Windows, an extension of .dll. This means that my application would normally try to load my CellMLModelRepository plugin before my Core plugin. However, each of my plugins contains a function which provides my application with some information about it (type of plugin and its dependencies, if any). Basically, I have (on Windows) something like:

extern "C" __declspec(dllexport) PluginInfo CellMLModelRepositoryPluginInfo();Now, what I thought I would do (and in fact do, except that it doesn't work in the case I have described in my original message) is to use QLibrary::resolve to load that function and retrieve the plugin's dependencies. This means that in the case of my CellMLModelRepository plugin, my application would find out that it requires my Core plugin and would therefore try to load my Core plugin before loading (using QPluginLoader) my CellMLModelRepository plugin.

Well, now, I am starting to think that even though I only want to resolve a 'self-contained' function in my CellMLModelRepository plugin, I would still need to load my Core plugin first. So, to test that hypothesis, I reworked my Core plugin to become a Bore plugin :), and... well... bingo, it works now!

So, it seems like I might finally have the cause of my problem. Two things now. First, why is it that my problem doesn't occur when I run my application from within Qt Creator (I am happy to accept that things are different on Linux and/or Mac OS X, hence I am not asking about those environments)? Secondly, is there really no way for me to extract from my plugin what its dependencies are? I would expect it to be possible since, after all, Dependency Walker (http://www.dependencywalker.com/) does it... Alternatively, I could do the same as Qt Creator, i.e. first read an XML file that contains some information about the plugin and then load the plugin itself (after having loaded its dependencies first). I don't like that solution though. I like the idea of my plugins consisting of only one file and not two of them...

=== AFTER SOME MORE TESTING... ===

Hmm... I just tried something which is to make a copy of my Core plugin to the folder where my application executable is, and... everything works fine. So, if anything, it does confirm that the issue was my CellMLModelRepository plugin does require my Core plugin and that it couldn't find it, hence even a 'simple' QLibrary:resolve wouldn't work.

Oh! Now, I understand why it works fine when running my application from within Qt Creator, but not from outside it! This is because when running my application from within Qt Creator, all my binary files (EXE + DLLs) are in the same folder... while this is not the case when I did my test by running my application from outside Qt Creator (the EXE would be in a specific folder while my DLLs (plugins) would be in another, and corresponds to the case where my files have been deployed). Doh!!

Ok, one more thing I don't understand (but won't try to 'crack' as it's getting late here!) is why is it that adding my plugins' folder using QCoreApplication::addLibraryPath didn't solve my problem...?

ChrisW67
3rd July 2011, 10:52
Ok, one more thing I don't understand (but won't try to 'crack' as it's getting late here!) is why is it that adding my plugins' folder using QCoreApplication::addLibraryPath didn't solve my problem...?
The path you are setting inside Qt is the path it will use to file "LibraryB.dll" when you ask for it, but it is the operating system that has to resolve run time dependencies when Qt tries to load LibraryB. When the operating system loads the dynamic library it has to be able to find the other libraries that it depends on. If they are are already loaded in the application address space then the should be no issue. If not the operating system uses the operating system's mechanisms to find a library to satisfy the requirement and fails if it cannot do so. This is outside of Qt.

If you have LibraryB that needs LibraryA then either explicitly load LibraryA first, or tell the operating system how to find LibraryA.

agarny
3rd July 2011, 11:25
The path you are setting inside Qt is the path it will use to file "LibraryB.dll" when you ask for it, but it is the operating system that has to resolve run time dependencies when Qt tries to load LibraryB. When the operating system loads the dynamic library it has to be able to find the other libraries that it depends on. If they are are already loaded in the application address space then the should be no issue. If not the operating system uses the operating system's mechanisms to find a library to satisfy the requirement and fails if it cannot do so. This is outside of Qt.

If you have LibraryB that needs LibraryA then either explicitly load LibraryA first, or tell the operating system how to find LibraryA.Ok, so the question for me is: is there a way to tell Windows where to find my plugins?

I am currently working on another way to get things to work, but that requires extracting the dependencies of my plugins from the .dll file, and though I have it all figured out in my head, it's a solution I would rather avoid, if at all possible (it looks a bit like a hack).

agarny
3rd July 2011, 19:33
For information, I have found some code (at http://pastebin.com/XNMtZF8G) which can be used to extract a DLL's dependencies. I have reworked that code quite a bit so that it does only what I need. For those who are interested, they can find 'my' code at https://github.com/opencor/opencor/blob/master/src/plugins/plugin.cpp (look for the Plugin::dependencies method).

With the above, I am now going to be able to retrieve some information about my plugins, but QLibrary::resolve a self-contained function in my plugin. The issue I had on Windows was that if that plugin depended on other plugins (not 'visible' to the system), then my application couldn't retrieve the information and then load the plugin. Now, with the above reworked code, I will be able to do it. It's rather crazy, I know, but the whole point of this is that I can have all of my plugins in their own folder (which is nice and clean, and as is the case for Qt Creator) without the need for an additional (XML, for example) file that contains some information about the plugin itself (i.e. as Qt Creator does, but which I find 'unclean').

ChrisW67
3rd July 2011, 23:21
http://msdn.microsoft.com/en-us/library/7d83bc18%28v=vs.80%29.aspx


With both implicit and explicit linking, Windows first searches for "known DLLs", such as Kernel32.dll and User32.dll. Windows then searches for the DLLs in the following sequence:

The directory where the executable module for the current process is located.
The current directory.
The Windows system directory. The GetSystemDirectory function retrieves the path of this directory.
The Windows directory. The GetWindowsDirectory function retrieves the path of this directory.
The directories listed in the PATH environment variable.

agarny
4th July 2011, 12:00
Argh! :) Why? Well, because I spent/wasted quite some time yesterday trying to come up with a working solution to my problem (which I did) while all I had to do was to use QDir::setCurrent() with the path to my plugins. I have just tried it and, as expected, it works fine. Doh! Well, still learning every day... :)

ChrisW67
5th July 2011, 05:54
That has side effects with respect to file open/save dialogs and the like if they are allowed to default to the current working directory.

agarny
5th July 2011, 11:11
Yes and this is the reason I am changing the directory just before loading the plugin and changing it back immediately after.