Load assembly to temporary Appdomain from another directory - c#

As a part of BaaS project, I need to dynamically execute a code from "guest" assembly. I've tried all the examples and answers in other similar questions including AppDomainToolKit approach, but got no luck.
Similar with the plugin approach, I got positive result by copying related assemblies into host application's bin path. However, this is not possible for the current scenario:
permissions and restrictions needs to be applied per request
each request should be evaluated in a temporary appdomain
load the required assembles and referenced types from a path to temporary domain
So far, my latest piece of code is below
// ClassLibrary1.dll and ClassLibrary2.dll are in the same directory, both are marked as Serializable
var binPath = #"C:\AssemblyDemo\ClassLibrary1\bin\Debug\";
var lib1Path = binPath + "ClassLibrary1.dll";
var lib2Path = binPath + "ClassLibrary2.dll";
var setup = new AppDomainSetup();
setup.ApplicationBase = binPath;
AppDomain domain = AppDomain.CreateDomain("domainname", null, setup);
ObjectHandle handle = domain.CreateInstanceFrom(lib1Path, "ClassLibrary1.Class1");
var unwrap = handle.Unwrap();
var m1 = unwrap.GetType().GetMethod("Method1");
var result = m1.Invoke(unwrap, null);
handle.Unwrap() throws exception Type is not resolved for member 'ClassLibrary1.Class1,ClassLibrary1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.

If you want to load the assembly only in the temporary AppDomain you cannot call Unwrap in the main domain. Unwrap loads the type there as well. You must move all access to objects from the temporary assembly to the temporary AppDomain. You can do this using AppDomain.DoCallBack or a custom MarshalByRef class that you instantiate in the child domain and call from the parent domain.

Related

Dotnet Core, is Assembly.Load(string) idempotent?

I've inherited some code that I think is a little odd, and want to make sure if it's a potential issue or not.
First, it gets the name of a loaded assembly based on a type it contains:
private readonly string _assemblyName = Assembly.GetAssembly(typeof(IEntityBase)).FullName;
Then, on each request to process a named entity, it loads the assembly by name, even though the assembly is already loaded:
var assembly = Assembly.Load(_assemblyName);
var type = assembly.GetType(entityFullName);
var instance = Activator.CreateInstance(type);
I'm wondering if the Load calls are potentially causing issues - in this case, since the assembly is already loaded, is the call idempotent and returns the already-loaded assembly, or does it create a new assembly each time?

Activator.CreateInstance throws Exception "The system cannot find the file specified."

