Я создаю .NET-приложение для клиента, которое выполняет ввод-вывод с одной из сторонних систем. Поскольку они регулярно меняют пароль этой системы, я должен извлекать его динамически, вызывая собственную DLL, которую они предоставляют в выделенном каталоге (не считая моего EXE-файла).
Однако у меня проблемы с динамической загрузкой DLL с использованием LoadLibraryEx . Странно то, что я могу вызвать библиотеку, используя DllImportAttribute .
Это то, что я сделал до сих пор:
Согласно этому ответу SO , я использую следующий код (в конструкторе), чтобы попытаться динамически загрузить 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.
- Когда я открываю собственную DLL в Dependencies (что по сути является Dependency Walker для Win10), я ясно вижу, что отсутствует одна из статически связанных DLL
- Однако, если я скопирую DLL помимо моего EXE-файла и использую атрибут 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
}
РЕДАКТИРОВАТЬ после комментария Ганса Пассанта:
Общая цель - возможность заменить / обновить собственную DLL, когда мое приложение (служба Windows) работает. Я обнаружил изменение файла и затем перезагрузил DLL. Я не совсем уверен, возможно ли это с DllImportAttribute
без перезапуска службы.
И я должен быть более конкретным в данной проблеме: я не смог загрузить нативную DLL-библиотеку, используя LoadLibraryEx
, независимо от того, была ли она размещена рядом с моим EXE-файлом, или в другой случайной папке, или в SysWow64. Почему это работает с DllImportAttribute
? Я почти уверен, что отсутствующая DLL-библиотека суб-зависимостей FastMM не присутствует в моей системе (ни рядом с реальной DLL, ни в любой Windows каталог).