/// <summary> /// Actually performs registration. The ComRegisterFunction decorated method will call this function /// internally with the flag appropriate for the operating system processor architecture. /// However, this function can also be called manually if needed. /// </summary> /// <param name="type">The type of object to register, this must be a SharpShellServer derived class.</param> /// <param name="registrationType">Type of the registration.</param> internal static void DoRegister(Type type, RegistrationType registrationType) { Logging.Log($"Preparing to register SharpShell Server {type.Name} as {registrationType}"); // Get the association data. var associationAttributes = type.GetCustomAttributes(typeof(COMServerAssociationAttribute), true) .OfType <COMServerAssociationAttribute>().ToList(); // Get the server type and the registration name. var serverType = ServerTypeAttribute.GetServerType(type); var registrationName = RegistrationNameAttribute.GetRegistrationNameOrTypeName(type); // Register the server associations, if there are any. if (associationAttributes.Any()) { ServerRegistrationManager.RegisterServerAssociations( type.GUID, serverType, registrationName, associationAttributes, registrationType); } // If a DisplayName attribute has been set, then set the display name of the COM server. var displayName = DisplayNameAttribute.GetDisplayName(type); if (!string.IsNullOrEmpty(displayName)) { ServerRegistrationManager.SetServerDisplayName(type.GUID, displayName, registrationType); } // Execute the custom register function, if there is one. CustomRegisterFunctionAttribute.ExecuteIfExists(type, registrationType); // Notify the shell we've updated associations. Shell32.SHChangeNotify(Shell32.SHCNE_ASSOCCHANGED, 0, IntPtr.Zero, IntPtr.Zero); Logging.Log($"Registration of {type.Name} completed"); }
/// <summary> /// Actually performs unregistration. The ComUnregisterFunction decorated method will call this function /// internally with the flag appropriate for the operating system processor architecture. /// However, this function can also be called manually if needed. /// </summary> /// <param name="type">The type of object to unregister, this must be a SharpShellServer derived class.</param> /// <param name="registrationType">Type of the registration to unregister.</param> internal static void DoUnregister(Type type, RegistrationType registrationType) { Logging.Log($"Preparing to unregister SharpShell Server {type.Name} as {registrationType}"); // Get the association data. var associationAttributes = type.GetCustomAttributes(typeof(COMServerAssociationAttribute), true) .OfType <COMServerAssociationAttribute>().ToList(); // Get the server type and the registration name. var serverType = ServerTypeAttribute.GetServerType(type); var registrationName = RegistrationNameAttribute.GetRegistrationNameOrTypeName(type); // Unregister the server associations, if there are any. if (associationAttributes.Any()) { ServerRegistrationManager.UnregisterServerAssociations( type.GUID, serverType, registrationName, associationAttributes, registrationType); } // Execute the custom unregister function, if there is one. CustomUnregisterFunctionAttribute.ExecuteIfExists(type, registrationType); // Notify the shell we've updated associations. Shell32.SHChangeNotify(Shell32.SHCNE_ASSOCCHANGED, 0, IntPtr.Zero, IntPtr.Zero); Logging.Log($"Unregistration of {type.Name} completed"); }
internal static void CustomUnregisterFunction(Type serverType, RegistrationType registrationType) { var keyName = RegistrationNameAttribute.GetRegistrationNameOrTypeName(serverType); Logging.Log($"IconOverlayHandler: Preparing to unregister {registrationType} Icon Overlay Handler for type '{serverType.Name}' with key name '{keyName}'"); // Open the local machine. using (var localMachineBaseKey = registrationType == RegistrationType.OS64Bit ? RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64) : RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32)) { // Open the ShellIconOverlayIdentifiers. using (var overlayIdentifiers = localMachineBaseKey .OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers", RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryRights.Delete | RegistryRights.EnumerateSubKeys | RegistryRights.ReadKey)) { // If we don't have the key, we've got a problem. if (overlayIdentifiers == null) { throw new InvalidOperationException("Cannot open the ShellIconOverlayIdentifiers key."); } // Delete the overlay key. if (overlayIdentifiers.GetSubKeyNames().Any(skn => skn == keyName)) { overlayIdentifiers.DeleteSubKey(keyName); } } } }
internal static void CustomRegisterFunction(Type serverType, RegistrationType registrationType) { var keyName = RegistrationNameAttribute.GetRegistrationNameOrTypeName(serverType); Logging.Log($"IconOverlayHandler: Preparing to register {registrationType} Icon Overlay Handler for type '{serverType.Name}' with key name '{keyName}'"); // Open the local machine. using (var localMachineBaseKey = registrationType == RegistrationType.OS64Bit ? RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64) : RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32)) { // Open the ShellIconOverlayIdentifiers. using (var overlayIdentifiers = localMachineBaseKey .OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers", RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryRights.EnumerateSubKeys | RegistryRights.QueryValues | RegistryRights.CreateSubKey | RegistryRights.CreateSubKey)) { // If we don't have the key, we've got a problem. if (overlayIdentifiers == null) { throw new InvalidOperationException("Cannot open the ShellIconOverlayIdentifiers key."); } // How many shell icon overlay identifiers do we have? var overlayHandlersCount = overlayIdentifiers.GetSubKeyNames().Count(); if (overlayHandlersCount >= MaximumOverlayIdentifiers) { Logging.Error("There are already the maximum number of overlay " + "handlers registered for the system. Although " + serverType.Name + " is " + "being registered, it will not be used by Windows Explorer."); } // Create the overlay key. using (var overlayKey = overlayIdentifiers.CreateSubKey(keyName)) { // If we don't have the overlay key, we've got a problem. if (overlayKey == null) { throw new InvalidOperationException("Cannot create the key for the overlay server."); } // Set the server CLSID. overlayKey.SetValue(null, serverType.GUID.ToRegistryString()); } } } }
/// <summary> /// Actually performs registration. The ComRegisterFunction decorated method will call this function /// internally with the flag appropriate for the operating system processor architecture. /// However, this function can also be called manually if needed. /// </summary> /// <param name="type">The type of object to register, this must be a SharpShellServer derived class.</param> /// <param name="registrationType">Type of the registration.</param> internal static void DoRegister(Type type, RegistrationType registrationType) { Logging.Log($"Preparing to register SharpShell Server {type.Name} as {registrationType}"); // Get the association data. var associationAttributes = type.GetCustomAttributes(typeof(COMServerAssociationAttribute), true) .OfType <COMServerAssociationAttribute>().ToList(); // Get the server type and the registration name. var serverType = ServerTypeAttribute.GetServerType(type); var registrationName = RegistrationNameAttribute.GetRegistrationNameOrTypeName(type); // Register the server associations, if there are any. if (associationAttributes.Any()) { ServerRegistrationManager.RegisterServerAssociations( type.GUID, serverType, registrationName, associationAttributes, registrationType); } // If a DisplayName attribute has been set, then set the display name of the COM server. var displayName = DisplayNameAttribute.GetDisplayName(type); if (!string.IsNullOrEmpty(displayName)) { ServerRegistrationManager.SetServerDisplayName(type.GUID, displayName, registrationType); } // If we are a *file* thumbnail handler, we must disable process isolation. // See: https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/cc144118(v%3Dvs.85)#thumbnail-processes if (serverType == ServerType.ShellItemThumbnailHandler) { Logging.Log($"Disabling process isolation for SharpFileThumbnailHandler named '{type.Name}'."); ServerRegistrationManager.SetDisableProcessIsolationValue(type.GUID, registrationType, 1 /* i.e. disabled */); } // Execute the custom register function, if there is one. CustomRegisterFunctionAttribute.ExecuteIfExists(type, registrationType); // Notify the shell we've updated associations. Shell32.SHChangeNotify(Shell32.SHCNE_ASSOCCHANGED, 0, IntPtr.Zero, IntPtr.Zero); Logging.Log($"Registration of {type.Name} completed"); }