我正在为一个客户端创建一个.NET应用程序,该客户端使用其中一个第三方系统执行I / O.由于他们经常更改此系统的密码,我应该通过调用它们在专用目录(不包括我的EXE文件)中提供的本机DLL来动态检索它。
但是,我无法使用LoadLibraryEx动态加载DLL。奇怪的是我可以使用DllImportAttribute调用库。
这是我到目前为止所做的:
根据这个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
调用DoNotResolveDllReferences
时,我得到一个工作句柄,但之后,我不能调用GetProcAddress
(错误代码127) - 我怀疑我必须为此完全加载DLL。[DllImport(DllPath, EntryPoint = GetPasswordEntryPoint, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)]
private static extern long GetPassword(long systemId, string user, byte[] password);
这怎么可能?我认为DllImportAttribute
背后的机制也在内部使用LoadLibrary。我的代码在哪里有所不同?我错过了一些明显的东西吗
只是一些说明:
DllImportAttribute
,因为我无法以这种方式在专用目录中指定搜索(DLL必须位于我的EXE文件旁边或在常见的Windows位置才能工作)。LoadLibrary
而不是LoadLibraryEx
,但结果相同。在Simons评论后编辑: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
是否可以在不重新启动服务的情况下实现这一点。
我应该更具体地解决实际问题:我无法使用LoadLibraryEx
加载本机DLL,无论它是放在我的EXE旁边,还是放在另一个随机文件夹中,还是放在SysWow64中。为什么它适用于DllImportAttribute
?我很确定我的系统上没有丢失的FastMM subdependency DLL(既不在实际的DLL旁边,也不在任何Windows目录中)。
这是因为DLL搜索顺序路径。在windows中,当应用程序尝试加载DLL时,底层系统会自动搜索DLL的某个路径,所以让我们假装Windows的DLL搜索路径如下所示:
A) . <-- current working directory of the executable, highest priority, first check B) \Windows C) \Windows\system32 D) \Windows\syswow64 <-- lowest priority, last check
您可以在this Microsoft documentation中阅读有关基础机制的更多信息。
搜索主DLL与其相关的DLL并找到它存储在系统中的位置,使用AddDllDirectory或SetDllDirectory将其目录添加到Windows的DLL搜索路径中。
@HansPassant和@David Heffernan是对的:我实际上试图加载两个不同版本的DLL(其中一个有FastMM子依赖,一个没有)。感谢您的帮助,感谢您的不便。