Use AppDomain to load/unload external assemblies - c#

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

Related

Can't load dll files as an embedded resource to my class library

I'm looking to embed and load dll files into my class library so that it can be contained in one dll.
I have a Class Library called Wraper.
I'm using a Console application called ConsoleApp to run the Wraper Class Library.
class Program
{
static void Main(string[] args)
{
Wallet X = new Wallet();
X.SendPayment("1", "Driver={ODBC Driver 17 for SQL Server};Server=.;Database=Home;Trusted_Connection=yes;");
}
}
I have my dll files in the Wraper project in a folder called EmbeddedAssemblies
I'm wanting to load these files in my project. Here is the code that I have in my Wraper Class Library:
public void SendPayment(string DCode, string ConnectionString)
{
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
...
Console.WriteLine("A break point WILL stop here.");
...
}
// This method does not seem to run. Why????
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
...
Console.WriteLine("A break point WILL NOT stop here.");
...
string baseResourceName = Assembly.GetExecutingAssembly().GetName().Name + "." + new AssemblyName(args.Name).Name;
byte[] assemblyOdbc = null;
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Wraper.EmbeddedAssemblies.System.Data.Odbc.dll"))
{
assemblyOdbc = new Byte[stream.Length];
stream.Read(assemblyOdbc, 0, assemblyOdbc.Length);
}
byte[] assemblyNewton = null;
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Wraper.EmbeddedAssemblies.Newtonsoft.Json.dll"))
{
assemblyNewton = new Byte[stream.Length];
stream.Read(assemblyNewton, 0, assemblyNewton.Length);
}
Console.WriteLine("loaded");
return Assembly.Load(assemblyOdbc, assemblyNewton);
}
I'm not sure why this is not working it builds with no errors however when I put a break point just inside the CurrentDomain_AssemblyResolve method it does not even go to the break point, thus the assembly(s) do not load.
What am I doing wrong?
the code to load the assembley has to be loaded from the Main method in the ConsoleApp? If so is there a work around? I don't has access to that area
Yes. The common workaround is to hook AssemblyResolve in a static constructor of the type that references the assembly. That's early enough becuse:
It initializes the class before the first instance is created or any
static members declared in that class (not its base classes) are
referenced. A static constructor runs before an instance constructor.
In particular the static constructor runs before any of the type's methods are JITted, which is when referenced assemblies need to be available.

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.

what is the difference between AppDomain.CreateInstance and Activator.CreateInstance?

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.

Why is this code (possibly event handler) causing a memory leak in my c# windows service?

