what is the difference between AppDomain.CreateInstance and Activator.CreateInstance? - c#

I want to ask a question to realize the difference between AppDomain and Activator, I loaded my dll via appdomain.CreateInstance. But I realized that more method to create instance. Therefore when or where do I choose this method?
Example1:
// Use the file name to load the assembly into the current
// application domain.
Assembly a = Assembly.Load("example");
// Get the type to use.
Type myType = a.GetType("Example");
// Get the method to call.
MethodInfo myMethod = myType.GetMethod("MethodA");
// Create an instance.
object obj = Activator.CreateInstance(myType);
// Execute the method.
myMethod.Invoke(obj, null);
Example2:
public WsdlClassParser CreateWsdlClassParser()
{
this.CreateAppDomain(null);
string AssemblyPath = Assembly.GetExecutingAssembly().Location;
WsdlClassParser parser = null;
try
{
parser = (WsdlClassParser) this.LocalAppDomain.CreateInstanceFrom(AssemblyPath,
typeof(Westwind.WebServices.WsdlClassParser).FullName).Unwrap() ;
}
catch (Exception ex)
{
this.ErrorMessage = ex.Message;
}
return parser;
}
Example3:
private static void InstantiateMyTypeSucceed(AppDomain domain)
{
try
{
string asmname = Assembly.GetCallingAssembly().FullName;
domain.CreateInstance(asmname, "MyType");
}
catch (Exception e)
{
Console.WriteLine();
Console.WriteLine(e.Message);
}
}
Can you explain why do I need more methods or what are the differences?

From sscli2.0 source code, it looks like the "CreateInstance" method calls in AppDomain class always delegates the call to Activator.
The sole purpose of the (almost static) Activator class is to "Create" instances of various classes, while AppDomain is introduced for quite different (and perhaps more ambitious) purposes, e.g.:
A light-weight unit of application isolation;
Optimize memory consumption because AppDomains can be Unloaded.
...
The 1st and the 3rd example are straightforward, just as zmbq noted. I guess your 2nd example is from this post, where the author showed how to unload an out-of-date proxy using AppDomain.

The first one creates an instance of type Example from the assembly 'example', and calls MethodA on it.
The third one creates an instance of MyType in a different AppDomain
I'm not sure about the second, I don't know what this is, but it seems to create a class in the current app-domain - that is, it's similar to the first.

Related

How to compile C# DLL on the fly, Load, and Use

