/// <summary> /// Opens the classes key. /// </summary> /// <param name="registrationType">Type of the registration.</param> /// <param name="registrationLocation">Location of the registration.</param> /// <param name="permissions">The permissions.</param> /// <returns></returns> private static RegistryKey OpenClassesKey(RegistrationType registrationType, RegistrationLocation registrationLocation, RegistryKeyPermissionCheck permissions) { // Get the classes base key. var classesBaseKey = OpenClassesRoot(registrationType, registrationLocation); // Open classes. var classesKey = classesBaseKey.OpenSubKey(KeyName_Classes, permissions, RegistryRights.QueryValues | RegistryRights.ReadPermissions | RegistryRights.EnumerateSubKeys); if (classesKey == null) { throw new InvalidOperationException("Cannot open classes."); } return(classesKey); }
/// <summary> /// Gets the class for an extension. /// </summary> /// <param name="extension">The extension.</param> /// <param name="registrationLocation">Location of the registration.</param> /// <returns>The class for the extension.</returns> public static string GetClassForExtension(string extension, RegistrationLocation registrationLocation) { // Make sure the extension starts with a dot. if (extension.StartsWith(".") == false) { extension = "." + extension; } using (var classesKey = OpenClassesRoot(Environment.Is64BitOperatingSystem ? RegistrationType.OS64Bit : RegistrationType.OS32Bit, registrationLocation)) { // Try and get the extension key. using (var extensionKey = classesKey.OpenSubKey(extension)) { // If we don't have it, we have no server. if (extensionKey == null) { return(null); } // Otherwise, we need the default value to get the class. return(extensionKey.GetValue(null, string.Empty).ToString()); } } }
/// <summary> /// Opens the classes root. /// </summary> /// <param name="registrationType">Type of the registration.</param> /// <param name="registrationLocation">Location of the registration.</param> /// <returns>The classes root key.</returns> private static RegistryKey OpenClassesRoot(RegistrationType registrationType, RegistrationLocation registrationLocation) { // Get the classes base key. RegistryKey classesBaseKey; switch (registrationLocation.UserSid) { case RegistrationLocation.CurrentUserPseudoSid: classesBaseKey = registrationType == RegistrationType.OS64Bit ? RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry64) : RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry32); classesBaseKey = classesBaseKey.OpenSubKey(@"SOFTWARE\Classes", true); break; case RegistrationLocation.LocalMachinePseudoSid: classesBaseKey = registrationType == RegistrationType.OS64Bit ? RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64) : RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32); classesBaseKey = classesBaseKey.OpenSubKey(@"SOFTWARE\Classes", true); break; default: classesBaseKey = registrationType == RegistrationType.OS64Bit ? RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Registry64) : RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Registry32); classesBaseKey = classesBaseKey.OpenSubKey(registrationLocation.UserSid + @"_Classes", true); break; } // Return the classes key. return(classesBaseKey); }
/// <summary> /// Creates the class names for associations. /// </summary> /// <param name="associationType">Type of the association.</param> /// <param name="associations">The associations.</param> /// <param name="registrationType">Type of the registration.</param> /// <param name="registrationLocation">Location of the registration.</param> /// <returns> /// The class names for the associations. /// </returns> private static IEnumerable <string> CreateClassNamesForAssociations(AssociationType associationType, IEnumerable <string> associations, RegistrationType registrationType, RegistrationLocation registrationLocation) { // Switch on the association type. switch (associationType) { case AssociationType.FileExtension: // We're dealing with file extensions only, so we can return them directly. return(associations); case AssociationType.ClassOfExtension: // Open the classes sub key. using (var classesKey = OpenClassesRoot(registrationType, registrationLocation)) { // The file type classes. var fileTypeClasses = new List <string>(); // We've got extensions, but we need the classes for them. foreach (var association in associations) { // Open the file type key. using (var fileTypeKey = classesKey.OpenSubKey(association)) { // If the file type key is null, we're done. if (fileTypeKey == null) { continue; } // Get the default value, this should be the file type class. var fileTypeClass = fileTypeKey.GetValue(null) as string; // If the file type class is valid, we can return it. fileTypeClasses.Add(fileTypeClass); } } // Return the file type classes. return(fileTypeClasses); } case AssociationType.Class: // We're dealing with classes only, so we can return them directly. return(associations); case AssociationType.AllFiles: // Return the all files class. return(new[] { SpecialClass_AllFiles }); case AssociationType.Directory: // Return the directory class. return(new[] { SpecialClass_Directory }); case AssociationType.Drive: // Return the directory class. return(new[] { SpecialClass_Drive }); case AssociationType.UnknownFiles: // Return the directory class. return(new[] { SpecialClass_UnknownFiles }); default: // Take a best guess, return the associations. return(associations); } }
/// <summary> /// Unregisters 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> /// <param name="registrationLocation">Location of the registration.</param> internal static void UnregisterServerAssociations(Guid serverClsid, ServerType serverType, string serverName, IEnumerable <COMServerAssociationAttribute> associationAttributes, RegistrationType registrationType, RegistrationLocation registrationLocation) { // Go through each association attribute. foreach (var associationAttribute in associationAttributes) { // Get the assocation classes. var associationClassNames = CreateClassNamesForAssociations(associationAttribute.AssociationType, associationAttribute.Associations, registrationType, registrationLocation); // Open the classes key. using (var classesKey = OpenClassesRoot(registrationType, registrationLocation)) { // For each one, create the server type key. foreach (var associationClassName in associationClassNames) { // Get the key for the association. var associationKeyPath = GetKeyForServerType(associationClassName, serverType, serverName); // Does it exist? bool exists; using (var associationKey = classesKey.OpenSubKey(associationKeyPath)) exists = associationKey != null; // If it does, delete it. if (exists) { classesKey.DeleteSubKeyTree(associationKeyPath); } // If we're a shell icon handler, we must also unset the defaulticon. if (serverType == ServerType.ShellIconHandler) { UnsetIconHandlerDefaultIcon(classesKey, associationClassName); } } } } }
/// <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> /// <param name="registrationLocation">Location of the registration.</param> internal static void RegisterServerAssociations(Guid serverClsid, ServerType serverType, string serverName, IEnumerable <COMServerAssociationAttribute> associationAttributes, RegistrationType registrationType, RegistrationLocation registrationLocation) { // Go through each association. foreach (var associationAttribute in associationAttributes) { // Get the assocation classes. var associationClassNames = CreateClassNamesForAssociations(associationAttribute.AssociationType, associationAttribute.Associations, registrationType, registrationLocation); // Open the classes key. using (var classesKey = OpenClassesRoot(registrationType, registrationLocation)) { // 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> /// Gets the server registration info. /// </summary> /// <param name="serverCLSID">The server CLSID.</param> /// <param name="registrationType">Type of the registration.</param> /// <param name="registrationLocation">Location of the registration.</param> /// <returns> /// The ServerRegistrationInfo if the server is registered, otherwise false. /// </returns> public static ShellExtensionRegistrationInfo GetServerRegistrationInfo(Guid serverCLSID, RegistrationType registrationType, RegistrationLocation registrationLocation) { // 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, registrationLocation, 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)); } } }
/// <summary> /// Gets the server registration info. /// </summary> /// <param name="server">The server.</param> /// <param name="registrationType">Type of the registration.</param> /// <param name="registrationLocation">Location of the registration.</param> /// <returns> /// The ServerRegistrationInfo if the server is registered, otherwise false. /// </returns> public static ShellExtensionRegistrationInfo GetServerRegistrationInfo(ISharpShellServer server, RegistrationType registrationType, RegistrationLocation registrationLocation) { // Call the main function. return(GetServerRegistrationInfo(server.ServerClsid, registrationType, registrationLocation)); }
/// <summary> /// Installs a SharpShell COM server. /// </summary> /// <param name="server">The server.</param> /// <param name="registrationType">Type of the registration.</param> /// <param name="registrationLocation">Location of the registration.</param> /// <param name="codeBase">if set to <c>true</c> use code base registration (i.e full assembly path, not the GAC).</param> public static void InstallServer(ISharpShellServer server, RegistrationType registrationType, RegistrationLocation registrationLocation, bool codeBase) { // Get the server registration information. var serverRegistrationInformation = GetServerRegistrationInfo(server, registrationType, registrationLocation); // If it is registered, unregister first. if (serverRegistrationInformation != null) { UninstallServer(server, registrationType, registrationLocation); } // Open the classes. using (var classesKey = OpenClassesKey(registrationType, registrationLocation, RegistryKeyPermissionCheck.ReadWriteSubTree)) { // Create the server key. using (var serverKey = classesKey.CreateSubKey(server.ServerClsid.ToRegistryString())) { if (serverKey == null) { throw new InvalidOperationException("Cannot create server key."); } // We always set the server key default value to the display name if we can. if (!string.IsNullOrEmpty(server.DisplayName)) { serverKey.SetValue(null, server.DisplayName, RegistryValueKind.String); } // Create the inproc key. using (var inproc32Key = serverKey.CreateSubKey(KeyName_InProc32)) { // Check the key. if (inproc32Key == null) { throw new InvalidOperationException("Cannot create InProc32 key."); } // Set the .NET value. inproc32Key.SetValue(null, KeyValue_NetFrameworkServer); // Create the values. var assemblyVersion = server.GetType().Assembly.GetName().Version.ToString(); var assemblyFullName = server.GetType().Assembly.FullName; var className = server.GetType().FullName; var runtimeVersion = server.GetType().Assembly.ImageRuntimeVersion; var codeBaseValue = server.GetType().Assembly.CodeBase; const string threadingModel = "Both"; // Register all details at server level. inproc32Key.SetValue(KeyName_Assembly, assemblyFullName); inproc32Key.SetValue(KeyName_Class, className); inproc32Key.SetValue(KeyName_RuntimeVersion, runtimeVersion); inproc32Key.SetValue(KeyName_ThreadingModel, threadingModel); if (codeBase) { inproc32Key.SetValue(KeyName_CodeBase, codeBaseValue); } // Create the version key. using (var versionKey = inproc32Key.CreateSubKey(assemblyVersion)) { // Check the key. if (versionKey == null) { throw new InvalidOperationException("Cannot create assembly version key."); } // Set the values. versionKey.SetValue(KeyName_Assembly, assemblyFullName); versionKey.SetValue(KeyName_Class, className); versionKey.SetValue(KeyName_RuntimeVersion, runtimeVersion); if (codeBase) { versionKey.SetValue(KeyName_CodeBase, codeBaseValue); } } } } } }
/// <summary> /// Enumerates Shell extensions. /// </summary> /// <param name="registrationType">Type of the registration.</param> /// <param name="registrationLocation">Location of the registration.</param> /// <param name="shellExtensionTypes">The shell extension types.</param> /// <returns></returns> public static IEnumerable <ShellExtensionRegistrationInfo> EnumerateExtensions(RegistrationType registrationType, RegistrationLocation registrationLocation, ShellExtensionType shellExtensionTypes) { var shellExtensionsGuidMap = new Dictionary <Guid, ShellExtensionRegistrationInfo>(); // Go through all classes. using (var classes = OpenClassesRoot(registrationType, registrationLocation)) { // Read each subkey. foreach (var className in classes.GetSubKeyNames().Where(cn => !cn.StartsWith("{"))) { // Go through every shell extension type. foreach (ShellExtensionType shellExtensionType in Enum.GetValues(typeof(ShellExtensionType))) { // Get the handler subkey. var handlerSubkey = shellExtensionType.GetAttribute <HandlerSubkeyAttribute>(); if (handlerSubkey == null) { continue; } // Check for the subkey. if (handlerSubkey.AllowMultipleEntries) { // Do we have the single subkey? var handlerKeyPath = string.Format("{0}\\shellex\\{1}", className, handlerSubkey.HandlerSubkey); using (var handlerSubKey = classes.OpenSubKey(handlerKeyPath, RegistryKeyPermissionCheck.ReadSubTree, RegistryRights.ReadKey | RegistryRights.QueryValues)) { if (handlerSubKey != null) { // Read entries. foreach (var entry in handlerSubKey.GetSubKeyNames()) { using (var entryKey = handlerSubKey.OpenSubKey(entry, RegistryKeyPermissionCheck.ReadSubTree, RegistryRights.QueryValues | RegistryRights.ReadKey)) { var guidVal = entryKey.GetValue(null, string.Empty).ToString(); Guid guid; if (Guid.TryParse(guidVal, out guid) == false) { continue; } System.Diagnostics.Trace.WriteLine(string.Format("{0} has {3} {1} guid {2}", className, shellExtensionType.ToString(), guid, entry)); // If we do not have a shell extension info for this extension, create one. if (!shellExtensionsGuidMap.ContainsKey(guid)) { shellExtensionsGuidMap[guid] = new ShellExtensionRegistrationInfo { DisplayName = entry, ShellExtensionType = shellExtensionType, ServerCLSID = guid, }; } // Add the class association. shellExtensionsGuidMap[guid].classRegistrations.Add(new ClassRegistration(className)); } } } } } else { // Do we have the single subkey? var handlerKeyPath = string.Format("{0}\\shellex\\{1}", className, handlerSubkey.HandlerSubkey); using (var handlerSubKey = classes.OpenSubKey(handlerKeyPath, RegistryKeyPermissionCheck.ReadSubTree, RegistryRights.ReadKey | RegistryRights.QueryValues)) { if (handlerSubKey != null) { var guidVal = handlerSubKey.GetValue(null, string.Empty).ToString(); Guid guid; if (Guid.TryParse(guidVal, out guid) == false) { continue; } System.Diagnostics.Trace.WriteLine(string.Format("{0} has {1} guid {2}", className, shellExtensionType.ToString(), guid)); // If we do not have a shell extension info for this extension, create one. if (!shellExtensionsGuidMap.ContainsKey(guid)) { shellExtensionsGuidMap[guid] = new ShellExtensionRegistrationInfo { ShellExtensionType = shellExtensionType, ServerCLSID = guid, }; } // Add the class association. shellExtensionsGuidMap[guid].classRegistrations.Add(new ClassRegistration(className)); } } } } } } return(shellExtensionsGuidMap.Values); }
/// <summary> /// Unregisters a SharpShell server. This will remove the associations defined by the /// server's COMServerAssociation attribute. /// </summary> /// <param name="server">The server.</param> /// <param name="registrationType">Type of the registration to undo.</param> /// <param name="registrationLocation">Location of the registration.</param> public static void UnregisterServer(ISharpShellServer server, RegistrationType registrationType, RegistrationLocation registrationLocation) { // Unapprove the extension. UnapproveExtension(server, registrationType); // Pass the server type to the SharpShellServer internal unregistration function and let it // take over from there. SharpShellServer.DoUnregister(server.GetType(), registrationType, registrationLocation); }
/// <summary> /// Uninstalls the server. /// </summary> /// <param name="server">The server.</param> /// <param name="registrationType">Type of the registration.</param> /// <param name="registrationLocation">Location of the registration.</param> /// <returns>True if the server WAS installed and has been uninstalled, false if the server was not found.</returns> public static bool UninstallServer(ISharpShellServer server, RegistrationType registrationType, RegistrationLocation registrationLocation) { // Open classes. using (var classesKey = OpenClassesKey(registrationType, registrationLocation, RegistryKeyPermissionCheck.ReadWriteSubTree)) { var subKeyTreeName = server.ServerClsid.ToRegistryString(); // If the subkey doesn't exist, we can return false - we're already uninstalled. if (classesKey.GetSubKeyNames().Any(skn => skn.Equals(subKeyTreeName, StringComparison.OrdinalIgnoreCase)) == false) { return(false); } // Delete the subkey tree. classesKey.DeleteSubKeyTree(subKeyTreeName); return(true); } }