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.
Related
I have the following project structure:
-Solution
-ProjectA
-ProjectB (has reference to ProjectA - projectreference tag that points to csproj of ProjectA)
Both projects have dynamic AssemblyName depending on build configuration. Let's say that if I build with "Debug" config then the outcome will be: DebugProjectA.dll and DebugProjectB.dll. After building in "Release" config, it's going to be: ReleaseProjectA.dll and ReleaseProjectB.dll. Namespace of classes in ProjectA is always the same and starts with ProjectA.*
My question is: How does ProjectB know how to find the correct DLL of ProjectA in runtime? To be more specific: How does it know the name of the referenced project's DLL (that changes in different build configs)?
My guess is that AssemblyName of ProjectA (of given build config) is somehow stored in ProjectB assembly file during the build. Is this correct? How does it work?
This is a direct quote from the documentation on Microsoft's website:
The runtime uses the following steps to resolve an assembly reference:
Determines the correct assembly version by examining applicable
configuration files, including the application configuration file,
publisher policy file, and machine configuration file. If the
configuration file is located on a remote machine, the runtime must
locate and download the application configuration file first.
Checks whether the assembly name has been bound to before and, if so,
uses the previously loaded assembly. If a previous request to load the
assembly failed, the request is failed immediately without attempting
to load the assembly.
Checks the global assembly cache. If the assembly is found there, the
runtime uses this assembly.
Probes for the assembly using the following steps:
If configuration and publisher policy do not affect the original
reference and if the bind request was created using the
Assembly.LoadFrom method, the runtime checks for location hints.
If a codebase is found in the configuration files, the runtime checks
only this location. If this probe fails, the runtime determines that
the binding request failed and no other probing occurs.
Probes for the assembly using the heuristics described in the probing
section. If the assembly is not found after probing, the runtime
requests the Windows Installer to provide the assembly. This acts as
an install-on-demand feature.
Of course, this includes projects referenced by the assembly.
Edit to clarify the OP's comment:
As you may see on the output window on visual studio during the build process, you may notice that all referenced projects get build first. Later, the final names of their assemblies get added to the main executable and that is how is able to identify them. That last part is not visible on the output.
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?)
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.
Can I reference two versions of a dll in the same project without putting them in the GAC?
Thanks
You've got two problems. First one is getting your app to compile. You've got two assembly references that contain types with the same namespace name and type name, the compiler won't know which one to choose. You solve that by using "extern alias", it lets you rename the namespace of one of the assemblies. Review this question for further help.
The second problem is the one you ask about. Without the GAC, you need to help the CLR finding the correct DLL. You must put the DLLs in a separate directory, say a subdirectory of your build directory, so that the CLR cannot find them. Use a post build event to create this directory and copy the DLLs into them. Give them distinct names.
Then implement the AppDomain.CurrentDomain.AssemblyResolve event. The CLR will fire it when it cannot find the DLLs you've hidden. Use Assembly.LoadFrom() to load and return the assembly it asks for. The e.Name property has the full assembly name, use the AssemblyName class to parse that string and retrieve the Version property.
Referring to an assembly without putting it into GAC means copying the dll in the bin folder of the project. And you cannot have two dlls of same name in the bin folder.
You cannot put reference of the same assembly twice.
I'm currently trying to dynamically load an assembly from within a asp.net httphandler. I have a dll that is built as part of a seperate library and my project contains a reference to said DLL and is deployed along with the service with CopyLocal true. I create a throwaway object to get the assembly path and I have confirmed the existence of the dll within the Temporary ASP.NET Files folder, but calling GetTypes() throws an exception.
I do something like:
string assemblyPath = new SomeClassInAssembly().GetType().Assembly.Location;
Type[] types = System.Reflection.Assembly.LoadFrom(assemblyPath).GetTypes();
I cannot add the assembly to the GAC since that would defeat what I am trying to do with the service (think sandbox service that loads assemblies when necessary) and I cannot find anything that has been able to fix my problem thus far.
For reference I'm using VS 2008.
Since you compile your web application with a reference to the assembly i don't see your need to load it using Assembly.LoadFrom. The GetTypes should be available using:
Type[] types = typeof(SomeClassInAssembly).Assembly.GetTypes();
Doh, I finally hooked up to the exception and looked at the LoaderMessage and I was missing a referenced assembly.