6

我正在开发一款在 Windows XP 和 Windows Vista 之间的所有 Windows 版本上运行的 C++ 软件。在我的代码中,我开发了一个链接到标准库(Qt 库)的 DLL。部署我的软件后,用户在他的系统上没有完全相同的 Qt 构建但配置略有不同的情况并不少见。可能有一些功能被禁用(因此他们的 Qt 构建不会导出相同的符号集)或者库甚至可能以使库二进制文件与原始文件不兼容的方式进行更改。

在某些时候,我正在通过 LoadLibrary() 调用加载我的 DLL。这会引入用户系统上的任何 Qt 库。如果幸运的话,他们的 Qt 版本与我在开发 DLL 时使用的版本兼容,因此 LoadLibrary() 成功。但是,根据他们对 Qt 构建所做的更改,LoadLibrary() 调用有时会失败并显示

  • “指定的模块无法找到。”; 如果他们的 Qt 构建包含的 DLL 少于我的 Qt 构建,通常会发生这种情况。所以我的 DLL 尝试加载例如 QtFoo.dll 但由于这个 dll 不是他们 Qt 构建的一部分,所以加载我的 DLL 失败。
  • "找不到指定的程序。"; 如果他们更改 Qt 构建以禁用某些功能,这通常会发生这种情况,从而导致导出的符号减少。

我的问题是:如何优雅地捕捉这些错误?对,我只是使用 GetLastError() 然后打印上述两条消息中的任何一条。但是,如果我知道找不到哪个模块,或者缺少哪个程序,那会更有用。我注意到,当在资源管理器中运行一个链接到缺少的 DLL 的应用程序时,资源管理器设法产生一个很好的“由于缺少所需的库 blah.dll,因此无法加载应用程序 foo”。是否有一些 API 可用于获取有关为什么 LoadLibrary() 调用完全失败的更多信息?

4

6 回答 6

2

在某些时候,我正在通过 LoadLibrary() 调用加载我的 DLL。这会引入用户系统上的任何 Qt 库。

不要这样做!您遇到的那种错误是幸运的,就像它很容易损坏内存和崩溃一样。传送 Qt 应用程序的规范方式是传送 DLL 或静态链接。查看帮助文件中的 Qt 部署指南。

后期编辑:

阅读您的评论后,我仍然不建议您使用这种方法,因为即使加载 DLL,您也无法确定它们是否是二进制兼容的,这可能导致难以跟踪错误。

不过,我相信您可以拦截 LoadLibrary 调用并查看哪些调用失败。MS Detours库可用于此目的。另请参阅Stackoverflow 问题。

于 2009-08-19T12:54:16.270 回答
2

要扩展 jeffamaphone 的答案,您可以尝试在调用之前检索文件版本详细信息LoadLibrary。您可以使用以下函数执行此操作:

BOOL GetFileDetails(LPCTSTR lpszPath, LPDWORD lpMajorVersion, 
                    LPDWORD lpMinorVersion)
{
    DWORD dwVersionHandle;

    DWORD dwVersionSize = GetFileVersionInfoSize((LPTSTR)lpszPath,
                                                 &dwVersionHandle);
    if (dwVersionSize == 0)
        return FALSE;

    LPBYTE lpVersion = new BYTE[dwVersionSize];

    if (!GetFileVersionInfo((LPTSTR)lpszPath, dwVersionHandle, 
                            dwVersionSize, lpVersion))
    {
        delete [] lpVersion;
        return FALSE;
    }

    VS_FIXEDFILEINFO *pVersionInfo = NULL;
    UINT nLength;

    if (!VerQueryValue(lpVersion, _T("\\"), (LPVOID *)&pVersionInfo, &nLength))
    {
        delete [] lpVersion;
        return FALSE;
    }

    *lpMajorVersion = pVersionInfo->dwFileVersionMS;
    *lpMinorVersion = pVersionInfo->dwFileVersionLS;

    return TRUE;
}

然后,您可以对照预期的版本检查主要/次要版本号。

于 2009-08-21T19:51:19.007 回答
1

如果没有将调试器附加到您的进程,我认为您不能。发生这种情况时通常弹出的消息是由 LoadLibrary 内部生成的。SetErrorMode 在很多应用程序中用于禁止这种形式的消息,我猜想在你的应用程序框架的某个地方,它调用SetErrorMode来禁止操作系统消息。

我见过的唯一一个生成自己的关于 dll 加载失败的详细消息的应用程序是 MS DevStudio - 它作为调试器附加,因此可以访问特殊的调试事件流。

于 2009-08-19T08:23:31.937 回答
1

可以MapAndLoad从 ImageHLP.DLL 中获得帮助。它返回一个 LOADED_IMAGE 结构。

于 2009-08-25T09:59:33.703 回答
0

在调用 LoadLibrary() 之前,您能否更主动地检查您需要的 QT 二进制文件的版本?然后你可以警告你的用户他们没有你的应用程序需要的版本,甚至可以为他们提供一个安装点的链接。

于 2009-08-21T17:26:54.883 回答
0

您也可以使用清单文件让 Windows 对此进行检查。该文件包含有关所用库版本要求的信息。更准确和完整的信息在msdn 站点上

查看有关如何将 LoadLibrary 与清单文件一起使用的问题的答案。

Qt 文档简要提到了 VS2005 清单文件的使用;对于早期版本,您必须自己创建它。

于 2009-08-25T10:10:24.883 回答