File: system\runtime\interopservices\registrationservices.cs
Project: ndp\clr\src\bcl\mscorlib.csproj (mscorlib)
// ==++==
// 
//   Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// ==--==
/*=============================================================================
**
** Class: RegistrationServices
**
**
** Purpose: This class provides services for registering and unregistering
**          a managed server for use by COM.
**
**
**
**
** Change the way how to register and unregister a managed server
**
=============================================================================*/
namespace System.Runtime.InteropServices {
    
    using System;
    using System.Collections;
    using System.IO;
    using System.Reflection;
    using System.Security;
    using System.Security.Permissions;
    using System.Text;
    using System.Threading;
    using Microsoft.Win32;
    using System.Runtime.CompilerServices;
    using System.Globalization;
    using System.Runtime.Versioning;
    using System.Diagnostics.Contracts;
 
    [Flags]
    public enum RegistrationClassContext
    {
    
 
        InProcessServer                 = 0x1, 
        InProcessHandler                = 0x2, 
        LocalServer                     = 0x4, 
        InProcessServer16               = 0x8,
        RemoteServer                    = 0x10,
        InProcessHandler16              = 0x20,
        Reserved1                       = 0x40,
        Reserved2                       = 0x80,
        Reserved3                       = 0x100,
        Reserved4                       = 0x200,
        NoCodeDownload                  = 0x400,
        Reserved5                       = 0x800,
        NoCustomMarshal                 = 0x1000,
        EnableCodeDownload              = 0x2000,
        NoFailureLog                    = 0x4000,
        DisableActivateAsActivator      = 0x8000,
        EnableActivateAsActivator       = 0x10000,
        FromDefaultContext              = 0x20000
    }
 
 
    [Flags]
    public enum RegistrationConnectionType
    {
        SingleUse                = 0, 
        MultipleUse              = 1, 
        MultiSeparate            = 2, 
        Suspended                = 4, 
        Surrogate                = 8, 
    }
 
    [Guid("475E398F-8AFA-43a7-A3BE-F4EF8D6787C9")]
    [ClassInterface(ClassInterfaceType.None)]
[System.Runtime.InteropServices.ComVisible(true)]
    public class RegistrationServices : IRegistrationServices
    {
        #region Constants
 
        private const String strManagedCategoryGuid = "{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}";
        private const String strDocStringPrefix = "";
        private const String strManagedTypeThreadingModel = "Both";
        private const String strComponentCategorySubKey = "Component Categories";
        private const String strManagedCategoryDescription = ".NET Category";
        private const String strImplementedCategoriesSubKey = "Implemented Categories";       
        private const String strMsCorEEFileName = "mscoree.dll";
        private const String strRecordRootName = "Record";      
        private const String strClsIdRootName = "CLSID";  
        private const String strTlbRootName = "TypeLib";
        private static Guid s_ManagedCategoryGuid = new Guid(strManagedCategoryGuid);
 
        #endregion
 
        
        #region IRegistrationServices
 
        [System.Security.SecurityCritical]  // auto-generated_required
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        public virtual bool RegisterAssembly(Assembly assembly, AssemblyRegistrationFlags flags)
        {
            // Validate the arguments.
            if (assembly == null)
                throw new ArgumentNullException("assembly");
 
            if (assembly.ReflectionOnly)
                throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_AsmLoadedForReflectionOnly"));
            Contract.EndContractBlock();
 
            RuntimeAssembly rtAssembly = assembly as RuntimeAssembly;
            if (rtAssembly == null)
                throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeAssembly"));
 
            // Retrieve the assembly names.
            String strAsmName = assembly.FullName;
            if (strAsmName == null)
                throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_NoAsmName"));
 
            // Retrieve the assembly codebase.
            String strAsmCodeBase = null;
            if ((flags & AssemblyRegistrationFlags.SetCodeBase) != 0)
            {
                strAsmCodeBase = rtAssembly.GetCodeBase(false);
                if (strAsmCodeBase == null)
                    throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_NoAsmCodeBase"));
            }
 
            // Go through all the registerable types in the assembly and register them.
            Type[] aTypes = GetRegistrableTypesInAssembly(assembly);
            int NumTypes = aTypes.Length;
 
            String strAsmVersion = rtAssembly.GetVersion().ToString();
            
            // Retrieve the runtime version used to build the assembly.
            String strRuntimeVersion = assembly.ImageRuntimeVersion;
 
            for (int cTypes = 0; cTypes < NumTypes; cTypes++)
            {
                if (IsRegisteredAsValueType(aTypes[cTypes]))
                    RegisterValueType(aTypes[cTypes], strAsmName, strAsmVersion, strAsmCodeBase, strRuntimeVersion);
                else if (TypeRepresentsComType(aTypes[cTypes]))
                    RegisterComImportedType(aTypes[cTypes], strAsmName, strAsmVersion, strAsmCodeBase, strRuntimeVersion);
                else
                    RegisterManagedType(aTypes[cTypes], strAsmName, strAsmVersion, strAsmCodeBase, strRuntimeVersion);
 
                CallUserDefinedRegistrationMethod(aTypes[cTypes], true);
            }
 
            // If this assembly has the PIA attribute, then register it as a PIA.
            Object[] aPIAAttrs = assembly.GetCustomAttributes(typeof(PrimaryInteropAssemblyAttribute), false);
            int NumPIAAttrs = aPIAAttrs.Length;
            for (int cPIAAttrs = 0; cPIAAttrs < NumPIAAttrs; cPIAAttrs++)
                RegisterPrimaryInteropAssembly(rtAssembly, strAsmCodeBase, (PrimaryInteropAssemblyAttribute)aPIAAttrs[cPIAAttrs]);
 
            // Return value indicating if we actually registered any types.
            if (aTypes.Length > 0 || NumPIAAttrs > 0)
                return true;
            else 
                return false;
        }
 
