Load the Same Dll Multiple Times

Load the same dll multiple times

It sounds like you want each instance of the DLL to have separate data segments. That's the only reason I can think of for the question.

The only way to achieve this is to make sure that each time you call LoadLibrary, the DLL has a different filename. Copy the DLL to a temporary file each time you need to load it, making sure that the name you use is different from any loaded instance of the DLL.

I echo the comments above that encourage you to re-design the system architecture.

Load a DLL More Than Once?

The MSDN documentation states:

The system maintains a per-process reference count on all loaded
modules. Calling LoadLibrary increments the reference count. Calling
the FreeLibrary or FreeLibraryAndExitThread function decrements the
reference count. The system unloads a module when its reference count
reaches zero or when the process terminates (regardless of the
reference count).

So it would appear that loading the module more than once (without matching calls to FreeLibrary) will return the same handle.

Import same DLL library multiple times, dynamic DllImport annotation

Thanks to @David Heffernan and few others i've came up with solution, that might work for differently built libraries as well, so i'm sharing it.

This relies heavily on three important things

  1. Library must be loaded each time from separate copy of DLL file
  2. ExternalLibrary.dll is implemented so it can live in it's own directory, not poluting other parts of project distribution files
  3. FunctionLoader here presented is only Windows-compatible (uses Kernel32.dll provided routines)

Solution was tested on top of Microsoft.NET.Sdk.Web sdk and net5.0 target, project was deployed using Kestrel not IIS, so please be aware of it, when testing.

1. Include template directory structure and original library in distribution

Having file structure like this

/RootSolution.sln
/Project/
/Project/Project.csproj
/Project/ExternalLibrary/1/ExternalLibrary.dll

Allows to create per-instance (guid identified) directories like this

/Project/ExternalLibrary/1/ExternalLibrary.dll
/Project/ExternalLibrary/516bbd6d-a5ec-42a5-93e0-d1949ca60767/ExternalLibrary.dll
/Project/ExternalLibrary/6bafaf3c-bc2b-4a1f-ae5c-696c37851b22/ExternalLibrary.dll
/Project/ExternalLibrary/0d0589fc-fc37-434d-82af-02e17a26d927/ExternalLibrary.dll

2. Transform original library header file

Starting with original library header file that looks like this:

// ExternalLibrary.cs
public class ExternalLibrary {
public const String ApiDll = "ExternalLibrary.dll";

[DllImport(ApiDll, CallingConvention = CallingConvention.Cdecl)]
public static extern Int32 ExternalRoutine(UInt32 Input, out UInt32 Output);
}

Transforming it into

// ExternalLibrary.cs
using System;
using System.IO;

public class ExternalLibrary {
public string LibraryName { get; }
public Guid InstanceNo { get; }
public string DefaultLibrarySource { get; } = Path.Combine("ExternalLibrary", "1", "ExternalLibrary.dll");
public string LibrarySourceTemplate { get; } = Path.Combine("ExternalLibrary", "{0}", "ExternalLibrary.dll");

public ExternalLibrary(Guid InstanceNo)
{
// use constructor provided Guid to construct full path to copy of library and it's living directory
LibraryName = String.Format(LibrarySourceTemplate, InstanceNo);
LibraryName = Path.GetFullPath(LibraryName);

InstanceNo = InstanceNo;

// create guid-appropriate directory if it does not exist
var dirName = Path.GetDirectoryName(LibraryName);
if (!Directory.Exists(dirName))
{
Directory.CreateDirectory(dirName);
}

// copy over the source library if it's not yet present in guid-appropriate directory
if (!File.Exists(LibraryName))
{
File.Copy(DefaultLibrarySource, LibraryName);
}

// load function from correct DLL file into exposed delegated routine
ExternalRoutine = FunctionLoader.LoadFunction<_ExternalRoutine>(LibraryName, "ExternalRoutine");
}

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate Int32 _ExternalRoutine(UInt32 Input, out UInt32 Output);
public _ExternalRoutine ExternalRoutine;
}

3. Include FunctionLoader class with your project

// FunctionLoader.cs
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Runtime.InteropServices;

