Providing path to externals assembly native dll dependecy - c#

I have C# application which loads set of managed assemblies. One of this assemblies loads two native dlls (each of them in different location) if they are avaiable. Iam trying to find way to provide search path to those native dlls.
Are there other options? I really dont want to provide those dlls with my software - copying them to programs directory of course solves the problem.
I've tried using SetDllDirectory system function but it is possible to provide only one path using it. Each call to this function resets path.
Setting PATH enviroment variable does not solve the problem too :/

I know this was an old post, but just so there's an answer: Using the LoadLibary function you can force load a native DLL:
public static class Loader
{
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string fileName);
}
You must call this before any other DLL does - I usually call it in a static constructor of my main program. I had to do this for DllImport(), and static constructors were always executed before the native DLLs were loaded - they only load actually the first time an imported function is called.
Example:
class Program
{
static Program()
{
Loader.LoadLibrary("path\to\native1.dll");
Loader.LoadLibrary("otherpath\to\native2.dll");
}
}
Once the library is loaded it should satisfy the DllImports() of the other managed assemblies you are loading. If not, they might be loaded using some other method, and you may have no other option but to copy them locally.
Note: This is a Windows solution only. To make this more cross-platform you'd have to detect operating systems yourself and use the proper import; for example:
[DllImport("libdl")]
public static extern IntPtr DLOpen(string fileName, int flags);
[DllImport("libdl.so.2")]
public static extern IntPtr DLOpen2(string fileName, int flags);
// (could be "libdl.so.2" also: https://github.com/mellinoe/nativelibraryloader/issues/2#issuecomment-414476716)
// ... etc ...

This could help:
private void Form1_Load(object sender, EventArgs e)
{
//The AssemblyResolve event is called when the common language runtime tries to bind to the assembly and fails.
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyResolve += new ResolveEventHandler(currentDomain_AssemblyResolve);
}
//This handler is called only when the common language runtime tries to bind to the assembly and fails.
Assembly currentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
string dllPath = Path.Combine(YourPath, new AssemblyName(args.Name).Name) + ".dll";
return (File.Exists(dllPath))
? Assembly.Load(dllPath)
: null;
}

Register yours dlls to GAC. More here.

Related

Load mixed assembly at runtime

I have a project uses Sqlite (System.Data.SQLite.DLL) it is mixed assembly. I need to load it at runtime. As I know it impossible using AssemblyResolve event. So the target is to unpack assembly to Temp directory and show to application where to find it.
The code is:
public static void SaveSqlite()
{
byte[] sqliteAsm = EmbedAssembly.System_Data_SQLite;
string tempFile = Path.GetTempPath();
File.WriteAllBytes(tempFile + "System.Data.SQLite.DLL", sqliteAsm);
}
// and the setup int start method
SaveSqlite();
AppDomain.CurrentDomain.SetupInformation.PrivateBinPath = Path.GetTempPath();
So it not works. Application cant find sqlite assembly but it successfuly saved to Temp dir. How to solve this problem? Thanks.
The solution has founded.
Not using mixed assembly but managed for sqlite interop Using
SQLite.
SQLite.Interop.dll - native dll (unpack to any directory)
Call SetDllDirectory WinAPI to set dll search directory
code for extract sqlite native dll from resource looks like that:
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetDllDirectory(string lpPathName);
//...
string text4 = Path.Combine(text, "SQLite.Interop.dll");
if (!File.Exists(text4))
{
byte[] array3 = LoadCompressedDllFromResource("nativedll.SQLite.Interop.z");
array3 = DecompressBytes(array3);
SaveToFile(array3, text4);
}
//...
pack all over managed assemblies using libz application

Using a 32bit or 64bit dll in C# DllImport

