Loading Assemblies into separate AppDomain, getting InvalidCastException - c#

I'm trying to load assemblies in a separate app domain, but am running into a very strange problem. Here's some code:
public static void LoadAssembly(string assemblyPath)
{
string pathToDll = Assembly.GetCallingAssembly().CodeBase;
AppDomainSetup domainSetup = new AppDomainSetup
{
PrivateBinPath = pathToDll
};
AppDomain newDomain = AppDomain.CreateDomain("AssemblyLoader",null,domainSetup);
AssemblyLoader loader = (AssemblyLoader)newDomain.CreateInstanceFromAndUnwrap(
pathToDll,
typeof(AssemblyLoader).FullName);
}
AssemblyLoader is another class in the same assembly as this one, and it inherits from MarshalByRef, however for some strange reason, I get a cast exception every time I try to run this. I even hardcoded the path to the DLL instead of using GetCallingAssembly().CodeBase yet I keep getting this exception.
I understand it's hard to answer a question like this without actually seeing it and having more information, but maybe someone has run into a similar situation and would know the common "gotchas" and what I should look out for.
EDIT: The reason I don't want to load it directly is because this is just part of the code. The ultimate goal is that this class will have a method that load assemblies, gets their GUID and some other info about them and stores them in a database for a project I'm working on. Therefore, if I load this assembly in a separate app domain, I can load the others there too and then unload the app domain. No point in having all these assemblies loaded for the duration of the app, if I only need that data.

(EDIT: after reading the exception given, changing answer completely)
It appears the problem is the CreateInstanceFromAndUnwrap call, which uses the LoadFrom semantics of 'pathToDll'. Suzanne Cook detailed the possible sticking point on her blog where your original AppDomain tries to call Load("SomeAssembly, [...]") as opposed to LoadFrom("pathToDll") when trying to resolve the type in question.
Her advice was to hook the AssemblyResolve event on the current domain to do the correct LoadFrom in order to get the type. A little bit of targetted googling brings up a possible solution to the problem based on Suzanne's suggestion.

I don't believe the PrivateBinPath configuration is necessary, beyond that you don't need to use the Path to the DLL, but rather the Assembly's fully qualified name for the first parameter; try:
AssemblyLoader loader = (AssemblyLoader)newDomain.CreateInstanceFromAndUnwrap(
typeof(AssemblyLoader).Assembly.FullName,
typeof(AssemblyLoader).FullName);

There's a lot of good information for what you're trying to do here: How to load a .NET assembly for reflection operations and subsequently unload it?

Check out this article.
Using the code in that article I got a cross app-domain object. I abstracted things a bit with generics and have three assemblies. (i.e. 1 defining the interface, 1 defining the plugin implementation, and the main program which tells the generic what to load.) The original articles code is easy to follow.

Related

Is code executable on loading an assembly into the AppDomain

Is it possible to write code, which will be automatically executed when loading an assembly into an AppDomain with Assembly.Load? I need this information because our PlugIn system loads PlugIns and then check if they are valid, because they contain a signature in an attribute.
EDIT
I want to know whether the creator of the plugin is able to execute code when i am loading the assembly. If this is possible, we got some security issues.
From a security perspective I made the following findings when investigating this issue in depth:
from another question on Stack overflow, Jon Skeet says: "I don't believe there's any way of forcing a method to be run on assembly load"
If this was said by anyone else I would have ignored it, but if Jon Skeet says its impossible, its probably impossible.
Secondly, I tested Module Initializers in depth.
This will only fire code right before a class in the assembly is instantiated.
If no class from the assembly is explicitly instantiated, the Module Initializer will also not fire.
What is possible is if somewhere in your code, or a 3rd parties code that is running in your domain, the code loads and instantiates all classes with a certain Interface or base class or Attribute through out your app domain, and you might not be aware of this code. In such a case, code in the constructors of these classes will fire as soon as they are instantiated.
but other than that, through testing all scenarios and ideas that I could think of, and doing searches across the web, I came to the conclusion that it is not possible to execute code in a assembly, by simply loading the assembly.
The AppDomain.AssemblyLoad Event allows you to handle an event when an assembly is loaded into the AppDomain. You can find an example on the page linked.
Although this not directly answering the question, it does provide a good workaround for the question asked.
If you need to reflect the types in the assembly, while wanting to be 100% sure that no code in the assembly is executed.
you can do this without actually loading the assembly in to your AppDomain by using the Assembly.ReflectionOnlyLoadFrom method.
this will allow you to look at they types in the assembly but will NOT allow you to instantiate any of them, and will also not load the assembly in to the AppDomain.
Look at this example as exlanation
public void AssemblyLoadTest(string assemblyToLoad)
{
var initialAppDomainAssemblyCount = AppDomain.CurrentDomain.GetAssemblies().Count(); //4
Assembly.ReflectionOnlyLoad(assemblyToLoad);
var reflectionOnlyAppDomainAssemblyCount = AppDomain.CurrentDomain.GetAssemblies().Count(); //4
//Shows that assembly is NOT loaded in to AppDomain with Assembly.ReflectionOnlyLoad
Assert.AreEqual(initialAppDomainAssemblyCount, reflectionOnlyAppDomainAssemblyCount); // 4 == 4
Assembly.Load(assemblyToLoad);
var loadAppDomainAssemblyCount = AppDomain.CurrentDomain.GetAssemblies().Count(); //5
//Shows that assembly is loaded in to AppDomain with Assembly.Load
Assert.AreNotEqual(initialAppDomainAssemblyCount, loadAppDomainAssemblyCount); // 4 != 5
}
If you just need to check information of the assembly like the publicKey, instead of loading the Assembly, load the AssemblyName directly:
AssemblyName an = AssemblyName.GetAssemblyName("myfile.exe");
byte[] publicKey = an.GetPublicKey();
CultureInfo culture = an.CultureInfo;
Version version = an.Version;

