/* * Util.AssemblyLocator.cs * Authors: Adam Nabinger * Copyright (c) 2007-2008 Cornell University This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ using System; using System.Collections.Generic; using System.Reflection; namespace Util { /// /// Collects and stores a list of referenced assemblies, for retrieving minigames dynamically. /// public static class AssemblyLocator { #region Fields // Should be Sets? // List of assemblies private static readonly ICollection assemblies = new List(); // List of minigame infos private static readonly List minigameinfos = new List(); // List of locations to search through private static readonly Queue searchLocations = new Queue(); // Whether or not the list of assemblies has been resolved. private static bool resolved; #endregion #region Properties /// /// A collection of strings, each item of which is the name of a known assembly. /// public static ICollection AllKnownAssemblies { get { if (!resolved) { Resolve(); } ICollection temp = new List(); foreach (AssemblyInfo info in assemblies) { temp.Add(info.path); } return temp; } } /// /// A collection of MiniGameInfos, each item of which representing a found minigame. /// public static List AllKnownMiniGames { get { if (!resolved) { Resolve(); } return minigameinfos; } } #endregion #region Creation /// /// Constructor /// static AssemblyLocator() { searchLocations.Enqueue(ProjectInfo.HomeDirectory); AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; } #endregion #region Location /// /// Add a directory path to search for minigames in. /// Subdirectories under the main executable are searched by default. /// /// The current root directory public static void AddSearchLocation(string rootDirectory) { searchLocations.Enqueue(rootDirectory); resolved = false; } /// /// Get the location of an assembly by it's name. /// /// The name of the desired assembly. /// The fully qualified path to the directory containing the assembly, or null if it is not known. public static string GetLocation(string assemblyName) { foreach (AssemblyInfo a in assemblies) { if (a.fullname == assemblyName) { return a.path.Substring(0, a.path.LastIndexOf('\\')); } } return null; } #endregion #region Resolve /// /// An Event triggered when a dependant assembly cannot be found by the C# libraries. /// /// Sender of the event /// All arguments associtated with the resolved event /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Reflection.Assembly.LoadFrom")] static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { #if DEBUG Console.WriteLine("Debug: Trying to resolve " + args.Name); #endif if (!resolved) { Resolve(); } foreach (AssemblyInfo info in assemblies) { if (args.Name != info.fullname) { continue; } if (info.assembly == null) { info.assembly = Assembly.LoadFrom(info.path); } return info.assembly; } throw new InvalidOperationException("Error trying to load " + args.Name + ". Could not find assembly."); } /// /// Search through the given locations for any assemblies. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Reflection.Assembly.LoadFrom")] private static void Resolve() { AppDomain privateAppDomain = AppDomain.CreateDomain("Private Minigame Search AppDomain"); string fullname = Assembly.GetExecutingAssembly().FullName; if (string.IsNullOrEmpty(fullname)) { return; } AssemblySearchWorker worker = (AssemblySearchWorker)privateAppDomain.CreateInstanceAndUnwrap(fullname, typeof(AssemblySearchWorker).FullName); worker.Resolve(searchLocations); foreach (AssemblyType pair in worker.Types) { if (!string.IsNullOrEmpty(pair.TypeName)) { Assembly asm = Assembly.LoadFrom(pair.Assembly); Type t = asm.GetType(pair.TypeName); assemblies.Add(new AssemblyInfo(asm, pair.Assembly, pair.FullName)); MiniGameInfo info = (MiniGameInfo)t.GetConstructor(Type.EmptyTypes).Invoke(null); info.RootDirectory = pair.Assembly.Substring(0, pair.Assembly.LastIndexOf('\\')); minigameinfos.Add(info); } else { assemblies.Add(new AssemblyInfo(null, pair.Assembly, pair.FullName)); } minigameinfos.Sort(); } AppDomain.Unload(privateAppDomain); } #endregion } /// /// Defines the info associated with any assembly /// internal sealed class AssemblyInfo { #region Fields /// /// The assembly /// internal Assembly assembly; /// /// The path name /// internal readonly string path; /// /// The full name /// internal readonly string fullname; #endregion #region Creation /// /// Constructor /// /// The assembly /// The path name /// The full name public AssemblyInfo(Assembly asm, string p, string name) { assembly = asm; path = p; fullname = name; } #endregion } }