        [System.Security.SecurityCritical]  // auto-generated_required
        public virtual bool UnregisterAssembly(Assembly assembly)
        {
            // Validate the arguments.
            if (assembly == null)
                throw new ArgumentNullException("assembly");
 
            if (assembly.ReflectionOnly)
                throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_AsmLoadedForReflectionOnly"));
            Contract.EndContractBlock();
 
            RuntimeAssembly rtAssembly = assembly as RuntimeAssembly;
            if (rtAssembly == null)
                throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeAssembly"));
 
            bool bAllVersionsGone = true;
 
            // Go through all the registrable types in the assembly and register them.
            Type[] aTypes = GetRegistrableTypesInAssembly(assembly);
            int NumTypes = aTypes.Length;
 
            // Retrieve the assembly version
            String strAsmVersion = rtAssembly.GetVersion().ToString();
            for (int cTypes = 0;cTypes < NumTypes;cTypes++)
            {
                CallUserDefinedRegistrationMethod(aTypes[cTypes], false);
 
                if (IsRegisteredAsValueType(aTypes[cTypes]))
                {
                    if (!UnregisterValueType(aTypes[cTypes], strAsmVersion))
                        bAllVersionsGone = false;
                }
                else if (TypeRepresentsComType(aTypes[cTypes]))
                {
                    if (!UnregisterComImportedType(aTypes[cTypes], strAsmVersion))
                        bAllVersionsGone = false;
                }
                else
                {
                    if (!UnregisterManagedType(aTypes[cTypes], strAsmVersion))
                        bAllVersionsGone = false;
                }
            }
 
            // If this assembly has the PIA attribute, then unregister it as a PIA.
            Object[] aPIAAttrs = assembly.GetCustomAttributes(typeof(PrimaryInteropAssemblyAttribute),false);
            int NumPIAAttrs = aPIAAttrs.Length;
            if (bAllVersionsGone)
            {
                for (int cPIAAttrs = 0;cPIAAttrs < NumPIAAttrs;cPIAAttrs++)
                    UnregisterPrimaryInteropAssembly(assembly, (PrimaryInteropAssemblyAttribute)aPIAAttrs[cPIAAttrs]);
            }
 
            // Return value indicating if we actually un-registered any types.
            if (aTypes.Length > 0 || NumPIAAttrs > 0)
                return true;
            else 
                return false;
        }
 
        [System.Security.SecurityCritical]  // auto-generated_required
        public virtual Type[] GetRegistrableTypesInAssembly(Assembly assembly)
        {
            // Validate the arguments.
            if (assembly == null)
                throw new ArgumentNullException("assembly");
            Contract.EndContractBlock();
 
            if (!(assembly is RuntimeAssembly))
                throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeAssembly"), "assembly");
 
            // Retrieve the list of types in the assembly.
            Type[] aTypes = assembly.GetExportedTypes();
            int NumTypes = aTypes.Length;
 
            // Create an array list that will be filled in.
            ArrayList TypeList = new ArrayList();
 
            // Register all the types that require registration.
            for (int cTypes = 0; cTypes < NumTypes; cTypes++)
            {
                Type CurrentType = aTypes[cTypes];
                if (TypeRequiresRegistration(CurrentType))
                    TypeList.Add(CurrentType);
            }
 
            // Copy the array list to an array and return it.
            Type[] RetArray = new Type[TypeList.Count];
            TypeList.CopyTo(RetArray);
            return RetArray;
        }
 
        [System.Security.SecurityCritical]  // auto-generated_required
        public virtual String GetProgIdForType(Type type)
        {
            return Marshal.GenerateProgIdForType(type);
        }
 
        [System.Security.SecurityCritical]  // auto-generated_required
        public virtual void RegisterTypeForComClients(Type type, ref Guid g)
        {
#if FEATURE_COMINTEROP_MANAGED_ACTIVATION
            if(type == null)
                throw new ArgumentNullException("type");
            Contract.EndContractBlock();
            if((type as RuntimeType) == null)
                throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeType"),"type");
            if(!TypeRequiresRegistration(type))
                throw new ArgumentException(Environment.GetResourceString("Argument_TypeMustBeComCreatable"),"type");
            
            // Call the native method to do CoRegisterClassObject
            RegisterTypeForComClientsNative(type, ref g);
#else // FEATURE_COMINTEROP_MANAGED_ACTIVATION
            throw new NotImplementedException("CoreCLR_REMOVED -- managed activation removed"); // @
#endif // FEATURE_COMINTEROP_MANAGED_ACTIVATION
        }
 
        public virtual Guid GetManagedCategoryGuid()
        {
            return s_ManagedCategoryGuid;
        }
 
        [System.Security.SecurityCritical]  // auto-generated_required
        public virtual bool TypeRequiresRegistration(Type type)
        {
            return TypeRequiresRegistrationHelper(type);
        }
 
        [System.Security.SecuritySafeCritical]  // auto-generated
        public virtual bool TypeRepresentsComType(Type type)
        {
            // If the type is not a COM import, then it does not represent a COM type.
            if (!type.IsCOMObject)
                return false;
 
            // If it is marked as tdImport, then it represents a COM type directly.
            if (type.IsImport)
                return true;
 
            // If the type is derived from a tdImport class and has the same GUID as the
            // imported class, then it represents a COM type.
            Type baseComImportType = GetBaseComImportType(type);
            Contract.Assert(baseComImportType != null, "baseComImportType != null");
            if (Marshal.GenerateGuidForType(type) == Marshal.GenerateGuidForType(baseComImportType))
                return true;
 
            return false;
        }
 
