当加载64位dll时出现HRESULT 0x8007007E的DllNotFoundException错误

7
我下载了zlib并编译了32位和64位的Windows动态链接库。现在我有zlibwapi.dllzlibwapi64.dll
这些dll文件已经复制到我的应用程序文件夹,并且被引用如下:
[DllImport(@"zlibwapi.dll",   EntryPoint = "uncompress", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = false)]
private static extern int uncompress32(
    IntPtr dest,
    ref uint destLen,
    [In(), MarshalAs(UnmanagedType.LPArray)] byte[] source,
    uint sourceLen
);

[DllImport(@"zlibwapi64.dll", EntryPoint = "uncompress", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = false)]
private static extern int uncompress64(
    IntPtr dest,
    ref uint destLen,
    [In(), MarshalAs(UnmanagedType.LPArray)] byte[] source,
    uint sourceLen
);

在运行时,我会检查自己是32位还是64位,并调用相应的版本。

如果我是32位,则这个方法可以正常工作,但64位版本会出现以下错误:

无法加载DLL“zlibwapi64.dll”:找不到模块。(HRESULT异常:0x8007007E)

我在互联网上找到了许多类似的问题,并且建议的原因是该库依赖于其他一些库,而可能找不到这些库。
但事实并非如此:

  • zlibwapi64.dll仅依赖于Kernel32.dll和MSVCR90.dll。我已经安装了VS2008 C++运行时库,包括32位和64位。
  • 当我尝试从非托管的C++应用程序中加载zlibwapi64.dll时,它可以顺利加载。只有使用C#时它才无法加载。

我尝试将64位dll的绝对路径设置为加载路径,但没有帮助。

我该如何使其工作?


1
你是在同一个应用程序中同时使用两者吗?请记住,32位应用程序只能使用32位dll,64位应用程序只能使用64位dll。 - Daniel
@Dani 我根据运行时检查使用其中一个。该框架在引用之前不会尝试加载dll。当我将项目设置为x86时,它可以正常运行并调用uncompress32而不尝试调用uncompress64。当我将项目设置为x64时,它不会尝试调用uncompress32并且无法调用uncompress64 - GSerg
2个回答

12

这是一种相当基本的“文件未找到”类型的错误,不幸的是它没有明确告诉您无法找到哪个DLL。您已经知道了有关依赖DLL的问题。请注意,您可以通过使用/MT编译代码来避免对msvcr90.dll的烦恼依赖。

您需要调试该问题,这需要了解程序在哪里查找DLL的见解。一个很好的工具是SysInternals' ProcMon实用程序,它会向您显示程序正在寻找文件的确切位置。您应该看到它正在探测DLL,搜索PATH目录并未能找到文件。

不幸的是,ProcMon有点啰嗦,并且有一种淹没您的数据的习惯。一个更专业的工具是GFlags.exe,这是Debugging Tools for Windows包中提供的工具。这些天已经包含在Windows SDK中了。安装后存储在c:\program files (x86)\debugging tools for windows\gflags.exe中。您可以打开“显示加载器快照”选项。在较新的Windows版本上,这将告诉Windows加载器在搜索DLL时生成调试消息。启用非托管调试时,它们将出现在输出窗口中。

首先尝试ProcMon,它更容易上手。

当然,考虑使用纯托管解决方案,这样您就不必解决这种安装问题。好的解决方案是DotNetZip和SharpZipLib,请查看它们的第一个谷歌搜索结果。


Procmon显示找到了zlibwapi64.dll,然后没有找到MSVCR90.dll。它按照默认顺序进行搜索,但是没有找到。这有点意料之中,因为该dll位于Windows\winsxs\<multiple locations>中。那么问题是,为什么非托管C++可以使用LoadLibrary加载它?VC++运行时以这种方式安装是否正确? - GSerg
2
肯定可以工作,因为您的非托管C++ exe已经加载了msvcr90.dll。您的DLL可能缺少所需的清单。不确定在您的DLL项目中发生了什么,这通常是自动完成的,但请认真考虑使用/MT来完全避免问题。 - Hans Passant
当然可以工作,因为您的非托管C++ exe已经加载了msvcr90.dll。 -> facepalm.jpg - GSerg
3
决定尝试使用Procmon。它揭示了特定的DLL加载问题。非常感激找到这个详细而富有启发性的答案。 - Alyoshak
请注意,您可以通过使用/MT编译代码来避免对msvcr90.dll的烦人依赖。+1 这解决了我的问题。 项目属性 > C/C++ > 代码生成 > 运行时库: /MT - Slate
显示剩余2条评论

3
另一个检查DLL依赖项的好工具是Dependency Walker(depends)。它会以一种静态的方式查看文件,因此比使用过程监视器要简单一些。 http://www.dependencywalker.com/

我已经完成了静态分析,这就是我知道Kernel32.dll和MSVCR90.dll的原因。 - GSerg
1
依赖项查看器在运行分析模式时也可以用作动态依赖项分析工具。这个功能非常出色。 - mox

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接