// Copyright © Microsoft Corporation. // This source file is subject to the Microsoft Permissive License. // See http://www.microsoft.com/resources/sharedsource/licensingbasics/sharedsourcelicenses.mspx. // All other rights reserved. #if !ROTOR using System; using System.Collections; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.Text; #if CCINamespace namespace Microsoft.Cci{ #else namespace System.Compiler { #endif #if !FxCop public #endif class GlobalAssemblyCache { private GlobalAssemblyCache() { } private static readonly object Lock = new object(); private static bool FusionLoaded; /// Uri pointing to the assembly public static bool Contains(Uri codeBaseUri) { if (codeBaseUri == null) { Debug.Fail("codeBaseUri == null"); return false; } lock (GlobalAssemblyCache.Lock) { if (!GlobalAssemblyCache.FusionLoaded) { GlobalAssemblyCache.FusionLoaded = true; System.Reflection.Assembly systemAssembly = typeof(object).Assembly; //^ assume systemAssembly != null && systemAssembly.Location != null; string dir = Path.GetDirectoryName(systemAssembly.Location); //^ assume dir != null; GlobalAssemblyCache.LoadLibrary(Path.Combine(dir, "fusion.dll")); } IAssemblyEnum assemblyEnum; int rc = GlobalAssemblyCache.CreateAssemblyEnum(out assemblyEnum, null, null, ASM_CACHE.GAC, 0); if (rc < 0 || assemblyEnum == null) return false; IApplicationContext applicationContext; IAssemblyName currentName; while (assemblyEnum.GetNextAssembly(out applicationContext, out currentName, 0) == 0) { //^ assume currentName != null; AssemblyName assemblyName = new AssemblyName(currentName); string scheme = codeBaseUri.Scheme; if (scheme != null && assemblyName.CodeBase.StartsWith(scheme)) { try { Uri foundUri = new Uri(assemblyName.CodeBase); if (codeBaseUri.Equals(foundUri)) return true; #if !FxCop } catch (Exception) { #else }finally{ #endif } } } return false; } } /// /// Returns the original location of the corresponding assembly if available, otherwise returns the location of the shadow copy. /// If the corresponding assembly is not in the GAC, null is returned. /// public static string GetLocation(AssemblyReference assemblyReference) { if (assemblyReference == null) { Debug.Fail("assemblyReference == null"); return null; } lock (GlobalAssemblyCache.Lock) { if (!GlobalAssemblyCache.FusionLoaded) { GlobalAssemblyCache.FusionLoaded = true; System.Reflection.Assembly systemAssembly = typeof(object).Assembly; //^ assume systemAssembly != null && systemAssembly.Location != null; string dir = Path.GetDirectoryName(systemAssembly.Location); //^ assume dir != null; GlobalAssemblyCache.LoadLibrary(Path.Combine(dir, "fusion.dll")); } IAssemblyEnum assemblyEnum; CreateAssemblyEnum(out assemblyEnum, null, null, ASM_CACHE.GAC, 0); if (assemblyEnum == null) return null; IApplicationContext applicationContext; IAssemblyName currentName; while (assemblyEnum.GetNextAssembly(out applicationContext, out currentName, 0) == 0) { //^ assume currentName != null; AssemblyName aName = new AssemblyName(currentName); if (assemblyReference.Matches(aName.Name, aName.Version, aName.Culture, aName.PublicKeyToken)) { string codeBase = aName.CodeBase; if (codeBase != null && codeBase.StartsWith("file:///")) return codeBase.Substring(8); return aName.GetLocation(); } } return null; } } [DllImport("kernel32.dll", CharSet = CharSet.Ansi)] private static extern IntPtr LoadLibrary(string lpFileName); [DllImport("fusion.dll", CharSet = CharSet.Auto)] private static extern int CreateAssemblyEnum(out IAssemblyEnum ppEnum, IApplicationContext pAppCtx, IAssemblyName pName, uint dwFlags, int pvReserved); private class ASM_CACHE { private ASM_CACHE() { } public const uint ZAP = 1; public const uint GAC = 2; public const uint DOWNLOAD = 4; } } internal class AssemblyName { IAssemblyName/*!*/ assemblyName; internal AssemblyName(IAssemblyName/*!*/ assemblyName) { this.assemblyName = assemblyName; //^ base(); } internal string/*!*/ Name { //set {this.WriteString(ASM_NAME.NAME, value);} get { return this.ReadString(ASM_NAME.NAME); } } internal Version Version { //set{ // if (value == null) throw new ArgumentNullException(); // this.WriteUInt16(ASM_NAME.MAJOR_VERSION, (ushort)value.Major); // this.WriteUInt16(ASM_NAME.MINOR_VERSION, (ushort)value.Minor); // this.WriteUInt16(ASM_NAME.BUILD_NUMBER, (ushort)value.Build); // this.WriteUInt16(ASM_NAME.REVISION_NUMBER, (ushort)value.Revision); //} get { int major = this.ReadUInt16(ASM_NAME.MAJOR_VERSION); int minor = this.ReadUInt16(ASM_NAME.MINOR_VERSION); int build = this.ReadUInt16(ASM_NAME.BUILD_NUMBER); int revision = this.ReadUInt16(ASM_NAME.REVISION_NUMBER); return new Version(major, minor, build, revision); } } internal string/*!*/ Culture { //set {this.WriteString(ASM_NAME.CULTURE, value);} get { return this.ReadString(ASM_NAME.CULTURE); } } internal byte[]/*!*/ PublicKeyToken { //set {this.WriteBytes(ASM_NAME.PUBLIC_KEY_TOKEN, value); } get { return this.ReadBytes(ASM_NAME.PUBLIC_KEY_TOKEN); } } internal string StrongName { get { uint size = 0; this.assemblyName.GetDisplayName(null, ref size, (uint)AssemblyNameDisplayFlags.ALL); if (size == 0) return ""; StringBuilder strongName = new StringBuilder((int)size); this.assemblyName.GetDisplayName(strongName, ref size, (uint)AssemblyNameDisplayFlags.ALL); return strongName.ToString(); } } internal string/*!*/ CodeBase { //set {this.WriteString(ASM_NAME.CODEBASE_URL, value);} get { return this.ReadString(ASM_NAME.CODEBASE_URL); } } public override string ToString() { return this.StrongName; } internal string GetLocation() { IAssemblyCache assemblyCache; CreateAssemblyCache(out assemblyCache, 0); if (assemblyCache == null) return null; ASSEMBLY_INFO assemblyInfo = new ASSEMBLY_INFO(); assemblyInfo.cbAssemblyInfo = (uint)Marshal.SizeOf(typeof(ASSEMBLY_INFO)); assemblyCache.QueryAssemblyInfo(ASSEMBLYINFO_FLAG.VALIDATE | ASSEMBLYINFO_FLAG.GETSIZE, this.StrongName, ref assemblyInfo); if (assemblyInfo.cbAssemblyInfo == 0) return null; assemblyInfo.pszCurrentAssemblyPathBuf = new string(new char[assemblyInfo.cchBuf]); assemblyCache.QueryAssemblyInfo(ASSEMBLYINFO_FLAG.VALIDATE | ASSEMBLYINFO_FLAG.GETSIZE, this.StrongName, ref assemblyInfo); String value = assemblyInfo.pszCurrentAssemblyPathBuf; return value; } private string/*!*/ ReadString(uint assemblyNameProperty) { uint size = 0; this.assemblyName.GetProperty(assemblyNameProperty, IntPtr.Zero, ref size); if (size == 0 || size > Int16.MaxValue) return String.Empty; IntPtr ptr = Marshal.AllocHGlobal((int)size); this.assemblyName.GetProperty(assemblyNameProperty, ptr, ref size); String str = Marshal.PtrToStringUni(ptr); //^ assume str != null; Marshal.FreeHGlobal(ptr); return str; } private ushort ReadUInt16(uint assemblyNameProperty) { uint size = 0; this.assemblyName.GetProperty(assemblyNameProperty, IntPtr.Zero, ref size); IntPtr ptr = Marshal.AllocHGlobal((int)size); this.assemblyName.GetProperty(assemblyNameProperty, ptr, ref size); ushort value = (ushort)Marshal.ReadInt16(ptr); Marshal.FreeHGlobal(ptr); return value; } private byte[]/*!*/ ReadBytes(uint assemblyNameProperty) { uint size = 0; this.assemblyName.GetProperty(assemblyNameProperty, IntPtr.Zero, ref size); IntPtr ptr = Marshal.AllocHGlobal((int)size); this.assemblyName.GetProperty(assemblyNameProperty, ptr, ref size); byte[] value = new byte[(int)size]; Marshal.Copy(ptr, value, 0, (int)size); Marshal.FreeHGlobal(ptr); return value; } [DllImport("fusion.dll", CharSet = CharSet.Auto)] private static extern int CreateAssemblyCache(out IAssemblyCache ppAsmCache, uint dwReserved); private class CREATE_ASM_NAME_OBJ_FLAGS { private CREATE_ASM_NAME_OBJ_FLAGS() { } public const uint CANOF_PARSE_DISPLAY_NAME = 0x1; public const uint CANOF_SET_DEFAULT_VALUES = 0x2; } private class ASM_NAME { private ASM_NAME() { } public const uint PUBLIC_KEY = 0; public const uint PUBLIC_KEY_TOKEN = 1; public const uint HASH_VALUE = 2; public const uint NAME = 3; public const uint MAJOR_VERSION = 4; public const uint MINOR_VERSION = 5; public const uint BUILD_NUMBER = 6; public const uint REVISION_NUMBER = 7; public const uint CULTURE = 8; public const uint PROCESSOR_ID_ARRAY = 9; public const uint OSINFO_ARRAY = 10; public const uint HASH_ALGID = 11; public const uint ALIAS = 12; public const uint CODEBASE_URL = 13; public const uint CODEBASE_LASTMOD = 14; public const uint NULL_PUBLIC_KEY = 15; public const uint NULL_PUBLIC_KEY_TOKEN = 16; public const uint CUSTOM = 17; public const uint NULL_CUSTOM = 18; public const uint MVID = 19; public const uint _32_BIT_ONLY = 20; } [Flags] internal enum AssemblyNameDisplayFlags { VERSION = 0x01, CULTURE = 0x02, PUBLIC_KEY_TOKEN = 0x04, PROCESSORARCHITECTURE = 0x20, RETARGETABLE = 0x80, ALL = VERSION | CULTURE | PUBLIC_KEY_TOKEN | PROCESSORARCHITECTURE | RETARGETABLE } private class ASSEMBLYINFO_FLAG { private ASSEMBLYINFO_FLAG() { } public const uint VALIDATE = 1; public const uint GETSIZE = 2; } [StructLayout(LayoutKind.Sequential)] private struct ASSEMBLY_INFO { public uint cbAssemblyInfo; public uint dwAssemblyFlags; public ulong uliAssemblySizeInKB; [MarshalAs(UnmanagedType.LPWStr)] public string pszCurrentAssemblyPathBuf; public uint cchBuf; } [ComImport(), Guid("E707DCDE-D1CD-11D2-BAB9-00C04F8ECEAE"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IAssemblyCache { [PreserveSig()] int UninstallAssembly(uint dwFlags, [MarshalAs(UnmanagedType.LPWStr)] string pszAssemblyName, IntPtr pvReserved, int pulDisposition); [PreserveSig()] int QueryAssemblyInfo(uint dwFlags, [MarshalAs(UnmanagedType.LPWStr)] string pszAssemblyName, ref ASSEMBLY_INFO pAsmInfo); [PreserveSig()] int CreateAssemblyCacheItem(uint dwFlags, IntPtr pvReserved, out object ppAsmItem, [MarshalAs(UnmanagedType.LPWStr)] string pszAssemblyName); [PreserveSig()] int CreateAssemblyScavenger(out object ppAsmScavenger); [PreserveSig()] int InstallAssembly(uint dwFlags, [MarshalAs(UnmanagedType.LPWStr)] string pszManifestFilePath, IntPtr pvReserved); } } [ComImport(), Guid("CD193BC0-B4BC-11D2-9833-00C04FC31D2E"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] interface IAssemblyName { [PreserveSig()] int SetProperty(uint PropertyId, IntPtr pvProperty, uint cbProperty); [PreserveSig()] int GetProperty(uint PropertyId, IntPtr pvProperty, ref uint pcbProperty); [PreserveSig()] int Finalize(); [PreserveSig()] int GetDisplayName(StringBuilder szDisplayName, ref uint pccDisplayName, uint dwDisplayFlags); [PreserveSig()] int BindToObject(object refIID, object pAsmBindSink, IApplicationContext pApplicationContext, [MarshalAs(UnmanagedType.LPWStr)] string szCodeBase, long llFlags, int pvReserved, uint cbReserved, out int ppv); [PreserveSig()] int GetName(out uint lpcwBuffer, out int pwzName); [PreserveSig()] int GetVersion(out uint pdwVersionHi, out uint pdwVersionLow); [PreserveSig()] int IsEqual(IAssemblyName pName, uint dwCmpFlags); [PreserveSig()] int Clone(out IAssemblyName pName); } [ComImport(), Guid("7C23FF90-33AF-11D3-95DA-00A024A85B51"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] interface IApplicationContext { void SetContextNameObject(IAssemblyName pName); void GetContextNameObject(out IAssemblyName ppName); void Set([MarshalAs(UnmanagedType.LPWStr)] string szName, int pvValue, uint cbValue, uint dwFlags); void Get([MarshalAs(UnmanagedType.LPWStr)] string szName, out int pvValue, ref uint pcbValue, uint dwFlags); void GetDynamicDirectory(out int wzDynamicDir, ref uint pdwSize); } [ComImport(), Guid("21B8916C-F28E-11D2-A473-00C04F8EF448"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] interface IAssemblyEnum { [PreserveSig()] int GetNextAssembly(out IApplicationContext ppAppCtx, out IAssemblyName ppName, uint dwFlags); [PreserveSig()] int Reset(); [PreserveSig()] int Clone(out IAssemblyEnum ppEnum); } } #else //TODO: provide a way to query ROTOR GAC #endif