/// <summary> /// Registers a URL protocol in the current system. /// </summary> /// <param name="target">The application being integrated.</param> /// <param name="urlProtocol">The URL protocol to register.</param> /// <param name="machineWide">Register the URL protocol machine-wide instead of just for the current user.</param> /// <param name="handler">A callback object used when the the user is to be informed about the progress of long-running operations such as downloads.</param> /// <param name="accessPoint">Indicates that the handler shall become the default handler for the protocol.</param> /// <exception cref="OperationCanceledException">The user canceled the task.</exception> /// <exception cref="IOException">A problem occurs while writing to the filesystem or registry.</exception> /// <exception cref="WebException">A problem occured while downloading additional data (such as icons).</exception> /// <exception cref="UnauthorizedAccessException">Write access to the filesystem or registry is not permitted.</exception> /// <exception cref="InvalidDataException">The data in <paramref name="urlProtocol"/> is invalid.</exception> public static void Register(FeedTarget target, [NotNull] Store.Model.Capabilities.UrlProtocol urlProtocol, bool machineWide, [NotNull] ITaskHandler handler, bool accessPoint = false) { #region Sanity checks if (urlProtocol == null) { throw new ArgumentNullException(nameof(urlProtocol)); } if (handler == null) { throw new ArgumentNullException(nameof(handler)); } #endregion if (string.IsNullOrEmpty(urlProtocol.ID)) { throw new InvalidDataException("Missing ID"); } var hive = machineWide ? Registry.LocalMachine : Registry.CurrentUser; if (urlProtocol.KnownPrefixes.Count == 0) { if (accessPoint) { // Can only be registered invasively by registering protocol ProgID (will replace existing and become default) using (var progIDKey = hive.CreateSubKeyChecked(FileType.RegKeyClasses + @"\" + urlProtocol.ID)) FileType.RegisterVerbCapability(progIDKey, target, urlProtocol, machineWide, handler); } } else { // Can be registered non-invasively by registering custom ProgID (without becoming default) using (var progIDKey = hive.CreateSubKeyChecked(FileType.RegKeyClasses + @"\" + FileType.RegKeyPrefix + urlProtocol.ID)) { // Add flag to remember whether created for capability or access point progIDKey.SetValue(accessPoint ? FileType.PurposeFlagAccessPoint : FileType.PurposeFlagCapability, ""); FileType.RegisterVerbCapability(progIDKey, target, urlProtocol, machineWide, handler); } if (accessPoint) { foreach (var prefix in urlProtocol.KnownPrefixes) { if (WindowsUtils.IsWindowsVista && !machineWide) { using (var someKey = Registry.CurrentUser.CreateSubKeyChecked(RegKeyUserVistaUrlAssoc + @"\" + prefix.Value + @"\UserChoice")) someKey.SetValue("ProgID", FileType.RegKeyPrefix + urlProtocol.ID); } else { // Setting default invasively by registering protocol ProgID using (var progIDKey = hive.CreateSubKeyChecked(FileType.RegKeyClasses + @"\" + prefix.Value)) FileType.RegisterVerbCapability(progIDKey, target, urlProtocol, machineWide, handler); } } } } }
/// <summary> /// Unregisters a URL protocol in the current system. /// </summary> /// <param name="urlProtocol">The URL protocol to remove.</param> /// <param name="machineWide">Unregister the URL protocol machine-wide instead of just for the current user.</param> /// <param name="accessPoint">Indicates that the handler was the default handler for the protocol.</param> /// <exception cref="IOException">A problem occurs while writing to the filesystem.</exception> /// <exception cref="UnauthorizedAccessException">Write access to the filesystem is not permitted.</exception> /// <exception cref="InvalidDataException">The data in <paramref name="urlProtocol"/> is invalid.</exception> public static void Unregister(Store.Model.Capabilities.UrlProtocol urlProtocol, bool machineWide, bool accessPoint = false) { #region Sanity checks if (urlProtocol == null) { throw new ArgumentNullException("urlProtocol"); } #endregion if (string.IsNullOrEmpty(urlProtocol.ID)) { throw new InvalidDataException("Missing ID"); } // TODO: Implement }
/// <summary> /// Registers a URL protocol in the current system. /// </summary> /// <param name="target">The application being integrated.</param> /// <param name="urlProtocol">The URL protocol to register.</param> /// <param name="machineWide">Register the URL protocol machine-wide instead of just for the current user.</param> /// <param name="handler">A callback object used when the the user is to be informed about the progress of long-running operations such as downloads.</param> /// <param name="accessPoint">Indicates that the handler shall become the default handler for the protocol.</param> /// <exception cref="OperationCanceledException">The user canceled the task.</exception> /// <exception cref="IOException">A problem occurs while writing to the filesystem.</exception> /// <exception cref="WebException">A problem occured while downloading additional data (such as icons).</exception> /// <exception cref="UnauthorizedAccessException">Write access to the filesystem is not permitted.</exception> /// <exception cref="InvalidDataException">The data in <paramref name="urlProtocol"/> is invalid.</exception> public static void Register(FeedTarget target, [NotNull] Store.Model.Capabilities.UrlProtocol urlProtocol, bool machineWide, [NotNull] ITaskHandler handler, bool accessPoint = false) { #region Sanity checks if (urlProtocol == null) { throw new ArgumentNullException("urlProtocol"); } if (handler == null) { throw new ArgumentNullException("handler"); } #endregion if (string.IsNullOrEmpty(urlProtocol.ID)) { throw new InvalidDataException("Missing ID"); } // TODO: Implement }
/// <summary> /// Registers an application as a candidate for a default program for some service in the current system. This can only be applied machine-wide, not per user. /// </summary> /// <param name="target">The application being integrated.</param> /// <param name="defaultProgram">The default program information to be registered.</param> /// <param name="handler">A callback object used when the the user is to be informed about the progress of long-running operations such as downloads.</param> /// <param name="accessPoint">Indicates that the program should be set as the current default for the service it provides.</param> /// <exception cref="OperationCanceledException">The user canceled the task.</exception> /// <exception cref="IOException">A problem occurs while writing to the filesystem or registry.</exception> /// <exception cref="WebException">A problem occured while downloading additional data (such as icons).</exception> /// <exception cref="UnauthorizedAccessException">Write access to the filesystem or registry is not permitted.</exception> /// <exception cref="InvalidDataException">The data in <paramref name="defaultProgram"/> is invalid.</exception> public static void Register(FeedTarget target, [NotNull] Store.Model.Capabilities.DefaultProgram defaultProgram, [NotNull] ITaskHandler handler, bool accessPoint = false) { #region Sanity checks if (defaultProgram == null) { throw new ArgumentNullException("defaultProgram"); } if (handler == null) { throw new ArgumentNullException("handler"); } #endregion if (string.IsNullOrEmpty(defaultProgram.ID)) { throw new InvalidDataException("Missing ID"); } if (string.IsNullOrEmpty(defaultProgram.Service)) { throw new InvalidDataException("Missing Service"); } using (var serviceKey = Registry.LocalMachine.CreateSubKeyChecked(RegKeyMachineClients + @"\" + defaultProgram.Service)) { using (var appKey = serviceKey.CreateSubKeyChecked(defaultProgram.ID)) { // Add flag to remember whether created for capability or access point appKey.SetValue(accessPoint ? FileType.PurposeFlagAccessPoint : FileType.PurposeFlagCapability, ""); appKey.SetValue("", target.Feed.Name); FileType.RegisterVerbCapability(appKey, target, defaultProgram, true, handler); // Set callbacks for Windows SPAD using (var installInfoKey = appKey.CreateSubKeyChecked(RegSubKeyInstallInfo)) { string exePath = Path.Combine(Locations.InstallBase, "0install-win.exe").EscapeArgument(); installInfoKey.SetValue(RegValueReinstallCommand, exePath + " integrate-app --machine --batch --add=defaults " + target.Uri.ToStringRfc().EscapeArgument()); installInfoKey.SetValue(RegValueShowIconsCommand, exePath + " integrate-app --machine --batch --add=icons " + target.Uri.ToStringRfc().EscapeArgument()); installInfoKey.SetValue(RegValueHideIconsCommand, exePath + " integrate-app --machine --batch --remove=icons " + target.Uri.ToStringRfc().EscapeArgument()); installInfoKey.SetValue(RegValueIconsVisible, 0, RegistryValueKind.DWord); } if (defaultProgram.Service == Store.Model.Capabilities.DefaultProgram.ServiceMail) { var mailToProtocol = new Store.Model.Capabilities.UrlProtocol { Verbs = { new Verb { Name = Verb.NameOpen } } }; using (var mailToKey = appKey.CreateSubKeyChecked(@"Protocols\mailto")) FileType.RegisterVerbCapability(mailToKey, target, mailToProtocol, true, handler); } } if (accessPoint) { serviceKey.SetValue("", defaultProgram.ID); } } }
/// <summary> /// Registers an application as a candidate for a default program for some service in the current system. This can only be applied machine-wide, not per user. /// </summary> /// <param name="target">The application being integrated.</param> /// <param name="defaultProgram">The default program information to be registered.</param> /// <param name="handler">A callback object used when the the user is to be informed about the progress of long-running operations such as downloads.</param> /// <param name="accessPoint">Indicates that the program should be set as the current default for the service it provides.</param> /// <exception cref="OperationCanceledException">The user canceled the task.</exception> /// <exception cref="IOException">A problem occurs while writing to the filesystem or registry.</exception> /// <exception cref="WebException">A problem occured while downloading additional data (such as icons).</exception> /// <exception cref="UnauthorizedAccessException">Write access to the filesystem or registry is not permitted.</exception> /// <exception cref="InvalidDataException">The data in <paramref name="defaultProgram"/> is invalid.</exception> public static void Register(FeedTarget target, [NotNull] Store.Model.Capabilities.DefaultProgram defaultProgram, [NotNull] ITaskHandler handler, bool accessPoint = false) { #region Sanity checks if (defaultProgram == null) throw new ArgumentNullException(nameof(defaultProgram)); if (handler == null) throw new ArgumentNullException(nameof(handler)); #endregion if (string.IsNullOrEmpty(defaultProgram.ID)) throw new InvalidDataException("Missing ID"); if (string.IsNullOrEmpty(defaultProgram.Service)) throw new InvalidDataException("Missing Service"); using (var serviceKey = Registry.LocalMachine.CreateSubKeyChecked(RegKeyMachineClients + @"\" + defaultProgram.Service)) { using (var appKey = serviceKey.CreateSubKeyChecked(defaultProgram.ID)) { // Add flag to remember whether created for capability or access point appKey.SetValue(accessPoint ? FileType.PurposeFlagAccessPoint : FileType.PurposeFlagCapability, ""); appKey.SetValue("", target.Feed.Name); FileType.RegisterVerbCapability(appKey, target, defaultProgram, true, handler); // Set callbacks for Windows SPAD using (var installInfoKey = appKey.CreateSubKeyChecked(RegSubKeyInstallInfo)) { string exePath = Path.Combine(Locations.InstallBase, "0install-win.exe"); installInfoKey.SetValue(RegValueReinstallCommand, new[] {exePath, "integrate", "--machine", "--batch", "--add", "defaults", target.Uri.ToStringRfc()}.JoinEscapeArguments()); installInfoKey.SetValue(RegValueShowIconsCommand, new[] {exePath, "integrate", "--machine", "--batch", "--add", MenuEntry.CategoryName, "--add", DesktopIcon.CategoryName, target.Uri.ToStringRfc()}.JoinEscapeArguments()); installInfoKey.SetValue(RegValueHideIconsCommand, new[] {exePath, "integrate", "--machine", "--batch", "--remove", MenuEntry.CategoryName, "--remove", DesktopIcon.CategoryName, target.Uri.ToStringRfc()}.JoinEscapeArguments()); installInfoKey.SetValue(RegValueIconsVisible, 0, RegistryValueKind.DWord); } if (defaultProgram.Service == Store.Model.Capabilities.DefaultProgram.ServiceMail) { var mailToProtocol = new Store.Model.Capabilities.UrlProtocol {Verbs = {new Verb {Name = Verb.NameOpen}}}; using (var mailToKey = appKey.CreateSubKeyChecked(@"Protocols\mailto")) FileType.RegisterVerbCapability(mailToKey, target, mailToProtocol, true, handler); } } if (accessPoint) serviceKey.SetValue("", defaultProgram.ID); } }
/// <summary> /// Unregisters a URL protocol in the current system. /// </summary> /// <param name="urlProtocol">The URL protocol to remove.</param> /// <param name="machineWide">Unregister the URL protocol machine-wide instead of just for the current user.</param> /// <param name="accessPoint">Indicates that the handler was the default handler for the protocol.</param> /// <exception cref="IOException">A problem occurs while writing to the filesystem or registry.</exception> /// <exception cref="UnauthorizedAccessException">Write access to the filesystem or registry is not permitted.</exception> /// <exception cref="InvalidDataException">The data in <paramref name="urlProtocol"/> is invalid.</exception> public static void Unregister([NotNull] Store.Model.Capabilities.UrlProtocol urlProtocol, bool machineWide, bool accessPoint = false) { #region Sanity checks if (urlProtocol == null) { throw new ArgumentNullException(nameof(urlProtocol)); } #endregion if (string.IsNullOrEmpty(urlProtocol.ID)) { throw new InvalidDataException("Missing ID"); } var hive = machineWide ? Registry.LocalMachine : Registry.CurrentUser; if (urlProtocol.KnownPrefixes.Count == 0) { if (accessPoint) { // Was registered invasively by registering protocol ProgID try { hive.DeleteSubKeyTree(FileType.RegKeyClasses + @"\" + urlProtocol.ID); } #region Error handling catch (ArgumentException) { // Ignore missing registry keys } #endregion } } else { // Was registered non-invasively by registering custom ProgID if (accessPoint) { foreach (var prefix in urlProtocol.KnownPrefixes) { // TODO: Restore previous default } } // Remove appropriate purpose flag and check if there are others bool otherFlags; using (var progIDKey = hive.OpenSubKey(FileType.RegKeyClasses + @"\" + FileType.RegKeyPrefix + urlProtocol.ID, writable: true)) { if (progIDKey == null) { otherFlags = false; } else { progIDKey.DeleteValue(accessPoint ? FileType.PurposeFlagAccessPoint : FileType.PurposeFlagCapability, throwOnMissingValue: false); otherFlags = progIDKey.GetValueNames().Any(name => name.StartsWith(FileType.PurposeFlagPrefix)); } } // Delete ProgID if there are no other references if (!otherFlags) { try { hive.DeleteSubKeyTree(FileType.RegKeyClasses + @"\" + FileType.RegKeyPrefix + urlProtocol.ID); } #region Error handling catch (ArgumentException) { // Ignore missing registry keys } #endregion } } #endregion }