I have 2 problems handling events across appdomains. I have my main appdomain and a new appdomain that loads a plugin, in total 1 exe and 2 dll's. Calling methods in the loaded plugin from the main domain works, but I am not able to receive an event generated in the plugin into the main appdomain. My event receiving method is however being in the context of the new appdomain (that must have loaded it I guess... I am confused here). I have worked based on the solution provided here C# Dynamic Loading/Unloading of DLLs Redux (using AppDomain, of course) as my final program is to be loading and unloading plugins on the fly. Here is the code so far:
This is DynamicLoadText.exe:
namespace DynamicLoadTest
{
class Program
{
static void EventReceiver(object sender, EventArgs e)
{
Console.WriteLine("[{1}] Received an event from {0}", sender.ToString(), AppDomain.CurrentDomain.FriendlyName);
}
static void Main(string[] args)
{
var appDir = AppDomain.CurrentDomain.BaseDirectory;
var appDomainSetup = new AppDomainSetup
{
ApplicationName = "",
ShadowCopyFiles = "true",
ApplicationBase = Path.Combine(appDir, "Plugins"),
CachePath = "VSSCache"
};
var apd = AppDomain.CreateDomain("My new app domain", null, appDomainSetup);
var proxy = (MyPluginFactory)apd.CreateInstance("PluginBaseLib", "PluginBaseLib.MyPluginFactory").Unwrap();
var instance = proxy.CreatePlugin("SamplePlugin", "SamplePlugin.MySamplePlugin");
instance.MyEvent += EventReceiver;
instance.TestEvent();
instance.MyEvent -= EventReceiver;
AppDomain.Unload(apd);
}
}
}
This is PluginBaseLib.dll:
namespace PluginBaseLib
{
public abstract class MyPluginBase : MarshalByRefObject
{
public abstract event EventHandler MyEvent;
protected MyPluginBase()
{ }
public abstract void TestEvent();
}
public class MyPluginFactory : MarshalByRefObject
{
public MyPluginBase CreatePlugin(string assemblyName, string typeName)
{
return (MyPluginBase)Activator.CreateInstance(assemblyName, typeName).Unwrap();
}
}
}
And this is SamplePlugin.dll:
namespace SamplePlugin
{
public class MySamplePlugin : MyPluginBase
{
public override event EventHandler MyEvent;
public MySamplePlugin()
{ }
public override void TestEvent()
{
if (MyEvent != null)
MyEvent(this, new EventArgs());
}
}
}
The file-placement prior to execution of the DynamicLoadTest.exe is this:
dir\DynamicLoadText.exe
dir\PluginBaseLib.dll
dir\Plugins\PluginBaseLib.dll
dir\Plugins\SamplePlugin.dll
PluginBaseLib is present twice as both DynamicLoadText.exe and SamplePlugin.dll depend on it.
When I start the exe, all goes well until it hits the "+=" at which point I get a TargetInvocationException (InnerException: FileNotFoundException):
{"Could not load file or assembly 'DynamicLoadTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.":"DynamicLoadTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"}
I tried to move SamplePlugin.dll to the same directory as DynamicLoadTest.exe and changed the AppDomainSetup.ApplicationBase to point at the base directory and run again. This made it all run without throwing exceptions and I even got en event:
[My new app domain] Received an event from SamplePlugin.MySamplePlugin
I am now concerned that my EventReceiver that received the event isn't the one in the main appdomain, but is a clone running in the new appdomain (the one that holds the plugin).
So, how to I make this just right, i.e. having the plugin dll's be in the Plugins directory and receiving the event in the main appdomain's EventReceiver method?
Edit:
I have tried to get more insight into this but failed. The only way I can subscribe to the event in the secondary appdomain is when the secondary appdomain loads the primary appdomain (which then exists twice). I have checked it using a statically assigned key in the primary appdomain which I change during runtime. The change is only seen from the primary appdomain. The key is the statically assigned value when seen from the event handler when triggered by the secondary appdomain. This is clearly not what I want.
I have found no guide or explanation (I could understand) explaining if what I try is possible or not.