Marshalling Assembly from another AppDomain - c#

Is it possible to hold a reference to an Assembly from another appdomain without having that assembly loaded into the current appdomain?
I'm working on fixing a memory leak in a Windows Service that dynamically generates Assemblies and runs the dynamically generated code. The problem is the generated Assemblies are loaded into the Current app domain and can never be unloaded.
There is a method in one of the Windows Service libraries that has the follow signature:
public Assembly CreateMethod(ObservableCollection<Field> sourceFields, Field destinationField)
This method creates the code for the assembly and loads it with the CSScript library LoadMethod function:
result = CSScript.LoadMethod(scriptFunction.ToString());
Later this Assembly reference from CreateMethod is used to run a function inside the generated assembly.
public object Run(Field destinationField, ObservableCollection<LinkField> sourceLinkFields, DataRow mainRow, Assembly script) {
...
var method = script.GetStaticMethodWithArgs("*.a" + Id.ToString().Replace("-", String.Empty), argumentTypes.ToArray());
return method(arguments.ToArray());
}
I'm wondering if it is possible to load the dynamically generated assembly into another app domain and run them through some type of proxy without having it loaded into the current app domain.
Edit:
I want to know if I can use an Assembly class reference in one AppDomain when the assembly is loaded in another AppDomain. Looking at the MSDN documentation they show how to use MarshalByRefObject. Basically I am trying to avoid changing the signature to my CreateMethod function, however I may need to change it to return MarshalByRefObject if this is not possible.
Update:
I ended up putting the call to CSScript.LoadMethod in the other app domain where I keep a Dictionary I then made CreateMethod return a Guid instead of an Assembly and then I pass this Guid around until the Run call. The Run call now takes a Guid as an argument instead of an Assembly. Inside the Run call I pass the Guid to the other app domain, run the method, and return the result object through a class that inherits MarshalByRefObject.

If you don't want the dynamic assembly in your main AppDomain, you have to move CreateMethod to another AppDomain, because as soon as you have an instance of Assembly, it's been loaded. In other words, no it is not possible to hold a reference to an assembly in another application domain, only to call into that assembly across application domains.
Without changing the signature and a bunch of your code, it seems like you need to move the minimum amount: 1) assembly creation and 2) Run. Then have the implementation of Run marshall the results.
As far as CreateMethod I think you want a method in the other assembly to "wrap" CreateMethod and return some sort of token that can be passed to Run. It's almost like changing the signature in a way...

This is one of the major features of an AppDomain! Just go look at the documentation

Related

C# DLL Injection - Call Method of Running Application