Loading .dll into separate application domain (MEF)

I'm trying to load an assembly into a separate applicaiton domain.
I don't want to create any instances or execute the assembly. So I'm not using CreateInstanceAndUnwrap method. I just want to load the dll file (not creating any types from it etc..)
var cat = new AggregateCatalog();
pluginApplicationDomain = AppDomain.CreateDomain("pluginApplicationDomain", AppDomain.CurrentDomain.Evidence,
appDomainInfo);
cat.Catalogs.Add(new AssemblyCatalog(pluginApplicationDomain.Load(File.ReadAllBytes(plugin))));
and plugin is a relative path to the dll file.
../../Extensions/Plugins\MyAssembly.dll
This throws 'System.IO.FileNotFoundException'
The path is correct, and if I do Assembly.LoadFrom(plugin), it doesn't throw any exceptions, so I'm guessing it's not the path that's incorrect.
Other solutions all used CreateInstanceAndUnwrap, but what if the assembly and its types are black box to me? I know it impements one interface and that's it?
Load dll into another domain exception
would this work? However my method's signature doesn't match that one of CrossAppDomainDelegate
MEF is more about extensibility and it seems like you actually looking for [MAF]1. Check out this SO question. From my personal experience if your plugins going to invoke some native code (interact with hardware for example) AppDomains will not prevent crashes. It's safer to host such plugins in separate processes.

Avoid creating dummy variable to force a .NET assembly to load

Background:
I have a .NET 4.5 application that uses Enterprise Library 6.0 to connect to an Oracle Database. In order to use the Oracle Data Provider for .NET, you have use EntLibContrib.Data.OdpNet.
I have a separate project and solution that contains the business and data access logic to connect to the database and returns domain objects.
Issue:
I went to use the assemblies in another project (in another solution) and ran into issues. I had added references to the following:
The .NET dlls produced by building the other solution
Microsoft.Practices.EnterpriseLibrary.Common
Microsoft.Practices.EnterpriseLibrary.Data
EntLibContrib.Data.OdpNet
After adding the appropriate settings to the configuration it should have worked as it has in other projects -- but when I tried to connect to the database, I received the following error:
The type 'EntLibContrib.Data.OdpNet.OracleDatabase,
EntLibContrib.Data.OdpNet' cannot be resolved. Please verify the
spelling is correct or that the full type name is provided.
I created a question for this error a while back. I never got a response, but the issue seem to "fix itself" when I added additional config information, but I never dug into what was causing the issue.
Since I ran into this issue again, I investigated and narrowed it that I need to have reference to an object that was part of EntLibContrib.Data.OdpNet. It also works if I have a reference an object that references references an object that is part of EntLibContrib.Data.OdpNet.
My solution was simply to do write a dummy variable in a class of my new project:
private static EntLibContrib.Data.OdpNet.OracleDataReaderWrapper dummyVarNotUsed;
Even though dummyVarNotUsed is never used, simply having that line allows the EntLibContrib.Data.OdpNet assembly to be referenced correctly.
This is a hack, could someone shed some light on what is happening and how to better reference the dll?
Based off of this question, and the associated answer, I'd suggest trying to handle the AssemblyResolve event, so something like this:
//in your startup method
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyResolve += new ResolveEventHandler(MyResolveEventHandler);
//...
private static Assembly MyResolveEventHandler(object sender, ResolveEventArgs args)
{
//not sure if this will be what the name is, you'd have to play with it
if (args.Name == "EntLibContrib.Data.OdpNet.OracleDatabase")
{
return typeof(EntLibContrib.Data.OdpNet.OracleDataReaderWrapper).Assembly;
}
//not sure if this is best practice or not (to return null if truly unknown)
return null;
}
That answer does suggest your current solution as the preferred method, but I agree that it does feel hacky. Not sure if this other method will feel any less hacky to you; it does to me in that at least this way you can clearly document this event handler instead of having a bogus variable somewhere with comments like //DON'T REMOVE, VERY IMPORTANT!!!