        #endregion
 
        
        #region Public methods not on IRegistrationServices
        [System.Security.SecurityCritical]  // auto-generated_required
        [ComVisible(false)]
        public virtual int RegisterTypeForComClients(Type type, RegistrationClassContext classContext, RegistrationConnectionType flags)
        {
#if FEATURE_COMINTEROP_MANAGED_ACTIVATION
            if (type == null)
                throw new ArgumentNullException("type");
            Contract.EndContractBlock();
            if ((type as RuntimeType) == null)
                throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeType"),"type");
            if (!TypeRequiresRegistration(type))
                throw new ArgumentException(Environment.GetResourceString("Argument_TypeMustBeComCreatable"),"type");
            
            // Call the native method to do CoRegisterClassObject
            return RegisterTypeForComClientsExNative(type, classContext, flags);
#else // FEATURE_COMINTEROP_MANAGED_ACTIVATION
            throw new NotImplementedException("CoreCLR_REMOVED -- managed activation removed"); // @
#endif // FEATURE_COMINTEROP_MANAGED_ACTIVATION
        }
 
        [System.Security.SecurityCritical]  // auto-generated_required
        [ComVisible(false)]
        public virtual void UnregisterTypeForComClients(int cookie)
        {
            // Call the native method to do CoRevokeClassObject.
            CoRevokeClassObject(cookie);
        }
 
        #endregion
 
 
        #region Internal helpers
 
        [System.Security.SecurityCritical]  // auto-generated_required
        internal static bool TypeRequiresRegistrationHelper(Type type)
        {
            // If the type is not a class or a value class, then it does not get registered.
            if (!type.IsClass && !type.IsValueType)
                return false;
 
            // If the type is abstract then it does not get registered.
            if (type.IsAbstract)
                return false;
 
            // If the does not have a public default constructor then is not creatable from COM so 
            // it does not require registration unless it is a value class.
            if (!type.IsValueType && type.GetConstructor(BindingFlags.Instance | BindingFlags.Public,null,new Type[0],null) == null)
                return false;
 
            // All other conditions are met so check to see if the type is visible from COM.
            return Marshal.IsTypeVisibleFromCom(type);
        }
 
        #endregion
 
 
        #region Private helpers
 
