Load and unload Child App Domain Assemblies

B

brianbender

I am trying to load and unload assemblies dynamically and call methods
and properties when loaded into an Appdomain

I can load assemblies all day in the current AppDomain without
references and without interfaces if need be. But try as I may they
will ot unload. I have been working on this problem for weeks. I have
seen other apps using Remoting but I know there has got to be a way to
create a child AppDomain and reference obkect via reflection and uload
the child domain when finished.

My Scenario. I have windows services which are basically libraries
called obviously by a service wrapper. Since the wrapper does the
instansiation, I want to update these dlls without stopping and
restarting my service.

I have created a mockup using a form and a separate dll

Here is the mockup simple dll the form will call:

using System;

namespace TestClass
{
public class Test : MarshalByRefObject
{
public string GetResponse()
{
return "Test Response 1";
}
}
}


Here is the form code:


using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Reflection;

namespace DomainTest
{

public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Button btnLoad;
private System.Windows.Forms.Button btnUnload;
private System.Windows.Forms.Button btnCall;
private System.Windows.Forms.TextBox txtResponse;

private AppDomain _runDomain;
private Assembly _assembly;
private Type _typTest;
Object _objTest;
AppDomainSetup _appSetup;

private System.ComponentModel.Container components = null;

public Form1()
{
InitializeComponent();
_appSetup = new AppDomainSetup();
_appSetup.ApplicationBase =
@"E:\Development\.Net\CSharp\Examples\AppDomain\AppDomainEnvironment";
_runDomain = AppDomain.CreateDomain("MyDomain", null, _appSetup);


}

protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}

#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.btnLoad = new System.Windows.Forms.Button();
this.btnUnload = new System.Windows.Forms.Button();
this.btnCall = new System.Windows.Forms.Button();
this.txtResponse = new System.Windows.Forms.TextBox();
this.SuspendLayout();
//
// btnLoad
//
this.btnLoad.Location = new System.Drawing.Point(30, 35);
this.btnLoad.Name = "btnLoad";
this.btnLoad.Size = new System.Drawing.Size(70, 25);
this.btnLoad.TabIndex = 0;
this.btnLoad.Text = "Load";
this.btnLoad.Click += new System.EventHandler(this.btnLoad_Click);
//
// btnUnload
//
this.btnUnload.Enabled = false;
this.btnUnload.Location = new System.Drawing.Point(300, 35);
this.btnUnload.Name = "btnUnload";
this.btnUnload.Size = new System.Drawing.Size(70, 25);
this.btnUnload.TabIndex = 1;
this.btnUnload.Text = "Unload";
this.btnUnload.Click += new System.EventHandler(this.btnUnload_Click);
//
// btnCall
//
this.btnCall.Enabled = false;
this.btnCall.Location = new System.Drawing.Point(166, 35);
this.btnCall.Name = "btnCall";
this.btnCall.Size = new System.Drawing.Size(70, 25);
this.btnCall.TabIndex = 2;
this.btnCall.Text = "Call";
this.btnCall.Click += new System.EventHandler(this.btnCall_Click);
//
// txtResponse
//
this.txtResponse.Location = new System.Drawing.Point(30, 85);
this.txtResponse.Name = "txtResponse";
this.txtResponse.Size = new System.Drawing.Size(340, 20);
this.txtResponse.TabIndex = 3;
this.txtResponse.Text = "";
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(402, 441);
this.Controls.Add(this.txtResponse);
this.Controls.Add(this.btnCall);
this.Controls.Add(this.btnUnload);
this.Controls.Add(this.btnLoad);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);

}
#endregion


[STAThread]
static void Main()
{
Application.Run(new Form1());
}

private void btnLoad_Click(object sender, System.EventArgs e)
{


_assembly = AppDomain.CurrentDomain.Load("TestClass");
_typTest = _assembly.GetType("TestClass.Test");
_objTest = _runDomain.CreateInstance("TestClass", "TestClass.Test");
System.Diagnostics.Debug.WriteLine(_objTest.GetType());

btnCall.Enabled = _objTest != null;
btnUnload.Enabled = _objTest != null;
btnLoad.Enabled = _objTest == null;
}

private void btnCall_Click(object sender, System.EventArgs e)
{
MethodInfo method = _typTest.GetMethod("GetResponse");
txtResponse.Text = (string) method.Invoke(_objTest, null);
}

private void btnUnload_Click(object sender, System.EventArgs e)
{
AppDomain.Unload(_runDomain);
btnCall.Enabled = false;
btnLoad.Enabled = true;
btnUnload.Enabled = false;
}


}
}


The Results:

The issue is that when calligna method off of the newly created object
I get the following error:

Object does not match target type.

I looked at the actual object type from the object created with this
line of code:

_objTest = _runDomain.CreateInstance("TestClass", "TestClass.Test");

The object is not a TestClass.Test Type object, it is a
System.Runtime.Remoting.ObjectHandle type object.

Soooooooooooooooooooo....

How to get around this? Am I even close here?
 
B

Bennie Haelen

Hi Brian,

You can use the "CreateInstanceAndUnwrap" method on the AppDomain, which
creates a new instance of the specified Type in the app domain. Below is
a sample:


AppDomain newAppdomain = AppDomain.CreateDomain("NewDomain");
AppDomainController controller =
(AppDomainController)newAppdomain.CreateInstanceAndUnwrap(
"AppDomainSample", "AppDomainSample.AppDomainController");

CreateInstanceAndUnwrap will unwrap the handle, and convert it into an
object reference, which you can then cast to the correct Type.

