How to call arbitrary method from another AppDomain - c#

I'm making a plugin manager, which interfaces with third-party code. I would like the plugins to be reloadable at runtime and also access the third-party code (in the default AppDomain) from the plugins.
I tried to use AppDomains, but I haven't found a way to call any method without having to wrap each available method/object.
I've looked at a couple of questions, and for example, this answer, the links in it and this answer gave some insight on how cross-AppDomain communication works.
I have successfully created a proxy class for a single method to be called in the default AppDomain thanks to the second answer, but this would require me to add one for each possible method call that would be called from a plugin.
I also tried to use the code with Action as the passed type, to allow the plugin to pass some code to be executed in the default AppDomain, but it failed with various errors. Here's my last attempt.
Main application:
[Serializable]
public sealed class DelegateWrapper<T1>
{
private Action<T1> _someDelegate;
public Action<T1> SomeDelegate
{
get
{
return _someDelegate;
}
set
{
if (value == null)
_someDelegate = null;
else
_someDelegate = new myDelegateWrapper(value).Invoke;
}
}
private sealed class myDelegateWrapper : MarshalByRefObject
{
public void Invoke(T1 input)
{
_delegate(input);
}
private Action<T1> _delegate;
public myDelegateWrapper(Action<T1> dlgt)
{
_delegate = dlgt;
}
}
}
[Serializable]
public sealed class P
{
public Action Action { get; }
public P(Action action)
{
this.Action = action;
}
}
private static readonly DelegateWrapper<P> PerformWrapper=new DelegateWrapper<P>();
public static void Init()
{
PerformWrapper.SomeDelegate = p => p.Action();
}
Inside the new AppDomain, the Perform method is calling perform.SomeDelegate.
Plugin (also executing in the new AppDomain):
Perform(new P(() =>
{
//Third-party code
}));
This results in an exception saying that it cannot find the plugin's assembly I loaded manually in the plugin AppDomain. I assume it's trying to load it in the default domain as well. Is there a way to get around it?
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.IO.FileNotFoundException: Could not load file or assembly 'Plugin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies
Full stack trace here: https://pastebin.com/xZV7bXeV

Related

System.Runtime.Serialization.SerializationException: Type is not resolved for member

I have written a piece of code that loads DLL-files from disk on demand and then unloads them when a client has finished using them. The idea is that those DLLs are implementing an interface known to the client and the client can use them as plugins.
Everything seemed fine in my first tests until I created a second "plugin". The second plugin is identical with my first test case, yet I get a System.Runtime.Serialization.SerializationException. Below you see my class that I can resolve.
namespace ResolverTest.TestController1
{
using System;
...
[Serializable]
public class TestController1 : IProductController
{
public IProductConfigurationValidationResult ValidateProductConfiguration(ProductConfiguration productConfiguration)
{
var result = new ProductConfigurationValidationResult();
result.Success = true;
return result;
}
public IPerformanceCreationResult CreateProductConfigurationPerformance(ProductConfiguration productConfiguration)
{
throw new System.NotImplementedException();
}
}
}
and here is the class that it cannot resolve.
namespace ResolverTest2.TestController2
{
using System;
...
[Serializable]
public class TestController2 : IProductController
{
public IProductConfigurationValidationResult ValidateProductConfiguration(ProductConfiguration productConfiguration)
{
var result = new ProductConfigurationValidationResult();
result.Success = true;
return result;
}
public IPerformanceCreationResult CreateProductConfigurationPerformance(ProductConfiguration productConfiguration)
{
throw new System.NotImplementedException();
}
}
}
The exact exception I get says:
Unhandled Exception: System.Runtime.Serialization.SerializationException: Type i
s not resolved for member 'ResolverTest2.TestController2.TestController2,Resolve
rTest2.TestController2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
Note that both classes are public, so there is no problem accessing them. My class that handles the loading of the DLLs into an appdomain inherits MarshalByRefObject so that part is also properly defined. It seems like there is something different between TestController1 and TestController2. The .net versions are the same and I even checked the assembly info. They do not have different dependencies than each other and the reference list is exactly the same for both of them. Last but not least, the class that resolves the DLLs is checking that the path passed is correct, otherwise it throws and exception.
What else should I look at?

