8

我正在努力实现的长篇故事

我正在开发一个将 DLL 作为插件动态加载的程序。我正在使用 Microsoft Visual C++ 2008 编译程序。不过,我们假设应该支持 Qt 使用的任何 Visual C++ 版本。程序目录布局如下:

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

program.exe发现所有插件 DLL 文件,对其执行 LoadLibrary() 并调用某个签名函数来确定它是否真的是插件。这在安装了 vcredist for MSVC90 的计算机上运行良好。自然,为了使程序在所有计算机上运行,​​我必须使用 msvc*.dll 文件和适当的清单文件重新分发它。Qt DLL 也需要 redist 才能运行。

现在,我已经将 cmake 设置为根据所选的 Visual Studio 版本自动复制适当的 redist DLL 和清单。为了简单起见,让我们继续假设我正在使用 MSVC90。当 redist 被复制到程序目录时,布局如下所示:

| 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

关于清单文件中的错误:http ://www.cmake.org/pipermail/cmake/2008-September/023822.html

问题

具有这种布局的程序现在可以在没有安装 redist 的计算机上运行,​​但插件没有被加载。为了让插件加载,我必须执行以下操作之一:

  1. 将清单文件复制到plugins/目录。从清单文件中删除对 msvc*.dll 文件的所有引用。这行得通,但不是很好,因为我必须支持不同版本的已编辑清单文件,具体取决于使用的 MSVC 的版本。另外,我不知道这是否不会与 2008 年以外的 Visual Studio 中断。
  2. 将整个 redist 复制到plugins/目录。这不需要对清单文件进行任何修改,但现在program.exe愚蠢地尝试加载 msvc*.dll 文件,认为它们是插件。自然,这会优雅地失败,因此不会造成太大的伤害。另一个缺点是程序包的大小增加了 1 MB 以上。不过,这两个问题都是我可以忍受的。
  3. 使用 /MT 开关编译插件。简短的测试表明这确实有效,但我不确定如果 Qt 和program.exe都是 /MD 将来它是否不会破坏任何东西。

问题

最好的解决方案是什么?什么是正确的解决方案?如果有多个正确的解决方案,那么最佳实践是哪个?我是第一个尝试这样做的人吗?

更新 1(2012 年 11 月 18 日)

虽然这个问题仍未得到解答,但我决定走最不让人头疼的路线。到目前为止,我一直在使用第 1 号解决方案,我决定坚持使用它。如果 CMake 检测到用户使用的 MSVC 版本与 2008 不同,它将显示一条警告消息,指出不完全支持自动打包。

4

3 回答 3

1

您可以为“LoadLibrary”提供完整的文件路径,这样您就可以使用它们的路径加载您的插件。我已经使用这种精确的布局从 Visual Studio 2005 中当前 dll 的子目录加载同一库的多个版本。

您首先需要使用以下命令获取当前 dll 的当前路径:

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

尽管如果您的 program.exe 已经在发现这些插件文件,我会假设您已经可以访问它们的完整路径。

于 2014-11-27T11:41:36.160 回答
1

如果您的目标操作系统的 _WIN32_WINNT >= 0x0502 则可以使用函数

SetDllDirectory()

在加载插件之前。

放置主程序文件夹的路径。

该调用覆盖系统加载顺序:

  1. 加载应用程序的目录。
  2. SetDllDirectory() 调用中的路径指定的目录。

因此,您可以在应用程序启动后调用该函数。在所有情况下都是安全的。祝你好运!

于 2012-11-07T11:00:04.567 回答
0

您可以在安装过程中使用 CreateHardLink() 函数创建到 VC dll 的硬链接。使用您描述的方法(1),VCRT dll的不同副本可能存在一些问题。Hardlinks 或 SetDllDirectory() 似乎是最好的解决方案。

不要混合到 MSVCRT 的单个进程静态和动态链接 - 它总是会给你带来问题!

于 2012-11-07T15:56:11.593 回答