Some notes and comments before my answer:
1) You have a reference to ClassLibrary1 and ClassLibrary2, and instantiate Class1 from ClassLibrary1. In that case compiler throws away reference to ClassLibrary2 and Assembly.GetReferencedAssemblies()
returns to you actual optimized list of references. When you use any type from ClassLibrary2, complier will respect that reference also. The best way for making strong refs is:
static class StrongReferencesLoader
{
public static void Load()
{
var ref1 = typeof(Class1); // ClassLibrary1.dll
var ref2 = typeof(Class2); // ClassLibrary2.dll
}
}
This allows to reference static classes also and this allows to force loading of every strong-referenced library by calling this method.
2) You do use an AppDomain.AssemblyLoad event. Consider subscribing here from your entry point. Be careful, because if your entry point make something else this can lead to loading assembly before subscribing occurs.
class Program
{
static void Main()
{
AppDomain.CurrentDomain.AssemblyLoad += CurrentDomain_AssemblyLoad;
new Class1();
}
static void CurrentDomain_AssemblyLoad(object sender, AssemblyLoadEventArgs args)
{
Console.WriteLine("! " + args.LoadedAssembly.FullName);
}
}
in this sample, you will not get an output about loading ClassLibrary1, because loading of library was performed before executing main method. Runtime loads all necessary libraries for executing specified method. If we extract this call to another method - everything become fine.
class Program
{
static void Main()
{
AppDomain.CurrentDomain.AssemblyLoad += CurrentDomain_AssemblyLoad;
RealMain();
}
static void RealMain()
{
new Class1();
}
static void CurrentDomain_AssemblyLoad(object sender, AssemblyLoadEventArgs args)
{
Console.WriteLine("! " + args.LoadedAssembly.FullName);
}
}
In that case, Main is executed without problem, and subscribes immediately. After that, assembly loading performed due to call RealMain that contain a byte-code referenced to ClassLibrary1, and your event handler works.
3) Note that in any time you can list all loaded assemblies by AppDomain.CurrentDomain.GetAssemblies()
I'm not fully understand your problem. Is that only solution-wide analysis, or generic idea for future plugins, created by any one else? Solution-wide analysis can be solved in compile-time or run-time, but open plugin system is only runtime.
Run time solution
First of all, you should understand that it is impossible to detect every plugins. Let say, Class1 have extensions, written by some body for another application and installed in another directory:
some pseudo-structure:
\GAC\ClassLibrary1.dll
\ProgramFiles\MyApp\ConsoleApplication1.exe // our entry point
\ProgramFiles\SomeApp\ClassLibrary3.dll // some library from another solution, that references shared ClassLibrary1
You can detect plugins in your GAC, AppDomainBase path, RelativeSearchPath (all of that done by built-in assembly resolver), any custom hardcoded path, and paths of already loaded assemblies AppDomain.CurrentDomain.GetAssemblies().Select(x => Path.GetDirectoryName(x.Location)).Distinct();
And you can analyse that paths recursively.
But you'l never find path like \ProgramFiles\SomeApp\ClassLibrary3.dll unless you scan entire computer.
I hope, that searching in deep to already loaded assemblies location can solve your problem.
Note that you can use Mono.Cecil, but it is convenient to use Assembly.ReflectionOnlyLoad
for that purpose. You can load by file name using Assembly.ReflectionOnlyLoadFrom
, and you can help resolve assemblies in another location by AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve
event. But you can not use that for custom attributes analysis. In that case some hack is required, like special class name (static class MySuperMark{}
), which existance can be checked with easy. It is less hacky to use special interface that every plugin required to implement (but it is not so close to your case).
Compile time solution
Only for solution-wide analysis.
Since you have noted, that
I want to create a generic solution that would not need to be altered each time I add another reference.
Probably you are looking for solution-wide answer. I can suggest some metaprogramming here. Usually I'm solving such task by metacreator http://code.google.com/p/metacreator/. You can run some piece of code before compilation of ConsoleApplication1. For example, you can create a class holding strong references for every referenced dlls automatically by metacode and you can run such class for ensuring early loading of referenced assemblies. This class will actualized on every compilation automatically.
Please, describe more details, which case I can describe more deeply.