在 Windows 中的 Qt 中加载外部 dll

Posted

技术标签:

【中文标题】在 Windows 中的 Qt 中加载外部 dll【英文标题】:Load external dll in Qt in Windows 【发布时间】:2016-01-06 03:47:04 【问题描述】:

假设,我有一个包含我的程序的文件夹,还有另一个包含外部库的文件夹。

bin
    myprog.exe
etc
    lib.dll
    sublib.dll

就我而言,我想从我的主程序myprog.exe 加载lib.dll。问题是lib.dllsublib.dll 链接。

所以我尝试这样做:

QCoreApplication a(argc, argv);
QLibrary lib;
QString path = "C:/etc/lib.dll";
a.addLibraryPath(path);

if(QLibrary::isLibrary(path)) 
    lib.setFileName(path);
    lib.load();
    if(lib.isLoaded())
        qDebug() << "Ok\n";
    else
        qDebug() << "Error " << lib.errorString() << "\n";
 else
    qDebug() << "Not a library\n";
return a.exec(); 

运行应用程序后出现错误:

无法加载库 lib.dll 找不到指定的模块

如果我将lib.dllsublib.dll 都放在bin 目录中,它可以正常工作。但这不是我想做的。

我试过了

a.addLibraryPath("C:/etc");

但这不起作用。 据我了解QCoreApplication::addLibraryPath() 设置 Qt 程序的路径,而不是系统范围的设置。所以,在这种情况下,lib.dll 仍然找不到 sublib.dll 尽管它位于同一目录中。

所以我的问题 - 我如何在 Qt 程序中加载外部共享库,以防这个库有自己的依赖项?

【问题讨论】:

你确定给定正确 EXE 的 lib.dll 会加载 sublib.dll 吗? 是的,如果我将两个 dll 都放在 c:/binC:/Windows/System32 中,它可以正常工作。 逃离 Windows dll 地狱的唯一安全方法是将 dll 及其依赖项复制到应用程序目录中。磁盘空间很便宜。受苦的肯定方法是将它们转储到 system32 目录中。 【参考方案1】:

那是 Windows 的问题。 DLL 将在当前进程目录中查看,然后在系统 PATH 中查看。某些C:\etc\lib.dll 中包含的代码在其自己的进程中运行,除非实现的非常具体的逻辑将按照系统规则运行。

详情请参考 MSDN 文章Dynamic-Link Library Search Order。如果该 lib.dll 的源代码可用,则检查LoadLibrary 调用是有意义的。如果没有提供具体路径,则:

搜索的第一个目录是包含图片的目录 用于创建调用进程的文件(有关更多信息,请参阅 CreateProcess 函数)。这样做允许私有动态链接 与进程关联的库 (DLL) 文件,无需找到 将进程的安装目录添加到 PATH 环境中 多变的。如果指定了相对路径,则整个相对路径为 附加到 DLL 搜索路径列表中的每个标记。加载模块 从相对路径而不搜索任何其他路径,使用 GetFullPathName 获取非相对路径并调用 LoadLibrary 非相对路径。有关 DLL 搜索顺序的更多信息, 请参阅动态链接库搜索顺序。

【讨论】:

谢谢@AlexanderVX!正如我所见,唯一的方法是将c:/etc 添加到 PATH 变量中,因为这两个 dll 都不是我的,我不知道它们是如何加载的。【参考方案2】:

没有什么可以阻止您显式预加载 lib.dll 所依赖的库。一旦预加载,它们就可以供您随后打开的任何库使用。毕竟,您知道它们在哪里,因此迭代它们并尝试加载它们是一件简单的事情。由于这些库之间可能存在依赖关系,您必须继续加载它们,直到没有更多进展:

   QString path;
   QSet<QString> libraries;
   QDirIterator itpath, "*.dll";
   while (it.hasNext())
     libraries << it.next();
   bool progress = true;
   while (progress) 
     progress = false;
     for (auto i = libraries.begin(); i != libraries.end();) 
       QLibrary lib*i;
       if (lib.load()) 
         progress = true;
         i = libraries.erase(i);
        else
         ++i;
      
   

要么就是这样,要么使用你选择的PE库来自己构建依赖树,并且只打开必要的库,按依赖顺序。

附注:你不拥有C:\Windows,你不应该在任何现代安装程序中在那里(也不在任何子文件夹中)写任何东西。

【讨论】:

以上是关于在 Windows 中的 Qt 中加载外部 dll的主要内容,如果未能解决你的问题,请参考以下文章

QT5:在 windows 中加载 psql 驱动程序失败

在Python中加载DLL时出现Windows错误1114

QPixmap 在 Qt 中加载许多图像

PyQt5 & PySide2 / 无法在“”中加载 Qt 平台插件“windows”,即使找到了

为啥内存中加载的 DLL 与原始 DLL 文件不完全对应?

在 C# 中的自定义 appdomain 中加载 dll