In a windows service I know I have a memory leak. How I know is outside the scope of this question but you can see the initial question here
I have a windows service with some classes:
public partial class VLSService : ServiceBase
{
ReportedContentProcess reportedContent;
protected override void OnStart(string[] args)
{
//when a user reports a video
reportedContent = new ReportedContentProcess();
reportedContent.ProcessTimer.Elapsed += new ElapsedEventHandler(ReportedContentTimer_Elapsed);
}
void ReportedContentTimer_Elapsed(object sender, ElapsedEventArgs e)
{
reportedContent = new ReportedContentProcess();
reportedContent.Process();
reportedContent.ProcessReinstated();
}
}
public class ReportedContentProcess : ProcessBase
{
//location on the library
string libraryArchivedFilePath;
string libraryRealHiFilePath;
string libraryRealLoFilePath;
string libraryFlashHiFilePath;
string libraryFlashLoFilePath;
//location on the reported folder
string reportedContentArchivedFilePath;
string reportedContentRealHiFilePath;
string reportedContentRealLoFilePath;
string reportedContentFlashHiFilePath;
string reportedContentFlashLoFilePath;
string reportedContentFolderPath;
static EmailSettings emailSettings;
/// <summary>
/// This process will move reported content out of the 'real' and 'flash' mounted folders on the
/// hard drive/ storeage location so they cannot under any circumstances be got to by any users
/// of the library.
/// </summary>
public ReportedContentProcess(): base(1021)
{
DirectoryInfo arciveFolderPathInfo = new DirectoryInfo(fileSystemReferencesForService.ArchiveDir);
DirectoryInfo contentFolder = arciveFolderPathInfo.Parent;
reportedContentFolderPath = contentFolder.FullName.ToString() + #"\ReportedContent\";
emailSettings = settingsManagerForService.GetEmailSettings();
}
public override void Process()
{
if (!EnumsAndConstants.ApplicationLocks.ReportedContentProcessRunning)
{
EnumsAndConstants.ApplicationLocks.ReportedContentProcessRunning = true;
videosToProcess = contentManagerForService.GetReportedVideos(false);
//get the reportedvideo object for this video
CreateReportedVideoContentFolder();
ReportedVideo reportedVideo;
foreach (Video v in videosToProcess)
{
string flashVideoExt = string.Empty;
if (v.IsAudio)
{
flashVideoExt = ".mp3";
}
else
{
flashVideoExt = ".mp4";
}
//library location of each file for video
libraryArchivedFilePath = fileSystemReferencesForService.ArchiveDir + v.LocalFile;
libraryRealHiFilePath = fileSystemReferencesForService.RealDir + v.Url.ToString() + "_hi.rm";
libraryRealLoFilePath = fileSystemReferencesForService.RealDir + v.Url.ToString() + "_lo.rm";
libraryFlashHiFilePath = fileSystemReferencesForService.FlashDir + v.Url.ToString() + "_hi" + flashVideoExt;
libraryFlashLoFilePath = fileSystemReferencesForService.FlashDir + v.Url.ToString() + "_lo" + flashVideoExt;
//new location for file to go to
reportedContentArchivedFilePath = reportedContentFolderPath + v.LocalFile;
}
}
/// <summary>
/// A base class that holds all the Global objects for any process that operates under the
/// service. This process works with
/// </summary>
public abstract class ProcessBase
{
public Timer processTimer;
public Timer ProcessTimer{get{ return processTimer;}set{processTimer=value;}}
protected SqlConnection connection;
protected VlsContent contentManagerForService;
protected VlsSecurity securityManagerForService;
protected VlsSettings settingsManagerForService;
protected FileSystemReferences fileSystemReferencesForService;
protected List<Video> videosToProcess;
protected ExeReferences exeReferenecesForService;
protected GeneralSettings generalSettingsForService;
public abstract void Process();
//sets up all the common objects
public ProcessBase()
{
connection = new SqlConnection(ConfigurationManager.ConnectionStrings["Db"].ToString());
contentManagerForService = new VlsContent(connection);
settingsManagerForService = new VlsSettings(connection);
securityManagerForService = new VlsSecurity(connection);
fileSystemReferencesForService = settingsManagerForService.GetFileSystemReferences();
exeReferenecesForService = settingsManagerForService.GetExeReferences();
generalSettingsForService = settingsManagerForService.GetGeneralSettings();
}
//This constructor will call the default constructor ^
protected ProcessBase(long intervalArg) : this()
{
processTimer = new Timer(intervalArg);
processTimer.Enabled = true;
}
}
After profiling this code it seems that this is causing a memory leak. What im wondering is why?
Im thinking that the problematic line is:
reportedContent = new ReportedContentProcess(); [located in the event handler]
But I cant really see why. Surely it will creat pointer in memory called 'reportedContent' then when the above is called it will place actual value on the heap with new values for the members of ReportedContentProcess(). Then when the event handler is run again after about 1 second it will then just replace the GC root pointer 'reportedContent' with a new allocated heap item for the ReportedContentProcess() class. Then the old one (and all of its now abandoned child objects will be garbaged collected as their root is no longer referenced by the call stack..? This should just happen over and over again (out with the old in with the new) style.
Hope some can help I sort of hope this is the problem so I can fix it but want to check before I start re-factoring code.
The profile is here:
I don't know what ProcessBase is but I am thinking it is where your problem is.
Does it deal with anything outside of the .NET/C# environment? Should it implement an IDisposable interface and should the existing ReportedProcess be disposed before a new one is created.
Also, you create an event handler to the initial ReportedProcess in OnStart(). You then lose a reference to this with the first event by assigning a new instance to its reference, however the initial ReportedProcess is kept alive by the fact that it has a contained object that has a subscribed event handler, it will not be garbage collected until the end of the programme and is technically a memory leak.
If somewhere in the ReportedProcess class you apply the same strategy with regards to cleaning up event handlers then there is your problem.
** UPDATED ANSWER **
Just looking at your updated code, it's missing ProcessReinstated() code, but I think it maybe the fact that you do not explicitly Close your SqlConnection. Your ProcessBase should implement an IDisposable interface and your timer code should explicitly dispose of existing ReportedProcess classes before instantiating new ones, this is the best strategy with things like Database connections and sockets and other external connections.
It doesn’t seem to have a memory leak. Do you have any code similar to this:
reportedContent.ProcessTimer.Elapsed += new ElapsedEventHandler(ReportedContentTimer_Elapsed);
If you have then you have a reference to all of the reportedContect that you have created and GC doesn't release them.

Categories