        [System.Security.SecurityCritical]  // auto-generated
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)]
        private void RegisterValueType(Type type, String strAsmName, String strAsmVersion, String strAsmCodeBase, String strRuntimeVersion)
        {
            // Retrieve some information that will be used during the registration process.
            String strRecordId = "{" + Marshal.GenerateGuidForType(type).ToString().ToUpper(CultureInfo.InvariantCulture) + "}";           
 
            // Create the HKEY_CLASS_ROOT\Record key.
            using (RegistryKey RecordRootKey = Registry.ClassesRoot.CreateSubKey(strRecordRootName))
            {
                // Create the HKEY_CLASS_ROOT\Record\<RecordID> key.
                using (RegistryKey RecordKey = RecordRootKey.CreateSubKey(strRecordId))
                {
                    // Create the HKEY_CLASS_ROOT\Record\<RecordId>\<version> key.
                    using (RegistryKey RecordVersionKey = RecordKey.CreateSubKey(strAsmVersion))
                    {                   
                        // Set the class value.
                        RecordVersionKey.SetValue("Class", type.FullName);
 
                        // Set the assembly value.
                        RecordVersionKey.SetValue("Assembly", strAsmName);
 
                        // Set the runtime version value.
                        RecordVersionKey.SetValue("RuntimeVersion", strRuntimeVersion);
 
                        // Set the assembly code base value if a code base was specified.
                        if (strAsmCodeBase != null)
                            RecordVersionKey.SetValue("CodeBase", strAsmCodeBase);
                    }
                }
            }
        }
 
        [System.Security.SecurityCritical]  // auto-generated
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)]
        private void RegisterManagedType(Type type, String strAsmName, String strAsmVersion, String strAsmCodeBase, String strRuntimeVersion)
        {
            //
            // Retrieve some information that will be used during the registration process.
            //
 
            String strDocString = strDocStringPrefix + type.FullName;
            String strClsId = "{" + Marshal.GenerateGuidForType(type).ToString().ToUpper(CultureInfo.InvariantCulture) + "}";
            String strProgId = GetProgIdForType(type);
 
 
            //
            // Write the actual type information in the registry.
            //
 
            if (strProgId != String.Empty)
            {
                // Create the HKEY_CLASS_ROOT\<wzProgId> key.
                using (RegistryKey TypeNameKey = Registry.ClassesRoot.CreateSubKey(strProgId))
                {
                    TypeNameKey.SetValue("", strDocString);
 
                    // Create the HKEY_CLASS_ROOT\<wzProgId>\CLSID key.
                    using (RegistryKey ProgIdClsIdKey = TypeNameKey.CreateSubKey("CLSID"))
                    {
                        ProgIdClsIdKey.SetValue("", strClsId);
                    }
                }
            }
 
            // Create the HKEY_CLASS_ROOT\CLSID key.
            using (RegistryKey ClsIdRootKey = Registry.ClassesRoot.CreateSubKey(strClsIdRootName))
            {           
                // Create the HKEY_CLASS_ROOT\CLSID\<CLSID> key.
                using (RegistryKey ClsIdKey = ClsIdRootKey.CreateSubKey(strClsId))
                {
                    ClsIdKey.SetValue("", strDocString);
 
                    // Create the HKEY_CLASS_ROOT\CLSID\<CLSID>\InprocServer32 key.
                    using (RegistryKey InProcServerKey = ClsIdKey.CreateSubKey("InprocServer32"))
                    {
                        InProcServerKey.SetValue("", strMsCorEEFileName);
                        InProcServerKey.SetValue("ThreadingModel", strManagedTypeThreadingModel);
                        InProcServerKey.SetValue("Class", type.FullName);
                        InProcServerKey.SetValue("Assembly", strAsmName);
                        InProcServerKey.SetValue("RuntimeVersion", strRuntimeVersion);
                        if (strAsmCodeBase != null)
                            InProcServerKey.SetValue("CodeBase", strAsmCodeBase);
 
                        // Create the HKEY_CLASS_ROOT\CLSID\<CLSID>\InprocServer32\<Version> subkey
                        using (RegistryKey VersionSubKey = InProcServerKey.CreateSubKey(strAsmVersion))
                        {
                            VersionSubKey.SetValue("Class", type.FullName);
                            VersionSubKey.SetValue("Assembly", strAsmName);
                            VersionSubKey.SetValue("RuntimeVersion", strRuntimeVersion);
                            if (strAsmCodeBase != null)
                                VersionSubKey.SetValue("CodeBase", strAsmCodeBase);
                        }
 
                        if (strProgId != String.Empty)
                        {
                            // Create the HKEY_CLASS_ROOT\CLSID\<CLSID>\ProdId key.
                            using (RegistryKey ProgIdKey = ClsIdKey.CreateSubKey("ProgId"))
                            {
                                ProgIdKey.SetValue("", strProgId);
                            }
                        }
                    }
 
                    // Create the HKEY_CLASS_ROOT\CLSID\<CLSID>\Implemented Categories\<Managed Category Guid> key.
                    using (RegistryKey CategoryKey = ClsIdKey.CreateSubKey(strImplementedCategoriesSubKey))
                    {
                        using (RegistryKey ManagedCategoryKey = CategoryKey.CreateSubKey(strManagedCategoryGuid)) {}
                    }
                }
            }
 
 
            //
            // Ensure that the managed category exists.
            //
 
            EnsureManagedCategoryExists();
        } 
        
        [System.Security.SecurityCritical]  // auto-generated
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)]
        private void RegisterComImportedType(Type type, String strAsmName, String strAsmVersion, String strAsmCodeBase, String strRuntimeVersion)
        {
            // Retrieve some information that will be used during the registration process.
            String strClsId = "{" + Marshal.GenerateGuidForType(type).ToString().ToUpper(CultureInfo.InvariantCulture) + "}";
 
            // Create the HKEY_CLASS_ROOT\CLSID key.
            using (RegistryKey ClsIdRootKey = Registry.ClassesRoot.CreateSubKey(strClsIdRootName))
            {
                // Create the HKEY_CLASS_ROOT\CLSID\<CLSID> key.
                using (RegistryKey ClsIdKey = ClsIdRootKey.CreateSubKey(strClsId))
                {
                    // Create the HKEY_CLASS_ROOT\CLSID\<CLSID>\InProcServer32 key.
                    using (RegistryKey InProcServerKey = ClsIdKey.CreateSubKey("InprocServer32"))
                    {              
                        // Set the class value.
                        InProcServerKey.SetValue("Class", type.FullName);
 
                        // Set the assembly value.
                        InProcServerKey.SetValue("Assembly", strAsmName);
 
                        // Set the runtime version value.
                        InProcServerKey.SetValue("RuntimeVersion", strRuntimeVersion);
 
                        // Set the assembly code base value if a code base was specified.
                        if (strAsmCodeBase != null)
                            InProcServerKey.SetValue("CodeBase", strAsmCodeBase);
 
                        // Create the HKEY_CLASS_ROOT\CLSID\<CLSID>\InprocServer32\<Version> subkey
                        using (RegistryKey VersionSubKey = InProcServerKey.CreateSubKey(strAsmVersion))
                        {
                            VersionSubKey.SetValue("Class", type.FullName);
                            VersionSubKey.SetValue("Assembly", strAsmName);
                            VersionSubKey.SetValue("RuntimeVersion", strRuntimeVersion);
                            if (strAsmCodeBase != null)
                                VersionSubKey.SetValue("CodeBase", strAsmCodeBase);
                        }
                    }
                }
            }
        }
 
        [System.Security.SecurityCritical]  // auto-generated
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        private bool UnregisterValueType(Type type, String strAsmVersion)
        {
            bool bAllVersionsGone = true;
 
            // Try to open the HKEY_CLASS_ROOT\Record key.
            String strRecordId = "{" + Marshal.GenerateGuidForType(type).ToString().ToUpper(CultureInfo.InvariantCulture) + "}";
            
            using (RegistryKey RecordRootKey = Registry.ClassesRoot.OpenSubKey(strRecordRootName, true))
            {
                if (RecordRootKey != null)
                {
                    // Open the HKEY_CLASS_ROOT\Record\{RecordId} key.
                    using (RegistryKey RecordKey = RecordRootKey.OpenSubKey(strRecordId,true))
                    {
                        if (RecordKey != null)
                        {
                            using (RegistryKey VersionSubKey = RecordKey.OpenSubKey(strAsmVersion,true))
                            {
                                if (VersionSubKey != null)
                                {
                                    // Delete the values we created.
                                    VersionSubKey.DeleteValue("Assembly",false);
                                    VersionSubKey.DeleteValue("Class",false);
                                    VersionSubKey.DeleteValue("CodeBase",false);
                                    VersionSubKey.DeleteValue("RuntimeVersion",false);
 
                                    // delete the version sub key if no value or subkeys under it
                                    if ((VersionSubKey.SubKeyCount == 0) && (VersionSubKey.ValueCount == 0))
                                        RecordKey.DeleteSubKey(strAsmVersion);
                                }
                            }
 
                            // If there are sub keys left then there are versions left.
                            if (RecordKey.SubKeyCount != 0)
                                bAllVersionsGone = false;
 
                            // If there are no other values or subkeys then we can delete the HKEY_CLASS_ROOT\Record\{RecordId}.
                            if ((RecordKey.SubKeyCount == 0) && (RecordKey.ValueCount == 0))
                                RecordRootKey.DeleteSubKey(strRecordId);
                        }
                    }
 
                    // If there are no other values or subkeys then we can delete the HKEY_CLASS_ROOT\Record.
                    if ((RecordRootKey.SubKeyCount == 0) && (RecordRootKey.ValueCount == 0))
                        Registry.ClassesRoot.DeleteSubKey(strRecordRootName);
                }
            }
 
            return bAllVersionsGone;
        }
 
        // UnregisterManagedType
        //
        // Return :
        //      true:   All versions are gone.
        //      false:  Some versions are still left in registry
        [System.Security.SecurityCritical]  // auto-generated
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        private bool UnregisterManagedType(Type type,String strAsmVersion)
        {
            bool bAllVersionsGone = true;
            
            //
            // Create the CLSID string.
            //
 
            String strClsId = "{" + Marshal.GenerateGuidForType(type).ToString().ToUpper(CultureInfo.InvariantCulture) + "}";
            String strProgId = GetProgIdForType(type);
 
 
            //
            // Remove the entries under HKEY_CLASS_ROOT\CLSID key.
            //
 
            using (RegistryKey ClsIdRootKey = Registry.ClassesRoot.OpenSubKey(strClsIdRootName, true))
            {
                if (ClsIdRootKey != null)
                {
                    //
                    // Remove the entries under HKEY_CLASS_ROOT\CLSID\<CLSID> key.
                    //
 
                    using (RegistryKey ClsIdKey = ClsIdRootKey.OpenSubKey(strClsId, true))
                    {
                        if (ClsIdKey != null)
                        {
                            //
                            // Remove the entries in the HKEY_CLASS_ROOT\CLSID\<CLSID>\InprocServer32 key.
                            //
 
                            using (RegistryKey InProcServerKey = ClsIdKey.OpenSubKey("InprocServer32", true))
                            {
                                if (InProcServerKey != null)
                                {
                                    //
                                    // Remove the entries in HKEY_CLASS_ROOT\CLSID\<CLSID>\InprocServer32\<Version>
                                    //
 
                                    using (RegistryKey VersionSubKey = InProcServerKey.OpenSubKey(strAsmVersion, true))
                                    {
                                        if (VersionSubKey != null)
                                        {
                                            // Delete the values we created
                                            VersionSubKey.DeleteValue("Assembly",false);
                                            VersionSubKey.DeleteValue("Class",false);
                                            VersionSubKey.DeleteValue("RuntimeVersion",false);
                                            VersionSubKey.DeleteValue("CodeBase",false);
 
                                            // If there are no other values or subkeys then we can delete the VersionSubKey.
                                            if ((VersionSubKey.SubKeyCount == 0) && (VersionSubKey.ValueCount == 0))
                                                InProcServerKey.DeleteSubKey(strAsmVersion);
                                        }
                                    }
 
                                    // If there are sub keys left then there are versions left.
                                    if (InProcServerKey.SubKeyCount != 0)
                                        bAllVersionsGone = false;
 
                                    // If there are no versions left, then delete the threading model and default value.
                                    if (bAllVersionsGone)
                                    {
                                        InProcServerKey.DeleteValue("",false);
                                        InProcServerKey.DeleteValue("ThreadingModel",false);
                                    }
 
                                    InProcServerKey.DeleteValue("Assembly",false);
                                    InProcServerKey.DeleteValue("Class",false);
                                    InProcServerKey.DeleteValue("RuntimeVersion",false);
                                    InProcServerKey.DeleteValue("CodeBase",false);
 
                                    // If there are no other values or subkeys then we can delete the InProcServerKey.
                                    if ((InProcServerKey.SubKeyCount == 0) && (InProcServerKey.ValueCount == 0))
                                        ClsIdKey.DeleteSubKey("InprocServer32");
                                }
                            }
 
                            // remove HKEY_CLASS_ROOT\CLSID\<CLSID>\ProgId
                            // and HKEY_CLASS_ROOT\CLSID\<CLSID>\Implemented Category
                            // only when all versions are removed
                            if (bAllVersionsGone)
                            {
                                // Delete the value we created.
                                ClsIdKey.DeleteValue("",false);
 
                                if (strProgId != String.Empty)
                                {
                                    //
                                    // Remove the entries in the HKEY_CLASS_ROOT\CLSID\<CLSID>\ProgId key.
                                    //
 
                                    using (RegistryKey ProgIdKey = ClsIdKey.OpenSubKey("ProgId", true))
                                    {
                                        if (ProgIdKey != null)
                                        {
                                            // Delete the value we created.
                                            ProgIdKey.DeleteValue("",false);
 
                                            // If there are no other values or subkeys then we can delete the ProgIdSubKey.
                                            if ((ProgIdKey.SubKeyCount == 0) && (ProgIdKey.ValueCount == 0))
                                                ClsIdKey.DeleteSubKey("ProgId");
                                        }
                                    }
                                }
                
            
                                //
                                // Remove entries in the  HKEY_CLASS_ROOT\CLSID\<CLSID>\Implemented Categories\<Managed Category Guid> key.
                                //
        
                                using (RegistryKey CategoryKey = ClsIdKey.OpenSubKey(strImplementedCategoriesSubKey, true))
                                {
                                    if (CategoryKey != null)
                                    {
                                        using (RegistryKey ManagedCategoryKey = CategoryKey.OpenSubKey(strManagedCategoryGuid, true))
                                        {
                                            if (ManagedCategoryKey != null)
                                            {
                                                // If there are no other values or subkeys then we can delete the ManagedCategoryKey.
                                                if ((ManagedCategoryKey.SubKeyCount == 0) && (ManagedCategoryKey.ValueCount == 0))
                                                    CategoryKey.DeleteSubKey(strManagedCategoryGuid);
                                            }
                                        }
 
                                        // If there are no other values or subkeys then we can delete the CategoryKey.
                                        if ((CategoryKey.SubKeyCount == 0) && (CategoryKey.ValueCount == 0))
                                            ClsIdKey.DeleteSubKey(strImplementedCategoriesSubKey);
                                    }
                                }
                            }
 
                            // If there are no other values or subkeys then we can delete the ClsIdKey.
                            if ((ClsIdKey.SubKeyCount == 0) && (ClsIdKey.ValueCount == 0))
                                ClsIdRootKey.DeleteSubKey(strClsId);
                        }
                    }
 
                    // If there are no other values or subkeys then we can delete the CLSID key.
                    if ((ClsIdRootKey.SubKeyCount == 0) && (ClsIdRootKey.ValueCount == 0))
                        Registry.ClassesRoot.DeleteSubKey(strClsIdRootName);
                }
            
 
                //
                // Remove the entries under HKEY_CLASS_ROOT\<wzProgId> key.
                //
 
                if (bAllVersionsGone)
                {
                    if (strProgId != String.Empty)
                    {
                        using (RegistryKey TypeNameKey = Registry.ClassesRoot.OpenSubKey(strProgId, true))
                        {                            
                            if (TypeNameKey != null)
                            {
                                // Delete the values we created.
                                TypeNameKey.DeleteValue("",false);
 
 
                                //
                                // Remove the entries in the HKEY_CLASS_ROOT\<wzProgId>\CLSID key.
                                //
 
                                using (RegistryKey ProgIdClsIdKey = TypeNameKey.OpenSubKey("CLSID", true))
                                {
                                    if (ProgIdClsIdKey != null)
                                    {
                                        // Delete the values we created.
                                        ProgIdClsIdKey.DeleteValue("",false);
 
                                        // If there are no other values or subkeys then we can delete the ProgIdClsIdKey.
                                        if ((ProgIdClsIdKey.SubKeyCount == 0) && (ProgIdClsIdKey.ValueCount == 0))
                                            TypeNameKey.DeleteSubKey("CLSID");
                                    }
                                }
 
                                // If there are no other values or subkeys then we can delete the TypeNameKey.
                                if ((TypeNameKey.SubKeyCount == 0) && (TypeNameKey.ValueCount == 0))
                                    Registry.ClassesRoot.DeleteSubKey(strProgId);
                            }
                        }
                    }
                }
            }
 
            return bAllVersionsGone;
        }
 
        // UnregisterComImportedType
        // Return:
        //      true:      All version information are gone.
        //      false:     There are still some version left in registry
        [System.Security.SecurityCritical]  // auto-generated
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        private bool UnregisterComImportedType(Type type, String strAsmVersion)
        {
            bool bAllVersionsGone = true;
            
            String strClsId = "{" + Marshal.GenerateGuidForType(type).ToString().ToUpper(CultureInfo.InvariantCulture) + "}";
        
            // Try to open the HKEY_CLASS_ROOT\CLSID key.
            using (RegistryKey ClsIdRootKey = Registry.ClassesRoot.OpenSubKey(strClsIdRootName, true))
            {
                if (ClsIdRootKey != null)
                {
                    // Try to open the HKEY_CLASS_ROOT\CLSID\<CLSID> key.
                    using (RegistryKey ClsIdKey = ClsIdRootKey.OpenSubKey(strClsId, true))
                    {
                        if (ClsIdKey != null)
                        {
                            // Try to open the HKEY_CLASS_ROOT\CLSID\<CLSID>\InProcServer32 key.
                            using (RegistryKey InProcServerKey = ClsIdKey.OpenSubKey("InprocServer32", true))
                            {
                                if (InProcServerKey != null)
                                {
                                    // Delete the values we created.
                                    InProcServerKey.DeleteValue("Assembly",false);
                                    InProcServerKey.DeleteValue("Class",false);
                                    InProcServerKey.DeleteValue("RuntimeVersion",false);
                                    InProcServerKey.DeleteValue("CodeBase",false);
                                
                                    // Try to open the entries in HKEY_CLASS_ROOT\CLSID\<CLSID>\InProcServer32\<Version>
                                    using (RegistryKey VersionSubKey = InProcServerKey.OpenSubKey(strAsmVersion,true))
                                    {
                                        if (VersionSubKey != null)
                                        {
                                            // Delete the value we created
                                            VersionSubKey.DeleteValue("Assembly",false);
                                            VersionSubKey.DeleteValue("Class",false);
                                            VersionSubKey.DeleteValue("RuntimeVersion",false);
                                            VersionSubKey.DeleteValue("CodeBase",false);
 
                                            // If there are no other values or subkeys then we can delete the VersionSubKey
                                            if ((VersionSubKey.SubKeyCount == 0) && (VersionSubKey.ValueCount == 0))
                                                InProcServerKey.DeleteSubKey(strAsmVersion);
                                        }
                                    }
 
                                    // If there are sub keys left then there are versions left.
                                    if (InProcServerKey.SubKeyCount != 0)
                                        bAllVersionsGone = false;
 
                                    // If there are no other values or subkeys then we can delete the InProcServerKey.
                                    if ((InProcServerKey.SubKeyCount == 0) && (InProcServerKey.ValueCount == 0))
                                        ClsIdKey.DeleteSubKey("InprocServer32");
                                }
                            }
 
                            // If there are no other values or subkeys then we can delete the ClsIdKey.
                            if ((ClsIdKey.SubKeyCount == 0) && (ClsIdKey.ValueCount == 0))
                                ClsIdRootKey.DeleteSubKey(strClsId);                            
                        }                       
                    }
 
                    // If there are no other values or subkeys then we can delete the CLSID key.
                    if ((ClsIdRootKey.SubKeyCount == 0) && (ClsIdRootKey.ValueCount == 0))
                        Registry.ClassesRoot.DeleteSubKey(strClsIdRootName);                    
                }
            }
 
            return bAllVersionsGone;
        }
 
        [System.Security.SecurityCritical]  // auto-generated
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)]
        private void RegisterPrimaryInteropAssembly(RuntimeAssembly assembly, String strAsmCodeBase, PrimaryInteropAssemblyAttribute attr)
        {
            // Validate that the PIA has a strong name.
            if (assembly.GetPublicKey().Length == 0)
                throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_PIAMustBeStrongNamed"));
 
            String strTlbId = "{" + Marshal.GetTypeLibGuidForAssembly(assembly).ToString().ToUpper(CultureInfo.InvariantCulture) + "}";
            String strVersion = attr.MajorVersion.ToString("x", CultureInfo.InvariantCulture) + "." + attr.MinorVersion.ToString("x", CultureInfo.InvariantCulture);
 
            // Create the HKEY_CLASS_ROOT\TypeLib key.
            using (RegistryKey TypeLibRootKey = Registry.ClassesRoot.CreateSubKey(strTlbRootName))
            {
                // Create the HKEY_CLASS_ROOT\TypeLib\<TLBID> key.
                using (RegistryKey TypeLibKey = TypeLibRootKey.CreateSubKey(strTlbId))
                {
                    // Create the HKEY_CLASS_ROOT\TypeLib\<TLBID>\<Major.Minor> key.
                    using (RegistryKey VersionSubKey = TypeLibKey.CreateSubKey(strVersion))
                    {
                        // Create the HKEY_CLASS_ROOT\TypeLib\<TLBID>\PrimaryInteropAssembly key.
                        VersionSubKey.SetValue("PrimaryInteropAssemblyName", assembly.FullName);
                        if (strAsmCodeBase != null)
                            VersionSubKey.SetValue("PrimaryInteropAssemblyCodeBase", strAsmCodeBase);
                    }
                }
            }
        }
 
        [System.Security.SecurityCritical]  // auto-generated
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        private void UnregisterPrimaryInteropAssembly(Assembly assembly, PrimaryInteropAssemblyAttribute attr)
        {
            String strTlbId = "{" + Marshal.GetTypeLibGuidForAssembly(assembly).ToString().ToUpper(CultureInfo.InvariantCulture) + "}";
            String strVersion = attr.MajorVersion.ToString("x", CultureInfo.InvariantCulture) + "." + attr.MinorVersion.ToString("x", CultureInfo.InvariantCulture);
 
            // Try to open the HKEY_CLASS_ROOT\TypeLib key.
            using (RegistryKey TypeLibRootKey = Registry.ClassesRoot.OpenSubKey(strTlbRootName, true))
            {
                if (TypeLibRootKey != null)
                {
                    // Try to open the HKEY_CLASS_ROOT\TypeLib\<TLBID> key.
                    using (RegistryKey TypeLibKey = TypeLibRootKey.OpenSubKey(strTlbId, true))
                    {
                        if (TypeLibKey != null)
                        {
                            // Try to open the HKEY_CLASS_ROOT\TypeLib<TLBID>\<Major.Minor> key.
                            using (RegistryKey VersionSubKey = TypeLibKey.OpenSubKey(strVersion, true))
                            {
                                if (VersionSubKey != null)
                                {
                                    // Delete the values we created.
                                    VersionSubKey.DeleteValue("PrimaryInteropAssemblyName",false);
                                    VersionSubKey.DeleteValue("PrimaryInteropAssemblyCodeBase",false);
 
                                    // If there are no other values or subkeys then we can delete the VersionKey.
                                    if ((VersionSubKey.SubKeyCount == 0) && (VersionSubKey.ValueCount == 0))
                                        TypeLibKey.DeleteSubKey(strVersion);
                                }
                            }
 
                            // If there are no other values or subkeys then we can delete the TypeLibKey.
                            if ((TypeLibKey.SubKeyCount == 0) && (TypeLibKey.ValueCount == 0))
                                TypeLibRootKey.DeleteSubKey(strTlbId);                            
                        }
                    }
 
                    // If there are no other values or subkeys then we can delete the TypeLib key.
                    if ((TypeLibRootKey.SubKeyCount == 0) && (TypeLibRootKey.ValueCount == 0))
                        Registry.ClassesRoot.DeleteSubKey(strTlbRootName);                    
                }
            }
        }
 
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        private void EnsureManagedCategoryExists()
        {
            if (!ManagedCategoryExists())
            {
                // Create the HKEY_CLASS_ROOT\Component Category key.
                using (RegistryKey ComponentCategoryKey = Registry.ClassesRoot.CreateSubKey(strComponentCategorySubKey))
                {
                    // Create the HKEY_CLASS_ROOT\Component Category\<Managed Category Guid> key.
                    using (RegistryKey ManagedCategoryKey = ComponentCategoryKey.CreateSubKey(strManagedCategoryGuid))
                    {
                        ManagedCategoryKey.SetValue("0", strManagedCategoryDescription);
                    }
                }
            }
        }
 
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        private static bool ManagedCategoryExists()
        {
            using (RegistryKey componentCategoryKey = Registry.ClassesRoot.OpenSubKey(strComponentCategorySubKey, 
#if FEATURE_MACL
                                                                                      RegistryKeyPermissionCheck.ReadSubTree))
#else
                                                                                      false))
