/// <summary>
        /// Registers the server associations.
        /// </summary>
        /// <param name="serverClsid">The server CLSID.</param>
        /// <param name="serverType">Type of the server.</param>
        /// <param name="serverName">Name of the server.</param>
        /// <param name="associationAttributes">The association attributes.</param>
        /// <param name="registrationType">Type of the registration.</param>
        internal static void RegisterServerAssociations(Guid serverClsid, ServerType serverType, string serverName, 
            IEnumerable<COMServerAssociationAttribute> associationAttributes, RegistrationType registrationType)
        {
            //  Go through each association.
            foreach (var associationAttribute in associationAttributes)
            {
                //  Get the assocation classes.
                var associationClassNames = CreateClassNamesForAssociations(associationAttribute.AssociationType,
                    associationAttribute.Associations, registrationType);

                //  Open the classes key.
                using (var classesKey = OpenClassesRoot(registrationType))
                {
                    //  For each one, create the server type key.
                    foreach (var associationClassName in associationClassNames)
                    {
                        //  Create the server key.
                        using (var serverKey = classesKey.CreateSubKey(GetKeyForServerType(associationClassName, serverType, serverName)))
                        {
                            //  Set the server class id.
                            if (serverKey != null)
                                serverKey.SetValue(null, serverClsid.ToRegistryString());
                        }

                        //  If we're a shell icon handler, we must also set the defaulticon.
                        if (serverType == ServerType.ShellIconHandler)
                            SetIconHandlerDefaultIcon(classesKey, associationClassName);
                    }
                }
            }
        }
        /// <summary>
        /// Determines whether an extension is approved.
        /// </summary>
        /// <param name="serverClsid">The server CLSID.</param>
        /// <param name="registrationType">Type of the registration.</param>
        /// <returns>
        ///   <c>true</c> if the extension is approved; otherwise, <c>false</c>.
        /// </returns>
        /// <exception cref="System.InvalidOperationException">Failed to open the Approved Extensions key.</exception>
        private static bool IsExtensionApproved(Guid serverClsid, RegistrationType registrationType)
        {
            //  Open the approved extensions key.
            using (var approvedKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine,
                registrationType == RegistrationType.OS64Bit ? RegistryView.Registry64 : RegistryView.Registry32)
                .OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved", RegistryKeyPermissionCheck.ReadSubTree))
            {
                //  If we can't open the key, we're going to have problems.
                if (approvedKey == null)
                    throw new InvalidOperationException("Failed to open the Approved Extensions key.");

                return approvedKey.GetValueNames().Any(vn => vn.Equals(serverClsid.ToRegistryString(), StringComparison.OrdinalIgnoreCase));
            }
        }
        /// <summary>
        /// Gets the server registration info.
        /// </summary>
        /// <param name="serverCLSID">The server CLSID.</param>
        /// <param name="registrationType">Type of the registration.</param>
        /// <returns>
        /// The ServerRegistrationInfo if the server is registered, otherwise false.
        /// </returns>
        public static ShellExtensionRegistrationInfo GetServerRegistrationInfo(Guid serverCLSID, RegistrationType registrationType)
        {
            //  We can very quickly check to see if the server is approved.
            bool serverApproved = IsExtensionApproved(serverCLSID, registrationType);

            //  Open the classes.
            using (var classesKey = OpenClassesKey(registrationType, RegistryKeyPermissionCheck.ReadSubTree))
            {
                //  Do we have a subkey for the server?
                using (var serverClassKey = classesKey.OpenSubKey(serverCLSID.ToRegistryString()))
                {
                    //  If there's no subkey, the server isn't registered.
                    if (serverClassKey == null)
                        return null;

                    //  Do we have an InProc32 server?
                    using(var inproc32ServerKey = serverClassKey.OpenSubKey(KeyName_InProc32))
                    {
                        //  If we do, we can return the server info for an inproc 32 server.
                        if (inproc32ServerKey != null)
                        {
                            //  Get the default value.
                            var defaultValue = GetValueOrEmpty(inproc32ServerKey, null);

                            //  If we default value is null or empty, we've got a partially registered server.
                            if (string.IsNullOrEmpty(defaultValue))
                                return new ShellExtensionRegistrationInfo(ServerRegistationType.PartiallyRegistered, serverCLSID);

                            //  Get the threading model.
                            var threadingModel = GetValueOrEmpty(inproc32ServerKey, KeyName_ThreadingModel);

                            //  Is it a .NET server?
                            if (defaultValue == KeyValue_NetFrameworkServer)
                            {
                                //  We've got a .NET server. We should have one subkey, with the assembly version.
                                var subkeyName = inproc32ServerKey.GetSubKeyNames().FirstOrDefault();

                                //  If we have no subkey name, we've got a partially registered server.
                                if (subkeyName == null)
                                    return new ShellExtensionRegistrationInfo(ServerRegistationType.PartiallyRegistered, serverCLSID);

                                //  Otherwise we now have the assembly version.
                                var assemblyVersion = subkeyName;

                                //  Open the assembly subkey.
                                using (var assemblySubkey = inproc32ServerKey.OpenSubKey(assemblyVersion))
                                {
                                    //  If we can't open the key, we've got a problem.
                                    if (assemblySubkey == null)
                                        throw new InvalidOperationException("Can't open the details of the server.");

                                    //  Read the managed server details.
                                    var assembly = GetValueOrEmpty(assemblySubkey, KeyName_Assembly);
                                    var @class = GetValueOrEmpty(assemblySubkey, KeyName_Class);
                                    var runtimeVersion = GetValueOrEmpty(assemblySubkey, KeyName_RuntimeVersion);
                                    var codeBase = assemblySubkey.GetValue(KeyName_CodeBase, null);

                                    //  Return the server info.
                                    return new ShellExtensionRegistrationInfo(ServerRegistationType.ManagedInProc32, serverCLSID)
                                               {
                                                   ThreadingModel = threadingModel,
                                                   Assembly = assembly,
                                                   AssemblyVersion = assemblyVersion,
                                                   Class = @class,
                                                   RuntimeVersion = runtimeVersion,
                                                   CodeBase = codeBase != null ? codeBase.ToString() : null,
                                                   IsApproved = serverApproved
                                               };
                                }
                            }

                            //  We've got a native COM server.

                            //  Return the server info.
                            return new ShellExtensionRegistrationInfo(ServerRegistationType.NativeInProc32, serverCLSID)
                                       {
                                           ThreadingModel = threadingModel,
                                           ServerPath = defaultValue,
                                           IsApproved = serverApproved
                                       };
                        }
                    }

                    //  If by this point we haven't return server info, we've got a partially registered server.
                    return new ShellExtensionRegistrationInfo(ServerRegistationType.PartiallyRegistered, serverCLSID);
                }
            }
        }