I have a project Processor and it depend on other 2 projects.
When i compile project I get dll Processor.dll and other project's depend dll in Bin folder. Processor.dll BusinessAction.dll and Repository.dll.
I tried to call method from Processor.dll by initiating type ProcessRequest class.
Like this
Assembly processorAssembly = Assembly.LoadFile(path + "Processor.DLL"));
Type myType= processorAssembly.GetType("Namespace.ProcessRequest");
myType.InvokeMember("Process", BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public, null, submitOfferType, new object[] { 12345 });
Process method has some logic to process and save it to database.
when i invoke procee method using InvokeMember()
i get exception Could not load file or assembly 'Namespace.BusinessAction, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
I do i invoke method?
Appdomain has an event which is fired if it can't find a reference:
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
In the event handler you can search for the assembly manually:
Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
string s = #"C:\lib\" + args.Name.Remove(args.Name.IndexOf(',')) + ".dll";
return Assembly.LoadFile(s);
}
try
Assembly processorAssembly = Assembly.LoadFrom(path + "Processor.DLL"));
this will load all supporting DLLs
#hcb has the correct answer and in 2022 it can written simply as:
AppDomain.CurrentDomain.AssemblyResolve += (sdr, ar) => {
string dllPath = $"C:\lib\{ar.Name.Remove(ar.Name.IndexOf(','))}.dll";
return Assembly.LoadFile(dllPath);
};
or
AppDomain.CurrentDomain.AssemblyResolve += (sdr, ar) => {
return Assembly.LoadFile($"C:\\lib\\{ar.Name.Remove(ar.Name.IndexOf(','))}.dll");
};
where c:\lib\ is the path where you have the dependent dll
Related
I need to create a new AppDomain for my solution so that I can invoke the method from it's own AppDomain. All of the dependent .dll files for my solution have been embedded into my.exe using Fody/Costura. I have created a Loader : MarshalByRefObject class to invoke my method but when I execute the loader at runtime I get an exception of:
Could not load file or assembly 'my.Assembly, Version=19.1.7242.23931, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
The code is looking for the .dll files in my AppDomain.CurrentDomain.BaseDirectory to execute my method via the loader but they are not there but are embedded in the executable. Is there a way for my new AppDomain to know about the assemblies that have been embedded into my executable without having to load them from the BaseDirectory?
I have looked for ways to load the other assemblies into my new AppDomain but haven't found a way to do it. I also suspect there might be Fody/Costura API to do it but haven't found anything that worked for this scenario.
internal class Loader : MarshalByRefObject
{
object CallInternal(string dll, string typename, string method, object[] parameters)
{
Assembly a = Assembly.LoadFile(dll);
Type t = a.GetType(typename);
MethodInfo m = t.GetMethod(method);
Console.WriteLine("Invoking " + m.Name);
return m.Invoke(null, parameters);
}
public static object Call(string dll, string typename, string method, params object[] parameters)
{
object result = null;
try
{
AppDomain newAppDomain = AppDomain.CreateDomain("myNewDomain", AppDomain.CurrentDomain.Evidence, AppDomain.CurrentDomain.SetupInformation);
Loader ld = (Loader)newAppDomain.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, typeof(Loader).FullName); //DLL file not found exception thrown from here
result = (Loader)ld.CallInternal(dll, typename, method, parameters);
AppDomain.Unload(newAppDomain);
}
catch (Exception ee)
{
Console.WriteLine("Exception Message [" + ee.Message + "]");
PDFUtil.Report.ErrSilent(ee);
}
return result;
}
}
I am looking to have my Loader class be instantiated without a need to load the .dlls from disk during runtime. Is this possible with Fody/Costura embedded .dlls?
I added my .dlls to my Visual Studio Project and set Build-Action to be Embedded Resource
I am running this bit of code to get the assembly into my new AppDomain:
string resName = null;
foreach (string resourceName in Assembly.GetExecutingAssembly().GetManifestResourceNames())
{
Console.WriteLine("Assembly.GetExecutingAssembly().GetManifestResourceNames() [" + resourceName + "] ");
if (resourceName.Contains("my.Assembly"))
{
resName = resourceName;
Console.WriteLine("CurrentDomain Assembly my.Assembly [" + "] Resource name [" + resName + "]");
}
}
Stream s = Assembly.GetExecutingAssembly().GetManifestResourceStream(resName);
if (s != null)
{
byte[] data = new BinaryReader(s).ReadBytes((int)s.Length);
Assembly a = Assembly.Load(data);
newAppDomain.Load(data);
foreach (Assembly cdAssem in newAppDomain.GetAssemblies())
{
Console.WriteLine("newAppDomain Assembly 2 [" + cdAssem.GetName().FullName + "]");
}
}
I can see that the assembly is part of my new AppDomain:
newAppDomain Assembly 2 [mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]
newAppDomain Assembly 2 my.Assembly Version=19.1.7243.19014, Culture=neutral, PublicKeyToken=null]
But using CreateInstanceAndUnwrap() still fails saying it can't find my.Assembly.dll. I am wondering if the only way one can load an assembly into the AppDomain is by loading it from the Base Directory?
I hope it wouldn't be a duplicate, I see plenty of similar questions, but any of them help can't help me.
I have such Method code in nameofdll.dll:
DllMethod()
{
Assembly assembly = Assembly.GetExecutingAssembly();
Type type = assembly.GetType("Class");
MethodInfo method = type.GetMethod("Method");
object obj = Activator.CreateInstance(type);
method.Invoke(...);
}
and Class - constructor add AssemblyResolver
class Class
{
public Class()
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(MyResolveEventHandler);
}
public void Method()
{...}
private Assembly MyResolveEventHandler(object sender, ResolveEventArgs args)
{...}
}
Till now AssemblyResolver is invoked when I call metod from my main app.exe which is in the same folder as nameofdll.dll, let's name the folder as mainfolder and also from powershell script in this way:
load dll(mainfolder + nameofdll.dll)
call DllMethod()
Everything works fine, but I have to unload dll which AssemblyResolver had loaded, because Method is called multiple times witch different dlls versions.
So I modified DllMethod() like that:
{
Assembly assembly = Assembly.GetExecutingAssembly();
MethodInfo method = type.GetMethod("Method");
//new part
AppDomainSetup domainSetup = new AppDomainSetup();
domainSetup.ApplicationBase = Path.GetDirectoryName(assembly.Location);
AppDomain Domain = AppDomain.CreateDomain(Class, null, domainSetup);
object obj = Domain.CreateInstanceAndUnwrap(assembly.FullName, Class);
method.Invoke(...);
AppDomain.Unload(Domain);
}
I use
domainSetup.ApplicationBase = Path.GetDirectoryName(assembly.Location);
because BaseDirecotry was powershell folder and CreateInstanceAndUnwrap fails to find nameofdll.dll.
And finally the problem is that everyting is ok when using app.exe, but when I called that dll method from powershell script, the AssemblyResolver wasn't invoked and immediately I got error that it could not load some dlls.
I also try to copy domain setting, and add
AppDomainSetup domainSetup = AppDomain.CurrentDomain.SetupInformation;
where CurrentDomain is domain in which AssemblyResolver works fine before, but it didn't solve the problem.
I am trying to load a dll in a SSIS project that ins't in a GAC. I followed the following code from here
the code:
[Microsoft.SqlServer.Dts.Tasks.ScriptTask.SSISScriptTaskEntryPointAttribute]
public partial class ScriptMain : Microsoft.SqlServer.Dts.Tasks.ScriptTask.VSTARTScriptObjectModelBase
{
static ScriptMain()
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}
static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
if (args.Name.Contains("ssisHelper"))
{
string path = #"c:\temp\";
return System.Reflection.Assembly.LoadFile(System.IO.Path.Combine(path, "ssisHelper.dll"));
}
return null;
}
}
This enabled the loading of the dll but it keep giving me the following error because Entity Framework was referenced inside the dll.
Could not load file or assembly 'EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' or one of its dependencies. The system cannot find the file specified.
So I tried to set the Working directory of dll with no success because dll's don't have an entry point. So I got the idea to put the file in a console application from here. I built an .exe and changed it to a .dll. Now in theory I have an entry point where I could set the working directory.
System.Environment.CurrentDirectory = #"PathToDll";
Yet I still get the same error. Does any one know of a solution?
Try to add an else if clause for each Assembly name:
static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
if (args.Name.Contains("ssisHelper"))
{
string path = #"c:\temp\";
return System.Reflection.Assembly.LoadFile(System.IO.Path.Combine(path, "ssisHelper.dll"));
}
else if (args.Name.Contains("xxx"))
{
string path = #"c:\temp\";
return System.Reflection.Assembly.LoadFile(System.IO.Path.Combine(path, "xxx.dll"));
}; return null;
I use Assembly.Load(byte[]) to load an assembly. This loaded assembly internally uses codeDomProvider.CompileAssemblyFromSource() to compile code which is failing with error 'The type or namespace name '<ClassName>' could not be found (are you missing a using directive or an assembly reference?)'.
AppDomain.AssemblyResolve event is not fired for the references present in the code to compile.
FYI: I am trying to load assembly which uses FileHelpers.dll and it calls on ClassBuilder.ClassFromString function. Assembly load fails with RunTimeCompilationException.
How should I resolve such dependencies?
UPDATE:
Here is code I am using
ctor()
{
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainOnAssemblyResolve;
}
private Assembly CurrentDomainOnAssemblyResolve(object sender, ResolveEventArgs args)
{
AssemblyName assemblyName = new AssemblyName(args.Name);
return LoadAssembly(assemblyName);
}
public Assembly GetAssembly(string assemblyFullName)
{
AssemblyName assemblyName = new AssemblyName(assemblyFullName);
Assembly assembly = LoadAssembly(assemblyName) ?? Assembly.Load(assemblyName);
}
private Assembly LoadAssembly(AssemblyName assemblyName)
{
byte[] assemblyBytes = GetAssemblyBytes(); // Get assembly bytes from external source
return Assembly.Load(assemblyBytes);
}
Thanks
Yash
You need to handle the CurrentDomain.ResolveEventHandler event as below, this event will be triggered whenever a reference is required:
code to load assemblies:
private void LoadAssem(){
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
//use File.ReadAllBytes to avoid assembly locking
Assembly asm2 = Assembly.Load(File.ReadAllBytes("AssemblyPath"));
}
Once a reference is needed it will call the below event:
private static string asmBase;
//asmBase contains the folder where all your assemblies found.
public static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
//This handler is called only when the common language runtime tries to bind to the assembly and fails.
//Retrieve the list of referenced assemblies in an array of AssemblyName.
Assembly MyAssembly, objExecutingAssemblies;
string strTempAssmbPath = "";
objExecutingAssemblies = args.RequestingAssembly;
AssemblyName[] arrReferencedAssmbNames = objExecutingAssemblies.GetReferencedAssemblies();
//Loop through the array of referenced assembly names.
foreach (AssemblyName strAssmbName in arrReferencedAssmbNames)
{
//Check for the assembly names that have raised the "AssemblyResolve" event.
if (strAssmbName.FullName.Substring(0, strAssmbName.FullName.IndexOf(",")) == args.Name.Substring(0, args.Name.IndexOf(",")))
{
//Build the path of the assembly from where it has to be loaded.
strTempAssmbPath = asmBase + "\\" + args.Name.Substring(0, args.Name.IndexOf(",")) + ".dll";
break;
}
}
//Load the assembly from the specified path.
MyAssembly = Assembly.Load(File.ReadAllBytes(strTempAssmbPath));
//Return the loaded assembly.
return MyAssembly;
}
At run time I want to load an assembly and need to find the names of its dependent assemblies, so that I can determine which assemblies are required to execute the given DLL file.
You'll need to load the assembly (DLL file) into a Reflection-Only context.
After that you can use GetReferencedAssembles to find dependencies.
I used this some time ago in a nasty bit of code:
Where you load your assembly, register the resolve event:
AppDomain.CurrentDomain.AssemblyResolve += Assemblies_AssemblyResolve;
Assembly.LoadFile("<path to your assembly>");
AppDomain.CurrentDomain.AssemblyResolve -= Assemblies_AssemblyResolve;
The resolve event handler is called for every referenced dll. here i try to load the assembly.
Assembly Assemblies_AssemblyResolve(object sender, ResolveEventArgs args)
{
if (args.RequestingAssembly != null)
{
return LoadAssemblyFromPath(new AssemblyName(args.Name), args.RequestingAssembly.Location);
}
if (assemblyTryPath != null)
{
return LoadAssemblyFromPath(new AssemblyName(args.Name), assemblyTryPath);
}
return null;
}
And a little helper where the actual loading happens:
private Assembly LoadAssemblyFromPath(AssemblyName assemblyName,string fullPath)
{
if (assemblyName == null||fullPath==null)
return null;
string path = Path.GetDirectoryName(fullPath);
string dllName = assemblyName.Name + ".dll";
string fullPath2Try = Path.Combine(path, dllName);
Assembly loadedAssembly = Assembly.LoadFrom(fullPath2Try);
return loadedAssembly;
}
Hope, that helps!
I found answer. If we want to find the Referenced assemblies of unloaded assembly, we can find from following way.
Assembly _Assembly = Assembly.ReflectionOnlyLoadFrom(#"H:\Account.dll");
AssemblyName[] _AN = _Assembly.GetReferencedAssemblies();