C++: Manifests and dynamically loading DLLs from different directory
Asked Answered
W

3

8

Long story of what I'm trying to achieve

I'm working on a program that dynamically loads DLLs as plugins. I'm compiling the program using Microsoft Visual C++ 2008. Still, let's assume that any Visual C++ version with which Qt works should be supported. The program directory layout is following:

| plugins/
|   plugin1.dll
|   plugin2.dll
| QtCore4.dll
| QtGui4.dll
| program.exe

program.exe discovers all plugins DLL files, performs LoadLibrary() on them and calls a certain signature function to find out if it's actually a plugin or not. This works pretty well on computers which have vcredist for MSVC90 installed. Naturally, to make the program work on all computers I have to redistribute it with the msvc*.dll files and with the appropriate manifest file. Qt DLLs also require the redist to run.

Now, I've set up cmake to automatically copy over appropriate redist DLLs and manifest depending on the selected Visual Studio version. For the sake of simplicity let's keep assuming that I'm working with MSVC90. When the redist gets copied to the program directory the layout looks like this:

| plugins/
|   plugin1.dll
|   plugin2.dll
| QtCore4.dll
| QtGui4.dll
| msvcm90.dll
| msvcp90.dll
| msvcr90.dll
| Microsoft.VC90.CRT.manifest (I'm also aware that this file is bugged in VS2008)
| program.exe

Regarding the bug in manifest file: http://www.cmake.org/pipermail/cmake/2008-September/023822.html

The problem

The program with this layout now works on computers which do not have the redist installed, but the plugins are not getting loaded. In order to get the plugins to load I have to do one of the following:

  1. Copy over the manifest file to plugins/ directory. Remove all references to msvc*.dll files from the manifest file. This works but it's not nice because I'd have to support different versions of edited manifest files depending on the version of used MSVC. Also, I have no idea if this won't break with Visual Studio other than 2008.
  2. Copy entire redist to plugins/ directory. This doesn't require any modification to the manifest file, but now program.exe stupidly tries to load the msvc*.dll files thinking they are plugins. Naturally, this fails gracefully so no big harm is done. The other drawback is that the size of the program package grows by over 1 MB. Both these issues are something that I can live with, though.
  3. Compile plugins with /MT switch. Brief testing has shown that this actually works but I'm not sure if it won't break anything in the future if both Qt and program.exe are /MD.

The question(s)

What's the best solution? What's the correct solution? If there is more than one correct solution then which is the best practice? Am I the first person to ever try to do this?

Update 1 (18 Nov. 2012)

While the question remains unanswered I decided to go for the route that causes the least headache. Until now I've been using solution number 1 and I decided to stick with it. If CMake detects that user is using a different MSVC version than 2008 it will display a warning message saying that automatic packaging is not fully supported.

Waldron answered 4/11, 2012 at 21:59 Comment(3)
Why don't you statically link the run-time for you app and for your plugins?Sycophant
Qt already requires the vcredist so I'll have to include it anyway. Besides there are issues with resource allocation when resource is allocated in one statically linked entity and then released in another one - it's unsafe and the program may crash. I don't know how this will behave with Qt if Qt is /MD.Waldron
I think you should let the authors of the plugins decide how they want to link with the run time library and not try and force them to dynamically link against a particular version of the run time. As for the allocation issues you mentioned, if you are calling into the plugin to allocate a resource, then you should call back into the plugin to free the resource.Sycophant
F
1

If your target OS has _WIN32_WINNT >= 0x0502 than you can use function

SetDllDirectory()

before you do loading the plugins.

Put path to main program folder.

The call overrides system load order:

  1. The directory from which the application loaded.
  2. The directory specified by the path in SetDllDirectory() call.

So, you may call the function after application starts up. It is safe in all cases. Good luck!

Foscalina answered 7/11, 2012 at 11:0 Comment(0)
P
1

You can provide full file paths to "LoadLibrary", so you can load your plugins with their paths. I've used this exact layout to load multiple versions of the same library from subdirectories of the current dll in Visual Studio 2005.

You first need to get the current path of the current dll using:

static LPSTR strDLLPath1 = new TCHAR[_MAX_PATH+1];
::GetModuleFileName((HINSTANCE)&__ImageBase, strDLLPath1, _MAX_PATH);

Although if your program.exe is already discovering these plugin files, I would assume you already have access to their full paths.

Paratuberculosis answered 27/11, 2014 at 11:41 Comment(0)
F
0

You can create hardlinks to VC dlls with CreateHardLink() function at installation process. With method (1) that you have described, there may be some problems with different copies of VCRT dlls. Hardlinks or SetDllDirectory() seems the best solution.

Don't mix in a single process static and dynamic link to MSVCRT - it ALWAYS gives you problems!

Foscalina answered 7/11, 2012 at 15:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.