Type mismatch with runtime loaded assembly - c#

I'm writing a plugin architecture for testing purposes (I know there are frameworks to do that), this is how I've done it right now:
I have a common library shared by the main program and the plugins dll, I'll call it PluginBase assembly.
That assembly is referenced in a plugin MyPlugin which is in the folder Debug\Plugins.
It is also referenced in the main program, which is in the folder Debug.
The problem is, when I dynamically load the interfaces contained in the PluginBase, it gets the interface's Type object from the assembly located in Debug\Plugins.
But when I try to use the same interface (IMyPlugin) in the main program, it loads it from the PluginBase assembly located in the Debug folder.
And that causes invalid cast exceptions when I try to use the interface in the main Program.
The folder structure:
\Debug\MainProgram.exe
\Debug\PluginBase.dll
\Debug\Plugins\MyPlugin.dll
\Debug\Plugins\PluginBase.dll
The scenario of the loading is:
The ServiceProvider loads the plugins definition, ans also dynamically loads the interfaces contained in the PluginBase assembly (located in Debug\Plugins)
The main program calls the ServiceProvider to get a specific interface (but this time loaded from the Debug folder)
The types of the interfaces don't match (even if they are the same)....
I tried to locate the PluginBase dll in the Debug folder, but then I get compilation issues since the PluginBase dll gets copied to the Debug folder after the compilation of my plugin project.
Also I don't think this is a clean way to do this.
Is there a way to overcome this problem ? (like perhaps setting a build order in a way?)

Related

Hosted CLR: how to set base directory of default AppDomain?

If I host CLR in my C++ executable, is there any way to set base directory of default AppDomain to something other than location of the executable?
Here's why I need it. I have a rather complex application that loads .NET plugins using a plugin loader executable, PluginBox.exe. The plugins are located outside of the main application folder. We run one instance of PluginBox.exe per plugin.
Currently PluginBox.exe is written in C++. I want to convert it to a managed app. PluginBox uses unmanaged CLR hosting (ICLRMetaHost, ICLRRuntimeInfo, etc.), and locates plugin assemblies by implementing IHostAssemblyStore interface. There is only one AppDomain, and its base directory is the application directory. When searching for assemblies, CLR invokes the assembly store before looking at the application base directory. So, if the plugin and the main app contain an assembly with the same name, the assembly store can load the plugin-specific version.
As a first step towards making PluginBox.exe managed, I would like to get rid of the assembly store and replace it with an assembly resolver. The trouble is, unlike assembly store, assembly resolver is called after the application base directory has been considered. So, if the plugin and the main app contain an assembly with the same name, main app's assembly wins. This disrupts plugin execution.
I would like to switch base directory of the hosted CLR to where the plugin is located. So far, I found only two ways to do that, both of them unacceptabe: move PluginBox.exe to the plugin directory, or create a second AppDomain, which is problematic for a variety of internal reasons. This is a huge application with a lot of history, and any drastic moves are bound to cause problems.
Any thoughts and ideas are appreciated.
PS. Current CLR version is 4.0.

Assembly being optimized out of build

I have an assembly entirely filled with classes that are implementing interfaces in another assembly. For instance:
Main Assembly (Reference to both assemblies)
Shared Assembly
-----IModule
Class Assembly (Reference to shared assembly)
-----unknownType : IModule
-----unknownType2 : IModule
The Main assembly has no direct reference to any of the types in the Class assembly. I am looking for the types like so:
// Load all referenced assemblies:
Assembly.GetExecutingAssembly().GetReferencedAssemblies()
.Except(AppDomain.CurrentDomain.GetAssemblies().Select(a => a.GetName()))
.Distinct()
.ToList()
.ForEach(act => Assembly.Load(act));
// Find all instances of IModule in loaded assemblies:
var modules = from asm in AppDomain.CurrentDomain.GetAssemblies()
from provider in asm.GetTypes()
where typeof(IModule).IsAssignableFrom(provider)
...instantiate type etc...
If I have a reference to just an arbitrary type in the Class Assembly, then it shows up in GetReferencedAssemblies, gets loaded, and returned correctly - but as soon as i remove the reference to the type, it does not get transferred to the build directory or show up as a referenced assembly causing the load to fail.
Is there any way to force VS to include this assembly in the output directory? The main application should have no knowledge of any of the types within the class assembly.
I think I get your problem, but can you clarify your question? Are you expecting an assembly that you've added as a reference to your project to be copied to your output folder with your application? How did you add the reference? Is it a .dll or .exe that you browsed to in a file dialog, or is it a COM or GAC assembly that you picked from a list in the Add References dialog?
If you browsed to it, and it's not in the GAC directory, this kind of optimization is expected. Select your reference in the Solution Explorer, and make sure "Copy Local" in the properties window is set to true. You might try toggling this setting, even if it is. Your .vsproj file might just need to be rebuilt to include the reference.
If you're trying to reference a .dll from a file name stored in a string, or selected at runtime, then Visual Studio will have no way of knowing that your application is using it, nonetheless to copy it to your output directory. It shouldn't be too hard to do that with File.Copy, though, if you have the path of the .dll.

C# Load Assembly w/ Common References

I've run into a slight issue - I'm writing a program that loads DLLs, each of which contain a class which inherits from a class existing in a library referenced by both the loaded DLL and the "host" program that is loading the DLL. The issue here is that, when I try to load and cast to the superclass:
var assembly = Assembly.LoadFrom(dllPath);
var type = assembly.GetTypes().FirstOrDefault(x => x.IsSubclassOf(typeof (MySuperclass)));
...
Although both are referencing the class containing MySuperclass, since the dll is referencing the built class library (a separate file from the class library file the loading program is referencing) IsSubclassOf never returns true, because it considers the two classes to be different as they come from two different assemblies.
Furthermore, if you try casting a created instance of the loaded class to the superclass type, it can't do it since they're not the same (different assemblies).
So, my question is: how do you handle loading assemblies which reference common code, so that c# recognizes that you're loading a class which inherits from a common superclass?
You simply must use the same assembly files (even if they are identical) if you want programs to work together using common code. Here's how I solved the issue:
The program is loading a DLL from a subdirectory of its own.
Folder structure:
MyApp Folder -->
MyProgram.exe
CommonDependency.dll
Submodules ->
MySubmodule.dll
To get MySubmodule to use CommonDependency.dll within the next folder up, it's quite simple. Configure Visual Studio to not copy these dll dependencies to the build folder. Next, create an App.config in Visual Studio and add the following:
<configuration>
<runtime>
<assemblyBinding>
<probing privatePath="../"/>
</assemblyBinding>
</runtime>
</configuration>
This will tell the system to search for the assemblies in the parent folder ../ - if you want to have multiple folders, perhaps a separate dependency folder (relative to the location of the .dll) you can use ; as the delimiter - ../;../bin/; - the program will search these locations for the dependencies.
The ancestor class (superclass) must come from the same assembly for any chance for the loading code to work with descendants as a superclass reference.

How singletons are supposed to behave when different versions of same assembly are loaded?

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

Late Loading a .net plugin dll

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.

Categories

Resources