I am having a problem with Marshalling an object across application domains in a .NET Windows Service.
I have created two app's that marshal an object across an app domain and run code through a proxy MarshalByRefObject
The first application was a simple proof of concept since I do not have a lot of experience with marshalling across application domains. It contains a single project where the MarshalByRefObject is defined in the same namespace as the rest of the project. This app works fine, using almost the same code as my other app. The main difference is that the class I am Marshalling in the second app is defined in another Namespace.
The other project is more complex, it is a Windows Service with multiple projects. The main Windows Service loads a library that that does the marshalling. The class type of the Marshal target type is defined in another library, so I use the fully qualified namespace/class name.
The problem I am facing is that when it gets to the last line of the code below it throws an exception:
Could not load CompanyName.ProductGroup.BusinessObjects.ProductName.MarshalByRefScriptCompiler from assembly ProductNameService where product name is the main Windows Service class.
The Code:
AppDomain compilerDomain = null;
AppDomainSetup compilerDomainSetup;
CompanyName.ProductGroup.BusinessObjects.ProductName.MarshalByRefScriptCompiler scriptCompiler;
...
// Setup a seperate AppDomain
compilerDomainSetup = new AppDomainSetup();
exeAssembly = Assembly.GetEntryAssembly().FullName;
compilerDomainSetup.ApplicationBase = System.Environment.CurrentDirectory;
compilerDomainSetup.DisallowBindingRedirects = false;
compilerDomainSetup.DisallowCodeDownload = true;
compilerDomainSetup.ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
compilerDomain = AppDomain.CreateDomain("LiveLinkCSScriptDomain", null, compilerDomainSetup);
// Create an instance of the MarshalByRefScriptCompiler in the other AppDomain
scriptCompiler = (CompanyName.ProductGroup.BusinessObjects.ProductName.MarshalByRefScriptCompiler)compilerDomain.CreateInstanceAndUnwrap(exeAssembly, typeof(CompanyName.ProductGroup.BusinessObjects.ProductName.MarshalByRefScriptCompiler).FullName);
I have done research on this exception and almost everything I find says that it is a problem with versioning of the DLL, however my DLL's are not in the GAC, and there are no other versions installed. I am doing a clean build and installing the service with installutil.
I used the MSDN documentation as a guide for creating the code that does the marshalling
I am wondering if there is an issue with loading the MarshalByRefScriptCompiler because the type is in another library. I am able to create a MarshalByRefScriptCompiler in a simple winforms app, but I get the exception in my windows service.
Any tips or insight would be greatly appreciated!
I should be able to help you. I've spent many hours recently (How do I pass references as method parameters across AppDomains?) working on different cross appdomain marshaling problems. My first suggestion would be to try using CreateInstanceFromAndUnwrap instead of CreateInstanceAndUnwrap.
I'm also a little wary of this line:
compilerDomainSetup.ApplicationBase = System.Environment.CurrentDirectory;
How is your original AppDomain being created? Are you hosted in IIS, in which case your original AppDomain will be using ShadowCopy? Are all the dlls involved in a single folder?
EDIT:
To summarize, you can use CreateInstanceAndUnwrap if your compilerDomainSetup.ApplicationBase is set to the directory containing your dll and you pass in the correct first param (e.g. typeof(MarshalByRefScriptCompiler).Assembly.FullName).
Or, you can use CreateInstanceFromAndUnwrap and just pass in the location (e.g. typeof(MarshalByRefScriptCompiler).Assembly.Location) of the containing assembly as the first param.
As a starting point, you could try Process Monitor to determine where it's trying to load your missing type from. It's very possible that it's as simple as it looking in the wrong directory for your assembly.
Related
Recently i discover that running few instances of method compiled to .exe is faster than running the same method in f.e. few new Tasks. I don't know if that applies to every method, but it does to getting data from API.
I was searching internet to find answer how to manage that. I got answers to try run method in new appDomains. So i create .exe assembly with methods that i want to be run (it is Console application). I Load it by right click on References -> Add Reference. I could easily access that method by exeName.ClassName.Method(params). The thing is that I don't know how to run this methods in new appDomains. Every answer that i found in web was with loaded assembly by path.
I will also be very happy for answers other than creating AppDomain. I just want to pass data to this method and get results.
TL;DR: Method run in Parallel.For(0,4,i=> method()) works slower than run the same method in 4 instances of compiled .exe file.
You could use a multi process architecture using IPC protocol or host your methods inside different domains. In both situations i recommend .net remoting over wcf because you would write almost the same code for both aproaches and because for talking to a class found in another app domain hosted in the same process, .net remoting is the only way (sadly for many devs but not for me). BUT I am almost sure that generally this would NOT be more faster than just creating some threads and calling them asinchronous. Inter domains / process communication must rely on message serialization/ deserialization that adds huge overhead, specially if the method call itself is very light.
After some researching and asking i found solution:
var ad = AppDomain.CreateDomain("mydomain");
ad.DoCallBack(() =>
{
//stuff to do
}
Probably there'll be some issues with passing data to new AppDomain. Easiest way for me is:
ad.SetData("key", value);
and to retrive in AppDomain:
var value = (valueType)AppDomain.CurrentDomain.GetData("key");
I am little confused about usage of Application class and AppDomain class.
For example Application.StartupPath is equal to AppDomain.CurrentDomain.BaseDirectory
I usually used Application class and recently discovered AppDomain - Can someone explain to me AppDomain class and its usage?
They have nothing in common, really.
Application is a class specific to Windows Forms, a .NET GUI technology. The Application.StartupPath is handled by the Kernel32 function GetModuleFileName. Through not passing a pointer to a module, the main module's path is returned - that is the exe file, basically.
AppDomain is a core .NET concept for domain isolation. Basically, it allows you to isolate (imperfectly of course) multiple applications in a single native process. Most applications only have a single AppDomain, but you can create as many as you like. The base path of an application domain is handled by Fusion, a .NET assembly loading technology. A very typical example would be ASP.NET applications hosted in IIS - each application has its own AppDomain, but they're all hosted in a single native process ("application pool"). Each logical application can be restarted without touching the others, and they don't have (simple) access to each other, but a process-tearing exception (e.g. StackOverflowException) will still kill the whole pool.
Another interesting class that's somewhat related is Environment. You can use Environment.CommandLine to get the process command line (which includes the path to the executable, including the name of the executable). This is basically a communication interface between the CLR and the underlying system - in this case, it takes care of saving the arguments for the application (which are passed by the OS to the Main function) and making them available at any time in the future.
Environment.CommandLine is somewhat clunky to parse (it's the raw command-line, basically - I assume it will have different conventions on Windows than on Linux, for example), but it's the only way you can always get to the executable. Again, Application.StartupPath is Winforms specific, and you can have more than one AppDomain - and possibly, the AppDomain might not even have a reasonable BaseDirectory.
The .NET Reflection APIs also give you a few ways. For example, Assembly.GetEntryAssembly() will give you the executable assembly - however, this only works for the main AppDomain - other domains will have their own entry assemblies (in fact, they'll usually just return null :)). You can get the path to an assembly through the Assembly.CodeBase property, but do note that this might not always be what you expect. You can also use Assembly.Location, or get the FullyQualifiedName of any of the assembly's modules (again, most assemblies only have a single module; and again, ASP.NET is one of the prime examples of when this isn't the case).
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.
I have a 3rd party COM object(32 bit) that I need to call from my c# application (64 bit).
I know I have to run the COM object in a separate process.
This COM object has many classes implemented in it, so I'm trying to avoid writing my own remoting wrapper that exposes all the methods. COM+ seems to be the most straightforward solution. I opened the Component Services menu, created a new COM+ Application, added my COM object as a component to this application. Everything seemed to import beautifully.
In my C# application, I added the original COM object as a reference (which automatically generates the type library). Using the type library reference, I can create objects from from the COM+ component (I see them begin to spin in the Component Services window), but when I try to access on of the methods of the object, I get an error saying the interface is not registered.
Does anyone have a clue? I went back and ran regsvr32 on the COM object, but I don't think it was necessary, and it didn't help.
Is my usage in C# correct? VS2008 autocomplete had no problem seeing those methods.
The exact exception is:
"Interface not registered (Exception from HRESULT:0x80040155)"
Unclear about exactly what the permissions and roles are about in the Component Services, I tried setting up the COM+ object identity to run under the System Account, both as a local service and as interactive user. I've added Everyone as a user in the Roles.
Everything is running locally, so there shouldn't be an issue with file privileges or anything like that.
I also want to reiterate that this COM object contains many classes. I successfully instantiated one class object in my client and set some property values.
I also successfully instantiated another class object, but received this exception when attempting to call a method of this second object .... so I don't think there is an issue with which registry my COM object is registered in.
We had a similar situation, working with a COM dll from VFP.
It all depends on rights and permissions, like Yahia says.
We got it working by doing this:
Install VFP oledb 9 drivers (dunno what you have so probably not required).
give Network Service IIS_IUSR full control on the COM folder (required so the DLL can do some logging in its own folder, when called from the website).
run regsvr32.exe "c:\xxx\yourfile.dll" -> this should be successful!
Create COM+ application, and add the DLL as a part
Set the application COM+ credentials on a user wigh sufficient rights
and we had to do some more settings on rights in application pool / IIS, but thats not required for you I guess.
Anyways, just make sure you have enough logging, make sure the dll is registered, and after that its all about rights rights rights..
Good luck with it!
Sorry to use the "Answer" to respond to comments, but it seems to be my only avenue.
The whole purpose of moving to a 64bit operating system was to gain the extra addressable memory space, so running the entire application in 32bit mode is not an option.
It might be relevant to the problem that after successfully creating three class objects, I was able to set properties in one, call a method with no arguments in the second, but it was calling a method in the third, which took the other two objects as arguments that threw the exception.
I have a program which needs to use a large number of plugins.
Each plugin must support a very basic interface, this interface is defined in a DLL (IBaseComponent for simplicity of question sake).
Each plugin will be in a specific directory (AppDirectory\plugin\plugin-type).
Each plugin can have any name for the plugin's dll (AppDirectory\plugin\plugin-type\plugin-name.dll).
So I need to check through each plugin subdirectory, find each plugin that has a class which supports the IBaseComponent interface, instantiate the class and call some functions on the plug in.
ok, all fine and dandy, none of this is particularly hard. The problem though is that I seem to be running into some weird issues.
Every plugin needs to have the Base.dll file in the individual plugin folders (instead of just in the program that will be loading the plugin) and also it seems that I get many errors and warnings around dynamically loading a dll which have dll's that also need to be loaded.
I'm using:
pluginModule = System.Reflection.Assembly.ReflectionOnlyLoadFrom(PathToAssembly);
in order to grab the plugin dll and using:
types = moduleAssembly.GetTypes();
in order to grab the types contained within the dll.
I am iterating over the types and checking if the individual type is of the IBaseComponent interface (signifying this is a valid to load class) with:
if (type.GetInterface("FrameworkNameSpace.IBaseComponent") != null)
//it's of the IBaseComponent interface
Later, in order to actually create an instance of the class from the dll I use:
pluginModule = System.Reflection.Assembly.LoadFrom(PathToAssembly);
and then use:
types = component.GetTypes();
In order to get the types within the module then select and load the class which supports the interface same as above.
The problem seems to be on when I use:
types = component.GetTypes();
When actually trying to load the class, Instead of simply looking at it. (Hence my different usage of LoadFrom and ReflectionOnlyLoad)
The Exception I receive on the GetTypes call (on a second plug in, but never the first!) is:
{"Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information."}
With the LoaderExceptions property as:
{"The specified module could not be found. (Exception from HRESULT: 0x8007007E)":null}
I'm unsure why this occurs. The DLL is in the plugin folder, and the DLL containing the IBaseComponent interface is also in every one of the plugin directories. Am I going about this the wrong way?
Also is it required for me to keep a copy of the DLL containing IBaseComponent within each plugin subdirectory as well as the one used by the program itself, or am I doing something incorrectly that would allow me to remove this requirement?
I am aware of MEF which is what I wanted to use but unfortunately because I am required to support this on .net 2.0 I am unable to use MEF.
This is a LoadLibrary() failure. Sounds to me that you plugins have a dependency on some unmanaged DLLs. Yes, Windows is going to have a hard time finding these DLLs, it has no reason to look in the plugin directories. Maybe it works the first time because your app's default directory happens to be set to the correct directory. Which would then also be the workaround, use Environment.CurrentDirectory.
Finding out exactly which dependencies can not be found is key. Unmanaged ones are not going to show up in fuslogvw.exe, you can dig it out of a trace from SysInternals' ProcMon utility.