A) compiling C# EXE's and DLL's on the fly are relatively easy.
B) Executing an EXE means that a new application is run. Loading a DLL means that methods and functions can be used in cases that may be shared between applications or projects.
Now, the quickest and easiest way to compile your EXE (or with mild modifications, DLL) can be found from the MSDN or for your convenience:
private bool CompileCSharpCode(string script)
{
lvErrors.Items.Clear();
try
{
CSharpCodeProvider provider = new CSharpCodeProvider();
// Build the parameters for source compilation.
CompilerParameters cp = new CompilerParameters
{
GenerateInMemory = false,
GenerateExecutable = false, // True = EXE, False = DLL
IncludeDebugInformation = true,
OutputAssembly = "eventHandler.dll", // Compilation name
};
// Add in our included libs.
cp.ReferencedAssemblies.Add("System.dll");
cp.ReferencedAssemblies.Add("System.Windows.Forms.dll");
cp.ReferencedAssemblies.Add("Microsoft.VisualBasic.dll");
// Invoke compilation. This works from a string, but you can also load from a file using FromFile()
CompilerResults cr = provider.CompileAssemblyFromSource(cp, script);
if (cr.Errors.Count > 0)
{
// Display compilation errors.
foreach (CompilerError ce in cr.Errors)
{
//I have a listview to display errors.
lvErrors.Items.Add(ce.ToString());
}
return false;
}
else
{
lvErrors.Items.Add("Compiled Successfully.");
}
provider.Dispose();
}
catch (Exception e)
{
// never really reached, but better safe than sorry?
lvErrors.Items.Add("SEVERE! "+e.Message + e.StackTrace.ToString());
return false;
}
return true;
}
Now that you can compile on the fly, there are a few variances between how to load the DLL. Typically speaking, you would add it as a reference in Visual Studios to be compiled into the project. This is rather easy and you have probably done it many times over, but we want to use it in our current project, and we can't very well require the user to recompile the entire project every time they want to test out their new DLL. Therefor, I will simply discuss how one loads a library 'on the fly'. Another term here would by "programmatically". To do this, after a successful compile, we load up an Assembly as follows:
Assembly assembly = Assembly.LoadFrom("yourfilenamehere.dll");
If you have an AppDomain, you can try this:
Assembly assembly = domain.Load(AssemblyName.GetAssemblyName("yourfilenamehere.dll"));
Now that the lib is "referenced", we can open it up and use it. There are 2 ways to do this. One requires you to know if the method has parameters, another will check for you. I'll do the later, you can check the MSDN for the other.
// replace with your namespace.class
Type type = assembly.GetType("company.project");
if (type != null)
{
// replace with your function's name
MethodInfo method = type.GetMethod("method");
if (method != null)
{
object result = null;
ParameterInfo[] parameters = method.GetParameters();
object classInstance = Activator.CreateInstance(type, null);
if (parameters.Length == 0) // takes no parameters
{
// method A:
result = method.Invoke(classInstance, null);
// method B:
//result = type.InvokeMember("method", BindingFlags.InvokeMethod, null, classInstance, null);
}
else // takes 1+ parameters
{
object[] parametersArray = new object[] { }; // add parameters here
// method A:
result = method.Invoke(classInstance, parametersArray);
// method B:
//result = type.InvokeMember("method", BindingFlags.InvokeMethod, null, classInstance, parametersArray);
}
}
}
PROBLEM:
First compile works fine. First execution works fine. However, the recompile attempt will error, saying that your *.PDP (debugger database) is in use. I've heard some hints about marshaling, and AppDomains, but I haven't quite cleared up the problem. Recompile will only fail after the DLL has been loaded.
Current attempt at Marshaling && AppDomain:
class ProxyDomain : MarshalByRefObject
{
private object _instance;
public object Instance
{
get { return _instance; }
}
private AppDomain _domain;
public AppDomain Domain
{
get
{
return _domain;
}
}
public void CreateDomain(string friendlyName, System.Security.Policy.Evidence securityinfo)
{
_domain = AppDomain.CreateDomain(friendlyName, securityinfo);
}
public void UnloadDomain()
{
try
{
AppDomain.Unload(_domain);
}
catch (ArgumentNullException dne)
{
// ignore null exceptions
return;
}
}
private Assembly _assembly;
public Assembly Assembly
{
get
{
return _assembly;
}
}
private byte[] loadFile(string filename)
{
FileStream fs = new FileStream(filename, FileMode.Open);
byte[] buffer = new byte[(int)fs.Length];
fs.Read(buffer, 0, buffer.Length);
fs.Close();
return buffer;
}
public void LoadAssembly(string path, string typeName)
{
try
{
if (_domain == null)
throw new ArgumentNullException("_domain does not exist.");
byte[] Assembly_data = loadFile(path);
byte[] Symbol_data = loadFile(path.Replace(".dll", ".pdb"));
_assembly = _domain.Load(Assembly_data, Symbol_data);
//_assembly = _domain.Load(AssemblyName.GetAssemblyName(path));
_type = _assembly.GetType(typeName);
}
catch (Exception ex)
{
throw new InvalidOperationException(ex.ToString());
}
}
private Type _type;
public Type Type
{
get
{
return _type;
}
}
public void CreateInstanceAndUnwrap(string typeName)
{
_instance = _domain.CreateInstanceAndUnwrap(_assembly.FullName, typeName);
}
}
Errors on _instance = _domain.CreateInstanceAndUnwrap(_assembly.FullName, typeName); saying that my Assembly isn't serializable. Tried adding [Serializable] tag to my class with no luck. Still researching fixes.
Seems things can get a bit confusing when you can't see how they're being used, so here's making it easy?
private void pictureBox1_Click(object sender, EventArgs e)
{
pd.UnloadDomain();
if (CompileCSharpCode(header + tScript.Text + footer))
{
try
{
pd.CreateDomain("DLLDomain", null);
pd.LoadAssembly("eventHandler.dll", "Events.eventHandler");
pd.CreateInstanceAndUnwrap("Events.eventHandler"); // Assembly not Serializable error!
/*if (pd.type != null)
{
MethodInfo onConnect = pd.type.GetMethod("onConnect");
if (onConnect != null)
{
object result = null;
ParameterInfo[] parameters = onConnect.GetParameters();
object classInstance = Activator.CreateInstance(pd.type, null);
if (parameters.Length == 0)
{
result = pd.type.InvokeMember("onConnect", BindingFlags.InvokeMethod, null, classInstance, null);
//result = onConnect.Invoke(classInstance, null);
}
else
{
object[] parametersArray = new object[] { };
//result = onConnect.Invoke(classInstance, parametersArray);
//result = type.InvokeMember("onConnect", BindingFlags.InvokeMethod, null, classInstance, parametersArray);
}
}
}*/
//assembly = Assembly.LoadFrom(null);
}
catch (Exception er)
{
MessageBox.Show("There was an error executing the script.\n>" + er.Message + "\n - " + er.StackTrace.ToString());
}
finally
{
}
}
}
Once you have loaded a DLL into (the default appdomain of) a running process, the file on disk cannot be overwritten until the process is terminated. DLLs cannot be unloaded in managed code like they can be in unmanaged code.
You need to create a new appdomain in your host process and load the newly created DLL assembly into that appdomain. When you are ready to compile a new version of the DLL, you can dispose of the appdomain. This will unload the DLL from memory and release the lock on the DLL file, so that you can compile a new DLL to that same file. You can then construct a new appdomain to load the new DLL into.
The main hazard of using appdomains is that all calls across the appdomain boundary must be marshalled, much like an IPC or network RPC. Try to keep the interface of the objects you need to call across the appdomain boundary to a minimum.
You can also compile the assembly to memory, receiving a byte array or stream as the output, then load that assembly into the separate appdomain. This avoids creating debris on disk that will need to be deleted eventually.
Do not use compile to memory as a workaround for the file lock issue. The core issue is that assemblies cannot be removed from memory when they are loaded into the default appdomain of the process. You MUST create a new appdomain and load the DLL into that appdomain if you want to unload that assembly from memory later in the lifetime of the process.
Here's a rough outline of how to construct an object in the context of another appdomain:
var appdomain = AppDomain.CreateDomain("scratch");
byte[] assemblyBytes = // bytes of the compiled assembly
var assembly = appdomain.Load(assemblyBytes);
object obj = appdomain.CreateInstanceAndUnwrap(assembly.FullName, "mynamespace.myclass");
After this sequence, obj will contain a reference to a proxy that links to the actual object instance inside the appdomain. You can invoke methods on obj using reflection or typecast obj to a common interface type and call methods directly. Be prepared to make adjustments to support RPC marshalling of the method call parameters. (see .NET remoting)
When working with multiple appdomains, you have to be careful how you access types and assemblies because a lot of .NET functions default to operating in the current appdomain of the caller, which is usually not what you want when you have multiple appdomains. compilerResult.CompiledAssembly, for example, internally performs a Load of the generated assembly in the caller's appdomain. What you want is to load the assembly into your other appdomain. You have to do that explicitly.
Update:
In your recently added code snippet showing how you load your appdomain, this line is your problem:
_assembly = Assembly.LoadFrom(path);
That loads the DLL into the current appdomain (the caller's appdomain), not into the target appdomain (referenced by _domain in your example). You need to do use _domain.Load() to load the assembly into that appdomain.
if you have no need to debug, or don't mind to debug the "dynamic" code, with some missing information.
you can generate the code in memory.. this will allow you to compile the code several times.. but will not generate a .pdb
cp.GenerateInMemory = true;
in alternative if you have no need to able to locate the assembly on disk you can ask the compiler to dump all the code in the temp directory and generate a temp name for the dll (wich will always be unique)
cp.TempFiles = new TempFileCollection(Path.GetTempPath(), false);
//cp.OutputAssembly = "eventHandler.dll";
in both this cases to access the dll and it's types you can get it from the compiler results
Assembly assembly = cr.CompiledAssembly;
not explicitly loading is necessary
but if non of this situations apply and you must and a physical .dll with a .pdp in a known folder.. the only advice i can give you it to put a version number on the dll..
and in case you don't have a simple way to control the amount of times the dll was compiled you can always resort to a timestamp..
cp.OutputAssembly = "eventHandler"+DateTime.Now.ToString("yyyyMMddHHmmssfff")+".dll";
of course you must realize that every time you compile a new .dll will be loaded into memory and wont be unloaded unless you use separate app domains.. but that goes out of scope for this question..

creating an extension for c# application which calls static functions defined in host program

I want to create engine for extensions, right now I have a basic class called "Module" which contains lot of virtual functions, each extension is a class that inherits "Module" and override the functions (mostly hooks) with own code.
I want to be able to put these extensions to separate binary and to load it "on the fly".
I created a simple handler for loading these plugins:
public static bool LoadMod(string path)
{
try
{
if (File.Exists(path))
{
System.Reflection.Assembly library = System.Reflection.Assembly.LoadFrom(path);
if (library == null)
{
Program.Log("Unable to load " + path + " because the file can't be read", true);
return false;
}
Type[] types = library.GetTypes();
Type type = library.GetType("wmib.RegularModule");
Type pluginInfo = null;
foreach (Type curr in types)
{
if (curr.IsAssignableFrom(type))
{
pluginInfo = curr;
break;
}
}
if (pluginInfo == null)
{
Program.Log("Unable to load " + path + " because the library contains no module", true);
return false;
}
Module _plugin = (Module)Activator.CreateInstance(pluginInfo);
return true;
}
}
catch (Exception fail)
{
core.handleException(fail);
}
return false;
}
My problem is that these modules are calling functions in static classes that I have in host application. Is it possible to create some "skeleton" of this static class in source code of extension? I tried creating the class in the second source code with empty functions of same name and parameters, but that doesn't work for some reason (every time I attempt to load it I get: Exception has been thrown by the target of an invocation.mscorlib at System.Reflection.MonoCMethod.Invoke)
Here is example of what I want to be able to do:
There is a host application that contains extension handler, and some own static functions.
The host application loads the module from binary file and insert it to some array of modules (this is what is done in constructor of every class that inherits "Module". This module contains some functions which are calling the static functions of host application. What I need to be able to do is being able to call the static function that is defined in source code of host application, even in source code of plugin, which doesn't contain the static function's code. I hope it's clear a bit :)
Ok, it's actually not that hard as I thought, I just referenced the host binary to extension, which enabled all static elements and classes of host in the extension.

Invoking Method on Object Instantiated From DLL

I am having some trouble with assemblies and DLL's.
instrument_ is declared as an object and I'm creating an instance of "PP150" from the dll whose path is specified by path_.
string className = ContineoProperties.getSingleton().getClassName(path_);
assembly_ = Assembly.LoadFrom(path_);
Type classType = assembly_.GetType("Instrument." + className);
instrument_ = Activator.CreateInstance(classType);
Later I to call the method isntrument_.instrumentCommand(cmd.getCommandName())
The error I get is with when i call the method.
'object' does not contain a definition for 'instrumentCommand'
The isntrument_ is created fine. its just the method call that's giving me a problem. The method does exist in the "PP150.dll". Do I need some DLLImport to allow it to recognize it as a function?
Thanks,
P
If object type is not known in compile time,
To call a method defined on an object, you must use Reflection.
MethodInfo mInfo = classType.GetMethod("instrumentCommand");
mInfo.Invoke(instrument_, new Object[] { _parameters});
The compiler is never going to recognize the methods on a type that you are loading via reflection (e.g. using Assembly.GetType() and Activator.CreateInstance()). Unless you have the type metadata available at build time, you will always get that error if you try to call methods that are not defined on Object itself.
You have two options for making that kind of method call. Both of them require you to give up type safety, the only difference is the amount of work required. In both cases, if you make a mistake, the compiler will not tell you -- you will get a runtime exception instead.
Declare instrument_ as dynamic instead of object. This, obviously, only works in .NET 4.0, but it accomplishes exactly what you're trying to do. The method call will be dispatched at runtime, so as long as the instance that instrument_ references actually has a method call with the appropriate name, it will work.
Use reflection to call the method. You're already using reflection to load the type, so you are halfway there. You would need to add something like this:
// The array of types is the parameter list; assuming instrumentCommand takes
// a string it would look like this:
MethodInfo method = classType.GetMethod("instrumentCommand", new Type[] { typeof(string) });
method.Invoke(instrument_, new object[] { cmd.getCommandName() });
This happens because Activator.CreateInstance returns an object. I would create a separate DLL for the interface which is implemented by the class you want to instantiate. Both the DLL containing this class, and the executable should reference the DLL containing the interface. This way you could cast the object returned by Activator.CreateInstance to the interface, and call its methods:
IInstrument.dll:
interface IInstrument
{
void instrumentCommand(string cmd);
}
Instrument.dll (add IInstrument.dll as reference):
class Instrument : IInstrument
{
public void instrumentCommand(string cmd)
{
// ... implementation ...
}
}
InstrumentApp.exe (add IInstrument.dll as reference):
class Program
{
public static void Main()
{
// ... load Instrument.dll into assembly object ...
// ... load the type from the assembly ...
IInstrument instrument_ = (IInstrument)Activator.CreateInstance(classType);
instrument_.instrumentCommand(cmd.getCommandName());
}
}
The most simple thing would be to link agains PP150.
If you did link against the dll you must use Assembly.LoadFile or Assembly.Load and not LoadFrom because the last one will cause the assembly load to load your assembly in the LoadFrom loader context which will alter type identity.
Suppose you load the Type T from Assembly A via LoadFrom and you link against A as well.
object CreateTypeFrom()
{
var A = Assembly.LoadFrom(#"xxxx");
return A.CreateInstance("T");
}
void Test()
{
object t = CreateTypeFrom();
T RealT = new T(); // no prob
T Castedt = (T)t; // this will throw an InvalidCastException
T isNull = t as T; // this will result in a null instance
}
As you can see although you did create two times an instance of T they cannot be casted to due to different loader context which will make the type pretty useless.
To get rid of these things you could simply use Reflection to create a proxy type which will forward your calls to the proxy type. If you are using .NET 4 you can take advantage of the DLR to find the best matching methods at runtime. The code below creats a Version object and returns it as dynamic object. Then I do call the Major property to an integer and print it out to console. This does work with no exceptions nor compile time errors if you are using .NET 4 or later.
dynamic CreateTypeFrom()
{
var assembly = typeof(string).Assembly;
return assembly.CreateInstance("System.Version", true, BindingFlags.CreateInstance, null, new object[] { 1, 2, 3, 4 }, null, null);
}
[TestMethod]
public void Test()
{
var t = CreateTypeFrom();
int major = t.Major;
Console.WriteLine(major);
}

What is the most appropriate way to handle corrupt input data in a C# constructor?

I'm reading data in from a file and creating objects based on this data. The data format is not under my control and is occasionally corrupt. What is the most appropriate way of handling these errors when constructing the objects in C#?
In other programming languages I have returned a null, but that does not appear to be an option with C#.
I've managed to figure out the following options, but I would appreciate advice from more experienced C# programmers:
Option 1. Read the file inside the constructor and throw an exception when the source data is corrupt:
try
{
obj = Constructor(sourceFile);
... process object ...
}
catch (IOException ex)
{
...
}
Option 2. Create the object, then use a method to read data from the source file:
obj = Constructor();
obj.ReadData(sourceFile);
if (obj.IsValid)
{
... process object ...
}
or possibly throw exceptions on error:
obj = Constructor();
try
{
obj.Read(sourceFile);
... process object ...
}
catch
{
...
}
Option 3. Create the object using a static TryParse method:
if (ObjClass.TryParse(sourceFile, out obj))
{
... process object ...
}
and if so, should I implement option 3 internally using option 1?
public static bool TryParse(FileStream sourceFile, out ObjClass obj)
{
try
{
obj = Constructor(sourceFile);
return true;
}
catch (IOException ex)
return false;
}
I would do something along the lines of option 3):
class ObjectClass
{
protected ObjectClass(...constructor parameters your object depends on...)
{
}
public static ObjectClass CreateFromFile(FileStream sourceFile)
{
.. parse source file
if (parseOk)
{
return new ObjectClass(my, constructor, parameters);
}
return null;
}
}
And then use it like this:
ObjClass.CreateFromFile(sourcefile);
In general the constructor should take as parameters all properties which essentially define the class. Doing heavyweight calculations (like parsing a file) is best left to factory methods as it is usually not expected for the constructor to perform complex and potentially long running tasks.
Update: As mentioned in comments a better pattern is this:
public static ObjectClass CreateFromFile(FileStream sourceFile)
{
.. parse source file
if (!parseOk)
{
throw new ParseException(parseErrorDescription);
}
return new ObjectClass(my, constructor, parameters);
}
public static bool TryCreateFromFile(FileStream sourceFile, out ObjectClass obj)
{
obj = null;
.. parse source file
if (!parseOk)
{
return false;
}
obj = new ObjectClass(my, constructor, parameters);
return true;
}
I would not put anything into a constructor that might throw an exception - except for if something goes really wrong.
If your constructor has a possible return value other than a valid object, you should encapsulate it.
The safest way would probably be to create a factory method (public static function in the class that accepts a file reference and returns a new instance of the class or null). This method should first validate the file and its data and only then create a new object.
If the file data has a simple structure, you can first load it into some local variable and construct the object with this data.
Otherwise, you can still decide - inside of your factory method - if you rather want to try / catch the construction or use any of the other points mentioned above.
Both Options #1 and #3 are good choices and common in the .Net framework. It's also common to provide both for the same type. Consider Int32.TryParse and Int32.Parse. Providing both gives developers a bit more flexibility without detracting from the integrity of the type.
I would strongly advise you to avoid #2. This pattern forces both the type author and type consumer to handle instances of the type in multiple states
Constructed but not fully initialized
Initialized and valid
Initialized and invalid
This puts a burden on every consumer to deal with instances being in all different states (even if the response is to just throw). Additionally it forces a non-standard pattern on consumers. Developers have to understand your type is special and that it needs to be constructed and then initialized. It goes against the standard way objects are created in .Net.
Note for #3 though I would approach it a bit different. The exception form should be implemented in terms of the try form. This is the standard pattern when providing both options to the user. Consider the following pattern
class MyType {
struct ParsedData {
// Data from the file
}
public MyType(string filePath) : this(Parse(filePath)) {
// The Parse method will throw here if the data is invalid
}
private MyType(ParsedData data) {
// Operate on the valid data. This doesn't throw since the errors
// have been rooted out already in TryParseFile
}
public static bool TryParse(string filePath, out MyType obj) {
ParsedData data;
if (!TryParseFile(filePath, out data)) {
obj = null;
return false;
}
obj = new MyType(data);
return true;
}
private static ParsedData Parse(string filePath) {
ParsedData data;
if (!TryParseFile(filePath, out data)) {
throw new Exception(...);
}
return data;
}
private static bool TryParseFile(string filePath, out ParsedData data) {
// Parse the file and implement error detection logic here
}
}
From Microsoft Constructor Design Guidelines (MSDN),
Do throw exceptions from instance constructors if appropriate.
Constructors should throw and handle exceptions like any method. Specifically, a constructor should not catch and hide any exceptions that it cannot handle.
Factory Method is not the right way to approach this problem. See Constructors vs Factory Methods
From Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries
5.3 Constructor Design
Consider using a static factory method instead of a constructor if the
semantics of the desired operation do not map directly to the construction
of a new instance, or if following the constructor design guidelines
feels unnatural.
Do throw exceptions from instance constructors if appropriate.
.NET BCL implementations do throw exceptions from constructors
For example, the List Constructor (Int32), throws an ArgumentOutOfRangeException when the capacity argument of the list is negative.
var myList = new List<int>(-1); // throws ArgumentOutOfRangeException
Similarly, your constructor should throw an appropriate type of exception when it reads the file. For example, it could throw FileNotFoundException if the file does not exist at the specified location, etc.
More Information
Code Contracts
Throwing exceptions from constructor in .Net
Throwing ArgumentNullException in constructor?
Constructor parameter validation in C# - Best practices
All these solutions work, but as you said, C# doesn't allow to return null from a constructor. You either get an object or an exception. Since this is the C# way to go, I wouldn't choose option 3, because that merely mimics that other language you're talking about.
Lots of people [edit] among which is Martin, as I read in his answer :) [/edit] think it is good to keep your constructor clean and small. I'm not so sure about that. If your object is of no use without that data, you could read in the data in the constructor too. If you want to construct the object, set some options, and then read the data (especially with the possility to try again if the read fails), a separate method would be fine as well. So option 2 is a good possibility too. Even better maybe, depending mainly on taste.
So as long as you don't choose 3, choose the one you're the most comfortable with. :)