/// <summary>
/// Helper function to dynamically load DLL contained functions on Windows only
/// </summary>
internal class FunctionLoader
{
[DllImport("Kernel32.dll", CharSet = CharSet.Ansi)]
private static extern IntPtr LoadLibrary(string path);

[DllImport("Kernel32.dll", CharSet = CharSet.Ansi)]
private static extern IntPtr GetProcAddress(IntPtr hModule, string procName);

/// <summary>
/// Map String (library name) to IntPtr (reference from LoadLibrary)
/// </summary>
private static ConcurrentDictionary<string, IntPtr> LoadedLibraries { get; } = new ConcurrentDictionary<string, IntPtr>();

/// <summary>
/// Load function (by name) from DLL (by name) and return its delegate
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="dllPath"></param>
/// <param name="functionName"></param>
/// <returns></returns>
public static T LoadFunction<T>(string dllPath, string functionName)
{
// normalize
if (!Path.IsPathFullyQualified(dllPath))
{
dllPath = Path.GetFullPath(dllPath);
}
// Get preloaded or load the library on-demand
IntPtr hModule = LoadedLibraries.GetOrAdd(
dllPath,
valueFactory: (string dllPath) =>
{
IntPtr loaded = LoadLibrary(dllPath);
if (loaded == IntPtr.Zero)
{
throw new DllNotFoundException($"Library not found in path {dllPath}");
}
return loaded;
}
);
// Load function
var functionAddress = GetProcAddress(hModule, functionName);
if (functionAddress == IntPtr.Zero)
{
throw new EntryPointNotFoundException($"Function {functionName} not found in {dllPath}");
}
// Return delegate, casting is hack-ish, but simplifies usage
return (T)(object)(Marshal.GetDelegateForFunctionPointer(functionAddress, typeof(T)));
}
}


Please let me know, if this solution worked for you or if you found more elegant way, thank you

Load same dll multiple times

No, identity of non-strongly-signed assembly is its name alone so you have to change the name.

Note that you can use other "Load" methods and even load from byte array, but generally it will bring you more pain than worth. Blog posts by Suzanne Cook is almost required reading when you deal with assembly identity, LoadFrom and related topics.

If reloading assembly is required one solution would be to build proxy that will forward calls to other AppDomain and load you ever-changing assembly into new AppDomains (assuming you can load fully trusted code into Autodesk 3ds Max so you can manage AppDomains yourself).

Link to the same DLL twice - Implicit and explicit at the same time

By "statically loads the DLL with a lib file and _declspec(dllimport/dllexport)" I assume you meant that that you compiled your executable with the .lib as a dependency, and at the runtime the .dll is automatically loaded by the exe (at the beginning). Here's a fragment from FreeLibrary (surprisingly) MSDN page:

The system maintains a per-process reference count for each loaded module. A module that was loaded at process initialization due to load-time dynamic linking has a reference count of one. The reference count for a module is incremented each time the module is loaded by a call to LoadLibrary. The reference count is also incremented by a call to LoadLibraryEx unless the module is being loaded for the first time and is being loaded as a data or image file.

So in other words, the .dll gets loaded at application startup (because you linked against it) and LoadLibrary just increments its ref count (). For more info you could also check DllMain, or this dll guide.

There's absolutely no reason to use both approaches for the same .dll in the same application.

The 2nd approach is the preferred one, if the .dll comes with a .h file (that holds the function definitions exported by the library, needed at compile time) and a .lib file (that instructs the liker to add references from the .dll file into the executable).

The 1st approach on the other hand is the only way if you only have the .dll file and you somehow have the signatures of the functions it exports. In that case you must define in your app pointers to those functions and initialize them using GetProcAddress. There are cases when this approach is preferred, for example when the functionality in the .dll is needed only in a corner case of the program flow, in that case there's no point to link against the .lib file and load the .dll at app startup if let's say in 99% of the cases it won't be required. Also, a major advantage of this approach: if the .dll is somehow deleted then only the functionality related to it won't work (LoadLibrary will fail), while using the other approach, the application won't start.

Now, without details i can't get to the bottom of this specific problem you'r running into. You say that you call a function "normally" (from its definition in the .h file), it fails while if you call it (with the same arguments) using a function pointer it succeeds? What's the stack error message?

Note: From my experience a typical reason for stack corruptions in scenarios like this one is calling convention mismatch between the caller and the callee (stdcall vs cdecl or viceversa). Also mixing Debug and Release could introduce problems.

Can a process load two DLLs with exactly the same name?

From the documentation (emphasis mine):

Desktop applications can control the location from which a DLL is loaded by specifying a full path, using DLL redirection, or by using a manifest. If none of these methods are used, the system searches for the DLL at load time as described in this section.

Before the system searches for a DLL, it checks the following:

  • If a DLL with the same module name is already loaded in memory, the system uses the loaded DLL, no matter which directory it is in. The system does not search for the DLL.

So, the clause you're worried about doesn't apply when a full path is provided.



Related Topics



Leave a reply



Submit