.NET Reference dll from other location - c#

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.

Related

Static constructor is no longer run before Assembly Resolution in .NetCore (.Net6)

I am migrating huge, 32-bit, desktop (winforms) codebase from .NetFramework462 to .Net6. The code itself works well after migration when run from VisualStudio.
We have around 150 csprojs and some of the dlls are shared across multiple executables, so during installation we put shared assemblies in C:\ProgramFiles (x86)\MyProgram\Assemblies rather than copying each of them to each program's subfolder.
We have custom MyAssemblyLoader which correctly loads assemblies from our Assemblies folder if they're missing in application root folder. We register our assembly loader to handle AssemblyResolve event in static constructor of our program like this:
static class Program
{
private static MyAssemblyResolver _assemblyResolver = new MyAssemblyResolver();
static Program()
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => _assemblyResolver.AssemblyResolve(args.Name);
}
// Main() and other code is here
}
The problem is that when I build the program under .NetFramework462 and remove one of dependency dlls from bin\Debug folder it correctly loads it from Assemblies folder. I can set a breakpoint in static constructor and see that it is being called.
In same scenario in .Net6 I get System.IO.FileNotFoundException: 'Could not load file or assembly' even before static constructor is run. The exception has no stack trace and it doesn't enter static constructor at all, like the exception is thrown even before it is called.
I tried to load some other assemblies which are not necessary for project to run from Assemblies folder with MyAssembliesLoader in .Net6 in Immediate window and it works fine.
Here are details about how it is referenced in csprojs (I believe it shouldn't matter for runtime resolver):
In .Net6 I have SDK csprojs with reference to missing dll via one of shared projects (dll is not directly referenced in csproj)
<ProjectReference Include="OneOfSharedProjects.csproj" />
and in OneOfSharedProjects.csproj
<ProjectReference Include="MissingDLL.csproj" />
While in .NetFramework462 the project is directly referenced
<ProjectReference Include="MissingDLL.csproj">
<Project>{12345678-FFBC-419B-AEAB-3A9B1A4D4847}</Project>
<Name>MissingDLL</Name>
</ProjectReference>
Assemblies folder path is resolved in runtime so I'd like to avoid hardcoding the path in config files.
How can I load referenced libraries from custom folder in .Net6?
PS:
Our libraries are not being registered in GAC so that's not the case
Moving AssemblyResolve assignment from static constructor to Main() doesn't help. The code behaves as before (exception before Main() is called)
Ok, I found the issue. It looks like the problem was in reference to class from missing library directly in Main() method.
Let's assume that class C is in missing library.
Having Main like this:
class Program
{
static Program()
{
AppDomain.CurrentDomain.AssemblyResolve += (s, e) => _assemblyResolver.AssemblyResolve(e.Name);
}
static void Main()
{
C c = new C();
ApplicationConfiguration.Initialize();
Application.Run(new Form1());
}
}
won't work because .Net6 tries to load library containing C definition before assembly resolver is added as event handler. But if class from missing library is not used directly, the problem is gone. Example:
class Program
{
static Program()
{
AppDomain.CurrentDomain.AssemblyResolve += (s, e) => _assemblyResolver.AssemblyResolve(e.Name);
}
static void Main()
{
NewMethod();
ApplicationConfiguration.Initialize();
Application.Run(new Form1());
}
private static void NewMethod()
{
C c = new C();
}
}
Now even if dll is moved somewhere else where AssemblyResolver can find it, the code will work just fine

Hooking into an "OnLoad" for class library

Does anyone know if there's a way to hook into an "OnLoad" event to run some operations when an assembly loads?
Specifically, I am creating a plug-in for an application. The plug-in's DLL gets loaded and objects start being used, but the problem is I need to load another assembly dynamically before anything happens. This assembly can't be copied to the application's directory and must remain invisible to it.
You need to hook on to AssemblyLoad event.
Refer-
http://msdn.microsoft.com/en-us/library/system.appdomain.assemblyload.aspx
It is really sad that writing a Main() function in an Assembly DLL is never called by the .NET framework.
It seems that Microsoft forgot that.
But you can easily implement it on your own:
In the DLL assembly you add this code:
using System.Windows.Forms;
public class Program
{
public static void Main()
{
MessageBox.Show("Initializing");
}
}
Then in the Exe Assembly that loads this DLL you add this function:
using System.Reflection;
void InitializeAssembly(Assembly i_Assembly)
{
Type t_Class = i_Assembly.GetType("Program");
if (t_Class == null)
return; // class Program not implemented
MethodInfo i_Main = t_Class.GetMethod("Main");
if (i_Main == null)
return; // function Main() not implemented
try
{
i_Main.Invoke(null, null);
}
catch (Exception Ex)
{
throw new Exception("Program.Main() threw exception in\n"
+ i_Assembly.Location, Ex);
}
}
Obviously you should call this function at the very beginning before doing anything else with that Assembly.
C# does not provide a way to do that but the underlying IL code does via module initializers. You can use tools like Fody/ModuleInit to turn a specially named static C# class to run as a module initializer which will be run when your dll is loaded.

GetExportedTypes() FileNotFoundException: Assembly couldn't be found

My task: Find all Forms (WindowsForm or WPF, doesn't matter) in a dll or exe file and return that list. In theory that works (meaning: if I've an assembly with a WPF or WindowsForm my code manages to get all Forms, TextBoxes, Labels etc. ). When it comes to "real" assemblies it fails. I get FileNotFound exceptions when calling GetExportedTypes() for every "custom" assembly (.NET assemblies are found, no problems there). I already use GetReferencedAssemblies() to load the referenced assemblies (Reflection.Assembly.LoadFrom) and yes it does work (all assemblies are found and loaded into the AppDomain) but it doesn't help.
I checked the version numbers (they match), I copied my executable and the assembly into one directory with all referenced assemblies, doesn't work.
Here is my code, maybe someone figures out what I'm (obviously) doing wrong:
foreach (AssemblyName reference in selectedAssembly.GetReferencedAssemblies())
{
if (System.IO.File.Exists(
System.IO.Path.GetDirectoryName(selectedAssembly.Location) +
#"\" + reference.Name + ".dll"))
{
System.Reflection.Assembly.LoadFrom(
System.IO.Path.GetDirectoryName(selectedAssembly.Location) +
#"\" + reference.Name + ".dll");
}
else if (System.IO.File.Exists(#"C:\dll\" + reference.Name + ".dll"))
{
System.Reflection.Assembly.LoadFrom(#"C:\dll\" + reference.Name + ".dll");
}
else
{
System.Reflection.Assembly.ReflectionOnlyLoad(reference.FullName);
}
selectedAssembly.GetExportedTypes();
}
at first check if the referenced dll exists in the directory where the assembly is, if not check if it exists in C:\dll and if it's not there try and use the GAC. it does work and I've no errors from there but as soon as I come to GetExportedTypes it fails with a FileNotFound exception on the first custom library.
*edit 1 what do I mean by "real assemblies": I mean assemblies which are more complex and have references to non-standard-.NET libraries/assemblies
Thanks for the hint to fuslogvw.exe Hans Passant but what do you mean by "with code like this"?
okay I used fuslogvw.exe and I get two exceptions for every single dll that is referenced by the "selectedAssembly".
The first one says something like
"The binding starts in LoadFrom-context
The image owned by the system isn't searched in LoadFrom-Context"
the other logentry says that the dll referenced by the selectedAssembly couldn't be found and it tried to download it from application's base path and all directories below...but not from it's actual location...so, key question: how do I change the Load-context to LoadFrom? And why is .NET so stubborn on this? I mean the assemblies are loaded in the AppDomain, it shouldn't care about the actual location of the assembly.
okay problem solved. Here is the solution:
http://ayende.com/blog/1376/solving-the-assembly-load-context-problem
I implemented that into my existing class (removed the static-keyword and put the body of the Init method into my method), compiled it and it worked.
Thanks for your help guys.
okay problem solved. Here is the solution: http://ayende.com/blog/1376/solving-the-assembly-load-context-problem
I implemented that into my existing class (removed the static-keyword and put the body of the Init method into my method), compiled it and it worked.
Thanks for your help guys.
just in case the website will someday be unavailable, here is the sourcecode from ayende
static Dictionary<string, Assembly>assemblies;
public static void Init()
{
assemblies = new Dictionary<string, Assembly>();
AppDomain.CurrentDomain.AssemblyLoad += new AssemblyLoadEventHandler(CurrentDomain_AssemblyLoad);
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}
static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
Assembly assembly = null;
assemblies.TryGetValue(args.Name, out assembly);
return assembly;
}
static void CurrentDomain_AssemblyLoad(object sender, AssemblyLoadEventArgs args)
{
Assembly assembly = args.LoadedAssembly;
assemblies[assembly.FullName] = assembly;
}
I would recommend using Reflector to see which references you may not have loaded. For instance, you are only loading the referenced assemblies that the current assembly is looking at. Do you step down through each child to find their referenced assemblies as well? The FileNotFound error is probably pointing you in the direction of a type that is declared in another assembly that isn't loaded.

Providing path to externals assembly native dll dependecy

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.

CurrentDomain.AssemblyResolve not being able resolve 'some' assemblies

After finding the answer my last question on assembly resolving ( AppDomain.CurrentDomain.AssemblyResolve asking for a <AppName>.resources assembly? ) now i'm able to embed references assemblies on my program except that this does not work some references somehow.
First of all i setup my assembly resolver at the very first entrance of my Program.cs
// attach our embedded assembly loader.
AppDomain.CurrentDomain.AssemblyResolve += AssemblyManager.Instance.Resolver;
Here's my actual resolver;
public Assembly Resolver(object sender, ResolveEventArgs args)
{
AssemblyName askedAssembly = new AssemblyName(args.Name);
lock (this)
{
Assembly assembly;
string resourceName = string.Format("Assets.Assemblies.{0}.dll", askedAssembly.Name);
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
{
if (stream == null)
{
LogManager.Instance.Write(LogMessageTypes.Fatal, string.Format("Can not resolve asked assembly: {0}", askedAssembly.Name));
MessageBox.Show(i18n.CanNotLoadRequiredAssembliesMessage, i18n.CanNotLoadRequiredAssembliesTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
Environment.Exit(-1);
}
byte[] assemblyData = new byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
assembly = Assembly.Load(assemblyData);
}
LogManager.Instance.Write(LogMessageTypes.Trace, "Loaded embedded assembly: " + askedAssembly.Name);
return assembly;
}
}
Now my program references these libraries assemblies.
Esent.Collections.dll
Esent.Interop.dll
HtmlAgilityPack.dll
Ionic.Zip.Reduced.dll
System.Windows.Forms.Calendar.dll
AxInterop.ShockwaveFlashObjects
Interop.ShockwaveFlashObjects
irrKlang.NET4.dll
ikpMP3.dll
Nini.dll (SOLVED)
With my resolver above; i can embed Esent.Collections, Esent.Interop, HtmlAgilityPack, Ionic.Zip.Reduced,System.Windows.Forms.Calendar,AxInterop.ShockwaveFlashObjects,Interop.ShockwaveFlashObjects and resolve those on run-time.
The problem arrives with irrKlang.NET.4, Nini and ShockwaveFlash assemblies in which if i try to embed these assemblies and try to resolve them at runtime i'm having problems.
For irrKlang i can understand the problem as irrKlang.NET4 assembly references unmanaged ikpMP3.dll which i can't find on it's own.
For the Nini.dll, actually i can embed this assembly and using VS debug/release configurations runned it just works okay but when i startup the program on my own from the explorer, the program just refuses to startup (with no errors or any bit of info).
Any help appreciated.
Update
Now, thanks Marc Gravell's answer i can load the Nini.dll.
For the irrKlang part, as irrKlang.NET4.dll is a managed assembly which requires ikpMp3.dll -- an unmanaged one --, if i try to resolve irrKlang.NET4.dll on run-time it can access its required dependency ikpMp3. Is there work-around for this?
A common problem here is to use the types before you have chance to register the assembly-resolve event. In particular, note that the system must be able to JIT a method before it can start running a method - including Main().
So if your entry-point (Main()) does the event hooking and also talks to the types in question, it will attempt to resolve the types (for JIT) before or has chance to subscribe.
Do the minimum in main; move the "real" code to another method, that is only invoked once the event is definitely subscribed.
For example:
static void Main() {
SubscribeAssemblyResolver();
ExecuteApplication();
}
In extreme cases you may even need to use MethodImplAttribute to disable inlining of the methods above.

Categories