3

I'm am currently revising a plugin architecture so that it utilizes AppDomains. The main difference between my plugins and the many examples I have found online is that instead of the plugin running and sending its results back to the main application, my main application is the one sending information to the plugin.

As it currently stands, I create an instance of the loader in a separate AppDomain. It then does all your normal initialization such as loading the plugin assembly. As this point, I then use the proxy of the loader to call the methods that send the data from the main application to the new AppDomain.

My problem occurs when I am attempting to call a method which has an argument type that is not serializable, and which does not derive from MarshalByRefObject.

Since these types are from 3rd party code, and the plugins expect to receive them, I cannot seem to find a way do this. After finding this question ( How to solve "Must be MarshalByRefObject" in a good but multiple-inheritance amputated language like C#? ), I was thinking about creating some sort of wrapper. My drawback here is that I simply cant figure out a way to make one without modifying the 3rd party code.

Here is an example of my issue:

// Cant modify this class
class ThirdPartyClass
{
    // The properties are also 3rd party and nonserializable. These are accessed by the plugins.
    public AnotherClass Property1{ get; set; }
    public AnotherClass Property2{ get; set; }

    public ThirdPartyClass(){}
}

class Loader : MarshalByRefObject
{
    private Plugin plugin;

    public Loader()
    {
        Assembly pluginAssembly = Assembly.LoadFrom("Full/Assembly/Path");
        plugin = pluginAssembly.CreateInstance("Full.Plugin.Name") as Plugin;
    }

    public void DoSomething(ThirdPartyClass o)
    {
        ...
        plugin.DoSomethingElse(o);
    }
}

class PluginManager
{
    void Main(ThirdPartyClass o)
    {
        AppDomain pluginAppDomain = AppDomain.CreateDomain("Plugin AppDomain");
        Loader loader = pluginAppDomain.CreateInstanceFromAndUnwrap("Full/Loader/Path", "Full.Loader.Name") as Loader;

        // This is where I would have a problem.
        loader.DoSomething(o);
    }
}

Any help would be greatly appreciated.

Community
  • 1
  • 1
LTAcosta
  • 500
  • 3
  • 9

2 Answers2

2

If these are just instances that are crossing the app domain boundaries, meaning you dont use them in more than that one app domain, you can simply wrap them in a holder class that derives from MarshalByRefObject.

I use a similar approach in IronScheme's remoting code.

The problem is when you actually need to do more than just hold on to the instance. In this case, you can probably use interfaces to hook the needed methods and expose that via the 'holder' class.

leppie
  • 115,091
  • 17
  • 196
  • 297
  • You're right, I did try that initially, and it worked when I wasn't trying to access any methods or properties of the wrapped object. The problem is that the plugins do access the properties. I updated my example slightly to show that. – LTAcosta Jun 28 '12 at 17:52
1

Do you need to have the appdomain interact with the 3rd party object and it reflect back on the other side? If not (and based on your title it sounds like you wouldn't mind them being serializable) then why not serialize them? By that I mean so something similar to:

[Serializable]
class MyThirdPartyClass : ThirdPartyClass, ISerializable
{
    public MyThirdPartyClass()
    {
    }

    protected MyThirdPartyClass(SerializationInfo info, StreamingContext context)
    {
        Property1 = (AnotherClass)info.GetValue("Property1", typeof(AnotherClass));
        Property2 = (AnotherClass)info.GetValue("Property2", typeof(AnotherClass));
    }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("Property1", Property1);
        info.AddValue("Property2", Property2);
    }
}

Of course there are caveats with this approach, firstly it relies on you being able to derive the class and secondly it requires you to manually serialize the data (to a degree). You could automate that using reflection on the internal fields which is realistically the only thing you need to worry about and reconstruct on the other side, effectively manually serializing.

You might have been able to implement a surrogate selector but it seems it isn't exposed to the developer to change the default inter appdomain one :(

tyranid
  • 13,028
  • 1
  • 32
  • 34
  • This seemed promising, but when I tried it, I was running into the same problem as before since the properties are also no serializable. Would it be possible to recursively wrap everything and then unwrap it after moving it across appdomains? I'm starting to think I need to come up with a redesign. This 3rd party code it quite a headache. – LTAcosta Jun 29 '12 at 13:03
  • Indeed you would have to recursively do it. Are the other classes XML serializable perhaps? In which case you might be able to get away with just serializing the top level. – tyranid Jun 29 '12 at 18:18
  • I ended up going with something like this. The other classes weren't serializable, so I just made it wrap them recursively. But I made it so that it would only wrap them if they weren't already serializable, and also only if I had specified their types in an earlier list. This way it only wraps what I need. I'm still working on part of it, but this seems to be working better than what I had before, so hopefully this will last until I can do a redesign. Thanks – LTAcosta Jul 02 '12 at 11:40