Dynamic DLL loading reference issue

I am facing with a problem when loading dynamically a library with a shared reference. I understood where the problem could be, but I don't really know how I can fix it.
I will try to explain my problem better, this is my structure:
I have 3 assemblies
DataProviderAssembly
ContractsAppAssembly
AppAssembly
I start saying that I can not currently use MEF.
The "DataProviderAssembly" loads dinamically the "AppAssembly" because it has a type that implements the IApp interface from the "ContractsAssembly".
Following the code below, when I try to call the method OnSignRequest from the App class, I get a MissingMethodException. Seems that the method were not found, but, if I remove the "RestRequest" parameter, everything works fine.
I think that, the dynamic loading of that DLL miss some reference information for the dependent assemblies.
**ContractsAppAssembly**
public interface IApp {
void GetData();
}
public abstract class MainClassBase : IApp {
public virtual void GetData(){
//Do Stuff
}
protected void OnSignRequest(RestRequest request){
//calling sign request event
}
}
public abstract class SecondClassBase : MainClassBase {
// i need this because it overrides other methods i didn't specify here
}
**AppAssembly**
public class App : SecondClassBase {
public override void GetData(){
RestRequest request = new RestRequest();
base.OnSignRequest(request);//<-- this throws a MissingMethodException
}
}
**DataProviderAssembly**
public class DataProvider{
public DataProvider{
string[] appsLibs = Directory.GetFiles(appsFolderPath, "*.dll", SearchOption.TopDirectoryOnly);
foreach (string appLib in appsLibs)
{
Assembly appAssemlby = Assembly.LoadFrom(appLib);
Type appType = providerAssemlby.GetTypes().FirstOrDefault(t => t.GetInterface(typeof(IApp).Name, true) != null && !t.IsAbstract);
if (appType == default(Type))
{
continue;
}
try
{
IApp app = (IApp )Activator.CreateInstance(appType);
initApp(app);
this.apps.Add(app.Name, app);
}catch(Exception ex)
{
}
}
}
}
All the assemblies reference RestSharp.dll, the build structure is the following:
Debug:
DataProviderAssembly.dll
Debug/Apps:
ContractsAppAssembly.dll
AppAssembly.dll
Obviously the DataProviderAssembly.dll has the "probing" option inside of the app.config file.
Debugging this code I cannot reach the "base.OnSignRequest" line of code inside of the App class , but, if I declare virtual the method "OnSignRequest" inside of the MainClassBase, the debugger let me reach that line of code giving me the exception only when I try to do a step on.

How to create a form in C# ONLY if a referenced DLL exists

