I have a class that I am creating within an AppDomain using CreateInstanceAndUnwrap. The class contains a System.Threading.Timer.
The problem that I am having is that when the class is instantiated, the timer's callback method can't seem to see proper values of the class instance.
I have sample code below that illustrates the problem:
Library Class
using System;
using System.Threading;
namespace Library
{
[Serializable]
public class Class1
{
public Class1()
{
Started = false;
_Timer = new Timer(TimerMethod);
}
public bool Started { get; set; }
private readonly Timer _Timer;
private string _Message;
private string _TimerMessage;
public bool Start()
{
Started = true;
_Message = string.Format("Class1 says Started = {0}", Started);
_TimerMessage = "Timer message not set yet";
_Timer.Change(1000, 1000);
return Started;
}
public string GetMessage()
{
// _TimerMessage is never set by TimerMethod when this class is created within an AppDomain
return string.Format("{0}, {1}", _Message, _TimerMessage);
}
public void TimerMethod(object state)
{
// Started is always false here when this class is created within an AppDomain
_TimerMessage = string.Format("Timer says Started = {0} at {1}", Started, DateTime.Now);
}
}
}
Consumer Class
using System;
using System.Windows.Forms;
using Library;
namespace GUI
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
var appDomainSetup = new AppDomainSetup
{
ApplicationName = "GUI",
ApplicationBase = AppDomain.CurrentDomain.BaseDirectory
};
_AppDomain = AppDomain.CreateDomain(appDomainSetup.ApplicationName,
AppDomain.CurrentDomain.Evidence,
appDomainSetup);
_Class1 = _AppDomain.CreateInstanceAndUnwrap("Library", "Library.Class1") as Class1;
}
private readonly AppDomain _AppDomain;
private readonly Class1 _Class1;
private void button1_Click(object sender, EventArgs e)
{
_Class1.Start();
MessageBox.Show(_Class1.GetMessage());
}
private void button2_Click(object sender, EventArgs e)
{
MessageBox.Show(_Class1.GetMessage());
}
}
}
When the code above is run, GetMessage()
always returns:
Class1 says Started = True, Timer message not set yet
However, if I change to the constructor of the form above to create a local instance of Class1,
public Form1()
{
InitializeComponent();
_Class1 = new Class1();
}
GetMessage()
returns the expected message:
Class1 says Started = True, Timer says Started = True at 11/15/2011 12:34:06 PM
I have searched Google, MSDN and SO, but haven't found any information that specifically addresses the combination of AppDomain, Serialization and the System.Threading.Timer. Nor could I find any information on why the TimerCallback could not reference local members of the class that instantiated the Timer.