Lets say I have an executable 'foobar.exe' written in C# and now compiled, running on a Windows box. One of the functions in the application is the following (example):
public static async Task LoadBox(string msg)
{
System.Windows.Forms.MessageBox.Show(msg);
}
I would like to write a DLL in C# that calls this method in the application. The DLL, say 'injected.dll', will be injected into the running 'foobar.exe' process using the injector referenced here: http://www.codingvision.net/miscellaneous/c-inject-a-dll-into-a-process-w-createremotethread
Is it possible from the newly injected DLL to call the public function in the original exe? If so, any example code in C# would be appreciated.
My approach would be to use the concepts of Reflection. We could make the EXE load an assembly through reflection, discover a Type and invoke a method on this Type and then pass an instance of a class in your EXE to this method, which in turn does a call back. A round about way - but it would work.
You can use the method System.Reflection.Assembly.LoadFrom( to load an assembly compiled for .NET. This is a fairly old technique, nothing new about it. Dependency contains and applications that are meant to load plugins post deployment are written using this method.
Step 1
Load the plugin assembly into the current Appdomain using System.Reflection.LoadFrom
Step 2
Find the Type in this plugin assembly by using Assembly.GetTypes()
Step 3
Pass an instance of a class defined in your EXE into the plugin and have the plugin do a call back. This is what an event call back would actually do.
Link
https://learn.microsoft.com/en-us/dotnet/api/system.activator.createinstance?view=netframework-4.7.2#System_Activator_CreateInstance_System_AppDomain_System_String_System_String_System_Boolean_System_Reflection_BindingFlags_System_Reflection_Binder_System_Object___System_Globalization_CultureInfo_System_Object___

Limit memory usage using CSharpScript interface

I have an application where I'm using a high number of CSharpScript instances to control the application. The problem I'm facing is that the memory usage depends heavily on what type of global object I'm using. If the global type is defined in my application assembly (huge assembly) the memory usage rises by about ~100MB for every compiled script. If I instead put the global type in a separate assembly I find the memory usage to rise by ~10MB for each script which is still a lot for the amount of scripts I'm using.
Is there any way to limit this memory usage when using the CSharpScript API?
What I believe is happening here is that your script directly references an object defined in the main application. Since the script is compiled in to a separate AppDomain, this causes that AppDomain to pull in its own local copy of things from the AppDomain of the main application. Since you have 100s of scripts, each in their own AppDomain your main assembly gets loaded 100s of times.
One way to avoid this is to have any interfaces between the scripts and the application pass through a "shim" object, which is a small class, defined in its own assembly, that can "serialize" data across that AppDomain boundary. That Shim class should inherit from MarshalByReferenceObject. This can be tricky and complex, because the shim can't pass any of the classes defined in the application, or it will "suck in" the main assembly just like before. Everything must be passed as classes defined in .NET.
The MarshalByReferenceObject as a base class allows the shim to cross the domain boundary without bringing in an entire copy of the assembly. More information can be found at
https://msdn.microsoft.com/en-us/library/system.marshalbyrefobject(v=vs.110).aspx#Remarks
I don't know what is your specific implementation but it might be possible for you to do this:
Instead of giving the data to your scripts using big heavy global object, you can let the script give your application only their logic:
instead of this csx file, with HeavyObject as global:
// script.csx:
HeavyObject.DoSomeWork();
you can write this csx, with no global param needed:
// betterscript.csx:
public class ScriptWork : IHaveWork
{
Work(HeavyType obj)
{
obj.DoSomeWork();
}
}
return new ScriptWork();
you need this interface in your app:
public interface IHaveWork
{
void Work(HeavyType obj);
}
and the script should run with references to your application.
You would receive from your script an instance of IHaveWork, and then you should call Work within your application.

How does Assembly.Load(byte[]) work?

I was just wondering what happens if I was to load the same assembly bytes twice within a web app.
For example I have this code
byte[] assem = System.IO.File.ReadAllBytes(appRoot + "/Plugins/Plugin.dll");
var loadedAssem = Assembly.Load(assem);
var plugin = loadedAssem.CreateInstance("Plugin.ThePlugin") as IPlugin;
I ran this code and on the first request I assume it would load the assembly into ram ( or the http runtime appdomain? ) and then I can create instances of whatever is in there.
If I ran this code again, say on the second request what would happen to the assembly on the first request?
Would is still exist in ram? if so how does it differentiate between the two assemblies? or does it overwrite the previously declare classes?
This is for my understanding, as like I do in PHP its not just a case of "require_once".
This will load two distinct copies of the assembly, each of which can be used from your application. The types in each assembly are distinct types and will not inter-operate with one-another. For instance, if you take a Widget from Copy1 and try to pass it to a method that takes a Widget on Copy2, this will cause a runtime failure. It is not possible to unload assemblies once they have been loaded in this way (i.e. into your main AppDomain.)
Regarding instantiation:
If you use Assembly.CreateInstance (as shown in your post), this will create it from the Assembly instance you used to make the call.
If you use an Activator.CreateInstance that takes a string, you need to specify the assembly name. Since both loaded assemblies will have the same name in this case, it will use assembly resolution rules, which, I think by default, will favor the first match (so the assembly you loaded first.) I'm not certain of this. You can hook the AppDomain.AssemblyResolve event to provide your own prioritization and make it use your most-recently-loaded assembly.

Using AppDomain in C# to dynamically load and unload dll

In one of my application, which is related to system diagnostics, the related DLL is to be loaded and unloaded dynamically in C#. After some search I found that a separate DLL cannot be loaded dynamically its the complete AppDomain. So I have to create an AppDomain and use that DLL to be loaded unloaded dynamically. But I could not find anywhere how can I use that in code. I can not show the app code since it is against company rules.
Can somebody tell me some application code to use it. I want to load and unload the dll dynamically using appdomain and call a specific method in that dll, the dll does not have any entry point.
Thanks for answers.
Ashutosh
How to: Load Assemblies into an Application Domain
public static void Main()
{
// Use the file name to load the assembly into the current
// application domain.
Assembly a = Assembly.Load("example");
// Get the type to use.
Type myType = a.GetType("Example");
// Get the method to call.
MethodInfo myMethod = myType.GetMethod("MethodA");
// Create an instance.
object obj = Activator.CreateInstance(myType);
// Execute the method.
myMethod.Invoke(obj, null);
}
As for how to unload it, you have to unload the AppDomain itself, see this
AppDomain Temporary = AppDomain.CreateDomain("Temporary");
try
{
Gateway Proxy =
(Gateway) Temporary.CreateInstanceAndUnwrap("Shim", "Shim.Gateway");
Match M = Proxy.LoadAndMatch("Plugin.dll",
"Though the tough cough and hiccough, plough them through");
}
finally
{
AppDomain.Unload(Temporary);
}
It's difficult to understand your question, but I will try to make some suggestions.
There is no reason you cannot dynamically Load a dll directly into your application w/o a separate App Domain, the trick is that you cannot unload it. This is only important if you may load multiple versions of the same dll (i.e. you want the ability to update this diagnostic component to a new version without halting the execution of your application). If that is what you are trying to do, then I suggest this CodeProject article.
Actually you can dynamically load assemblies into your app domain and run code from it, the issue is that you cannot then unload the assembly. You can however load additional app domains (and assemblies into them) and unload the app domain when you are done.
As its name suggests though, you then have a new application domain, and you can't just simply call its code and use its types you need to marshal your calls and data across the domain boundaries. If you search you will find lots of examples on how to do this.
Something to consider though, is that this is a common pattern, and there are ready made solutions for it, the framework itself has a whole addin namespace that is dedicated to this type of plug-in behavior, it might be worth your while in having a close look at that first. There is an article here that shows how to use it.
Thanks guys, here is link where i found answer to my quetion:
The MSDN forum description for load and unload of assemblies dynamically
The other dll can be dynamically loaded and unloaded using another class which does load assembly and and call methods in that assembly...
AppDomain.CreateInstanceAndUnwrap generally wants input as assemblies from current project or generally current namespace. to solve that i need Assembly.LoadFrom(); to be used in some other class and create AppDomain and create instance of this class using AppDomain object as given in link.
Thanks for ur replies guys.

Pass and execute delegate in separate AppDomain

I want to exceute some piece of code in separate AppDomain with delegate. How can I do this?
UPD1: some more details about my problem
My program processing some data (one iteration is: get some data from DB, evaluate it and create assemblies at runtime, execute dynamic assemblies and write results to DB).
Current solution: each iteration running in separate thread.
Better solution: each iteration running in separate AppDomain (to unload dynamic asseblies).
UPD2: All, thanks for answers.
I have found one for me in this thread:
Replacing Process.Start with AppDomains
Although you can make a call into a delegate which will be handled by a separate AppDomain, I personally have always used the 'CreateInstanceAndUnwrap' method which creates an object in the foreign app domain and returns a proxy to it.
For this to work your object has to inherit from MarshalByRefObject.
Here is an example:
public interface IRuntime
{
bool Run(RuntimesetupInfo setupInfo);
}
// The runtime class derives from MarshalByRefObject, so that a proxy can be returned
// across an AppDomain boundary.
public class Runtime : MarshalByRefObject, IRuntime
{
public bool Run(RuntimeSetupInfo setupInfo)
{
// your code here
}
}
// Sample code follows here to create the appdomain, set startup params
// for the appdomain, create an object in it, and execute a method
try
{
// Construct and initialize settings for a second AppDomain.
AppDomainSetup domainSetup = new AppDomainSetup()
{
ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase,
ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile,
ApplicationName = AppDomain.CurrentDomain.SetupInformation.ApplicationName,
LoaderOptimization = LoaderOptimization.MultiDomainHost
};
// Create the child AppDomain used for the service tool at runtime.
childDomain = AppDomain.CreateDomain(
"Your Child AppDomain", null, domainSetup);
// Create an instance of the runtime in the second AppDomain.
// A proxy to the object is returned.
IRuntime runtime = (IRuntime)childDomain.CreateInstanceAndUnwrap(
typeof(Runtime).Assembly.FullName, typeof(Runtime).FullName);
// start the runtime. call will marshal into the child runtime appdomain
return runtime.Run(setupInfo);
}
finally
{
// runtime has exited, finish off by unloading the runtime appdomain
if(childDomain != null) AppDomain.Unload(childDomain);
}
In the above sample, it is coded to execute a 'Run' method passing in some setup information, and completion of the Run method is determined to indicate that all code in the child AppDomain has completed running, so we have a finally block that ensures the AppDomain is unloaded.
You often may want to be careful in which types you place in which assemblies - you may want to use an interface and place it in a separate assembly that both the caller (our code that sets up the appdomain, and calls into it) and the implementer (the Runtime class) are dependent on. This IIRC allows the parent AppDomain to only load the assembly that contains the interface, while the child appdomain will load both the assembly that contains Runtime and it's dependency (the IRuntime assembly). Any user defined types that are used by the IRuntime interface (e.g. our RuntimeSetupInfo class) should usually also be placed in the same assembly as IRuntime. Also, be careful of how you define these user defined types - if they are data transfer objects (as RuntimeSetupInfo probably is), you should probably mark them with the [serializable] attribute - so that a copy of the object is passed (serialized from the parent appdomain to the child). You want to avoid calls being marshalled from one appdomain to another since this is pretty slow. Passing DTOs by value (serialization) means accessing values on the DTO doesn't incur a cross-apartment call (since the child appdomain has it's own copy of the original). Of course, this also means that value changes are not reflected in the parent appdomain's original DTO.
As is coded in the example, the parent appdomain will actually end up loading both the IRuntime and Runtime assemblys but that is because in the call to CreateInstanceAndUnwrap I am using typeof(Runtime) to get the assembly name and fully qualified type name. You could instead hardcode or retrieve these strings from a file - which would decouple the dependency.
There also is a method on AppDomain named 'DoCallBack' which looks like it allows calling a delegate in a foreign AppDomain. However, the delegate type that it takes is of type 'CrossAppDomainDelegate'. The definition of which is:
public delegate void CrossAppDomainDelegate()
So, it won't allow you to pass any data into it. And, since I've never used it, I can't tell you if there are any particular gotchas.
Also, I'd recommend looking into the LoaderOptimization property. What you set this to, can have a significant affect on performance, since some settings of this property force the new appdomain to load separate copies of all assemblies (and JIT them etc.) even if (IIRC) the assembly is in the GAC (i.e. this includes CLR assemblies). This can give you horrible performance if you use a large # of assemblies from your child appdomain. For e.g., I've used WPF from child appdomains which caused huge startup delays for my app until I setup a more appropriate load policy.
In order to execute a delegate on another AppDomain you can use System.AppDomain.DoCallBack(). The linked MSDN page has an excellent example. Note that You can only use delegates of type CrossAppDomainDelegate.
You need to read up on .NET Remoting and specifically on Remote Objects as these are all you can pass through AppDomains.
The long and short of it is that your object is either passed by value or by reference (via a proxy).
By value requires that your object be Serializable. Delegates are not serializable afaik. That means that this is not a good route to follow.
By reference requires that you inherit from MarshalByRefObject. This way, the remoting infrastructure can create the proxy. However, it also means that your delegate will be executed on the machine that creates it - not on the client app domain.
All in all, it's gonna be tricky. You might want to consider making your delegates full fledged serializable objects so that they can be easily moved around with remoting (and will work well with other technologies).
This doesn't answer your question directly but perhaps it would be better to create a WCF service or web service in the other AppDomain to preserve isolation. I don't know your particular situation but isolated architectural design is almost always the right way to go.

Categories

Resources