Why does LoadLibrary fail while DllImportAttribute works?

0

I'm creating a .NET application for a client that performs I/O with one of their third-party systems. As they regularly change the password of this system, I should retrieve it dynamically by calling a native DLL that they provide in a dedicated directory (not besides my EXE file).

However, I have trouble loading the DLL dynamically using LoadLibraryEx. The weird thing is that I can call the library using the DllImportAttribute.

This is what I have done so far:

According to this SO answer, I use the following code (in a constructor) to try to load the DLL dynamically:

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);
}
  • When LoadLibraryEx is called, the resulting handle is null, the error code is 126 which usually means that the DLL or one of its dependencies could not be found.
  • When I call LoadLibraryEx with DoNotResolveDllReferences, then I get a working handle but afterwards, I cannot call GetProcAddress (error code 127) - I suspect that I have to fully load the DLL for this.
  • When I open the native DLL in Dependencies (which essentially is Dependency Walker for Win10), I can clearly see that one of the statically linked DLLs is missing Missing FastMM DLL
  • However, if I copy the DLL besides my EXE file and use the DllImportAttribute, I can call into the DLL
[DllImport(DllPath, EntryPoint = GetPasswordEntryPoint, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)]
private static extern long GetPassword(long systemId, string user, byte[] password);

How is this possible? I thought that the mechanism behind DllImportAttribute uses LoadLibary internally, too. Where does my code differ? Am I missing something obvious?

Just some notes:

  • I can't just use DllImportAttribute as I cannot specify searching in a dedicated directory this way (the DLL must lie beside my EXE file or in a common Windows location for this to work).
  • I also tried LoadLibrary instead of LoadLibraryEx but with the same results.

EDIT after Simons comment: NativeMethods is defined as followed:

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
}

EDIT after Hans Passant's comment:

The overall goal is the ability to replace / update the native DLL while my application (a Windows Service) is running. I detect a file change and then reload the DLL. I am not quite sure if this is possible with DllImportAttribute without restarting the service.

And I should be more specific on the actual problem: I couldn't load the native DLL using LoadLibraryEx, no matter if it was placed next to my EXE, or in another random folder, or in SysWow64. Why does it work with DllImportAttribute? I'm pretty sure that the missing FastMM subdependency DLL is not present on my system (neither next to the actual DLL, nor in any Windows directory).

c#
.net
dll
interop
pinvoke
asked on Stack Overflow Sep 10, 2018 by feO2x • edited Sep 10, 2018 by feO2x

2 Answers

0

It's because the DLL search order path. In windows when application try to load a DLL the underlying system automatically search some path for the DLL ,So let's pretend Windows's DLL search path looks something like this:

A) . <-- current working directory of the executable, highest priority, first check

B) \Windows

C) \Windows\system32

D) \Windows\syswow64 <-- lowest priority, last check

You can read more about the underlying mechanism in this Microsoft documentation.

Search for DLL which your main DLL has dependency to it and find where it store on system, add the directory of it to DLL search path of Windows using AddDllDirectory or SetDllDirectory.

  • If the dll already loaded into memory by any of running process Windows automatically use it instead of searching, so you can load FastMM DLL into memory using LoadLibrary manually and then try to load the main DLL and it should solve the problem too.
answered on Stack Overflow Sep 10, 2018 by Mojtaba Tajik • edited Jun 20, 2020 by Community
0

@HansPassant and @David Heffernan are right: I actually tried to load two different versions of the DLL (one of them had the FastMM subdependency, one did not). Thanks for your help and sorry for the inconvenience.

answered on Stack Overflow Sep 11, 2018 by feO2x

User contributions licensed under CC BY-SA 3.0