c# - 为什么 LoadLibrary 失败而 DllImportAttribute 工作?
问题描述
我正在为使用其第三方系统之一执行 I/O 的客户端创建一个 .NET 应用程序。由于他们定期更改此系统的密码,我应该通过调用他们在专用目录中提供的本机 DLL 来动态检索它(除了我的 EXE 文件之外)。
但是,我无法使用LoadLibraryEx动态加载 DLL 。奇怪的是我可以使用DllImportAttribute调用库。
这是我到目前为止所做的:
根据this SO answer,我使用以下代码(在构造函数中)尝试动态加载DLL:
public PasswordProvider(string dllPath)
{
if (!File.Exists(dllPath))
throw new FileNotFoundException($"The DLL \"{dllPath}\" does not exist.");
_dllHandle = NativeMethods.LoadLibraryEx(dllPath, IntPtr.Zero, LoadLibraryFlags.None);
if (_dllHandle == IntPtr.Zero)
throw CreateWin32Exception($"Could not load DLL from \"{dllPath}\".");
var procedureHandle = NativeMethods.GetProcAddress(_dllHandle, GetPasswordEntryPoint);
if (procedureHandle == IntPtr.Zero)
throw CreateWin32Exception("Could not retrieve GetPassword function from DLL.");
_getPassword = Marshal.GetDelegateForFunctionPointer<GetPasswordDelegate>(procedureHandle);
}
- 调用 LoadLibraryEx 时,结果句柄为空,错误代码为 126,这通常意味着找不到 DLL 或其依赖项之一。
- 当我用 调用
LoadLibraryEx
时DoNotResolveDllReferences
,我得到了一个工作句柄,但之后,我无法调用GetProcAddress
(错误代码 127)——我怀疑我必须为此完全加载 DLL。 - 当我在Dependencies (本质上是 Win10 的 Dependency Walker)中打开本机 DLL 时,我可以清楚地看到其中一个静态链接的 DLL 丢失了
- 但是,如果我复制 EXE 文件之外的 DLL 并使用 DllImportAttribute,我可以调用 DLL
[DllImport(DllPath, EntryPoint = GetPasswordEntryPoint, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)]
private static extern long GetPassword(long systemId, string user, byte[] password);
这怎么可能?我认为背后的机制DllImportAttribute
也在内部使用 LoadLibary。我的代码在哪里不同?我错过了一些明显的东西吗?
只是一些注释:
- 我不能只使用
DllImportAttribute
,因为我不能以这种方式指定在专用目录中搜索(DLL 必须位于我的 EXE 文件旁边或在常见的 Windows 位置才能工作)。 - 我也尝试过
LoadLibrary
,LoadLibraryEx
但结果相同。
西蒙斯评论后编辑:
NativeMethods
定义如下:
private static class NativeMethods
{
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr LoadLibrary(string dllName);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr LoadLibraryEx(string dllFileName, IntPtr reservedNull, LoadLibraryFlags flags);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetProcAddress(IntPtr moduleHandle, string procedureName);
[DllImport("kernel32.dll")]
public static extern bool FreeLibrary(IntPtr moduleHandle);
}
[Flags]
private enum LoadLibraryFlags : uint
{
None = 0,
DoNotResolveDllReferences = 0x00000001,
LoadIgnoreCodeAuthorizationLevel = 0x00000010,
LoadLibraryAsDatafile = 0x00000002,
LoadLibraryAsDatafileExclusive = 0x00000040,
LoadLibraryAsImageResource = 0x00000020,
LoadLibrarySearchApplicationDir = 0x00000200,
LoadLibrarySearchDefaultDirs = 0x00001000,
LoadLibrarySearchDllLoadDir = 0x00000100,
LoadLibrarySearchSystem32 = 0x00000800,
LoadLibrarySearchUserDirs = 0x00000400,
LoadWithAlteredSearchPath = 0x00000008
}
在 Hans Passant 发表评论后编辑:
总体目标是能够在我的应用程序(Windows 服务)运行时替换/更新本机 DLL。我检测到文件更改,然后重新加载 DLL。我不太确定在DllImportAttribute
不重新启动服务的情况下这是否可行。
而且我应该更具体地说明实际问题:我无法使用 加载本机 DLL LoadLibraryEx
,无论它是放在我的 EXE 旁边,还是在另一个随机文件夹中,还是在 SysWow64 中。为什么它适用于DllImportAttribute
? 我很确定我的系统上不存在丢失的FastMM 子依赖 DLL(既不在实际 DLL 旁边,也不在任何 Windows 目录中)。
解决方案
@HansPassant 和 @David Heffernan 是对的:我实际上尝试加载两个不同版本的 DLL(其中一个具有 FastMM 子依赖项,一个没有)。感谢您的帮助,对于给您带来的不便,我们深表歉意。
推荐阅读
- laravel - 如何在 Laravel 中将 sql 转换为 QueryBuilder
- r - R - 以每年的生存为条件计算年人口
- python - 如何在打印语句后显示分隔符
- batch-file - 如何在 Windows 批处理文件中捕获 File Not Found 消息
- php - Symfony 安全性在身份验证期间使用了错误的 sql 表别名
- javascript - FullCalendar.js 外部元素拖动颜色和数据
- c++ - 使用自定义分布生成序列
- caching - Apollo Client 中的查询是否应该在发出网络请求之前查找不同查询缓存的结果?
- mongodb - MongoDB 索引 - 具有高读写比的集合的性能注意事项
- powershell - 是否可以使用没有 SMTP 的 Powershell 通过电子邮件发送附件?