Here is the situation, I'm using a C based dll in my dot.net application. There are 2 dlls, one is 32bit called MyDll32.dll and the other is a 64bit version called MyDll64.dll.
There is a static variable holding the DLL file name: string DLL_FILE_NAME.
and it is used in the following way:
[DllImport(DLL_FILE_NAME, CallingConvention=CallingConvention.Cdecl, EntryPoint=Func1")]
private static extern int is_Func1(int var1, int var2);
Simple so far.
As you can imagine, the software is compiled with "Any CPU" turned on.
I also have the following code to determine if the system should use the 64bit file or the 32bit file.
#if WIN64
public const string DLL_FILE_NAME = "MyDll64.dll";
#else
public const string DLL_FILE_NAME = "MyDll32.dll";
#endif
By now you should see the problem.. DLL_FILE_NAME is defined in compilation time and not in execution time so the right dll isn't loaded according to the execution context.
What would be the correct way to deal with this issue? I do not want two execution files (one for 32bit and the other for 64bit)? How can I set DLL_FILE_NAME before it is used in the DllImport statement?
I've found the simplest way to do this is to import the two methods with different names, and calling the right one. The DLL won't be loaded until the call is made so it's fine:
[DllImport("MyDll32.dll", EntryPoint = "Func1", CallingConvention = CallingConvention.Cdecl)]
private static extern int Func1_32(int var1, int var2);
[DllImport("MyDll64.dll", EntryPoint = "Func1", CallingConvention = CallingConvention.Cdecl)]
private static extern int Func1_64(int var1, int var2);
public static int Func1(int var1, int var2) {
return IntPtr.Size == 8 /* 64bit */ ? Func1_64(var1, var2) : Func1_32(var1, var2);
}
Of course, if you have many imports, this can be become quite cumbersome to maintain manually.
Here is another alternative that requires that the two DLLs have the same name and are placed in different folders. For instance:
win32/MyDll.dll
win64/MyDll.dll
The trick is to manually load the DLL with LoadLibrary before the CLR does it. It will then see that a MyDll.dll is already loaded and use it.
This can be done easily in the static constructor of the parent class.
static class MyDll
{
static MyDll()
{
var myPath = new Uri(typeof(MyDll).Assembly.CodeBase).LocalPath;
var myFolder = Path.GetDirectoryName(myPath);
var is64 = IntPtr.Size == 8;
var subfolder = is64 ? "\\win64\\" : "\\win32\\";
LoadLibrary(myFolder + subfolder + "MyDll.dll");
}
[DllImport("kernel32.dll")]
private static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("MyDll.dll")]
public static extern int MyFunction(int var1, int var2);
}
EDIT 2017/02/01: Use Assembly.CodeBase so that it works even if Shadow Copying is enabled.
In this case, i should do like this (make 2 folders, x64 and x86 + put the corresponding dll, WITH THE SAME NAME, in both folders):
using System;
using System.Runtime.InteropServices;
using System.Reflection;
using System.IO;
class Program {
static void Main(string[] args) {
var path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
path = Path.Combine(path, IntPtr.Size == 8 ? "x64" : "x86");
bool ok = SetDllDirectory(path);
if (!ok) throw new System.ComponentModel.Win32Exception();
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool SetDllDirectory(string path);
}
There is a static variable holding the DLL file name
It is not a static variable. It's a constant, at compile time. You can't change a compile time constant at runtime.
What would be the correct way to deal with this issue?
Honestly I would recommend just targeting x86 and forgetting the 64-bit version all together, and letting your application run on WOW64, unless your application has a compelling need to run as x64.
If there is a need for x64, you could:
Change the DLLs to have the same name, such as MyDll.dll, and at install / deploy time, put the right one in place. (If the OS is x64, deploy the 64-bit version of the DLL, otherwise the x86 version).
Have two separate builds altogether, one for x86 and one for x64.
What you describe is known as "side-by-side assembly" (two versions of the same assembly, one 32 and the other 64 bit)... I think you will find these helpful:
Using Side-by-Side assemblies to load the x64 or x32 version of a DLL
http://blogs.msdn.com/b/gauravseth/archive/2006/03/07/545104.aspx
http://www.thescarms.com/dotnet/Assembly.aspx
Here you can find a walkthrough for exactly your scenario (.NET DLL wrapping C++/CLI DLL referencing a native DLL).
RECOMMENDATION:
Just build it as x86 and be done with it... or have 2 builds (one x86 and one x64)... as the above techniques are rather complicated...
an alternative approach may be
public static class Sample
{
public Sample()
{
string StartupDirEndingWithSlash = System.IO.Path.GetDirectoryName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName) + "\\";
string ResolvedDomainTimeFileName = StartupDirEndingWithSlash + "ABCLib_Resolved.dll";
if (!File.Exists(ResolvedDomainTimeFileName))
{
if (Environment.Is64BitProcess)
{
if (File.Exists(StartupDirEndingWithSlash + "ABCLib_64.dll"))
File.Copy(StartupDirEndingWithSlash + "ABCLib_64.dll", ResolvedDomainTimeFileName);
}
else
{
if (File.Exists(StartupDirEndingWithSlash + "ABCLib_32.dll"))
File.Copy(StartupDirEndingWithSlash + "ABCLib_32.dll", ResolvedDomainTimeFileName);
}
}
}
[DllImport("ABCLib__Resolved.dll")]
private static extern bool SomeFunctionName(ref int FT);
}
Based on Julien Lebosquain's great answer, this is what I ended up doing in a similar case:
private static class Api32
{
private const string DllPath = "MyDll32.dll";
[DllImport(DllPath, CallingConvention = CallingConvention.Cdecl)]
private static extern int Func1(int var1, int var2);
[DllImport(DllPath, CallingConvention = CallingConvention.Cdecl)]
private static extern int Func2();
...
}
private static class Api64
{
private const string DllPath = "MyDll64.dll";
[DllImport(DllPath, CallingConvention = CallingConvention.Cdecl)]
private static extern int Func1(int var1, int var2);
[DllImport(DllPath, CallingConvention = CallingConvention.Cdecl)]
private static extern int Func2();
...
}
public static int Func1(int var1, int var2) {
return Environment.Is64BitProcess
? Api64.Func1(var1, var2)
: Api32.Func1(var1, var2);
}
I think this option scales better if you have multiple entry points in the same DLL for the following reasons:
The Api32 and Api64 classes are completely the same except for the single constant defining the path to the DLL file. This means that I can just copy & paste the declarations from one class to the other if anything changes.
No need to specify the EntryPoint, reducing the possibility of typos.
I have used one of the approaches meantioned by vcsjones:
"Change the DLLs to have the same name, such as MyDll.dll, and at install / deploy time, put the right one in place."
This approach requires maintaining two build platforms though see this link for more details: https://stackoverflow.com/a/6446638/38368
The trick I use for V8.Net is this:
Create a new C# "proxy interface" project with all the defines to switch between the different architectures. In my case the project was named V8.Net-ProxyInterface; example:
public unsafe static class V8NetProxy
{
#if x86
[DllImport("V8_Net_Proxy_x86")]
#elif x64
[DllImport("V8_Net_Proxy_x64")]
#else
[DllImport("V8_Net_Proxy")] // (dummy - NOT USED!)
#endif
public static extern NativeV8EngineProxy* CreateV8EngineProxy(bool enableDebugging, void* debugMessageDispatcher, int debugPort);
THIS is the project you will reference. DO NOT reference the next two:
Create two more projects to generate x64 and x86 versions of the library. This is VERY EASY: Just copy-n-paste to duplicate the .csproj file in the same folder and renamed them. In my case the project file was renamed to V8.Net-ProxyInterface-x64 and V8.Net-ProxyInterface-x86, then I added the projects to my solution. Open the project settings for each of them in Visual Studio and make sure the Assembly Name has either x64 or x86 in the name. At this point you have 3 projects: the first "placeholder" project, and the 2 architecture-specific ones. For the 2 new projects:
a) Open the x64 interface project settings, go to the Build tab, select All Platforms for Platform at the top, then enter x64 in Conditional compilation symbols.
b) Open the x86 interface project settings, go to the Build tab, select All Platforms for Platform at the top, then enter x86 in Conditional compilation symbols.
Open Build->Configuration Manager... and make sure that x64 is selected as the platform for x64 projects, and x86 is selected for the x86 projects, for BOTH Debug AND Release configurations.
Make sure the 2 new interface projects (for x64 and x86) output to the same location of your host project (see project setting Build->Output path).
The final magic: In a static constructor for my engine I quickly attach to the assembly resolver:
static V8Engine()
{
AppDomain.CurrentDomain.AssemblyResolve += Resolver;
}
In the Resolver method, I just load the file based on the current platform indicated by the current process (note: this code is a stripped-down version and not tested):
var currentExecPath = Assembly.GetExecutingAssembly().Location;
var platform = Environment.Is64BitProcess ? "x64" : "x86";
var filename = "V8.Net.Proxy.Interface." + platform + ".dll"
return Assembly.LoadFrom(Path.Combine(currentExecPath , fileName));
Finally, go to your host project in the solution explorer, expand References, select the first dummy project you created in step 1, right-click it to open the properties, and set Copy Local to false. This allows you to develop with ONE name for each P/Invoke function, while using the resolver to figure out which one to actually load.
Note that the assembly loader only runs when needed. It is only triggered (in my case) automatically by the CLR system upon the first access to the engine class. How that translates to you depends on how your host project is designed.
I think this could help to load the DLL dynamically:
#if X64
[DllImport("MyDll64.dll", CallingConvention=CallingConvention.Cdecl, EntryPoint=Func1")]
#else
[DllImport("MyDll32.dll", CallingConvention=CallingConvention.Cdecl, EntryPoint=Func1")]
#endif
private static extern int is_Func1(int var1, int var2);

.NET Reference dll from other location

I'm making a program depending on some DLLs included in a third party program. I'm not allowed to distribute these DLLs myself. The third party program must be installed for my program to work.
How can i make a reference to these DLLs? I know the exact location of them through a registry key set by the program.
I have tried to add the files in Project->References and set CopyLocal to false but when i start i then get a FileNotFoundException "Could not load file or assembly".
I have tried to add an event to AppDomain.CurrentDomain.AssemblyResolve and load the files there but the problem is that i get the exception before my program even starts. Even if i put a breakpoint on the first line the exception will be thrown before the breakpoint is hit.
From C# 3.0 in a Nutshell, 3rd edition, by Joseph and Ben Albahari, p. 557-558:
Deploying Assemblies Outside the Base Folder
Sometimes you might choose to deploy assemblies to locations other than the application base directory [...] To make this work, you must assist the CLR in finding the assemblies outside the base folder. The easiest solution is to handle the AssemblyResolve event.
(We can ignore the fact that in your case, someone other than you is deploying the assemblies.)
Which you tried. But a very important clue follows somewhat later. Read the two code comments:
public static void Loader
{
static void Main(string[] args)
{
AppDomain.CurrentDomain.AssemblyResolve += FindAssem;
// We must switch to another class before attempting to use
// any of the types in C:\ExtraAssemblies:
Program.Go();
}
static Assembly FindAssem(object sender, ResolveEventArgs args)
{
string simpleName = new AssemblyName(args.Name).Name;
string path = #"C:\ExtraAssemblies\" + simpleName + ".dll";
if (!File.Exists(path)) return null;
return Assembly.LoadFrom(path);
}
}
public class Program
{
public static void Go()
{
// Now we can reference types defined in C:\ExtraAssemblies
}
}
As you see, the class where you resolve the external assemblies must not refer to any type from any of the external DLLs anywhere. If it did, code execution would stop way before your AssemblyResolve ever gets a chance to run.
Your app bombs because the JIT compiler is the first one that needs to load the assembly. You'll need to carefully avoid using types from the assembly in your Main() method. That's not hard to do, just write another Main method and give it an attribute that tells the jitter that it should never inline that method. Without the attribute it may still bomb in the Release build when the optimizer inlines the method. Like this:
using System;
using System.Runtime.CompilerServices;
class Program {
static void Main(string[] args) {
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
DelayedMain(args);
}
[MethodImpl(MethodImplOptions.NoInlining)]
static void DelayedMain(string[] args) {
// etc..
}
static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) {
// etc...
}
}
Before you commit doing it this way, do contact the vendor and ask for recommendations. There has to be an easier way to exercise your license rights. The GAC would be a common choice, maybe you just need to run gacutil on your dev machine.

Adding a directory temporarily to Windows 7's DLL search paths

I want to temporarily add a directory to the DLL search paths - is there a correct way to do this under Windows 7?
Scenario
I've got a C# application, let's call it WonderApp.
WonderApp needs to call a C++ DLL, located in C:\MyPath. So as part of WonderApp's Program.Main(), I added the following command:
Environment.SetEnvironmentVariable("PATH",
"C:\\MyPath;" + Environment.GetEnvironmentVariable("PATH"));
According to this article, adding a directory to the PATH should also add it to the directories search for DLLs.
The solution works fine in Windows XP: if I add the directory to the PATH, the DLL loads and the program works just fine. If I don't add the directory, the DLL doesn't load, failing with a "not found" error.
However, this doesn't work for Windows 7.
So I figured, let's try using SetDllDirectory(). Like this:
[System.Runtime.InteropServices.DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetDllDirectory(string lpPathName);
And, later on:
bool success = SetDllDirectory(Util.Paths.GetApplicationDataDir());
The value of success is true, but the DLL still fails to load.
Finally, if I set the PATH to include C:\MyPath manually, before running the application - it all works! The DLL loads, and runs just fine.
So, to re-iterate:
Is there a correct way to temporarily add a directory to the DLL search paths under Windows 7?
UPDATE: Using Process Explorer, I checked the application's run-time Environment, and "C:\MyPath" was indeed in the PATH! Furthermore, I saw that Helper.dll was in the list of open handles (as a DLL, not just a file) - and it still claimed not to find it.
You can add custom DLL loading logic to a C# app using the 'AssemblyResolve' event.
This page has a good summary, with code samples: http://support.microsoft.com/kb/837908
Just as you did, I have found that changing the PATH environment variable of a running C# app does not affect the DLL search behaviour. Perhaps the AppDomain caches the PATH value at startup? You can use the AssemblyResolve event to work around this.
See also How to add folder to assembly search path at runtime in .NET?
I am thinking it has to do with permission problems.
Try turning off UAC and running your code again. Check to see if updating the path worked.
If it did, at least you know where to start...
My solution is simple, but I feel ridiculous resorting to it.
I've written another assembly, "Shell", that modifies the Environment, runs WonderApp, and exits.
By modifying the PATH before running the main application (WonderApp), the main application's DLL search-path includes the directories added to the modified PATH.
It looks like this:
namespace shell
{
static class program
{
[dllimport("kernel32.dll", charset = charset.auto, setlasterror = true)]
public static extern bool setenvironmentvariable(string lpname, string lpvalue);
private static string joinargstosinglestring(string[] args)
{
string s = string.empty;
for (int i = 0; i < args.length; ++i)
{
if (!string.isnullorempty(s))
{
s += " ";
}
s += "\"" + args[i] + "\"";
}
return s;
}
[stathread]
static void main(string[] args)
{
string pathbefore = environment.getenvironmentvariable("path");
string wewant = util.paths.getapplicationdatadir() + ";" + pathbefore;
setenvironmentvariable("path", wewant);
Process process = Process.Start(".\\WonderApp.exe", joinArgsToSingleString(args));
}
}
}
I wish I could find a better solution!

Dynamically loading a dll in C#

I have a window to use for editing. The editor should load a dll (which I have full control of) in response to the user's selection to know how to display the information visually.
(They're dll's, as a user will not necessarily want or need every single display model, and also allow new ones to be added without messing around with the main project)
They will all simply be stored in a subdirectory (for now anyway)
I'm pretty sure I can enumerate the available dlls but I need to do 2 more things that I'm not sure on
1) Some way to get metadata from\on the dll, so I can build the lists of possible display selections...
2) Load the selected dll, and unload it as necessary
Any suggestions would be greatly appreciated.
If you are using raw dll's and not .NET assemblies then here are some handy P/Invokes for you:
[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
private static extern IntPtr LoadLibrary(string lpFileName);
[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
private static extern void SetDllDirectory(string lpPathName);
[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
privatestatic extern int GetModuleFileName(IntPtr module, [Out] StringBuilder fileName, int size);
[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
private static bool FreeLibrary(IntPtr module);
[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
private IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
Note that SetDllDirectory may need some protection as it is not available on all versions of windows (Windows 2000, in particular doesn't have it).
And in use:
SetDllDirectory(candidateFolder);
IntPtr dllHandle = LoadLibrary(dllName);
if (dllHandle != IntPtr.Zero)
{
_dllHandle = dllHandle;
_location = candidateFolder;
_fullPath = Path.Combine(candidateFolder, dllName);
IntPtr p = GetProcAddress(_dllHandle, procName);
if (p == IntPtr.Zero)
throw new ArgumentException("procName");
SomeDelegateType d = (SomeDelegateType)Marshal.GetDelegateForFunctionPointer(p, typeof(SomeDelegateType));
d(/* args */);
}
otherwise, you will be using Assembly methods. Looking at assembly level attributes or object level attributes is a good way to get extra information, although if what you want is a plug-in system, you should use a plug-in system, like the Managed Add-In Framework at CodePlex. See also this SO question and answer.
Take a look at the Castle Windsor framework. It is designed to handle all of your requirements including DLL unloading. It's also free and open source.
I don't know if changing how your program works is an option, but, you could use dependency injection for this, as long as they adhere to a certain interface.
The user selects, you dynamically set class to be loaded, and then just get an instance of the class.
I am not dealing with the unloading, I am just thinking about how you could possibly get classes, and since plinth already gave links to the functions for actually handling the dll, I think I will just end here.
For a native module, the simplest way to get "metadata" would be to define some C-exported (non-name-mangled) functions that return the information you want. At their simplest, these would return pointers to static data within the modules, e.g.:
extern "C" const char* GetModuleDescription();
...
const char* GetModuleDescription() { return "Dummy Module"; }
You would then load each ".dll" file in the directory using LoadLibrary, load and call your known exports from it using GetProcAddress. If you can't load a file or find the exports, then it's not a valid plugin module, so skip it.
Once you're done with a module, you can call FreeLibrary. Windows will then unload the module from your address space.
OK, I;ve figured out I need to use a second AppDomain, load the dll into that, and then I can unload the AppDomain as required.
string SignalSystemDLLPath = AppDomain.CurrentDomain.BaseDirectory + MyApp.Properties.Resources.SystemModuleFolder;
AppDomainSetup info = new AppDomainSetup();
info.ApplicationBase = DLLPath;
DLLDomain = AppDomain.CreateDomain("EditorDomain", null, info);
DLLPath is set to the subdir that holds the dll's.
I then foreach on all the dll's to get the AssemblyName, then later
I use
DLLDomain.Load(SelectedAssemblyName)
to load the DLL. I keep getting FileNotFound exceptions though.
After much googling I've decided its to much work at the moment, and I can refactor it later If I really need to do it...
Thank you for your replies though!
Found out how to do this very easy using MEF, simply use a DirectoryCatalog pointed at your plugin dir, and as long as you have matching [Export]s and [Import]s it works great.

Categories