I'm implementing a plugin system which allows them to be reloaded without restarting the host process. Everything works flawlessly in .NET 4.5 but when running in Mono 3.2.7 the plugin assembly gets leaked into the host AppDomain and the plugin can not be changed.
The assembly gets loaded into the host AppDomain when calling AppDomain.CreateInstanceFromAndUnwrap to create an instance of a type in the plugin assembly. This shouldn't cause problems because the type inherits MarshalByRefObject and shares a common interface.
You can see my code here: https://github.com/Rohansi/PluginTest The line which causes the assembly to leak is here.
I found a workaround for this issue. I've updated the Github repo with the fixes but I'll sum it up here.
To prevent leaking the assembly into the host AppDomain the assembly must not be in one of the locations the runtime will search for it. You either need to load it in a subdirectory or load it under a different file name. The assembly will no longer be automatically loaded into the host AppDomain but it started crashing when reloading.
I fixed this crash by replacing CreateInstanceFromAndUnwrap calls with this method. The PluginBridge instance is still created with CreateInstanceFromAndUnwrap.
After all this it seems I have working plugin reloading in both .NET and Mono (tested in 3.4).
Related
I've researched a lot on stackoverflow and no one really explained what to do in my case.
I have application that is using a TFS Api, and it is using a Nuget packages that contains dll like Microsoft.TeamFoundation.*.dll.
Here comes the tricky part - When I run application from debug everything is working fine since CLR is loading dlls from my , BUT when I deploy application and start using it, it loads dlls from the GAC if any of those dlls exists in GAC.
This causes numerous errors since it loads different versions e.g. Microsoft.TeamFoundation.1stdll.dll with version 11.2.2302 then Microsoft.TeamFoundation.2nddll.dll with version 11.0.2123 and there are cases when it starts with 10, and then asking for a reference 10 dll to resolve issue and I end up with exception.
What I did?
I've tried to point to probing path with without success. As soon as it finds for example version 11.0.2.1 in GAC and 11.3.1.2 in probing it resolves dll with GAC one.
I've tried as someone explained to create a new appdomain and to share dlls between domains but I've hit the dead end, no matter what I've tried it loaded it from the GAC. I've also tried to load dlls at entry point of my app, and than to redirect it to my path and resolved it. Again no success. I've tried to trick application and at resolving point return null for publicKeyToken in order to tell it that I am using a non-signed dll, in which case it wouldn't look at the GAC, but had no luck. I've tried to remove signing with Nirsoft snremove.exe and guess what? No success either.
I am looking for a code that will no matter what force my application to use my dlls instead of one in GAC. I want to avoid that during runtime it does not randomly pick up a 11.00.xxxx version but instead use my specific 11.92.21212.2 even when it has bigger or in some cases lower versions.
The only thing that I cant accept as a possible solution is to manually configure CLR interfaces for resolving and loading assemblies in C++ as someone mentioned on stackoverflow before.
One thing I can think of is you can try is to limit the permissions of the new Application domain you load to not have access to the file system, drive, or to the GAC path more precisely, for example on the first call into a method in the new domain or a constructor set the permissions like it's done here.
Then you can embed the needed assemblies in the Resources of the application and use the AssemblyResolve event to load them like this.
I am running a project that had been running without issue for some time but recently started throwing an error stating,
"Could not load file or assembly 'Microsoft.Practices.Unity,
Version=2.1.505.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'
or one of its dependencies. The system cannot find the file
specified."
I see that this assembly is for IoC/Dependency Injection support however I never explicitly added it to the solution; although I do see that it is in fact there.
When I check the version of the assembly it is showing the same version that is being referenced in the above error; so I cannot figure out why the file cannot be found by the project.
In effort of resolving the issue I've cleaned the solution, deleted my obj folder, rebuilt, removed and even reinstalled the assembly via nuget but the issue persists.
I've found somewhat similar issues reported here on SO but the proposed resolutions were either not applicable because it was not the same assembly reference as the one I'm having issue with OR it involved configuration of a XAML based application. This is ASP.NET.
The only other clue that I could find as to why I'm having the problem is that the targeted runtime framework of the assembly is v2.0.50727 and this application is .NET 4.5
Which would seem a reasonable explanation for the problem from my limited perspective except that the app was previously running without the problem.
I'd also mention that the assembly isn't explicitly being called from the block of code throwing the error; which is simply creating a web service client and calling a method.
long memberId = 1326728123;
ServiceClient sc = new ServiceClient("ServiceClientEndPoint");
var leadPackage = smc.GetLeadPackages(memberId);
So there could be other variables of this equation that may be attributing to the problem (e.g. Network blocking and etc)
I just wanted to make certain that I may not be missing something by running it past SO before wasting time going in the wrong direction for an answer.
Note that this could mean a number of things, including that one of the dependent assemblies of Microsoft.Practices.Unity could be loaded.
The first place searched is the GAC, if you are building and running on the same machine, this probably won't cause a problem because the runtime will also find the same library but if you are deploying, the project will sometimes bind to the GAC library whereas the production server might not have it installed and it will fail to run. CopyLocal=true should fix that but if you are deploying, you should check that the library is copied into the bin directory.
Secondly, you should open Microsoft.Practices.Unity.dll using reflector or ilspy.exe and see what other dependencies it has (other than the System.* libraries) since any other ones will need the same treatment as Microsoft.Practices.Unity i.e. adding to the project and copy local set to true.
I'm using castle.windsor version 3.3 for .net 3.5.
I have a weird issue,
It seems my app loads dll more than once.
i'm loading the assembly with:
WindsorContainer().Install(FromAssembly.Named([AssemblyName]);
When I check the container's nodes (after the registration), every assembly have only one occurrence,
but when i check what dll my application is loading (via process explorer),
I can see same dll more than once, with the same version and path.
Also, i want to add that my application works just fine,
but loading dll multiple time is not a normal behavior.
thanks.
I have a solution that includes several projects. A few are libs that are building dll's used in my main project in this solution.
My main project builds with output type console application.
This all works fine.
When i change the build output type to a class library (since i want to use this project as a plugin eventually). The project will still build, this time to a dll.
When i use this plugin in an application where i use it as a dll however, it will run up to a certain point where it's trying to load a type defined in an external dll (so NOT built by my solution) and throw the exception:
Could not load type 'externalinterface' from assembly 'externallib, version=3.0.0.0, Culture=neutral, PublicKeyToken=null'.
The dll's are all in the correct folder,etc.
Also worth noting, the plugin is tested in another location than where i built it. The executable still works on this location, the dll/plugin does not. Same amount of dll's in their folders etc.
EDIT: I already used ILSpy (dll inspector) to open the actual dll that is being referenced (so externallib in the errormessage) and checked if 'externalinterface' was present and it is.
EDIT2: RESOLVED! The program that loaded my plugin was loading the same dll that caused the exception. The dll it loaded was of another version than the one i loaded.
Check whether the type externalinterface is present in the referred dll.
You didn't include the details of the exception the application is throwing. However, based on the message you gave, it appears your assembly does not have a strong name. If the application attempting to load your assembly as a plugin does have a strong name, then .NET will require all assemblies loaded by it also have a strong name, so you need to configure your assembly to have a strong name before continuing.
Maybe some supported dll's which is used by the 'externalinterface' is missing in the target machine. In the target machine, check is all the necessary dll's are present in the output folder.
Or blindly copy paste all the dlls in the output folder from the machine where the code is working to the target machine where you have the problem. After this, if the code is working in the target machine, then try to analyze which supporting dll you are missed to copy.
I am writing a C# COM+ drop in for another COM+ dll. It has a very simple interface and I have successfully tested the drop in.
I am using 'component services' partly because the old system did and partly because it feel right.
Problem I have is when I register the legacy dll the path to the dll in the properties is the ACTUAL dll, also it just works.
When I register my drop in the path to the dll is mscoree.dll not my dll, and it seems hit and miss as to whether I have to add my dll to the GAC? I have tried code to add it to a cache automatically but it doesn't work?
Also, as I am using a WCF call with my COM+ call I am running into an issue as to where the configuration dll is currently it appears to be looking for settings in C:\Windows\system32\dllhost.exe.config
What I'd like is for it to look along side the actual dll? Am I missing something?
COM/COM+ is an unmanaged technology. It knows nothing about .NET managed code, so registering your .NET assembly directly in the COM registry could not possibly work. mscoree.dll is the .NET hosting library which loads the managed runtime and presents the unmanaged interfaces to COM which the COM registry requires. When an instance of the COM coclass that your assembly implements is activated, COM+ loads mscoree first, then mscoree has to load your assembly to hook up your implementation to the COM-callable wrapper which mscoree presents to COM+.
Where mscoree looks for your assembly in order to load it depends on how you registered it. It follows the normal path probing rules of the .NET Fusion loader, which means it will usually be looking in the GAC unless you have specified a codebase during registration (e.g. using the regasm command line argument /codebase).
Configuration settings for managed code are scoped by AppDomain, and by default the configuration file name for an AppDomain is obtained by adding the suffix .config to the path of the executable of the process hosting the AppDomain. Your component is hosted in COM+, so will execute in process which is an instance of DllHost.exe. So by default the configuration file for your component's AppDomain is going to be DllHost.exe.config. However, if you specify an Application Root directory for the COM+ application this will change the location where the AppDomain looks for its configuration, to [COM+ Application Root Directory]\[COM+ Application Name].config.