I have a C# .Net 2.0CF application where I would like to load a .NET 'plug-in' DLL at runtime.
As I understand it, I should be able to use the System.Reflection.Assembly.LoadFrom() to load the DLL to an Assembly. Then use Assembly.GetTypes() to get the list of types within the plugin to match them to my expected interfaces.
The problem is that when I call GetTypes(), I get a System.TypeLoadException(). Based on the exception message, I assume this is because this particular plugin references some other assembly that this program knows nothing about. But, that's part of the point of it being a plugin! I don't want my application to have to know anything about these other assemblies.
How can I load this plug-in DLL without having my application reference every assembly used in the plugin?
Thanks,
PaulH
The situation is not that your app should reference every assembly that a plug-in uses. The plug-in references another DLL, not your app, and the plug-in should be "installed" (maybe simply put in a directory) along with everything it needs. This is unavoidable.
Related
My project(some kind of processing engine) is separated into 2 dlls: one with interface declarations and one with functionality.Usually the project is used by external Delphi project via COM technology.
Lets say my program slices fruits. External delphi programm creates Fruit object and fills its properties: weight (int), Name (string) and ProgressUpdater (of type IProgressUpdater which is declared in second dll with interfaces).After this exst programm creater Slicer, makes Slicer.AddFruit(newFruit) and calls Slicer.Slice().
Nothing special. In real life delphi project is Outlook addin. But here is the problem - sometimes some VSTO addins makes Outlook work in "shadow copy files" mode, so when delphi project starts and creats Slicer object, our c# assembly will be put in temp folder and assembly will be created with this local path. Well... this is still not an issue. But the problem is when delphi project creates newFruit and then passes ProgressUpdater object, in my Slicer assembly I can not get external ProgressUpdater: "Return argument has an invalid type", but still can get field with simple types(Weight, Name).
It only happens when shadowCopyFiles mode is on. So my guess is - external ProgressUpdater's assembly and Slicer assembly are placed to different places, so they can not be passed. My question is how to avoid my dll being "shadow copied"? Or is there some different solution?
So as a result I still have no answer for exact question. But the problem is solved (thanks to HansPassant) by using GAC, because assemblies at GAC will be never shadow-copied (actually linker will always first probe assemblies in GAC and then in other places).
Possible answer to the question is to go currentDomain.AssemblyResolve way, but I could not apply this solution to dll which contains public interfaces(types) only. Maybe it will be suitable solution for some cases.
You can use reflection to load DLL dynamically from any location you want. If you can go this way, I can provide further code for loading DLLs.
I'm writing a plug-in for a 3rd party application (.NET). This application lets me choose the plug-in (as a .dll library file) to load. However, if I have two versions of the same library---they have the same name but are in different directories---and try to load one after the other, it only loads the first plug-in and treats the second as if it was the first. In other words, if the first plug-in is supposed to show a message box saying "First plug-in" and the second plug-in is supposed to show a message box saying "Second plug-in," then loading the second plug-in after the first will actually show a message saying "First plug-in" (i.e., the second plug-in was actually never loaded).
After searching and reading online, I believe that the problem is that the 3rd party application loads its plug-ins into its primary AppDomain. Therefore, plug-in libraries are never unloaded (become locked?) and subsequent attempts to load a plug-in with the same name simply uses the library that's already been loaded. I thought perhaps signing my plug-in libraries would fix the problem, but unfortunately, I'm unable to sign them because I depend on a .dll provided by the 3rd party application and it is not signed. Also, I cannot change the 3rd party application's config file, so I cannot play around with probing.
Our current solution is to re-name the assembly for every version of the plug-in library we have (for example, "PlugIn-1.0.dll" and "PlugIn-2.0.dll"), including re-naming all their dependent assemblies. I don't mean just changing their file name, but changing the AssemblyName property and re-compiling. This works, but I'd like to see if there's a cleaner solution. It wouldn't be so bad if it was just the plug-in assembly name we had to change, but we are also forced to change all their dependent .dll's (because different plug-ins may use different versions of these .dll's as well). I tried creating a config file for the plug-in library to change the probing directory, but this doesn't work. It looks like it is the application itself that does the probing, not the library that depends on the .dll's (am I correct in inferring this?)
Finally, I tried having my plug-in create an AppDomain and load its dependent .dll's into it, but unfortunately my plug-in directory location (and dependent .dll's) must be on a remote location relative to the 3rd party app. There are security/permission issues with loading assemblies over network locations in .NET 4.0 (which I'm using) that I haven't been able to solve.
What are my options? Thanks in advance.
I think you're knocking on the right door with spinning up your own AppDomain. That is the only way I know of to have different versions of the same assembly loaded into the same process.
And yes - as soon as a .NET appdomain has loaded an assembly, it will not load another version of that assembly. Whoever is first wins. And of course, there is no way to unload an assembly once it has been loaded.
As for your permission issues.. Can you copy the assemblies from that network location to a user folder (where the user has write permissions) and load them from there? I've been able to do this successfully for an auto-updating application.
I been searching this for a long time but haven't seen an answer.
I'm referencing two dlls, which are a C++ library API for .NET, in a wcf service library, it works fine when invoking the wrapped method from wcftestclient. But when self hosting the library in a websercice it says"can't find assembly xxx.dll (it's one of the c++ API dll) or one of its dependency". But I can see the dll it says missing is auto copied to the bin/debug folder for the host webserive project. Also, I can host the library in a simple console project in which calling the wrapper method has no problem at all.
I hope this is a simple config setting that i'm missing to specify in the web config file, anyone has any suggestion? Thanks.
I spent almost three days on this problem and now it's resolved. In case this can help someone else who might have the same situation here is why.
The two c++ library .net api dlls are late binding so it won't look for its dependency until runtime. I even tried to registry the whole external library package in GAC and it didn't help; this was because one of two .net api dlls is a weak assembly hence CLR won't even bother looking for it in GAC. What I did was create a static constructor for the service and within the static constructor load the late binding assembly into AppDomain. I am using Assembly.LoadFrom(string filePath) method (other methods are available on Assembly class to do this). Now I can host the service on IIS and consume it in any client. So if clr is complaining it can't find an assembly but you can see it in the bin/debug folder, it's probably a late bind assembly which needs to be pre-loaded into the AppDomain; the good thing about AppDomain is you only need to load the assembly once and it will never unload an individual assembly.
I have a existing Java Project which needs functionality from a SDK written in C#. It should open a WPF Window and send the information back to Java on close.
For a basic connection of those two worlds i created a Java Project ("DotNetCaller") calling native functions. These are implemented in a C++/CLI Project ("DotNetBridge") which calls the C# Project ("DotNetApplication").
I already can set Strings from Java in C# and callback from C# to Java.
But as soon as i add a WPF Window and try to launch it with:
Application app = new Application();
app.Run(new DotNetWindow());
in a STA Thread it crashes.
The DotNetApplication doesnt find mscorlib.resources, after i provide the DLL, PresentationFramework.resources is missing and if i provide that, the DotNetApplication.resource is missing (which i cant provide).
If i call the DotNetApplication alone or from the DotNetBridge the Window displays as expected.
Can anyone tell ma what i'm really missing here?
Thanks
Edit:
I looked at this example once more and tried to adapt it to my needs.
I have set the dll directory of the ResolveEventHandler to the .NET dir in "Referenced Assemblies"
C:\Program Files (x86)\Reference
Assemblies\Microsoft\Framework.NETFramework\v4.0
and added a Window in C#.
It failed aswell but with a new exception in the C++ part rather than C#.
The ResolveHandler gets called with an empty argument causing an uncatchable exception in mscorelib.
I added a check if the String is empty and this basic approach works fine now.
I'm still unsure if i have the correct approach for this, so feel free to contribute.
Your AppDomain::AssemblyResolve handler probably needs to be overhauled and based on your own understanding of what you want to do. There is some guidance here. The basic rule is that you return nullptr for requests that you can't handle.
But first you have to plan the locations in which you want to deploy (and/or debug) your assemblies. A simple layout would be to put all of the assemblies that your JNI DLL depends on in the same folder as the JNI DLL (with the exception of any that will be installed in the GAC). You can then use its location to satisfy resolution requests. But remember to return nullptr if no file containing a manifest for an assembly with the requested name is present there. (This is likely the case with your ".resources" requests. If there isn't one it's okay unless you know otherwise.)
I'd be a little surprised if an assembly in a Reference Assemblies folder wasn't also in the GAC—but it'd be up to the assembly provider. Reference Assemblies is for design and build tools (e.g. Visual Studio). (The old way was for each folder that had assemblies in it to be registered for each version of Visual Studio so the assemblies could be used for design and build.) If a dependency is not in the GAC, you can use the "Copy Local" property on the reference to make it available for debugging.
You might find the Assembly Binding Log Viewer useful while designing and troubleshooting. With it you can see all the folders and extensions that are tried before giving over to calling the AppDomain::AssemblyResolve handler chain. (Disable logging when you are done.)
Suppose there is a singleton class in an assembly named Common. This DLL is used by my main application and a different version could be used by a plugin DLL in a plugins folder.
The distribution looks like this:
\App.exe
\Common.dll (v1)
\Plugins\Plugin.dll
\Plugins\Common.dll (v2)
So code in both App and Plugin are using that singleton class. I'm facing a hard to trace error which makes me think that somehow sometimes that singleton class loses all of its properties' values and starts to act like it's a new instance. From the stack trace, this always happens when it is being accessed by the plugin DLL.
EDIT: I just found some stack traces where it was being accessed by App. So cancel the last line of above paragraph.
Both App and Plugin (different VS projects) are compiled and linked with references added to their respective Common DLL versions.
EDIT: Plugin is loaded using Assembly.LoadFrom in the main AppDomain.
EDIT: Common DLL v2 present in Plugins folder also gets loaded dynamically with the Plugin DLL since all DLLs are being loaded from that folder:
foreach( string extensionFile in Directory.GetFiles( ExtensionsDirectory, "*.dll" ) )
Could anyone help what's going on?
Although it is possible, Microsoft recomends not loading two versions of the same DLL: http://msdn.microsoft.com/en-us/library/dd153782.aspx#avoid_loading_multiple_versions