/// <summary> /// Creates a new Windows shortcut on the desktop. /// </summary> /// <param name="desktopIcon">Information about the shortcut to be created.</param> /// <param name="target">The target the shortcut shall point to.</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="machineWide">Create the shortcut machine-wide instead of just for the current user.</param> public static void Create([NotNull] DesktopIcon desktopIcon, FeedTarget target, [NotNull] ITaskHandler handler, bool machineWide) { #region Sanity checks if (desktopIcon == null) throw new ArgumentNullException(nameof(desktopIcon)); if (handler == null) throw new ArgumentNullException(nameof(handler)); #endregion string filePath = GetDesktopPath(desktopIcon.Name, machineWide); Create(filePath, target, desktopIcon.Command, handler, machineWide); }
/// <summary> /// Creates a new Windows shortcut in the quick launch bar. /// </summary> /// <param name="quickLaunch">Information about the shortcut to be created.</param> /// <param name="target">The target the shortcut shall point to.</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> public static void Create(QuickLaunch quickLaunch, FeedTarget target, ITaskHandler handler) { #region Sanity checks if (quickLaunch == null) throw new ArgumentNullException("quickLaunch"); if (handler == null) throw new ArgumentNullException("handler"); #endregion string filePath = GetQuickLaunchPath(quickLaunch.Name); Create(filePath, target, quickLaunch.Command, handler, machineWide: false); }
/// <summary> /// Creates a new Windows shortcut in the "Startup" menu. /// </summary> /// <param name="autoStart">Information about the shortcut to be created.</param> /// <param name="target">The target the shortcut shall point to.</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="machineWide">Create the shortcut machine-wide instead of just for the current user.</param> public static void Create(AutoStart autoStart, FeedTarget target, ITaskHandler handler, bool machineWide) { #region Sanity checks if (autoStart == null) throw new ArgumentNullException("autoStart"); if (handler == null) throw new ArgumentNullException("handler"); #endregion string filePath = GetStartupPath(autoStart.Name, machineWide); Create(filePath, target.GetRunStub(autoStart.Command, handler)); }
/// <summary> /// Creates a new Windows shortcut in the "Send to" menu. /// </summary> /// <param name="sendTo">Information about the shortcut to be created.</param> /// <param name="target">The target the shortcut shall point to.</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> public static void Create(SendTo sendTo, FeedTarget target, ITaskHandler handler) { #region Sanity checks if (sendTo == null) throw new ArgumentNullException(nameof(sendTo)); if (handler == null) throw new ArgumentNullException(nameof(handler)); #endregion string filePath = GetSendToPath(sendTo.Name); Create(filePath, target, sendTo.Command, handler, machineWide: false); }
/// <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 a COM server in the current system. /// </summary> /// <param name="target">The application being integrated.</param> /// <param name="comServer">The COM server to be registered.</param> /// <param name="machineWide">Register the COM server 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> /// <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="comServer"/> is invalid.</exception> public static void Register(FeedTarget target, [NotNull] Store.Model.Capabilities.ComServer comServer, bool machineWide, [NotNull] ITaskHandler handler) { #region Sanity checks if (comServer == null) throw new ArgumentNullException(nameof(comServer)); if (handler == null) throw new ArgumentNullException(nameof(handler)); #endregion if (string.IsNullOrEmpty(comServer.ID)) throw new InvalidDataException("Missing ID"); // TODO: Implement }
public void TestAddApp() { var capabilityList = CapabilityListTest.CreateTestCapabilityList(); var target = new FeedTarget(FeedTest.Test1Uri, new Feed {Name = "Test", CapabilityLists = {capabilityList}}); _integrationManager.AddApp(target); _integrationManager.AppList.Entries .Should().Equal(new AppEntry {InterfaceUri = FeedTest.Test1Uri, Name = target.Feed.Name, CapabilityLists = {capabilityList}}); _integrationManager.Invoking(x => x.AddApp(target)) .ShouldThrow<InvalidOperationException>(because: "Do not allow adding applications to AppList more than once."); }
/// <summary> /// Adds a context menu entry to the current system. /// </summary> /// <param name="target">The application being integrated.</param> /// <param name="contextMenu">The context menu entry to add.</param> /// <param name="machineWide">Add the context menu entry 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> /// <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="contextMenu"/> is invalid.</exception> public static void Apply(FeedTarget target, Store.Model.Capabilities.ContextMenu contextMenu, bool machineWide, ITaskHandler handler) { #region Sanity checks if (contextMenu == null) throw new ArgumentNullException("contextMenu"); if (handler == null) throw new ArgumentNullException("handler"); #endregion if (contextMenu.Verb == null) throw new InvalidDataException("Missing verb"); if (string.IsNullOrEmpty(contextMenu.Verb.Name)) throw new InvalidDataException("Missing verb name"); // TODO: Implement }
/// <summary> /// Registers an application as a candidate for a default program for some service in the current system. /// </summary> /// <param name="target">The application being integrated.</param> /// <param name="defaultProgram">The default program information to be registered.</param> /// <param name="machineWide">Apply the registration 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 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.</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="defaultProgram"/> is invalid.</exception> public static void Register(FeedTarget target, [NotNull] Store.Model.Capabilities.DefaultProgram defaultProgram, bool machineWide, [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"); // TODO: Implement }
/// <summary> /// Creates a new Windows shortcut in the start menu or on the start page. /// </summary> /// <param name="menuEntry">Information about the shortcut to be created.</param> /// <param name="target">The target the shortcut shall point to.</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="machineWide">Create the shortcut machine-wide instead of just for the current user.</param> public static void Create([NotNull] MenuEntry menuEntry, FeedTarget target, [NotNull] ITaskHandler handler, bool machineWide) { #region Sanity checks if (menuEntry == null) throw new ArgumentNullException(nameof(menuEntry)); if (handler == null) throw new ArgumentNullException(nameof(handler)); #endregion string dirPath = GetStartMenuCategoryPath(menuEntry.Category, machineWide); if (!Directory.Exists(dirPath)) Directory.CreateDirectory(dirPath); string filePath = GetStartMenuPath(menuEntry.Category, menuEntry.Name, machineWide); Create(filePath, target, menuEntry.Command, handler, machineWide); }
/// <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="handler">A callback object used when the the user is to be informed about the progress of long-running operations such as downloads.</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="appRegistration"/> or <paramref name="verbCapabilities"/> is invalid.</exception> public static void Register(FeedTarget target, [NotNull] Store.Model.Capabilities.AppRegistration appRegistration, IEnumerable<VerbCapability> verbCapabilities, bool machineWide, [NotNull] ITaskHandler handler) { #region Sanity checks if (appRegistration == null) throw new ArgumentNullException(nameof(appRegistration)); if (verbCapabilities == null) throw new ArgumentNullException(nameof(verbCapabilities)); if (handler == null) throw new ArgumentNullException(nameof(handler)); #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, IconProvider.GetIconPath(icon, handler, machineWide) + ",0"); using (var fileAssocsKey = capabilitiesKey.CreateSubKeyChecked(RegSubKeyFileAssocs)) { foreach (var fileType in verbCapabilities.OfType<Store.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, FileType.RegKeyPrefix + fileType.ID); } } using (var urlAssocsKey = capabilitiesKey.CreateSubKeyChecked(RegSubKeyUrlAssocs)) { foreach (var urlProtocol in verbCapabilities.OfType<Store.Model.Capabilities.UrlProtocol>()) { foreach (var prefix in urlProtocol.KnownPrefixes) urlAssocsKey.SetValue(prefix.Value, FileType.RegKeyPrefix + urlProtocol.ID); } } using (var startMenuKey = capabilitiesKey.CreateSubKeyChecked(RegSubKeyStartMenu)) { foreach (var defaultProgram in verbCapabilities.OfType<Store.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> /// Creates an application alias in the current system. /// </summary> /// <param name="target">The application being integrated.</param> /// <param name="command">The command within <paramref name="target"/> the alias shall point to; can be <c>null</c>.</param> /// <param name="aliasName">The name of the alias to be created.</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="machineWide">Create the alias machine-wide instead of just for the current user.</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> public static void Create(FeedTarget target, [CanBeNull] string command, [NotNull] string aliasName, [NotNull] ITaskHandler handler, bool machineWide) { #region Sanity checks if (string.IsNullOrEmpty(aliasName)) throw new ArgumentNullException(nameof(aliasName)); if (handler == null) throw new ArgumentNullException(nameof(handler)); #endregion if (string.IsNullOrEmpty(aliasName) || aliasName.IndexOfAny(Path.GetInvalidFileNameChars()) != -1) throw new IOException(string.Format(Resources.NameInvalidChars, aliasName)); // TODO: Find directory in search PATH // TODO: Write file }
/// <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("urlProtocol"); if (handler == null) throw new ArgumentNullException("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> /// Creates an application alias in the current system. /// </summary> /// <param name="target">The application being integrated.</param> /// <param name="command">The command within <paramref name="target"/> the alias shall point to; can be <see langword="null"/>.</param> /// <param name="aliasName">The name of the alias to be created.</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="machineWide">Create the alias machine-wide instead of just for the current user.</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> public static void Create(FeedTarget target, [CanBeNull] string command, [NotNull] string aliasName, [NotNull] ITaskHandler handler, bool machineWide) { #region Sanity checks if (string.IsNullOrEmpty(aliasName)) throw new ArgumentNullException("aliasName"); if (handler == null) throw new ArgumentNullException("handler"); #endregion if (string.IsNullOrEmpty(aliasName) || aliasName.IndexOfAny(Path.GetInvalidFileNameChars()) != -1) throw new IOException(string.Format(Resources.NameInvalidChars, aliasName)); // TODO: Find directory in search PATH //string stubDirPath = Locations.GetIntegrationDirPath("0install.net", machineWide, "desktop-integration", "aliases"); //string stubFilePath = Path.Combine(stubDirPath, aliasName); // TODO: Write file }
/// <summary> /// Creates an application alias in the current system. /// </summary> /// <param name="target">The application being integrated.</param> /// <param name="command">The command within <paramref name="target"/> the alias shall point to; can be <c>null</c>.</param> /// <param name="aliasName">The name of the alias to be created.</param> /// <param name="machineWide">Create the alias 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> /// <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> public static void Create(FeedTarget target, [CanBeNull] string command, [NotNull] string aliasName, bool machineWide, [NotNull] ITaskHandler handler) { #region Sanity checks if (string.IsNullOrEmpty(aliasName)) throw new ArgumentNullException(nameof(aliasName)); if (handler == null) throw new ArgumentNullException(nameof(handler)); #endregion if (string.IsNullOrEmpty(aliasName) || aliasName.IndexOfAny(Path.GetInvalidFileNameChars()) != -1) throw new IOException(string.Format(Resources.NameInvalidChars, aliasName)); string stubDirPath = GetStubDir(machineWide); PathEnv.AddDir(stubDirPath, machineWide); string stubFilePath = Path.Combine(stubDirPath, aliasName + ".exe"); StubBuilder.BuildRunStub(target, stubFilePath, handler, needsTerminal: true, command: command); AddToAppPaths(aliasName + ".exe", stubFilePath, machineWide); }
/// <summary> /// Creates an application alias in the current system. /// </summary> /// <param name="target">The application being integrated.</param> /// <param name="command">The command within <paramref name="target"/> the alias shall point to; can be <see langword="null"/>.</param> /// <param name="aliasName">The name of the alias to be created.</param> /// <param name="machineWide">Create the alias 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> /// <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> public static void Create(FeedTarget target, [CanBeNull] string command, [NotNull] string aliasName, bool machineWide, [NotNull] ITaskHandler handler) { #region Sanity checks if (string.IsNullOrEmpty(aliasName)) throw new ArgumentNullException("aliasName"); if (handler == null) throw new ArgumentNullException("handler"); #endregion if (string.IsNullOrEmpty(aliasName) || aliasName.IndexOfAny(Path.GetInvalidFileNameChars()) != -1) throw new IOException(string.Format(Resources.NameInvalidChars, aliasName)); string stubDirPath = Locations.GetIntegrationDirPath("0install.net", machineWide, "desktop-integration", "aliases"); string stubFilePath = Path.Combine(stubDirPath, aliasName + ".exe"); target.BuildRunStub(stubFilePath, handler, needsTerminal: true, command: command); AddToPath(stubDirPath, machineWide); AddToAppPaths(aliasName + ".exe", stubFilePath, machineWide); }
/// <summary> /// Builds a stub EXE in a well-known location. Future calls with the same arguments will return the same EXE. /// </summary> /// <param name="target">The application to be launched via the stub.</param> /// <param name="command">The command argument to be passed to the the "0install run" command; can be <c>null</c>.</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="machineWide">Store the stub in a machine-wide directory instead of just for the current user.</param> /// <returns>The path to the generated stub EXE.</returns> /// <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 occurs while writing to the filesystem.</exception> /// <exception cref="WebException">A problem occured while downloading additional data (such as icons).</exception> /// <exception cref="InvalidOperationException">Write access to the filesystem is not permitted.</exception> public static string GetRunStub(FeedTarget target, [CanBeNull] string command, [NotNull] ITaskHandler handler, bool machineWide = false) { #region Sanity checks if (handler == null) throw new ArgumentNullException(nameof(handler)); #endregion var entryPoint = target.Feed.GetEntryPoint(command); string exeName = (entryPoint != null) ? entryPoint.BinaryName ?? entryPoint.Command : FeedUri.Escape(target.Feed.Name); bool needsTerminal = (entryPoint != null && entryPoint.NeedsTerminal); string hash = (target.Uri + "#" + command).Hash(SHA256.Create()); string path = Path.Combine(Locations.GetIntegrationDirPath("0install.net", machineWide, "desktop-integration", "stubs", hash), exeName + ".exe"); CreateOrUpdateRunStub(target, path, command, needsTerminal, handler); return path; }
public void TestRemoveApp() { var target = new FeedTarget(FeedTest.Test1Uri, new Feed {Name = "Test"}); var appEntry = _integrationManager.AddApp(target); // Inject access point into AppEntry (without running integration) using (var unapplyFlag = new TemporaryFlagFile("0install-unit-tests")) { appEntry.AccessPoints = new AccessPointList {Entries = {new MockAccessPoint {UnapplyFlagPath = unapplyFlag}}}; _integrationManager.RemoveApp(appEntry); _integrationManager.AppList.Entries.Should().BeEmpty(); unapplyFlag.Set.Should().BeTrue(because: "Access points should be unapplied when their AppEntry is removed"); _integrationManager.Invoking(x => x.RemoveApp(appEntry)) .ShouldNotThrow(because: "Allow multiple removals of applications."); } }
/// <summary> /// Hooks into the creation of new processes on the current thread to inject API hooks. /// </summary> /// <param name="selections">The implementations chosen for launch.</param> /// <param name="executor">The executor used to launch the new process.</param> /// <param name="feedManager">Provides access to remote and local <see cref="Feed"/>s. Handles downloading, signature verification and caching.</param> /// <param name="handler">A callback object used when the the user needs to be asked questions or informed about download and IO tasks.</param> /// <exception cref="ImplementationNotFoundException">The main implementation is not cached (possibly because it is installed natively).</exception> public RunHook(Selections selections, IExecutor executor, IFeedManager feedManager, ITaskHandler handler) { var feed = feedManager[selections.InterfaceUri]; _target = new FeedTarget(selections.InterfaceUri, feed); var mainImplementation = selections.MainImplementation; _implementationDir = executor.GetImplementationPath(mainImplementation); _mainImplementation = feed[mainImplementation.ID]; _handler = handler; _registryFilter = GetRegistryFilter(); _relaunchControl = GetRelaunchControl(); Log.Info("Activating API hooking"); _hookW = LocalHook.Create(LocalHook.GetProcAddress("kernel32.dll", "CreateProcessW"), new UnsafeNativeMethods.DCreateProcessW(CreateProcessWCallback), null); _hookW.ThreadACL.SetInclusiveACL(new[] {Thread.CurrentThread.ManagedThreadId}); _hookA = LocalHook.Create(LocalHook.GetProcAddress("kernel32.dll", "CreateProcessA"), new UnsafeNativeMethods.DCreateProcessA(CreateProcessACallback), null); _hookA.ThreadACL.SetInclusiveACL(new[] {Thread.CurrentThread.ManagedThreadId}); }
/// <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> /// Creates a new Windows shortcut. /// </summary> /// <param name="path">The location to place the shorcut at.</param> /// <param name="target">The target the shortcut shall point to.</param> /// <param name="command">The command within <paramref name="target"/> the shorcut shall point to; can be <c>null</c>.</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="machineWide">Create the shortcut machine-wide instead of just for the current user.</param> private static void Create([NotNull] string path, FeedTarget target, [CanBeNull] string command, [NotNull] ITaskHandler handler, bool machineWide) { 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 : IconProvider.GetIconPath(icon, handler, machineWide), description: target.Feed.GetBestSummary(CultureInfo.CurrentUICulture, command)); }
/// <summary> /// Adds a context menu entry to the current system. /// </summary> /// <param name="target">The application being integrated.</param> /// <param name="contextMenu">The context menu entry to add.</param> /// <param name="machineWide">Add the context menu entry 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> /// <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="contextMenu"/> is invalid.</exception> public static void Apply(FeedTarget target, Store.Model.Capabilities.ContextMenu contextMenu, bool machineWide, ITaskHandler handler) { #region Sanity checks if (contextMenu == null) throw new ArgumentNullException("contextMenu"); if (handler == null) throw new ArgumentNullException("handler"); #endregion if (contextMenu.Verb == null) throw new InvalidDataException("Missing verb"); if (string.IsNullOrEmpty(contextMenu.Verb.Name)) throw new InvalidDataException("Missing verb name"); var hive = machineWide ? Registry.LocalMachine : Registry.CurrentUser; foreach (string keyName in GetKeyName(contextMenu.Target)) { using (var verbKey = hive.CreateSubKeyChecked(FileType.RegKeyClasses + @"\" + keyName + @"\shell\" + RegKeyPrefix + contextMenu.Verb.Name)) { string description = contextMenu.Verb.Descriptions.GetBestLanguage(CultureInfo.CurrentUICulture); if (description != null) verbKey.SetValue("", description); if (contextMenu.Verb.Extended) verbKey.SetValue(FileType.RegValueExtended, ""); using (var commandKey = verbKey.CreateSubKeyChecked("command")) commandKey.SetValue("", FileType.GetLaunchCommandLine(target, contextMenu.Verb, machineWide, handler)); } } }
/// <inheritdoc/> protected override AppEntry AddAppInternal(FeedTarget target) { // Prevent double entries if (AppList.ContainsEntry(target.Uri)) throw new InvalidOperationException(string.Format(Resources.AppAlreadyInList, target.Feed.Name)); // Get basic metadata and copy of capabilities from feed var appEntry = new AppEntry {InterfaceUri = target.Uri, Name = target.Feed.Name, Timestamp = DateTime.UtcNow}; appEntry.CapabilityLists.AddRange(target.Feed.CapabilityLists.CloneElements()); AppList.Entries.Add(appEntry); WriteAppDir(appEntry); return appEntry; }
/// <summary> /// Creates a dialog box for asking the the user to select an <see cref="Command"/>. /// </summary> /// <param name="target">The application to be launched.</param> public SelectCommandDialog(FeedTarget target) { _target = target; InitializeComponent(); }
/// <summary> /// Generates the C# to be compiled for the stub EXE. /// </summary> /// <param name="target">The application to be launched via the stub.</param> /// <param name="needsTerminal"><see langword="true"/> to build a CLI stub, <see langword="false"/> to build a GUI stub.</param> /// <param name="command">The command argument to be passed to the the "0install run" command; can be <see langword="null"/>.</param> /// <returns>Generated C# code.</returns> private static string GetRunStubCode(FeedTarget target, bool needsTerminal, [CanBeNull] string command = null) { // Build command-line string args = needsTerminal ? "" : "run --no-wait "; if (!string.IsNullOrEmpty(command)) args += "--command " + command.EscapeArgument() + " "; args += target.Uri.ToStringRfc().EscapeArgument(); // Load the template code and insert variables var code = typeof(StubBuilder).GetEmbedded("stub.template.cs").ReadToString() .Replace("[EXE]", Path.Combine(Locations.InstallBase, needsTerminal ? "0launch.exe" : "0install-win.exe") .Replace(@"\", @"\\")); code = code.Replace("[ARGUMENTS]", EscapeForCode(args)); code = code.Replace("[TITLE]", EscapeForCode(target.Feed.GetBestName(CultureInfo.CurrentUICulture, command))); return code; }
public static void Create(MenuEntry menuEntry, FeedTarget target, bool machineWide, ITaskHandler handler) { throw new NotImplementedException(); }
public static void Create(DesktopIcon desktopIcon, FeedTarget target, bool machineWide, ITaskHandler handler) { throw new NotImplementedException(); }
/// <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="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 should become the default handler for all <see cref="Store.Model.Capabilities.AutoPlay.Events"/>.</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="autoPlay"/> is invalid.</exception> public static void Register(FeedTarget target, [NotNull] Store.Model.Capabilities.AutoPlay autoPlay, bool machineWide, [NotNull] ITaskHandler handler, bool accessPoint = false) { #region Sanity checks if (autoPlay == null) throw new ArgumentNullException("autoPlay"); if (handler == null) throw new ArgumentNullException("handler"); #endregion if (string.IsNullOrEmpty(autoPlay.ID)) throw new InvalidDataException("Missing ID"); if (autoPlay.Verb == null) throw new InvalidDataException("Missing verb"); if (string.IsNullOrEmpty(autoPlay.Verb.Name)) throw new InvalidDataException("Missing verb name"); if (string.IsNullOrEmpty(autoPlay.Provider)) throw new InvalidDataException("Missing provider"); var hive = machineWide ? Registry.LocalMachine : Registry.CurrentUser; using (var commandKey = hive.CreateSubKeyChecked(FileType.RegKeyClasses + @"\" + FileType.RegKeyPrefix + ".AutoPlay" + autoPlay.ID + @"\shell\" + autoPlay.Verb.Name + @"\command")) commandKey.SetValue("", FileType.GetLaunchCommandLine(target, autoPlay.Verb, machineWide, handler)); using (var handlerKey = hive.CreateSubKeyChecked(RegKeyHandlers + @"\" + FileType.RegKeyPrefix + autoPlay.ID)) { // Add flag to remember whether created for capability or access point handlerKey.SetValue(accessPoint ? FileType.PurposeFlagAccessPoint : FileType.PurposeFlagCapability, ""); handlerKey.SetValue(RegValueProgID, FileType.RegKeyPrefix + ".AutoPlay" + autoPlay.ID); handlerKey.SetValue(RegValueVerb, autoPlay.Verb.Name); handlerKey.SetValue(RegValueProvider, autoPlay.Provider); handlerKey.SetValue(RegValueDescription, autoPlay.Descriptions.GetBestLanguage(CultureInfo.CurrentUICulture) ?? autoPlay.Verb.Name); var icon = autoPlay.GetIcon(Icon.MimeTypeIco) ?? target.Feed.GetIcon(Icon.MimeTypeIco, autoPlay.Verb.Command); if (icon != null) handlerKey.SetValue(RegValueIcon, IconProvider.GetIconPath(icon, handler, machineWide) + ",0"); } foreach (var autoPlayEvent in autoPlay.Events.Except(x => string.IsNullOrEmpty(x.Name))) { using (var eventKey = hive.CreateSubKeyChecked(RegKeyAssocs + @"\" + autoPlayEvent.Name)) eventKey.SetValue(FileType.RegKeyPrefix + autoPlay.ID, ""); if (accessPoint) { using (var chosenEventKey = hive.CreateSubKeyChecked(RegKeyChosenAssocs + @"\" + autoPlayEvent.Name)) chosenEventKey.SetValue("", FileType.RegKeyPrefix + autoPlay.ID); } } }
public void TestAddApp() { var capabilityList = CapabilityListTest.CreateTestCapabilityList(); var target = new FeedTarget(FeedTest.Test1Uri, new Feed {Name = "Test", CapabilityLists = {capabilityList}}); _integrationManager.AddApp(target); var expectedAppEntry = new AppEntry {InterfaceUri = FeedTest.Test1Uri, Name = target.Feed.Name, CapabilityLists = {capabilityList}}; CollectionAssert.AreEqual(new[] {expectedAppEntry}, _integrationManager.AppList.Entries); Assert.Throws<InvalidOperationException>(() => _integrationManager.AddApp(target), "Do not allow adding applications to AppList more than once."); }
public void TestRepair() { var target = new FeedTarget(FeedTest.Test1Uri, new Feed {Name = "Test"}); var appEntry = _integrationManager.AddApp(target); using (var applyFlag = new TemporaryFlagFile("0install-unit-tests")) { // Inject access point into AppEntry (without running integration) appEntry.AccessPoints = new AccessPointList {Entries = {new MockAccessPoint {ApplyFlagPath = applyFlag}}}; _integrationManager.Repair(uri => new Feed()); Assert.IsTrue(applyFlag.Set, "Access points should be reapplied"); } }