I developed a Winforms C# application in which there's one single form that requires a referenced dll to work. All the other application forms (including the main one) don't need it. I want to be able to check at program start if the referenced DLL exists, and create/load that specific form only if that is true. How could I do that? In its current state, my application crashes while starting if the dll doesnt exist.
Thanks
Unlike native imports, C# does all loading of dependent assemblies dynamically. It's still possible to have a dependency failure crash the program before any user code is executed, but it is very rare.
In order to call your Main function (usually in Program.cs), the runtime must resolve all types used for class members, as well as types used by Main(string[] args) itself. Types used by a function called from Main (such as your Form constructor) won't be loaded until Main begins executing and reaches that function.
In almost all cases, the exception related to loading the DLL will appear as an InnerException property on a TypeLoadException. Break in the debugger when the TypeLoadException happens, and check the call stack. That will let you know where to add a try/catch... and if it really does happen "while starting", it should tell you what class you need to avoid using inside Program.cs (or wherever your Main lives, if you've moved it)
The TypeLoadException can be caught, and if you do so, there's no hope of using the function that failed to compile, but you can still use your other classes, and even other methods in the same class as the function that couldn't compile. You don't have to play any tricks with explicit loading or assemblies.
Here is an illustration of usage which will cause either the whole class to fail to load (EarlyDependency) or allow the entire class to be usable except for MethodA (LateDependency):
class ClassA : EarlyDependency, IComparable<EarlyDependency>
{
EarlyDependency field;
property EarlyDependency PropertyA { get; set; }
int initialized = new EarlyDependency().Calculate();
int initializedB = LateDependency.LiteralConstant;
static ClassA
{
EarlyDependency localInStaticConstructor;
}
public ClassA()
{
EarlyDependency localInInstanceConstructor;
if (new Random().NextDouble() < .000001) {
try {
// you can't catch inside the function that fails to compile
// because code inside that function can't ever run
UsedByConstructor();
}
catch (TypeLoadException)
{
}
}
}
public EarlyDependency MethodWithReturnType();
public static EarlyDependency StaticMethodWithReturnType();
public void MethodWithParameter(EarlyDependency parameter);
public void UseIt()
{
LateDependency localInNonSpecialMethod;
}
public void Safe()
{
try {
// you can't catch inside the function that fails to compile
// because code inside that function can't ever run
UseIt();
}
catch (TypeLoadException)
{
}
}
public static void UseItSomeMore()
{
LateDependency localInStaticMethod;
}
private void UsedByConstructor()
{
LateDependency localInMethodNamedInConstructor;
}
}
You're going to need to ensure that the referenced DLL exists and then dynamically load a separate assembly that contains the form you want to show. If you try to load the assembly normally then you app will crash in the way you've described.
Try something like this:
if (File.Exists("Referenced.dll")
{
var assembly = Assembly.LoadFile("AssemblyContainingFormThatReferencesReferenced.dll");
var type = assembly.GetType("TheForm");
var form = Activator.CreateInstance(type) as Form;
form.ShowDialog();
}
You could check if the file exists, and if it does, then create/load your form.
if(File.Exists("myLibrary.dll")
{
MyForm frm = new MyForm();
frm.ShowDialog();
}
Of course, you'll have to make sure that you are looking in the proper path.

TypeLoadException is Not Caught by try/catch

I am trying to re-create a TypeLoadException for demonstration purposes, so I have a ridiculously goofy library setup that looks like this:
TestProject --> TheLibrary [1.0]
\-> ProxyForV2 -> TheLibrary [2.0]
TheLibrary version 1 has these relevant interfaces:
public interface IConsistentThing
{
int ConsistentProperty { get; set; }
}
public interface IShrinkingThing
{
int RemovedProperty { get; set; }
}
While version 2 of TheLibrary's interfaces look like:
public interface IConsistentThing
{
int ConsistentProperty { get; set; }
}
public interface IShrinkingThing
{ }
ProxyForV2 has this class which implements the version 2.0 IShrinkingThing:
public class ShrinkingThingImpl : IShrinkingThing
{
public int ConsistentProperty { get; set; }
}
So, in TestProject, I am expecting to cause a TypeLoadException if someone tries to assign a ProxyForV2.ShrinkingThingImpl, since the first version of the interface has a property that is not implemented by the second version. To prove this, I have a unit test which looks like:
[TestMethod]
public void ShrinkingThingBreaks()
{
try
{
IShrinkingThing thing = new ProxyForV2.ShrinkingThingImpl();
Assert.Fail("This should have caused a TypeLoadException");
}
catch (TypeLoadException)
{
// valid
}
}
Here's my problem: this unit test fails. But not due to the my Assert.Fail, as I would expect it to. The test output looks like this:
Test method TestProject.LoadTester.ShrinkingThingBreaks threw exception: System.TypeLoadException: Method 'get_RemovedProperty' in type 'ProxyForV2.ShrinkingThingImpl' from assembly 'ProxyForV2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation..
So a TypeLoadException is being thrown and although the only place it could possibly be thrown is in a try block with a catch (TypeLoadException), the exception refuses to be caught. Beyond that, even if I use a catch-all, the unit test fails with the same error as before:
[TestMethod]
public void ShrinkingThingBreaks()
{
try
{
IShrinkingThing thing = new ProxyForV2.ShrinkingThingImpl();
Assert.Fail("This should have caused a TypeLoadException");
}
catch
{
// valid
}
}
What is going on? Obviously, this is a completely contrived scenario, but I would still like to know what is going on so that this error can be avoided at run time or at least dealt with if it happens (yes, I'm aware the ultimate solution is to make sure all your library versions are the same).
The worst part is that any access to the class at all, such as typeof(ProxyForV2.ConsistentThingImpl) or ProxyForV2.ConsistentThingImpl.SomeStaticFunction() causes this un-catchable TypeLoadException, so it is clear that the problem originates when .NET tries to load the class at all, not from any assignment.
My only idea for mitigating this issue is to try to load the type in a different application domain so that it does not interfere and then do some crazy reflection stuff to see if the interface is compatible with the implementation, but that seems like complete and total overkill.
In summary: Why does it seem impossible to catch this problem in the "normal" way and how can I resolve issues like this at runtime?
The types get loaded before execution begins on the method that uses them. To do this, you need to:
[TestMethod]
public void ShrinkingThingBreaks()
{
try
{
InnerShrinkingThingBreaks();
Assert.Fail("This should have caused a TypeLoadException");
}
catch
{
// valid
}
}
[MethodImpl(MethodImplAttributes.NoInlining)]
private void InnerShrinkingThingBreaks()
{
IShrinkingThing thing = new ProxyForV2.ShrinkingThingImpl();
}

Problem with Order of "Registration" of .NET Classes in a Messaging Scenario

I've seen this problem come up a lot, but never adequately handled, and I haven't seen it on Stack Overflow, so here goes. I wish there were a way to put this shortly and succinctly without lacking clarity, but I can't seem to shorten it, so bear with me...
A good case-study (my current case, of course) to illustrate the problem follows:
I write code for many locations, a Parent Compary (parentco), and several satellite locations (centers). I have two 'Managers', one designed for the parentco, and one designed for the centers (deployed many times). I also have two libraries, one for the centers, and one generic library (that is used at the centers and the parentco), that programs can include to communicate to the appropriate Manager (via TCP). The library for the centers has several classes designed to wrap database tables and other 'Messages' to do other things, and the generic library has a few 'Messages,' too, such as 'end connection,' 'invoke a process,' and others.
The Question:
When the Manager recieves a Message that is defined in the 'generic' library, how can it know which type of message it is? The first-blush solution would be something like this:
namespace generic_library
{
public interface IMessage_Creator
{
public IMessage Create_Message(short id);
}
public interface IMessage
{
short Message_ID { get; }
}
/// <summary>Perhaps a message to kill the current connection</summary>
public class Generic_Message1 : IMessage
{
public short Message_ID { get { return ID; } }
internal const short ID = 1;
}
public static class Message_Handler
{
private static readonly System.Collections.Generic.List<IMessage_Creator> _creators =
new System.Collections.Generic.List<IMessage_Creator>();
public static void Add_Creator(IMessage_Creator creator)
{
_creators.Add(creator);
}
public static IMessage Get_Message(short id)
{
switch (id)
{//the Generic library knows about the generic messages...
case Generic_Message1.ID:
return new Generic_Message1();
}
//no generic message found, search the registered creators.
IMessage ret = null;
foreach (IMessage_Creator creator in _creators)
{
ret = creator.Create_Message(id);
if (ret != null)
{
return ret;
}
}
//null if no creator was found.
return ret;
}
}
}
namespace center
{
public class Center_Creator : generic_library.IMessage_Creator
{
static Center_Creator()
{
generic_library.Message_Handler.Add_Creator(new Center_Creator());
}
public generic_library.IMessage Create_Message(short id)
{
switch (id)
{//The center library knows about center-specific messages
case center_message1.ID:
return new center_message1();
}
//we return null to say, "I don't know about that message id."
return null;
}
}
public class center_message1 : generic_library.IMessage
{
public short Message_ID
{
get { return ID; }
}
internal const short ID = 2;
}
}
A little explanation. As you can see, the center and generic library have their own messages they can handle. The center interface (here represented by namespace center) registers his creator, Center_Creator, in the static constructor so when the Message_Handler gets a message of his type, the creator will be called on to generate the correct message.
The problem with this approach:
You may have already seen the problem here, and that is:
If the class Center_Creator is never accessed at all (one is never created, and a static method is never invoked) by code, which should be the case until a message of that type is recieved, the static constructor, static Center_Creator() is never invoked, so the Message_Handler never knows about this creator.
That's all fine and dandy, but I don't know how to fix it. Many people have suggested using reflection to invoke the Center_Creator Type Initializer, but I don't want to put that burden on every program that uses this library!
What is the Stack Overflow community's suggestion? Please let me know if I can simplify this to help make it more accessible for the community.
EDIT:
The code is for the generic library and the Center Library. As you can see, I will have the same issues with the Parent Company library.
A diagram of the architecture. http://cid-0676bb3c1f8d6777.office.live.com/self.aspx/Public/Manager.jpg
Image.
Let's break this down:
You have an application which is to send & receive certain types of messages.
A message type must be registered before it can be read, however,
You do not register the type until you send a message, but
You want to be able to read a message before you write one.
Clearly the answer is that you are registering your message types at the wrong time.
I would suggest an explicitly called Init() method for message types. This could be done by using reflection to scan the libraries to see would types are defined, or by manually listing them.
your message handlers can be seen as plugins which makes your problem a potential fit for the Managed Extensibility Framework. Since .Net 4 it's also shipped with the .Net framework.
You can find sample introductions to MEF here and here.
I've put together a litte example to show that it's quite simple to use basic MEF functionality (although there is much more you can do with it). First there is a PluginHost class which will host the plugins in its Plugins collection. Then there's a simple interface containing just the property Description and an example implementation of a plugin called ExamplePlugin.
The Plugins collection will be filled by the container.ComposeParts(..) method called in the constructor. All that's required to make that magic happen are the [Export] and [ImportMany] attributes.
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
namespace Playground
{
public class Program
{
static void Main(string[] args)
{
PluginHost host = new PluginHost();
host.PrintListOfPlugins();
Console.ReadKey();
}
}
public class PluginHost
{
[ImportMany]
public IEnumerable<IPlugin> Plugins { get; set; }
public PluginHost()
{
var catalog = new AssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly());
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
}
public void PrintListOfPlugins()
{
foreach (IPlugin plugin in Plugins)
Console.WriteLine(plugin.Description);
}
}
public interface IPlugin
{
string Description { get; }
}
[Export(typeof(IPlugin))]
public class ExamplePlugin : IPlugin
{
#region IPlugin Members
public string Description
{
get { return "I'm an example plugin!"; }
}
#endregion
}
}
UPDATE: You can use so called Catalogs to discover plugins in more than one assembly. For example there is a DirectoryCatalog which gives you all exports found in all assemblies in a given directory.
AppDomain.CurrentDomain.GetAssemblies(); returns an array of all assemblies loaded into the current AppDomain. You could then iterate over that array to create an AggregateCatalog containing an AssemblyCatalog per loaded assembly.
Some ideas:
Use .NET serialization to serialize/deserialize your messages and put them in a class library used by both ends (or even use WCF to handle communication).
Add a custom attribute to your creator classes and populate the creator list using reflection at the first time Get_Message is called ("if (!initialized) FindAndAddCreators();").
Introduce some initialization method in your library that registers all the creator classes.
Try using a factory pattern.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace SO
{
class Program
{
static void Main(string[] args)
{
MessageFactory factory = new MessageFactory();
IMessage msg = factory.CreateObject(1);
IMessage msg2 = factory.CreateObject(2);
}
}
public interface IMessage
{
short Message_ID { get; }
}
public class Generic_Message1 : IMessage
{
public short Message_ID { get { return ID; } }
internal const short ID = 1;
}
public class center_message1 : IMessage
{
public short Message_ID { get { return ID; } }
internal const short ID = 2;
}
public class MessageFactory
{
private Dictionary<short, Type> messageMap = new Dictionary<short, Type>();
public MessageFactory()
{
Type[] messageTypes = Assembly.GetAssembly(typeof(IMessage)).GetTypes();
foreach (Type messageType in messageTypes)
{
if (!typeof(IMessage).IsAssignableFrom(messageType) || messageType == typeof(IMessage))
{
// messageType is not derived from IMessage
continue;
}
IMessage message = (IMessage)Activator.CreateInstance(messageType);
messageMap.Add(message.Message_ID, messageType);
}
}
public IMessage CreateObject(short Message_ID, params object[] args)
{
return (IMessage)Activator.CreateInstance(messageMap[Message_ID], args);
}
}
}
EDIT to answer comment:
If the "generic" library is the one processing the messages, and it has no knowledge of the types of message is is processing, you obviously have to change that.
Either move to a "plug-in" model where your custom message dlls will be loaded from a specific directory on startup of the generic library, or read the custom message dlls from a config file at startup for the generic library.
// Read customMessageDllName and customMessageClassName from your config file
Assembly assembly = Assembly.Load(customMessageDllName);
IMessage customMessage = (IMessage)assembly.CreateInstance(customMessageClassName);
Why not simply use WCF? You'll get ease of development, great support, as well as interoperability with Java.
Gallactic Jello is on the right path. The part he left out is overcoming the problem of the generic library knowing about classes in the center library, which I have further addressed. I've created a sample solution with three projects, the full contents of which I'll spare you. Here is the gist.
Class Library: Generic lib
Contains a Message_Handler, his own IMessage_Creator, definitions of the interfaces, and an IMessage type of his own.
Class Library: Center Lib
Contains an IMessage_Creator, and his own IMessage type.
Application: Application
has a SVM (static void Main()) containing the following lines of code:
Generic_lib.IMessage msg = Generic_lib.Message_Handler.get_message(2); //a Center Message
if (msg is Center_lib.Center_Message)
{
System.Console.WriteLine("got center message");
}
You will be amazed how important the if statement is!!! I'll explain later
Here's the code in the Type Initializer for Generic_lib.Message_Handler:
static Message_Handler()
{
//here, do the registration.
int registered = 0;
System.Reflection.Assembly[] assemblies = System.AppDomain.CurrentDomain.GetAssemblies();
foreach (System.Reflection.Assembly asm in assemblies)
{
System.Type[] types = asm.GetTypes();
foreach (System.Type t in types)
{
System.Type[] interfaces = t.GetInterfaces();
foreach (System.Type i in interfaces)
{
if (i == typeof(IMessage_Creator))
{
System.Reflection.ConstructorInfo[] constructors = t.GetConstructors();
foreach (System.Reflection.ConstructorInfo ctor in constructors)
{
if (ctor.GetParameters().Length == 0)
{
Add_Creator(ctor.Invoke(new object[0]) as IMessage_Creator);
registered++;
}
}
}
}
}
}
System.Diagnostics.Debug.WriteLine("registered " + registered.ToString() + " message creators.");
}
Horrific, isn't it? First, we get all the assemblies in the current domain, and here's where the if statement comes in. If there was no reference to the 'Center__lib' anywhere in the program, the array of Assemblies won't contain Center_lib. You need to be sure that your reference to it is good. Creating a method that is never called that references it is not enough, a using statement is not good enough,
if (msg is Center_lib.Center_Message) ;
is not enough. It has to be a reference that can't be optimized away. The above are all optimized away (even in Debug mode, specifying `don't optimize.'
I hope someone can come up with an even more elegant solution, but this will have to do for now.
Aaron

Categories