Another thing I noticed: If you want to make sure that you can unload
the assembly successfully, you need to make sure that you do not
reference it in your original AppDomain.

For example, you do a:
_typTest = _assembly.GetType("TestClass.Test");

This will load the Type (and therefore it's assembly) in your original
AppDomain, which is probably NOT what you want.

If you use a controller class in the new AppDomain, you can go
indirectly through this controller, for example:

controller.Load("MyTestAssembly");
controller.Load("MyBadAssembly");

object result = controller.ExecuteMethod(
"MyTestAssembly",
"MyTestAssembly.MyClass",
"Add",
new object[] { 100, 22 });

Hope this helps,

Bennie Haelen


I am trying to load and unload assemblies dynamically and call methods
and properties when loaded into an Appdomain

I can load assemblies all day in the current AppDomain without
references and without interfaces if need be. But try as I may they
will ot unload. I have been working on this problem for weeks. I have
seen other apps using Remoting but I know there has got to be a way to
create a child AppDomain and reference obkect via reflection and uload
the child domain when finished.

My Scenario. I have windows services which are basically libraries
called obviously by a service wrapper. Since the wrapper does the
instansiation, I want to update these dlls without stopping and
restarting my service.

I have created a mockup using a form and a separate dll

Here is the mockup simple dll the form will call:

using System;

namespace TestClass
{
public class Test : MarshalByRefObject
{
public string GetResponse()
{
return "Test Response 1";
}
}
}


Here is the form code:


using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Reflection;

namespace DomainTest
{

public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Button btnLoad;
private System.Windows.Forms.Button btnUnload;
private System.Windows.Forms.Button btnCall;
private System.Windows.Forms.TextBox txtResponse;

private AppDomain _runDomain;
private Assembly _assembly;
private Type _typTest;
Object _objTest;
AppDomainSetup _appSetup;

private System.ComponentModel.Container components = null;

public Form1()
{
InitializeComponent();
_appSetup = new AppDomainSetup();
_appSetup.ApplicationBase =
@"E:\Development\.Net\CSharp\Examples\AppDomain\AppDomainEnvironment";
_runDomain = AppDomain.CreateDomain("MyDomain", null, _appSetup);


}

protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}

#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.btnLoad = new System.Windows.Forms.Button();
this.btnUnload = new System.Windows.Forms.Button();
this.btnCall = new System.Windows.Forms.Button();
this.txtResponse = new System.Windows.Forms.TextBox();
this.SuspendLayout();
//
// btnLoad
//
this.btnLoad.Location = new System.Drawing.Point(30, 35);
this.btnLoad.Name = "btnLoad";
this.btnLoad.Size = new System.Drawing.Size(70, 25);
this.btnLoad.TabIndex = 0;
this.btnLoad.Text = "Load";
this.btnLoad.Click += new System.EventHandler(this.btnLoad_Click);
//
// btnUnload
//
this.btnUnload.Enabled = false;
this.btnUnload.Location = new System.Drawing.Point(300, 35);
this.btnUnload.Name = "btnUnload";
this.btnUnload.Size = new System.Drawing.Size(70, 25);
this.btnUnload.TabIndex = 1;
this.btnUnload.Text = "Unload";
this.btnUnload.Click += new System.EventHandler(this.btnUnload_Click);
//
// btnCall
//
this.btnCall.Enabled = false;
this.btnCall.Location = new System.Drawing.Point(166, 35);
this.btnCall.Name = "btnCall";
this.btnCall.Size = new System.Drawing.Size(70, 25);
this.btnCall.TabIndex = 2;
this.btnCall.Text = "Call";
this.btnCall.Click += new System.EventHandler(this.btnCall_Click);
//
// txtResponse
//
this.txtResponse.Location = new System.Drawing.Point(30, 85);
this.txtResponse.Name = "txtResponse";
this.txtResponse.Size = new System.Drawing.Size(340, 20);
this.txtResponse.TabIndex = 3;
this.txtResponse.Text = "";
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(402, 441);
this.Controls.Add(this.txtResponse);
this.Controls.Add(this.btnCall);
this.Controls.Add(this.btnUnload);
this.Controls.Add(this.btnLoad);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);

}
#endregion


[STAThread]
static void Main()
{
Application.Run(new Form1());
}

private void btnLoad_Click(object sender, System.EventArgs e)
{


_assembly = AppDomain.CurrentDomain.Load("TestClass");
_typTest = _assembly.GetType("TestClass.Test");
_objTest = _runDomain.CreateInstance("TestClass", "TestClass.Test");
System.Diagnostics.Debug.WriteLine(_objTest.GetType());

btnCall.Enabled = _objTest != null;
btnUnload.Enabled = _objTest != null;
btnLoad.Enabled = _objTest == null;
}

private void btnCall_Click(object sender, System.EventArgs e)
{
MethodInfo method = _typTest.GetMethod("GetResponse");
txtResponse.Text = (string) method.Invoke(_objTest, null);
}

private void btnUnload_Click(object sender, System.EventArgs e)
{
AppDomain.Unload(_runDomain);
btnCall.Enabled = false;
btnLoad.Enabled = true;
btnUnload.Enabled = false;
}


}
}


The Results:

The issue is that when calligna method off of the newly created object
I get the following error:

Object does not match target type.

I looked at the actual object type from the object created with this
line of code:

_objTest = _runDomain.CreateInstance("TestClass", "TestClass.Test");

The object is not a TestClass.Test Type object, it is a
System.Runtime.Remoting.ObjectHandle type object.

Soooooooooooooooooooo....

How to get around this? Am I even close here?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top