I've copied code for an assembly that is used in a solution to create a similar assembly. The GUTS was different, but the shell stayed the same.
These assemblies are used in a project at a client that are add-on's and not part of our core code. Now that I'm finished the assembly does not want to load like it is supposed to.
The code that loads the assembly is
var assemblyName = ((XmlElement)xmlDoc.GetElementsByTagName("AssemblyName")[0]).InnerText;
var qualifiedClass = ((XmlElement)xmlDoc.GetElementsByTagName("QualifiedClass")[0]).InnerText;
IExternalAddOn addOn = (IExternalAddOn)Activator.CreateInstance(assemblyName, qualifiedClass).Unwrap();
var properties = new Dictionary<Type, object>();
properties[typeof(DevExpress.XtraBars.Ribbon.RibbonControl)] = mainForm.ribbon;
var form = addOn.ShowForm(properties);
if (form != null)
{
form.MdiParent = mainForm;
form.Text = pListRow.NAME;
form.Show();
I get the exception on the CreateInstance part.
The interesting thing is that when I use
Assembly ass = Assembly.LoadFrom(assemblyName); // this is test code
Type at = ass.GetType(qualifiedClass);
IExternalAddOn addOn = (IExternalAddOn)Activator.CreateInstance(at);
to load the assembly and get the type, and CreateInstance it works.
Why do you need the Unwrap? Is there a difference in the two different ways of loading? And WHY does the first one not work?
Thanks
J
As soon as Assembly.LoadFrom works, I guess you are passing a file name as an assemblyName parameter, which is wrong in case of Activator.CreateInstance(assemblyName, qualifiedClass).
According to Activator.CreateInstance documentation:
assemblyName can be either of the following:
The simple name of an assembly, without its path or file extension. For example, you would specify TypeExtensions for an assembly whose path and name are .\bin\TypeExtensions.dll.
The full name of a signed assembly, which consists of its simple name, version, culture, and public key token; for example, "TypeExtensions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=181869f2f7435b51".

Exception on receiving Assembly reference from another AppDomain

I am loading all DLLs from a specific "extensions" directory in a new AppDomain to get some Reflection related information out of those.
This is what I'm trying:
I created a new library AssemblyProxy in my solution which just has this class:
public class AssemblyProxy : MarshalByRefObject
{
public Assembly LoadFile( string assemblyPath )
{
try
{
return Assembly.LoadFile( assemblyPath );
}
catch
{
return null;
}
}
}
I make sure this DLL is present inside my "extensions" directory. Then I use the following code to load all assemblies from "extensions" directory into the new AppDomain.
foreach( string extensionFile in Directory.GetFiles( ExtensionsDirectory, "*.dll" ) )
{
Type type = typeof( AssemblyProxy.AssemblyProxy );
var value = (AssemblyProxy.AssemblyProxy) Domain.CreateInstanceAndUnwrap(
type.Assembly.FullName,
type.FullName );
var extensionAssembly = value.LoadFile( extensionFile );
types.AddRange( extensionAssembly.GetTypes() );
}
Some DLLs do get loaded successfully but on some DLLs an exception is thrown like this:
Could not load file or assembly 'Drivers, Version=2.3.0.77, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
EDIT: The exception is not thrown in the new AppDomain. The DLL gets successfully loaded in the new AppDomain. The exception gets thrown as soon as the assembly reference is returned to the main/calling AppDomain. Does the main/calling AppDomain try to load the assembly on its own on just receiving the reference?
Thanks.
You should not return an Assembly object from the new AppDomain because this will only work if you main AppDomain has access to those assemblies which it does not since the assemblies are located in a directory that:
is not the AppDomain's base directory (AppDomain.BaseDirectory),
is not in the directories specified in the AppDomain's relative search path (AppDomain.PrivateBinPath)
One way to avoid this to follow the leppie's comment and:
Create a serialized type that includes the minimum information you need from the Assembly object.
Add this type to new assembly that both AppDomain's have access to. The simplest way to do this by adding it to the GAC.
Another approach would be to use Mono.Cecil instead of System.Reflection. Mono.Cecil will allow you to inspect assemblies without loading them. For a very simple example look at the second half from this answer.

Why assembly is still visible? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Unloading the Assembly loaded with Assembly.LoadFrom()
I use custom AppDomain to load/unload assembly. But when assembly is unloaded I am able to see it under the AppDomain.CurrentDomain.
How it could be? Is this normal behavior or I am missing something?
Thank you for any clue!
string assemblyPath = #"C:\MyFile.dll";
var assemblyName = AssemblyName.GetAssemblyName(assemblyPath);
var ads = new AppDomainSetup
{
ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase,
DisallowCodeDownload = true
};
AppDomain newDomainName = AppDomain.CreateDomain("newDomainName", null, ads);
try
{
Assembly testLibrary = newDomainName.Load(assemblyName);
var c1 = AppDomain.CurrentDomain.GetAssemblies();
var c2 = newDomainName.GetAssemblies();
}
finally
{
AppDomain.Unload(newDomainName);
var c3 = AppDomain.CurrentDomain.GetAssemblies();
// The assembly is still visible here!!!
}
You are calling the Load() method of an AppDomain, which according to the documentation: "should be used only to load an assembly into the current application domain. This method is provided as a convenience for interoperability callers who cannot call the static Assembly.Load method. To load assemblies into other application domains, use a method such as CreateInstanceAndUnwrap."
In other words, you're loading the assembly into the primary AppDomain because you're calling Load() from the primary AppDomain (even though you're using calling it on an instance of your secondary AppDomain), and this is why it is appearing even after you unload your secondary AppDomain.
As indicated in the extract from the documentation above, you probably want to use AppDomain.CreateInstanceAndUnwrap.
You can't remove a loaded assembly from an app domain.
http://blogs.msdn.com/b/jasonz/archive/2004/05/31/145105.aspx
http://msdn.microsoft.com/en-us/library/ms173101(v=vs.80).aspx
There is no way to unload an individual assembly without unloading all
of the application domains that contain it. Even if the assembly goes
out of scope, the actual assembly file will remain loaded until all
application domains that contain it are unloaded.
http://blogs.msdn.com/b/suzcook/archive/2003/07/08/unloading-an-assembly.aspx
There's no way to unload an individual assembly without unloading all
of the appdomains containing it.

assemblies not resolving while creating instance from AppDomain

var processAssembly = Assembly.LoadFile(baseLocationCCDebugFolder+"\\CC.dll");
var processType = processAssembly.GetType("CC.Executor.CCProcess",true,true);
AppDomainSetup appDSetup = new AppDomainSetup()
{
ApplicationBase = baseLocationCCDebugFolder,
//PrivateBinPath = processAssembly.CodeBase
};
StrongName fullTrustAssembly = processAssembly.Evidence.GetHostEvidence<StrongName>();
AppDomain executionDomain = AppDomain.CreateDomain("ExecutionDomain",null,appDSetup,internetPS,fullTrustAssembly);
CCProcess process =(CCProcess)executionDomain.CreateInstanceAndUnwrap(processAssembly.ManifestModule.FullyQualifiedName, processType.FullName);
im encountering an error on the last line of this code which goes like this.
Could not load file or assembly 'D:\\work\\compilerCom\\CompileCom_Build_4_newArchitecture\\CompilerCom\\CC\\bin\\Debug\\CC.dll' or one of its dependencies. The given assembly name or codebase was invalid. (Exception from HRESULT: 0x80131047)<br/>
im still figuring my way in app domain.
what am i doing wrong?
EDIT:-
the value of baseLocationCCDebugFolder variable is D:\\work\\compilerCom\\CompileCom_Build_4_newArchitecture\\CompilerCom\\CC\\bin\\Debug
My guess is that it is not the Assembly not being found but one of the referenced assemblies not loading correctly. You may need to handle the resolve assembly event in the parent appdomain. There is a good post here describing it.
http://social.msdn.microsoft.com/forums/en-US/clr/thread/0a18ed66-6995-4e7c-baab-61c1e528fb82/

Categories