// NAnt - A .NET build tool // Copyright (C) 2001-2004 Gerry Shaw // // 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 // // Matt Mastracci // Gert Driesen (gert.driesen@ardatis.com) using System; using System.CodeDom.Compiler; using System.Collections; using System.Collections.Specialized; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.InteropServices; using System.Runtime.Remoting.Lifetime; using NAnt.Core; namespace NAnt.Core.Util { /// /// Helper class for determining whether assemblies are located in the /// Global Assembly Cache. /// public sealed class GacCache : IDisposable { #region Public Instance Constructors /// /// Initializes a new instance of the class in /// the context of the given . /// public GacCache(Project project) { _project = project; _gacQueryCache = CollectionsUtil.CreateCaseInsensitiveHashtable(); RecreateDomain(); } #endregion Public Instance Constructors #region Public Instance Destructors ~GacCache() { Dispose(false); } #endregion Public Instance Destructors #region Public Instance Properties /// /// Gets the context of the . /// /// /// The context of the . /// public Project Project { get { return _project; } } #endregion Public Instance Properties #region Private Instance Properties private AppDomain Domain { get { return _domain; } } private GacResolver Resolver { get { if (_resolver == null) { _resolver = ((GacResolver) Domain.CreateInstanceFrom( Assembly.GetExecutingAssembly().Location, typeof(GacResolver).FullName).Unwrap()); } return _resolver; } } #endregion Private Instance Properties #region Implementation of IDisposable public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool disposing) { if (!_disposed) { AppDomain.Unload(_domain); _disposed = true; } } #endregion Implementation of IDisposable #region Public Instance Methods public void RecreateDomain() { // don't recreate this domain unless it has actually loaded an assembly if (!_hasLoadedAssembly && _domain != null) return; if (_domain != null) AppDomain.Unload(_domain); _resolver = null; _domain = AppDomain.CreateDomain("GacCacheDomain", AppDomain.CurrentDomain.Evidence, AppDomain.CurrentDomain.SetupInformation); _hasLoadedAssembly = false; } /// /// Determines whether an assembly is installed in the Global /// Assembly Cache given its file name or path. /// /// The name or path of the file that contains the manifest of the assembly. /// /// if is /// installed in the Global Assembly Cache; otherwise, /// . /// /// /// /// To determine whether the specified assembly is installed in the /// Global Assembly Cache, the assembly is loaded into a separate /// . /// /// /// If the family of the current runtime framework does not match the /// family of the current target framework, this method will return /// for all assemblies as there's no way to /// determine whether a given assembly is in the Global Assembly Cache /// for another framework family than the family of the current runtime /// framework. /// /// public bool IsAssemblyInGac(string assemblyFile) { if (Project.RuntimeFramework.Family != Project.TargetFramework.Family) { return false; } string assemblyFilePath = Path.GetFullPath(assemblyFile); if (_gacQueryCache.Contains(assemblyFilePath)) { return (bool) _gacQueryCache[assemblyFilePath]; } _hasLoadedAssembly = true; _gacQueryCache[assemblyFilePath] = Resolver.IsAssemblyInGac(assemblyFilePath); return (bool) _gacQueryCache[assemblyFilePath]; } #endregion Public Instance Methods #region Private Instance Fields /// /// Holds the in which assemblies will be loaded /// to determine whether they are in the Global Assembly Cache. /// private AppDomain _domain; /// /// Holds the context of the . /// private Project _project; /// /// Holds a list of assembly files for which already has been determined /// whether they are located in the Global Assembly Cache. /// /// /// /// The key of the is the full path to the /// assembly file and the value is a indicating /// whether the assembly is located in the Global Assembly Cache. /// /// private Hashtable _gacQueryCache; private bool _hasLoadedAssembly; private GacResolver _resolver; /// /// Holds a value indicating whether the object has been disposed. /// private bool _disposed; #endregion Private Instance Fields private class GacResolver : MarshalByRefObject { #region Override implementation of MarshalByRefObject /// /// Obtains a lifetime service object to control the lifetime policy for /// this instance. /// /// /// An object of type used to control the lifetime /// policy for this instance. This is the current lifetime service object /// for this instance if one exists; otherwise, a new lifetime service /// object initialized with a lease that will never time out. /// public override Object InitializeLifetimeService() { ILease lease = (ILease) base.InitializeLifetimeService(); if (lease.CurrentState == LeaseState.Initial) { lease.InitialLeaseTime = TimeSpan.Zero; } return lease; } #endregion Override implementation of MarshalByRefObject #region Public Instance Methods /// /// Determines whether an assembly is installed in the Global /// Assembly Cache given its file name or path. /// /// The name or path of the file that contains the manifest of the assembly. /// /// if is /// installed in the Global Assembly Cache; otherwise, /// . /// public bool IsAssemblyInGac(string assemblyFile) { try { AssemblyName assemblyName = AssemblyName.GetAssemblyName(assemblyFile); // the assembly can't be in the GAC if it has no public key if (assemblyName.GetPublicKeyToken() == null) { return false; } // load assembly Assembly assembly = Assembly.Load(assemblyName); // tests whether the specified assembly is loaded in the // global assembly cache if (PlatformHelper.IsMono) { // TODO: remove mono specific code when FromGlobalAccessCache // is implemented return assembly.GlobalAssemblyCache; } else { return RuntimeEnvironment.FromGlobalAccessCache(assembly); } } catch { return false; } } #endregion Public Instance Methods } } }