//------------------------------------------------------------- // GACWrap.cs // // This implements managed wrappers to GAC API Interfaces // // Taken from Junfeng Zhang's MSDN blog: // http://blogs.msdn.com/junfeng/articles/229649.aspx //------------------------------------------------------------- using System; using System.Reflection; using System.Runtime.InteropServices; using System.Text; //[assembly:AssemblyKeyFileAttribute((String)"..\\invoker.snk")] namespace System.GACManagedAccess { //------------------------------------------------------------- // Interfaces defined by fusion //------------------------------------------------------------- [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("e707dcde-d1cd-11d2-bab9-00c04f8eceae")] internal interface IAssemblyCache { [PreserveSig()] int UninstallAssembly( int flags, [MarshalAs(UnmanagedType.LPWStr)] String assemblyName, InstallReference refData, out AssemblyCacheUninstallDisposition disposition); [PreserveSig()] int QueryAssemblyInfo( int flags, [MarshalAs(UnmanagedType.LPWStr)] String assemblyName, ref AssemblyInfo assemblyInfo); [PreserveSig()] int Reserved ( int flags, IntPtr pvReserved, out Object ppAsmItem, [MarshalAs(UnmanagedType.LPWStr)] String assemblyName); [PreserveSig()] int Reserved(out Object ppAsmScavenger); [PreserveSig()] int InstallAssembly( int flags, [MarshalAs(UnmanagedType.LPWStr)] String assemblyFilePath, InstallReference refData); }// IAssemblyCache [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("CD193BC0-B4BC-11d2-9833-00C04FC31D2E")] internal interface IAssemblyName { [PreserveSig()] int SetProperty( int PropertyId, IntPtr pvProperty, int cbProperty); [PreserveSig()] int GetProperty( int PropertyId, IntPtr pvProperty, ref int pcbProperty); [PreserveSig()] int Finalize(); [PreserveSig()] int GetDisplayName( StringBuilder pDisplayName, ref int pccDisplayName, int displayFlags); [PreserveSig()] int Reserved(ref Guid guid, Object obj1, Object obj2, String string1, Int64 llFlags, IntPtr pvReserved, int cbReserved, out IntPtr ppv); [PreserveSig()] int GetName( ref int pccBuffer, StringBuilder pwzName); [PreserveSig()] int GetVersion( out int versionHi, out int versionLow); [PreserveSig()] int IsEqual( IAssemblyName pAsmName, int cmpFlags); [PreserveSig()] int Clone(out IAssemblyName pAsmName); }// IAssemblyName [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("21b8916c-f28e-11d2-a473-00c04f8ef448")] internal interface IAssemblyEnum { [PreserveSig()] int GetNextAssembly( IntPtr pvReserved, out IAssemblyName ppName, int flags); [PreserveSig()] int Reset(); [PreserveSig()] int Clone(out IAssemblyEnum ppEnum); }// IAssemblyEnum [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("582dac66-e678-449f-aba6-6faaec8a9394")] internal interface IInstallReferenceItem { // A pointer to a FUSION_INSTALL_REFERENCE structure. // The memory is allocated by the GetReference method and is freed when // IInstallReferenceItem is released. Callers must not hold a reference to this // buffer after the IInstallReferenceItem object is released. // This uses the InstallReferenceOutput object to avoid allocation // issues with the interop layer. // This cannot be marshaled directly - must use IntPtr [PreserveSig()] int GetReference( out IntPtr pRefData, int flags, IntPtr pvReserced); }// IInstallReferenceItem [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("56b1a988-7c0c-4aa2-8639-c3eb5a90226f")] internal interface IInstallReferenceEnum { [PreserveSig()] int GetNextInstallReferenceItem( out IInstallReferenceItem ppRefItem, int flags, IntPtr pvReserced); }// IInstallReferenceEnum [ComVisible(false)] public enum AssemblyCommitFlags { Default = 1, Force = 2 }// enum AssemblyCommitFlags [ComVisible(false)] public enum AssemblyCacheUninstallDisposition { Unknown = 0, Uninstalled = 1, StillInUse = 2, AlreadyUninstalled = 3, DeletePending = 4, HasInstallReference = 5, ReferenceNotFound = 6 } [Flags] internal enum AssemblyCacheFlags { GAC = 2, } internal enum CreateAssemblyNameObjectFlags { CANOF_DEFAULT = 0, CANOF_PARSE_DISPLAY_NAME = 1, } [Flags] internal enum AssemblyNameDisplayFlags { VERSION = 0x01, CULTURE = 0x02, PUBLIC_KEY_TOKEN = 0x04, PROCESSORARCHITECTURE = 0x20, RETARGETABLE = 0x80, // This enum will change in the future to include // more attributes. ALL = VERSION | CULTURE | PUBLIC_KEY_TOKEN | PROCESSORARCHITECTURE | RETARGETABLE } [StructLayout(LayoutKind.Sequential)] [ComVisible(false)] public class InstallReference { public InstallReference(Guid guid, String id, String data) { cbSize = (int)(2*IntPtr.Size+16+(id.Length+data.Length)*2); flags = 0; // quiet compiler warning if (flags == 0) { } guidScheme = guid; identifier = id; description = data; } public Guid GuidScheme { get { return guidScheme;} } public String Identifier { get { return identifier; } } public String Description { get { return description;} } int cbSize; int flags; Guid guidScheme; [MarshalAs(UnmanagedType.LPWStr)] String identifier; [MarshalAs(UnmanagedType.LPWStr)] String description; } [StructLayout(LayoutKind.Sequential)] internal struct AssemblyInfo { public int cbAssemblyInfo; // size of this structure for future expansion public int assemblyFlags; public long assemblySizeInKB; [MarshalAs(UnmanagedType.LPWStr)] public String currentAssemblyPath; public int cchBuf; // size of path buf. } [ComVisible(false)] public class InstallReferenceGuid { public static bool IsValidGuidScheme(Guid guid) { return (guid.Equals(UninstallSubkeyGuid) || guid.Equals(FilePathGuid) || guid.Equals(OpaqueGuid) || guid.Equals(Guid.Empty)); } public readonly static Guid UninstallSubkeyGuid = new Guid("8cedc215-ac4b-488b-93c0-a50a49cb2fb8"); public readonly static Guid FilePathGuid = new Guid("b02f9d65-fb77-4f7a-afa5-b391309f11c9"); public readonly static Guid OpaqueGuid = new Guid("2ec93463-b0c3-45e1-8364-327e96aea856"); // these GUID cannot be used for installing into GAC. public readonly static Guid MsiGuid = new Guid("25df0fc1-7f97-4070-add7-4b13bbfd7cb8"); public readonly static Guid OsInstallGuid = new Guid("d16d444c-56d8-11d5-882d-0080c847b195"); } [ComVisible(false)] public static class AssemblyCache { public static void InstallAssembly(String assemblyPath, InstallReference reference, AssemblyCommitFlags flags) { if (reference != null) { if (!InstallReferenceGuid.IsValidGuidScheme(reference.GuidScheme)) throw new ArgumentException("Invalid reference guid.", "guid"); } IAssemblyCache ac = null; int hr = 0; hr = Utils.CreateAssemblyCache(out ac, 0); if (hr >= 0) { hr = ac.InstallAssembly((int)flags, assemblyPath, reference); } if (hr < 0) { Marshal.ThrowExceptionForHR(hr); } } // assemblyName has to be fully specified name. // A.k.a, for v1.0/v1.1 assemblies, it should be "name, Version=xx, Culture=xx, PublicKeyToken=xx". // For v2.0 assemblies, it should be "name, Version=xx, Culture=xx, PublicKeyToken=xx, ProcessorArchitecture=xx". // If assemblyName is not fully specified, a random matching assembly will be uninstalled. public static void UninstallAssembly(String assemblyName, InstallReference reference, out AssemblyCacheUninstallDisposition disp) { AssemblyCacheUninstallDisposition dispResult = AssemblyCacheUninstallDisposition.Uninstalled; if (reference != null) { if (!InstallReferenceGuid.IsValidGuidScheme(reference.GuidScheme)) throw new ArgumentException("Invalid reference guid.", "guid"); } IAssemblyCache ac = null; int hr = Utils.CreateAssemblyCache(out ac, 0); if (hr >= 0) { hr = ac.UninstallAssembly(0, assemblyName, reference, out dispResult); } if (hr < 0) { Marshal.ThrowExceptionForHR(hr); } disp = dispResult; } // See comments in UninstallAssembly public static String QueryAssemblyInfo(String assemblyName) { if (assemblyName == null) { throw new ArgumentException("Invalid name", "assemblyName"); } AssemblyInfo aInfo = new AssemblyInfo(); aInfo.cchBuf = 1024; // Get a string with the desired length aInfo.currentAssemblyPath = new String('\0', aInfo.cchBuf) ; IAssemblyCache ac = null; int hr = Utils.CreateAssemblyCache(out ac, 0); if (hr >= 0) { hr = ac.QueryAssemblyInfo(0, assemblyName, ref aInfo); } if (hr < 0) { Marshal.ThrowExceptionForHR(hr); } return aInfo.currentAssemblyPath; } } [ComVisible(false)] public class AssemblyCacheEnum { // null means enumerate all the assemblies public AssemblyCacheEnum(String assemblyName) { IAssemblyName fusionName = null; int hr = 0; if (assemblyName != null) { hr = Utils.CreateAssemblyNameObject( out fusionName, assemblyName, CreateAssemblyNameObjectFlags.CANOF_PARSE_DISPLAY_NAME, IntPtr.Zero); } if (hr >= 0) { hr = Utils.CreateAssemblyEnum( out m_AssemblyEnum, IntPtr.Zero, fusionName, AssemblyCacheFlags.GAC, IntPtr.Zero); } if (hr < 0) { Marshal.ThrowExceptionForHR(hr); } } public String GetNextAssembly() { int hr = 0; IAssemblyName fusionName = null; if (done) { return null; } // Now get next IAssemblyName from m_AssemblyEnum hr = m_AssemblyEnum.GetNextAssembly((IntPtr) 0, out fusionName, 0); if (hr < 0) { Marshal.ThrowExceptionForHR(hr); } if (fusionName != null) { return GetFullName(fusionName); } else { done = true; return null; } } private String GetFullName(IAssemblyName fusionAsmName) { StringBuilder sDisplayName = new StringBuilder(1024); int iLen = 1024; int hr = fusionAsmName.GetDisplayName(sDisplayName, ref iLen, (int)AssemblyNameDisplayFlags.ALL); if (hr < 0) { Marshal.ThrowExceptionForHR(hr); } return sDisplayName.ToString(); } private IAssemblyEnum m_AssemblyEnum = null; private bool done; }// class AssemblyCacheEnum [ComVisible(false)] public class AssemblyCacheInstallReferenceEnum { public AssemblyCacheInstallReferenceEnum(String assemblyName) { IAssemblyName fusionName = null; int hr = Utils.CreateAssemblyNameObject( out fusionName, assemblyName, CreateAssemblyNameObjectFlags.CANOF_PARSE_DISPLAY_NAME, IntPtr.Zero); if (hr >= 0) { hr = Utils.CreateInstallReferenceEnum(out refEnum, fusionName, 0, IntPtr.Zero); } if (hr < 0) { Marshal.ThrowExceptionForHR(hr); } } public InstallReference GetNextReference() { IInstallReferenceItem item = null; int hr = refEnum.GetNextInstallReferenceItem(out item, 0, IntPtr.Zero); if ((uint)hr == 0x80070103) { // ERROR_NO_MORE_ITEMS return null; } if (hr < 0) { Marshal.ThrowExceptionForHR(hr); } IntPtr refData; InstallReference instRef = new InstallReference(Guid.Empty, String.Empty, String.Empty); hr = item.GetReference(out refData, 0, IntPtr.Zero); if (hr < 0) { Marshal.ThrowExceptionForHR(hr); } Marshal.PtrToStructure(refData, instRef); return instRef; } private IInstallReferenceEnum refEnum; } [ComVisible(false)] internal class Utils { [DllImport("fusion.dll")] internal static extern int CreateAssemblyEnum( out IAssemblyEnum ppEnum, IntPtr pUnkReserved, IAssemblyName pName, AssemblyCacheFlags flags, IntPtr pvReserved); [DllImport("fusion.dll")] internal static extern int CreateAssemblyNameObject( out IAssemblyName ppAssemblyNameObj, [MarshalAs(UnmanagedType.LPWStr)] String szAssemblyName, CreateAssemblyNameObjectFlags flags, IntPtr pvReserved); [DllImport("fusion.dll")] internal static extern int CreateAssemblyCache( out IAssemblyCache ppAsmCache, int reserved); [DllImport("fusion.dll")] internal static extern int CreateInstallReferenceEnum( out IInstallReferenceEnum ppRefEnum, IAssemblyName pName, int dwFlags, IntPtr pvReserved); } }