/// <summary> /// Creates a new Windows shortcut. /// </summary> /// <param name="path">The location to place the shortcut at.</param> /// <param name="target">The target the shortcut shall point to.</param> /// <param name="command">The command within <paramref name="target"/> the shortcut shall point to; can be <c>null</c>.</param> /// <param name="iconStore">Stores icon files downloaded from the web as local files.</param> private static void Create(string path, FeedTarget target, string?command, IIconStore iconStore) { if (string.IsNullOrEmpty(command)) { command = Command.NameRun; } var entryPoint = target.Feed.GetEntryPoint(command); bool needsTerminal = (entryPoint != null && entryPoint.NeedsTerminal); string targetPath = Path.Combine(Locations.InstallBase, needsTerminal ? "0install.exe" : "0install-win.exe"); string arguments = "run "; if (!needsTerminal) { arguments += "--no-wait "; } if (command != Command.NameRun) { arguments += "--command " + command.EscapeArgument() + " "; } arguments += target.Uri.ToStringRfc().EscapeArgument(); var icon = target.Feed.GetIcon(Icon.MimeTypeIco, command); Create(path, targetPath, arguments, iconLocation: (icon == null) ? null : iconStore.GetPath(icon), description: target.Feed.GetBestSummary(CultureInfo.CurrentUICulture, command)); }
/// <summary> /// Registers a <see cref="VerbCapability"/> in a registry key. /// </summary> /// <param name="registryKey">The registry key to write the new data to.</param> /// <param name="target">The application being integrated.</param> /// <param name="capability">The capability to register.</param> /// <param name="iconStore">Stores icon files downloaded from the web as local files.</param> /// <param name="machineWide">Assume <paramref name="registryKey"/> is effective machine-wide instead of just for the current user.</param> /// <exception cref="OperationCanceledException">The user canceled the task.</exception> /// <exception cref="IOException">A problem occurred while writing to the filesystem or registry.</exception> /// <exception cref="WebException">A problem occurred 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="capability"/> is invalid.</exception> public static void Register(RegistryKey registryKey, FeedTarget target, VerbCapability capability, IIconStore iconStore, bool machineWide) { #region Sanity checks if (capability == null) { throw new ArgumentNullException(nameof(capability)); } if (iconStore == null) { throw new ArgumentNullException(nameof(iconStore)); } #endregion var icon = capability.GetIcon(Icon.MimeTypeIco) ?? target.Feed.GetIcon(Icon.MimeTypeIco); if (icon != null) { using var iconKey = registryKey.CreateSubKeyChecked("DefaultIcon"); iconKey.SetValue("", iconStore.GetPath(icon) + ",0"); } foreach (var verb in capability.Verbs) { if (string.IsNullOrEmpty(verb.Name)) { throw new InvalidDataException("Missing verb name"); } using var verbKey = registryKey.CreateSubKeyChecked($@"shell\{verb.Name}"); Register(verbKey, target, verb, iconStore, machineWide); } // Prevent conflicts with existing entries registryKey.DeleteSubKeyTree(@"shell\ddeexec", throwOnMissingSubKey: false); }
/// <summary> /// Registers a <see cref="Verb"/> in a registry key. /// </summary> /// <param name="verbKey">The registry key to write the new data to.</param> /// <param name="target">The application being integrated.</param> /// <param name="verb">The verb to register.</param> /// <param name="iconStore">Stores icon files downloaded from the web as local files.</param> /// <param name="machineWide">Assume <paramref name="verbKey"/> is effective machine-wide instead of just for the current user.</param> /// <exception cref="OperationCanceledException">The user canceled the task.</exception> /// <exception cref="IOException">A problem occurred while writing to the filesystem or registry.</exception> /// <exception cref="WebException">A problem occurred 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="verb"/> is invalid.</exception> public static void Register(RegistryKey verbKey, FeedTarget target, Verb verb, IIconStore iconStore, bool machineWide) { string?description = verb.Descriptions.GetBestLanguage(CultureInfo.CurrentUICulture); if (!string.IsNullOrEmpty(description)) { verbKey.SetValue("", description); verbKey.SetValue("MUIVerb", description); } if (verb.Extended) { verbKey.SetValue("Extended", ""); } var icon = target.Feed.GetIcon(Icon.MimeTypeIco, verb.Command); if (icon != null) { verbKey.SetValue("Icon", iconStore.GetPath(icon)); } using var commandKey = verbKey.CreateSubKeyChecked("command"); commandKey.SetValue("", GetLaunchCommandLine(target, verb, iconStore, machineWide)); }
/// <summary> /// Adds an AutoPlay handler registration to the current system. /// </summary> /// <param name="target">The application being integrated.</param> /// <param name="autoPlay">The AutoPlay handler information to be applied.</param> /// <param name="machineWide">Register the handler machine-wide instead of just for the current user.</param> /// <param name="iconStore">Stores icon files downloaded from the web as local files.</param> /// <param name="accessPoint">Indicates that the handler should become the default handler for all <see cref="Model.Capabilities.AutoPlay.Events"/>.</param> /// <exception cref="OperationCanceledException">The user canceled the task.</exception> /// <exception cref="IOException">A problem occurred while writing to the filesystem or registry.</exception> /// <exception cref="WebException">A problem occurred 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="autoPlay"/> is invalid.</exception> public static void Register(FeedTarget target, Model.Capabilities.AutoPlay autoPlay, IIconStore iconStore, bool machineWide, bool accessPoint = false) { #region Sanity checks if (autoPlay == null) { throw new ArgumentNullException(nameof(autoPlay)); } if (iconStore == null) { throw new ArgumentNullException(nameof(iconStore)); } #endregion if (string.IsNullOrEmpty(autoPlay.ID)) { throw new InvalidDataException("Missing ID"); } if (string.IsNullOrEmpty(autoPlay.Provider)) { throw new InvalidDataException("Missing provider"); } var verb = autoPlay.Verb; if (verb == null) { throw new InvalidDataException("Missing verb"); } if (string.IsNullOrEmpty(verb.Name)) { throw new InvalidDataException("Missing verb name"); } string handlerName = RegistryClasses.Prefix + autoPlay.ID; string progId = RegistryClasses.Prefix + "AutoPlay." + autoPlay.ID; using (var classesKey = RegistryClasses.OpenHive(machineWide)) using (var verbKey = classesKey.CreateSubKeyChecked($@"{progId}\shell\{verb.Name}")) RegistryClasses.Register(verbKey, target, verb, iconStore, machineWide); var hive = machineWide ? Registry.LocalMachine : Registry.CurrentUser; using (var handlerKey = hive.CreateSubKeyChecked($@"{RegKeyHandlers}\{handlerName}")) { handlerKey.SetValue(accessPoint ? RegistryClasses.PurposeFlagAccessPoint : RegistryClasses.PurposeFlagCapability, ""); handlerKey.SetValue(RegValueProgID, progId); handlerKey.SetValue(RegValueVerb, verb.Name); handlerKey.SetValue(RegValueProvider, autoPlay.Provider); handlerKey.SetValue(RegValueDescription, autoPlay.Descriptions.GetBestLanguage(CultureInfo.CurrentUICulture) ?? verb.Name); var icon = autoPlay.GetIcon(Icon.MimeTypeIco) ?? target.Feed.GetIcon(Icon.MimeTypeIco, verb.Command); if (icon != null) { handlerKey.SetValue(RegValueIcon, iconStore.GetPath(icon) + ",0"); } } foreach (var autoPlayEvent in autoPlay.Events.Except(x => string.IsNullOrEmpty(x.Name))) { using (var eventKey = hive.CreateSubKeyChecked($@"{RegKeyAssocs}\{autoPlayEvent.Name}")) eventKey.SetValue(handlerName, ""); if (accessPoint) { using var chosenEventKey = hive.CreateSubKeyChecked($@"{RegKeyChosenAssocs}\{autoPlayEvent.Name}"); chosenEventKey.SetValue("", handlerName); } } }
/// <summary> /// Applies application registration to the current system. /// </summary> /// <param name="target">The application being integrated.</param> /// <param name="appRegistration">The registration information to be applied.</param> /// <param name="verbCapabilities">The capabilities that the application is to be registered with.</param> /// <param name="machineWide">Apply the registration machine-wide instead of just for the current user.</param> /// <param name="iconStore">Stores icon files downloaded from the web as local files.</param> /// <exception cref="OperationCanceledException">The user canceled the task.</exception> /// <exception cref="IOException">A problem occurred while writing to the filesystem or registry.</exception> /// <exception cref="WebException">A problem occurred 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="appRegistration"/> or <paramref name="verbCapabilities"/> is invalid.</exception> public static void Register(FeedTarget target, Model.Capabilities.AppRegistration appRegistration, IEnumerable <VerbCapability> verbCapabilities, IIconStore iconStore, bool machineWide) { #region Sanity checks if (appRegistration == null) { throw new ArgumentNullException(nameof(appRegistration)); } if (verbCapabilities == null) { throw new ArgumentNullException(nameof(verbCapabilities)); } if (iconStore == null) { throw new ArgumentNullException(nameof(iconStore)); } #endregion if (string.IsNullOrEmpty(appRegistration.ID)) { throw new InvalidDataException("Missing ID"); } if (string.IsNullOrEmpty(appRegistration.CapabilityRegPath)) { throw new InvalidDataException("Invalid CapabilityRegPath"); } var hive = machineWide ? Registry.LocalMachine : Registry.CurrentUser; // TODO: Handle appRegistration.X64 using (var capabilitiesKey = hive.CreateSubKeyChecked(/*CapabilityPrefix +*/ appRegistration.CapabilityRegPath)) { capabilitiesKey.SetValue(RegValueAppName, target.Feed.Name ?? ""); capabilitiesKey.SetValue(RegValueAppDescription, target.Feed.Descriptions.GetBestLanguage(CultureInfo.CurrentUICulture) ?? ""); // Set icon if available var icon = target.Feed.GetIcon(Icon.MimeTypeIco); if (icon != null) { capabilitiesKey.SetValue(RegValueAppIcon, iconStore.GetPath(icon) + ",0"); } verbCapabilities = verbCapabilities.ToArray(); using (var fileAssocsKey = capabilitiesKey.CreateSubKeyChecked(RegSubKeyFileAssocs)) { foreach (var fileType in verbCapabilities.OfType <Model.Capabilities.FileType>().Except(x => string.IsNullOrEmpty(x.ID))) { foreach (var extension in fileType.Extensions.Except(x => string.IsNullOrEmpty(x.Value))) { fileAssocsKey.SetValue(extension.Value, RegistryClasses.Prefix + fileType.ID); } } } using (var urlAssocsKey = capabilitiesKey.CreateSubKeyChecked(RegSubKeyUrlAssocs)) { foreach (var urlProtocol in verbCapabilities.OfType <Model.Capabilities.UrlProtocol>()) { foreach (var prefix in urlProtocol.KnownPrefixes) { urlAssocsKey.SetValue(prefix.Value, RegistryClasses.Prefix + urlProtocol.ID); } } } using var startMenuKey = capabilitiesKey.CreateSubKeyChecked(RegSubKeyStartMenu); foreach (var defaultProgram in verbCapabilities.OfType <Model.Capabilities.DefaultProgram>().Except(x => string.IsNullOrEmpty(x.ID) || string.IsNullOrEmpty(x.Service))) { startMenuKey.SetValue(defaultProgram.Service, defaultProgram.ID); } } using var regAppsKey = hive.CreateSubKeyChecked(RegKeyMachineRegisteredApplications); regAppsKey.SetValue(appRegistration.ID, /*CapabilityPrefix +*/ appRegistration.CapabilityRegPath); }
/// <summary> /// Builds a stub EXE that executes the "0install run" command. /// </summary> /// <param name="target">The application to be launched via the stub.</param> /// <param name="path">The target path to store the generated EXE file.</param> /// <param name="iconStore">Stores icon files downloaded from the web as local files.</param> /// <param name="needsTerminal"><c>true</c> to build a CLI stub, <c>false</c> to build a GUI stub.</param> /// <param name="command">The command argument to be passed to the the "0install run" command; can be <c>null</c>.</param> /// <exception cref="OperationCanceledException">The user canceled the task.</exception> /// <exception cref="InvalidOperationException">There was a compilation error while generating the stub EXE.</exception> /// <exception cref="IOException">A problem occurred while writing to the filesystem.</exception> /// <exception cref="WebException">A problem occurred while downloading additional data (such as icons).</exception> /// <exception cref="UnauthorizedAccessException">Write access to the filesystem is not permitted.</exception> internal static void BuildRunStub(FeedTarget target, string path, IIconStore iconStore, bool needsTerminal, string?command = null) { #region Sanity checks if (string.IsNullOrEmpty(path)) { throw new ArgumentNullException(nameof(path)); } if (iconStore == null) { throw new ArgumentNullException(nameof(iconStore)); } #endregion var compilerParameters = new CompilerParameters { OutputAssembly = path, GenerateExecutable = true, TreatWarningsAsErrors = true, ReferencedAssemblies = { "System.dll" }, CompilerOptions = needsTerminal ? "/target:exe" : "/target:winexe" }; var icon = target.Feed.GetIcon(Icon.MimeTypeIco, command); if (icon != null) { try { string iconPath = iconStore.GetPath(icon); new System.Drawing.Icon(iconPath).Dispose(); // Try to parse icon to ensure it is valid compilerParameters.CompilerOptions += " /win32icon:" + iconPath.EscapeArgument(); } #region Error handling catch (UriFormatException ex) { Log.Warn(ex); } catch (WebException ex) { Log.Warn(ex); } catch (IOException ex) { Log.Warn($"Failed to store {icon}"); Log.Warn(ex); } catch (UnauthorizedAccessException ex) { Log.Warn($"Failed to store {icon}"); Log.Warn(ex); } catch (ArgumentException ex) { Log.Warn($"Failed to parse {icon}"); Log.Warn(ex); } #endregion } compilerParameters.CompileCSharp( code: GetRunStubCode(target, needsTerminal, command), manifest: typeof(StubBuilder).GetEmbeddedString("Stub.manifest")); }