Use AppDomain to load/unload external assemblies

My scenario is as follows:
Create new AppDomain
Load some assemblies into it
Do some magic with loaded dlls
Unload AppDomain to release memory & loaded libraries
Below is the code that I'm trying to use
class Program
{
static void Main(string[] args)
{
Evidence e = new Evidence(AppDomain.CurrentDomain.Evidence);
AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
Console.WriteLine("Creating new AppDomain");
AppDomain newDomain = AppDomain.CreateDomain("newDomain", e, setup);
string fullName = Assembly.GetExecutingAssembly().FullName;
Type loaderType = typeof(AssemblyLoader);
var loader = (AssemblyLoader)newDomain.CreateInstanceFrom(loaderType.Assembly.Location, loaderType.FullName).Unwrap();
Console.WriteLine("Loading assembly");
Assembly asm = loader.LoadAssembly("library.dll");
Console.WriteLine("Creating instance of Class1");
object instance = Activator.CreateInstance(asm.GetTypes()[0]);
Console.WriteLine("Created object is of type {0}", instance.GetType());
Console.ReadLine();
Console.WriteLine("Unloading AppDomain");
instance = null;
AppDomain.Unload(newDomain);
Console.WriteLine("New Domain unloaded");
Console.ReadLine();
}
public class AssemblyLoader : MarshalByRefObject
{
public Assembly LoadAssembly(string path)
{
return Assembly.LoadFile(path);
}
}
}
library.dll consists only of a single dummy class, with one huge string table(for easier tracking the memory consumption)
Now the problem is that memory actually isn't freed. What's more surprising, memory usage actually increases after AppDomain.Unload()
Can anyone shed some light on this issue?
This is not a complete answer: I just noticed that you use a string as payload. Strings are not useful for this, as literal strings are interned. The interned strings are shared among AppDomains, so that part is not unloaded when you unload your AppDomain. Try using a byte[] instead.
Answering my own question - don't know if there's better way to do it on StackOverflow... If there is, I'd be grateful for instructions...
Anyway, digging through internet I found another solution, which I hope is better.
Code below, if anyone finds any weak points - please respond.
class Program
{
static void Main(string[] args)
{
Console.ReadLine();
for(int i=0;i<10;i++)
{
AppDomain appDomain = AppDomain.CreateDomain("MyTemp");
appDomain.DoCallBack(loadAssembly);
appDomain.DomainUnload += appDomain_DomainUnload;
AppDomain.Unload(appDomain);
}
AppDomain appDomain2 = AppDomain.CreateDomain("MyTemp2");
appDomain2.DoCallBack(loadAssembly);
appDomain2.DomainUnload += appDomain_DomainUnload;
AppDomain.Unload(appDomain2);
GC.Collect();
GC.WaitForPendingFinalizers();
Console.ReadLine();
}
private static void loadAssembly()
{
string fullPath = #"E:\tmp\sandbox\AppDomains\AppDomains1\AppDomains1\bin\Debug\BigLib.dll";
var assembly = Assembly.LoadFrom(fullPath);
var instance = Activator.CreateInstance(assembly.GetTypes()[0]);
Console.WriteLine("Creating instance of {0}", instance.GetType());
Thread.Sleep(2000);
instance = null;
}
private static void appDomain_DomainUnload(object sender, EventArgs e)
{
AppDomain ap = sender as AppDomain;
Console.WriteLine("Unloading {0} AppDomain", ap.FriendlyName);
}
}
I have posted an example where 3 different assemblies are loaded in different app domains and unloaded successfully. Here is the link http://www.softwareinteractions.com/blog/2010/2/7/loading-and-unloading-net-assemblies.html
This is a late answer but would be worthwhile to have it here for any future views to this question. I needed to implement something similar to this but in a dynamic code compilation/execution fashion.
The best would be executing all methods in a separate domain, i.e.: remote domain, other than your main AppDomain, otherwise the app memory will always increase and increase. You can solve this problem via remote interfaces and proxies.
So you would expose your methods through an interface which you will get an instance of in your main AppDomain and then remotely execute those methods in the remote domain, unload the newly created domain (remote domain), nullify it and then force the GC to collect unused objects. I spent quite a long time debugging my code until I reliazed that I have to force the GC to do so and it works just fine. Bulk of my implementation is taken from: http://www.west-wind.com/presentations/dynamicCode/DynamicCode.htm.
//pseudo code
object ExecuteCodeDynamically(string code)
{
Create AppDomain my_app
src_code = "using System; using System.Reflection; using RemoteLoader;
namespace MyNameSpace{
public class MyClass:MarshalByRefObject, IRemoteIterface
{
public object Invoke(string local_method, object[] parameters)
{
return this.GetType().InvokeMember(local_method, BindingFlags.InvokeMethod, null, this, parameters);
}
public object ExecuteDynamicCode(params object[] parameters)
{
" + code + } } } ";// this whole big string is the remote application
//compile this code which is src_code
//output it as a DLL on the disk rather than in memory with the name e.g.: DynamicHelper.dll. This can be done by playing with the CompileParameters
// create the factory class in the secondary app-domain
RemoteLoader.RemoteLoaderFactory factory =
(RemoteLoader.RemoteLoaderFactory)loAppDomain.CreateInstance("RemoteLoader",
"RemoteLoader.RemoteLoaderFactory").Unwrap();
// with the help of this factory, we can now create a real instance
object loObject = factory.CreateInstance("DynamicHelper.dll", "MyNamespace.MyClass", null);
// *** Cast the object to the remote interface to avoid loading type info
RemoteLoader.IRemoteInterface loRemote = (RemoteLoader.IRemoteInterface)loObject;
if (loObject == null)
{
System.Windows.Forms.MessageBox.Show("Couldn't load class.");
return null;
}
object[] loCodeParms = new object[1];
loCodeParms[0] = "bla bla bla";
try
{
// *** Indirectly call the remote interface
object result = loRemote.Invoke("ExecuteDynamicCode", loCodeParms);// this is the object to return
}
catch (Exception loError)
{
System.Windows.Forms.MessageBox.Show(loError.Message, "Compiler Demo",
System.Windows.Forms.MessageBoxButtons.OK,
System.Windows.Forms.MessageBoxIcon.Information);
return null;
}
loRemote = null;
try { AppDomain.Unload(my_app); }
catch (CannotUnloadAppDomainException ex)
{ String str = ex.Message; }
loAppDomain = null;
GC.Collect();//this will do the trick and free the memory
GC.WaitForPendingFinalizers();
System.IO.File.Delete("ConductorDynamicHelper.dll");
return result;
}
Note that RemoteLoader is another DLL that should be already created and added to both you main App and your remote App. It's basically an interface and a factory loader. The following code is taken from the above website:
/// <summary>
/// Interface that can be run over the remote AppDomain boundary.
/// </summary>
public interface IRemoteInterface
{
object Invoke(string lcMethod,object[] Parameters);
}
naemspace RemoteLoader{
/// <summary>
/// Factory class to create objects exposing IRemoteInterface
/// </summary>
public class RemoteLoaderFactory : MarshalByRefObject
{
private const BindingFlags bfi = BindingFlags.Instance | BindingFlags.Public | BindingFlags.CreateInstance;
public RemoteLoaderFactory() {}
/// <summary> Factory method to create an instance of the type whose name is specified,
/// using the named assembly file and the constructor that best matches the specified parameters. </summary>
/// <param name="assemblyFile"> The name of a file that contains an assembly where the type named typeName is sought. </param>
/// <param name="typeName"> The name of the preferred type. </param>
/// <param name="constructArgs"> An array of arguments that match in number, order, and type the parameters of the constructor to invoke, or null for default constructor. </param>
/// <returns> The return value is the created object represented as ILiveInterface. </returns>
public IRemoteInterface Create( string assemblyFile, string typeName, object[] constructArgs )
{
return (IRemoteInterface) Activator.CreateInstanceFrom(
assemblyFile, typeName, false, bfi, null, constructArgs,
null, null, null ).Unwrap();
}
}
}
Hope this makes sense and helps...
.Net uses non-deterministic finalization. If you want to see if the memory drops you should do ...
GC.Collect();
GC.WaitForPendingFinalizers();
... after the unload. Also unless you have a need to force collection (rather un-likely) you should allow the system to collect on its own. Normally if you feel the need to force collection in production code there is a resource leak typically caused by not calling Dispose on IDisposable objects or for not Releasing unmanaged objects
using (var imdisposable = new IDisposable())
{
}
//
var imdisposable = new IDisposable();
imdisposable.Dispose();
//
Marshal.Release(intPtr);
//
Marshal.ReleaseComObject(comObject);
Each assembly is loaded into the main domain as well. Since you use Assembly instance, your main domain loads this assembly in order to be able to analyze all the types in it.
If you want to prevent loading assembly in both domains - use AppDomain.CreateInstance method.
Actually, combination of above answers pointed me to (I hope) correct answer:
My code is now as follows:
AppDomain newDomain = AppDomain.CreateDomain("newDomain", e, setup);
string fullName = Assembly.GetExecutingAssembly().FullName;
Type loaderType = typeof(AssemblyLoader);
FileStream fs = new FileStream(#"library.dll", FileMode.Open);
byte[] buffer = new byte[(int)fs.Length];
fs.Read(buffer, 0, buffer.Length);
fs.Close();
Assembly domainLoaded = newDomain.Load(buffer);
object loaded = Activator.CreateInstance(domainLoaded.GetTypes()[1]);
AppDomain.Unload(newDomain);
GC.Collect();
GC.WaitForPendingFinalizers();
I can't use AppDomain.CreateInstance, since it requires Assembly.FullName which I don't know - library is loaded dynamically.
Thanks for the help,
Bolek.
you can try this code:
http://www.west-wind.com/presentations/dynamicCode/DynamicCode.htm

Categories