Remove an assembly from an application domain

So I'm trying to remove an assembly from my
Thread.GetDomain().GetAssemblies()
area.
I've looked at a couple of explanations on the web so far but have just not been able to figure it out.
This is what I've gotten so far
AppDomain tempD = Thread.GetDomain();
tempD.DoCallBack(LoaderCallback);
AppDomain.Unload(tempD);
System.IO.File.Delete(assemblyName);
private static void LoaderCallback()
{
Assembly.ReflectionOnlyLoad("myAssembly");
//byte[] raw = System.IO.File.ReadAllBytes(test);
//Assembly yacsi = Assembly.Load(raw);
//((IScript)yacsi.CreateInstance("Script")).Go();
}
I've played around with this concept so far but I'm struggling to make the connection because Thread.GetDomain(); does not contain the Unload command
Also I wasn't able to try this part ((IScript)yacsi.CreateInstance("Script")).Go() because I don't know what assembly IScript is nor what to place in the "Script" text section
Any ideas will be appreciated
To add on top #RobertHarvey's comment and also take a step back, what you want to do is create a separate AppDomain and load your assembly into it.
After that you can marshal into your domain various types you intend to use.
When you're done with them, you can unload the application domain, thus getting rid of the assembly.
You should note that inter-domain instance marshaling is done through .NET Remoting, so you may want to play with the remoting lease policies which include, among other things, time values for a marshaled object's lifetime. The reason for modifying lease duration is that a marshaled instance expires after a predefined period of time, if it's not called/used at all. So, you may want to avoid re-marshaling instances over and over again and just prolong their lifetime in your domain.
I think this is a good sample showing how you can load an assembly into a new AppDomain and also unload it. Lots of other samples out there.
As long as you know what the process should be, finding code for it is straightforward.

Unable to cast transparent proxy to type from AppDomain

I'm trying to create an object in an appdomain:
var type = typeof (CompiledTemplate);
var obj = (CompiledTemplate) domain.CreateInstanceAndUnwrap (
type.Assembly.FullName, type.FullName);
However, I always get the following error:
Unable to cast transparent proxy to type 'Mono.TextTemplating.CompiledTemplate'.
I'm running on .NET 4.0, not Mono, despite what the namespace might suggest :)
As far as I know, this error happens when .NET thinks that the Type & Assembly do not exactly match in the two domains. However, when debugging, the FullName and Location are identical. Only the Assembly.Codebase property differs - in the child AppDomain its extension is uppercased to "DLL" for some reason.
I've tried adding an AssemblyResolve handler to the AppDomain, which uses Assembly.LoadFrom to load the filename explicitly, but the CodeBase's extension still gets uppercased. Since the original assembly was also loaded with Assembly.LoadFrom (via Mono.Addins), the difference between the CodeBase values seems very strange.
Any suggestions for fixing or working around this problem?
Could you be running into an issue with assembly load contexts?
(e.g. see here)
You have a type that's clearly in the load context (because you're using typeof(CompiledTemplate)), but you're saying that the type in the secondary AD is loaded into the load-from context...
Did you check with fuslogvw to determine exactly what assemblies are being loaded? The fuslog trace will also tell you if the assemblies are being loaded into different contexts.
Perhaps you can use the dynamic keyword instead of casting it to a specific type:
var type = typeof (CompiledTemplate);
dynamic obj = domain.CreateInstanceAndUnwrap (
type.Assembly.FullName, type.FullName);
That might at least give you a workaround to the problem. Of course, the potential drawbacks will be not having compile time checking and/or slower performance. However, these might be negligible trade-offs depending on your situation.
A second copy of the assembly is, indeed, being loaded into memory as it is.
An instance of a type in the runtime is specific to the instance of the assembly loaded - so even if the same DLL file is loaded in a second time, the types are not considered to match.
This is a typical problem when "DLLHell" is extended into the "GACAndDLLHell". "GACONLYHeaven" is a better place ... :).
That the filenames are subtly different (the .DLL extension has a different case) implies that the same DLL is being loaded from two places (that is: the GAC is case-insensitive/always lower case on filenames IIRC).
An abstract class or, preferably, an interface is what you need here.
If you can't make changes to the code base I would, first, make very sure that the DLL exists in only 1 place on the drive (or 0 places on the drive if it is being loaded from the GAC). A copy of the DLL that contains the type: 'CompiledTemplate' in your app /bin folder would be a real culprit ...?
Is this new code or existing code that is now failing for some reason?
I have a WCF net named pipes application that uses a callback (duplex) architecture.
I got this error because my service interface's [ServiceContract] was annotated with the wrong callback.

Categories