#endif
            {
                if (componentCategoryKey == null)
                    return false;
                using (RegistryKey managedCategoryKey = componentCategoryKey.OpenSubKey(strManagedCategoryGuid,
#if FEATURE_MACL
                                                                                        RegistryKeyPermissionCheck.ReadSubTree))
#else
                                                                                        false))
#endif
                {
                    if (managedCategoryKey == null)
                        return false;
                    object value = managedCategoryKey.GetValue("0");
                    if (value == null || value.GetType() != typeof(string))
                        return false;
                    string stringValue = (string)value;
                    if (stringValue != strManagedCategoryDescription)
                        return false;
                }
            }
            
            return true;
        }
        
        [System.Security.SecurityCritical]  // auto-generated
        private void CallUserDefinedRegistrationMethod(Type type, bool bRegister)
        {
            bool bFunctionCalled = false;
 
            // Retrieve the attribute type to use to determine if a function is the requested user defined
            // registration function.
            Type RegFuncAttrType = null;
            if(bRegister)
                RegFuncAttrType = typeof(ComRegisterFunctionAttribute);
            else 
                RegFuncAttrType = typeof(ComUnregisterFunctionAttribute);
 
            for(Type currType = type; !bFunctionCalled && currType != null; currType = currType.BaseType)
            {
                // Retrieve all the methods.
                MethodInfo[] aMethods = currType.GetMethods(BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Static);
                int NumMethods = aMethods.Length;
 
                // Go through all the methods and check for the ComRegisterMethod custom attribute.
                for(int cMethods = 0;cMethods < NumMethods;cMethods++)
                {
                    MethodInfo CurrentMethod = aMethods[cMethods];
 
                    // Check to see if the method has the custom attribute.
                    if(CurrentMethod.GetCustomAttributes(RegFuncAttrType, true).Length != 0)
                    {
                        // Check to see if the method is static before we call it.
                        if(!CurrentMethod.IsStatic)
                        {
                            if(bRegister)
                                throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_NonStaticComRegFunction",CurrentMethod.Name,currType.Name));
                            else
                                throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_NonStaticComUnRegFunction",CurrentMethod.Name,currType.Name));
                        }
 
                        // Finally check that the signature is string ret void.
                        ParameterInfo[] aParams = CurrentMethod.GetParameters();
                        if (CurrentMethod.ReturnType != typeof(void) || 
                            aParams == null ||
                            aParams.Length != 1 || 
                            (aParams[0].ParameterType != typeof(String) && aParams[0].ParameterType != typeof(Type)))
                        {
                            if(bRegister)
                                throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_InvalidComRegFunctionSig",CurrentMethod.Name,currType.Name));
                            else
                                throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_InvalidComUnRegFunctionSig",CurrentMethod.Name,currType.Name));
                        }
 
                        // There can only be one register and one unregister function per type.
                        if(bFunctionCalled)
                        {
                            if(bRegister)
                                throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_MultipleComRegFunctions",currType.Name));
                            else
                                throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_MultipleComUnRegFunctions",currType.Name));
                        }
 
                        // The function is valid so set up the arguments to call it.
                        Object[] objs = new Object[1];
                        if(aParams[0].ParameterType == typeof(String))
                        {
                            // We are dealing with the string overload of the function.
                            objs[0] = "HKEY_CLASSES_ROOT\\CLSID\\{" + Marshal.GenerateGuidForType(type).ToString().ToUpper(CultureInfo.InvariantCulture) + "}";
                        }
                        else
                        {
                            // We are dealing with the type overload of the function.
                            objs[0] = type;
                        }
 
                        // Invoke the COM register function.
                        CurrentMethod.Invoke(null, objs);
 
                        // Mark the function as having been called.
                        bFunctionCalled = true;
                    }
                }
            }
        }
 
        private Type GetBaseComImportType(Type type)
        {
            for (; type != null && !type.IsImport; type = type.BaseType);
            return type;
        }
 
        private bool IsRegisteredAsValueType(Type type)
        {
            if (!type.IsValueType)
                return false;
 
            return true;
        }
 
        #endregion
 
    
        #region FCalls and DllImports
 
#if FEATURE_COMINTEROP_MANAGED_ACTIVATION
        // GUID versioning can be controlled by using the GuidAttribute or 
        // letting the runtime generate it based on type and assembly strong name.
        [System.Security.SecurityCritical]  // auto-generated
        [ResourceExposure(ResourceScope.None)]
        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        private static extern void RegisterTypeForComClientsNative(Type type,ref Guid g);
        
        // GUID versioning can be controlled by using the GuidAttribute or 
        // letting the runtime generate it based on type and assembly strong name.
        [System.Security.SecurityCritical]  // auto-generated
        [ResourceExposure(ResourceScope.None)]
        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        private static extern int RegisterTypeForComClientsExNative(Type t, RegistrationClassContext clsContext, RegistrationConnectionType flags);
#endif // FEATURE_COMINTEROP_MANAGED_ACTIVATION
 
        [DllImport(Win32Native.OLE32,CharSet=CharSet.Auto,PreserveSig=false)]
        [ResourceExposure(ResourceScope.None)]
        private static extern void CoRevokeClassObject(int cookie);
        #endregion
    }
}