diff --git a/.pipelines/pipeline.user.windows.yml b/.pipelines/pipeline.user.windows.yml
index b16f511d15..dfa8d316fb 100644
--- a/.pipelines/pipeline.user.windows.yml
+++ b/.pipelines/pipeline.user.windows.yml
@@ -130,6 +130,7 @@ build:
- 'modules\launcher\Plugins\Microsoft.Plugin.WindowWalker\Wox.Infrastructure.dll'
- 'modules\launcher\Plugins\Microsoft.Plugin.WindowWalker\Wox.Plugin.dll'
- 'modules\launcher\Plugins\Microsoft.Plugin.WindowWalker\Telemetry.dll'
+ - 'modules\launcher\Plugins\Service\Microsoft.PowerToys.Run.Plugin.Service.dll'
- 'modules\launcher\PowerLauncher.dll'
- 'modules\launcher\PowerLauncher.exe'
- 'modules\launcher\PowerLauncher.Telemetry.dll'
diff --git a/PowerToys.sln b/PowerToys.sln
index 6e0ab38f2b..72da819c2f 100644
--- a/PowerToys.sln
+++ b/PowerToys.sln
@@ -151,6 +151,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PowerLauncher", "src\module
{FDB3555B-58EF-4AE6-B5F1-904719637AB4} = {FDB3555B-58EF-4AE6-B5F1-904719637AB4}
{59BD9891-3837-438A-958D-ADC7F91F6F7E} = {59BD9891-3837-438A-958D-ADC7F91F6F7E}
{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} = {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}
+ {0351ADA4-0C32-4652-9BA0-41F7B602372B} = {0351ADA4-0C32-4652-9BA0-41F7B602372B}
{787B8AA6-CA93-4C84-96FE-DF31110AD1C4} = {787B8AA6-CA93-4C84-96FE-DF31110AD1C4}
{F8B870EB-D5F5-45BA-9CF7-A5C459818820} = {F8B870EB-D5F5-45BA-9CF7-A5C459818820}
{74F1B9ED-F59C-4FE7-B473-7B453E30837E} = {74F1B9ED-F59C-4FE7-B473-7B453E30837E}
@@ -244,6 +245,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Plugin.Sys", "src
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Plugin.Sys.UnitTests", "src\modules\launcher\Plugins\Microsoft.Plugin.Sys.UnitTests\Microsoft.Plugin.Sys.UnitTests.csproj", "{DA5A6FE9-0040-40CC-83CC-764AE5306590}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerToys.Run.Plugin.Service", "src\modules\launcher\Plugins\Microsoft.PowerToys.Run.Plugin.Service\Microsoft.PowerToys.Run.Plugin.Service.csproj", "{0351ADA4-0C32-4652-9BA0-41F7B602372B}"
+EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "logger", "src\common\logger\logger.vcxproj", "{D9B8FC84-322A-4F9F-BBB9-20915C47DDFD}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SetttingsAPI", "src\common\SettingsAPI\SetttingsAPI.vcxproj", "{6955446D-23F7-4023-9BB3-8657F904AF99}"
@@ -560,6 +563,10 @@ Global
{DA5A6FE9-0040-40CC-83CC-764AE5306590}.Debug|x64.Build.0 = Debug|x64
{DA5A6FE9-0040-40CC-83CC-764AE5306590}.Release|x64.ActiveCfg = Release|x64
{DA5A6FE9-0040-40CC-83CC-764AE5306590}.Release|x64.Build.0 = Release|x64
+ {0351ADA4-0C32-4652-9BA0-41F7B602372B}.Debug|x64.ActiveCfg = Debug|x64
+ {0351ADA4-0C32-4652-9BA0-41F7B602372B}.Debug|x64.Build.0 = Debug|x64
+ {0351ADA4-0C32-4652-9BA0-41F7B602372B}.Release|x64.ActiveCfg = Release|x64
+ {0351ADA4-0C32-4652-9BA0-41F7B602372B}.Release|x64.Build.0 = Release|x64
{D9B8FC84-322A-4F9F-BBB9-20915C47DDFD}.Debug|x64.ActiveCfg = Debug|x64
{D9B8FC84-322A-4F9F-BBB9-20915C47DDFD}.Debug|x64.Build.0 = Debug|x64
{D9B8FC84-322A-4F9F-BBB9-20915C47DDFD}.Release|x64.ActiveCfg = Release|x64
@@ -686,6 +693,7 @@ Global
{7E1E3F13-2BD6-3F75-A6A7-873A2B55C60F} = {E4E03FE0-94FD-47C7-88C5-F17D0AA549D3}
{FD8EB419-FF9C-4D88-BB6F-BF6CED37747B} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
{DA5A6FE9-0040-40CC-83CC-764AE5306590} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
+ {0351ADA4-0C32-4652-9BA0-41F7B602372B} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
{D9B8FC84-322A-4F9F-BBB9-20915C47DDFD} = {E4E03FE0-94FD-47C7-88C5-F17D0AA549D3}
{6955446D-23F7-4023-9BB3-8657F904AF99} = {1AFB6476-670D-4E80-A464-657E01DFF482}
{58736667-1027-4AD7-BFDF-7A3A6474103A} = {5A7818A8-109C-4E1C-850D-1A654E234B0E}
diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs
index c6664efe05..83123f7110 100644
--- a/installer/PowerToysSetup/Product.wxs
+++ b/installer/PowerToysSetup/Product.wxs
@@ -250,6 +250,9 @@
+
+
+
@@ -876,9 +879,17 @@
+
+
+
+
+
+
+
+
-
+
@@ -888,7 +899,7 @@
-
+
@@ -974,6 +985,17 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Action.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Action.cs
new file mode 100644
index 0000000000..9fb2a6e4e0
--- /dev/null
+++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Action.cs
@@ -0,0 +1,13 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace Microsoft.PowerToys.Run.Plugin.Service
+{
+ public enum Action
+ {
+ Start,
+ Stop,
+ Restart,
+ }
+}
diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Helpers/ServiceHelper.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Helpers/ServiceHelper.cs
new file mode 100644
index 0000000000..b2c4a69eea
--- /dev/null
+++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Helpers/ServiceHelper.cs
@@ -0,0 +1,215 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Globalization;
+using System.Linq;
+using System.Reflection;
+using System.ServiceProcess;
+using Microsoft.PowerToys.Run.Plugin.Service.Properties;
+using Wox.Plugin;
+using Wox.Plugin.Logger;
+
+namespace Microsoft.PowerToys.Run.Plugin.Service.Helpers
+{
+ public static class ServiceHelper
+ {
+ public static IEnumerable Search(string search, string icoPath)
+ {
+ var services = ServiceController.GetServices();
+
+ return services
+ .Where(s => s.DisplayName.StartsWith(search, StringComparison.OrdinalIgnoreCase) || s.ServiceName.StartsWith(search, StringComparison.OrdinalIgnoreCase))
+ .Select(s => new Result
+ {
+ Title = GetResultTitle(s),
+ SubTitle = GetResultSubTitle(s),
+ IcoPath = icoPath,
+ ContextData = new ServiceResult(s),
+ });
+ }
+
+ public static void ChangeStatus(ServiceResult serviceResult, Action action, IPublicAPI contextAPI)
+ {
+ if (serviceResult == null)
+ {
+ throw new ArgumentNullException(nameof(serviceResult));
+ }
+
+ if (contextAPI == null)
+ {
+ throw new ArgumentNullException(nameof(contextAPI));
+ }
+
+ try
+ {
+ var info = new ProcessStartInfo
+ {
+ FileName = "net",
+ Verb = "runas",
+ UseShellExecute = true,
+ WindowStyle = ProcessWindowStyle.Hidden,
+ };
+
+ if (action == Action.Start)
+ {
+ info.Arguments = string.Join(' ', "start", serviceResult.ServiceName);
+ }
+ else if (action == Action.Stop)
+ {
+ info.Arguments = string.Join(' ', "stop", serviceResult.ServiceName);
+ }
+ else if (action == Action.Restart)
+ {
+ info.FileName = "cmd";
+ info.Arguments = string.Join(' ', "/c net stop", serviceResult.ServiceName, "&&", "net start", serviceResult.ServiceName);
+ }
+
+ var process = Process.Start(info);
+ process.WaitForExit();
+ var exitCode = process.ExitCode;
+
+ if (exitCode == 0)
+ {
+ contextAPI.ShowNotification(GetLocalizedMessage(serviceResult, action));
+ }
+ else
+ {
+ contextAPI.ShowNotification("An error occurred");
+ Log.Error($"The command returned {exitCode}", MethodBase.GetCurrentMethod().DeclaringType);
+ }
+ }
+ catch (Win32Exception ex)
+ {
+ Log.Error(ex.Message, MethodBase.GetCurrentMethod().DeclaringType);
+ }
+ }
+
+ public static void OpenServices()
+ {
+ try
+ {
+ var info = new ProcessStartInfo
+ {
+ FileName = "services.msc",
+ UseShellExecute = true,
+ };
+
+ Process.Start(info);
+ }
+ catch (Win32Exception ex)
+ {
+ Log.Error(ex.Message, MethodBase.GetCurrentMethod().DeclaringType);
+ }
+ }
+
+ private static string GetResultTitle(ServiceController serviceController)
+ {
+ if (serviceController == null)
+ {
+ throw new ArgumentNullException(nameof(serviceController));
+ }
+
+ var suffix = $"({serviceController.ServiceName})";
+ return serviceController.DisplayName.EndsWith(suffix, StringComparison.CurrentCulture) ? serviceController.DisplayName : $"{serviceController.DisplayName} {suffix}";
+ }
+
+ private static string GetResultSubTitle(ServiceController serviceController)
+ {
+ if (serviceController == null)
+ {
+ throw new ArgumentNullException(nameof(serviceController));
+ }
+
+ return $"{Resources.wox_plugin_service_status}: {GetLocalizedStatus(serviceController.Status)} - {Resources.wox_plugin_service_startup}: {GetLocalizedStartType(serviceController.StartType)}";
+ }
+
+ private static string GetLocalizedStatus(ServiceControllerStatus status)
+ {
+ if (status == ServiceControllerStatus.Stopped)
+ {
+ return Resources.wox_plugin_service_stopped;
+ }
+ else if (status == ServiceControllerStatus.StartPending)
+ {
+ return Resources.wox_plugin_service_start_pending;
+ }
+ else if (status == ServiceControllerStatus.StopPending)
+ {
+ return Resources.wox_plugin_service_stop_pending;
+ }
+ else if (status == ServiceControllerStatus.Running)
+ {
+ return Resources.wox_plugin_service_running;
+ }
+ else if (status == ServiceControllerStatus.ContinuePending)
+ {
+ return Resources.wox_plugin_service_continue_pending;
+ }
+ else if (status == ServiceControllerStatus.PausePending)
+ {
+ return Resources.wox_plugin_service_pause_pending;
+ }
+ else if (status == ServiceControllerStatus.Paused)
+ {
+ return Resources.wox_plugin_service_paused;
+ }
+ else
+ {
+ return status.ToString();
+ }
+ }
+
+ private static string GetLocalizedStartType(ServiceStartMode startMode)
+ {
+ if (startMode == ServiceStartMode.Boot)
+ {
+ return Resources.wox_plugin_service_start_mode_boot;
+ }
+ else if (startMode == ServiceStartMode.System)
+ {
+ return Resources.wox_plugin_service_start_mode_system;
+ }
+ else if (startMode == ServiceStartMode.Automatic)
+ {
+ return Resources.wox_plugin_service_start_mode_automatic;
+ }
+ else if (startMode == ServiceStartMode.Manual)
+ {
+ return Resources.wox_plugin_service_start_mode_manual;
+ }
+ else if (startMode == ServiceStartMode.Disabled)
+ {
+ return Resources.wox_plugin_service_start_mode_disabled;
+ }
+ else
+ {
+ return startMode.ToString();
+ }
+ }
+
+ private static string GetLocalizedMessage(ServiceResult serviceResult, Action action)
+ {
+ if (action == Action.Start)
+ {
+ return string.Format(CultureInfo.CurrentCulture, Resources.wox_plugin_service_started_notification, serviceResult.DisplayName);
+ }
+ else if (action == Action.Stop)
+ {
+ return string.Format(CultureInfo.CurrentCulture, Resources.wox_plugin_service_stopped_notification, serviceResult.DisplayName);
+ }
+ else if (action == Action.Restart)
+ {
+ return string.Format(CultureInfo.CurrentCulture, Resources.wox_plugin_service_restarted_notification, serviceResult.DisplayName);
+ }
+ else
+ {
+ return string.Empty;
+ }
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Images/service.dark.png b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Images/service.dark.png
new file mode 100644
index 0000000000..198dafd779
Binary files /dev/null and b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Images/service.dark.png differ
diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Images/service.light.png b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Images/service.light.png
new file mode 100644
index 0000000000..c8382bbcc9
Binary files /dev/null and b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Images/service.light.png differ
diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Main.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Main.cs
new file mode 100644
index 0000000000..ff835be8a7
--- /dev/null
+++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Main.cs
@@ -0,0 +1,143 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Threading.Tasks;
+using System.Windows.Input;
+using ManagedCommon;
+using Microsoft.PowerToys.Run.Plugin.Service.Helpers;
+using Microsoft.PowerToys.Run.Plugin.Service.Properties;
+using Wox.Plugin;
+
+namespace Microsoft.PowerToys.Run.Plugin.Service
+{
+ public class Main : IPlugin, IContextMenu, IPluginI18n
+ {
+ private PluginInitContext _context;
+ private string _icoPath;
+
+ public void Init(PluginInitContext context)
+ {
+ _context = context;
+ _context.API.ThemeChanged += OnThemeChanged;
+
+ UpdateIconPath(_context.API.GetCurrentTheme());
+ }
+
+ public List LoadContextMenus(Result selectedResult)
+ {
+ if (!(selectedResult?.ContextData is ServiceResult))
+ {
+ return new List();
+ }
+
+ var contextMenuResult = new List();
+ var serviceResult = selectedResult.ContextData as ServiceResult;
+
+ if (serviceResult.IsRunning)
+ {
+ // Stop
+ contextMenuResult.Add(new ContextMenuResult
+ {
+ PluginName = Assembly.GetExecutingAssembly().GetName().Name,
+ Title = Resources.wox_plugin_service_stop,
+ Glyph = "\xE71A",
+ FontFamily = "Segoe MDL2 Assets",
+ AcceleratorKey = Key.Enter,
+ Action = _ =>
+ {
+ Task.Run(() => ServiceHelper.ChangeStatus(serviceResult, Action.Stop, _context.API));
+ return true;
+ },
+ });
+
+ // Restart
+ contextMenuResult.Add(new ContextMenuResult
+ {
+ PluginName = Assembly.GetExecutingAssembly().GetName().Name,
+ Title = Resources.wox_plugin_service_restart,
+ Glyph = "\xE72C",
+ FontFamily = "Segoe MDL2 Assets",
+ AcceleratorKey = Key.R,
+ AcceleratorModifiers = ModifierKeys.Control | ModifierKeys.Shift,
+ Action = _ =>
+ {
+ Task.Run(() => ServiceHelper.ChangeStatus(serviceResult, Action.Restart, _context.API));
+ return true;
+ },
+ });
+ }
+ else
+ {
+ // Start
+ contextMenuResult.Add(new ContextMenuResult
+ {
+ PluginName = Assembly.GetExecutingAssembly().GetName().Name,
+ Title = Resources.wox_plugin_service_start,
+ Glyph = "\xEDB5",
+ FontFamily = "Segoe MDL2 Assets",
+ AcceleratorKey = Key.Enter,
+ Action = _ =>
+ {
+ Task.Run(() => ServiceHelper.ChangeStatus(serviceResult, Action.Start, _context.API));
+ return true;
+ },
+ });
+ }
+
+ // Open services
+ contextMenuResult.Add(new ContextMenuResult
+ {
+ PluginName = Assembly.GetExecutingAssembly().GetName().Name,
+ Title = Resources.wox_plugin_service_open_services,
+ Glyph = "\xE8A7",
+ FontFamily = "Segoe MDL2 Assets",
+ AcceleratorKey = Key.O,
+ AcceleratorModifiers = ModifierKeys.Control | ModifierKeys.Shift,
+ Action = _ =>
+ {
+ Task.Run(() => ServiceHelper.OpenServices());
+ return true;
+ },
+ });
+
+ return contextMenuResult;
+ }
+
+ public List Query(Query query)
+ {
+ var search = query?.Search ?? string.Empty;
+ return ServiceHelper.Search(search, _icoPath).ToList();
+ }
+
+ public string GetTranslatedPluginTitle()
+ {
+ return Resources.wox_plugin_service_plugin_name;
+ }
+
+ public string GetTranslatedPluginDescription()
+ {
+ return Resources.wox_plugin_service_plugin_description;
+ }
+
+ private void UpdateIconPath(Theme theme)
+ {
+ if (theme == Theme.Light || theme == Theme.HighContrastWhite)
+ {
+ _icoPath = "Images/service.light.png";
+ }
+ else
+ {
+ _icoPath = "Images/service.dark.png";
+ }
+ }
+
+ private void OnThemeChanged(Theme currentTheme, Theme newTheme)
+ {
+ UpdateIconPath(newTheme);
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Microsoft.PowerToys.Run.Plugin.Service.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Microsoft.PowerToys.Run.Plugin.Service.csproj
new file mode 100644
index 0000000000..48ea3996ea
--- /dev/null
+++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Microsoft.PowerToys.Run.Plugin.Service.csproj
@@ -0,0 +1,100 @@
+
+
+
+
+
+ netcoreapp3.1
+ Microsoft.PowerToys.Run.Plugin.Service
+ Microsoft.PowerToys.Run.Plugin.Service
+ $(Version).0
+ false
+ false
+ x64
+ en-US
+
+
+
+ true
+ ..\..\..\..\..\x64\Debug\modules\launcher\Plugins\Service\
+ DEBUG;TRACE
+ full
+ x64
+ 7.3
+ prompt
+ MinimumRecommendedRules.ruleset
+ 4
+ false
+ true
+
+
+
+ ..\..\..\..\..\x64\Release\modules\launcher\Plugins\Service\
+ TRACE
+ true
+ pdbonly
+ x64
+ 7.3
+ prompt
+ MinimumRecommendedRules.ruleset
+ 4
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+ GlobalSuppressions.cs
+
+
+ StyleCop.json
+
+
+
+
+ 1.1.118
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+
+
diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Properties/Resources.Designer.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Properties/Resources.Designer.cs
new file mode 100644
index 0000000000..5e44c1a68e
--- /dev/null
+++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Properties/Resources.Designer.cs
@@ -0,0 +1,277 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace Microsoft.PowerToys.Run.Plugin.Service.Properties
+{
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.PowerToys.Run.Plugin.Service.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Continue.
+ ///
+ internal static string wox_plugin_service_continue_pending {
+ get {
+ return ResourceManager.GetString("wox_plugin_service_continue_pending", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Open services (Ctrl+Shift+O).
+ ///
+ internal static string wox_plugin_service_open_services {
+ get {
+ return ResourceManager.GetString("wox_plugin_service_open_services", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Pausing.
+ ///
+ internal static string wox_plugin_service_pause_pending {
+ get {
+ return ResourceManager.GetString("wox_plugin_service_pause_pending", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Paused.
+ ///
+ internal static string wox_plugin_service_paused {
+ get {
+ return ResourceManager.GetString("wox_plugin_service_paused", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Manages Windows services.
+ ///
+ internal static string wox_plugin_service_plugin_description {
+ get {
+ return ResourceManager.GetString("wox_plugin_service_plugin_description", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Service.
+ ///
+ internal static string wox_plugin_service_plugin_name {
+ get {
+ return ResourceManager.GetString("wox_plugin_service_plugin_name", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Restart (Ctrl+Shift+R).
+ ///
+ internal static string wox_plugin_service_restart {
+ get {
+ return ResourceManager.GetString("wox_plugin_service_restart", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to {0} has been restarted.
+ ///
+ internal static string wox_plugin_service_restarted_notification {
+ get {
+ return ResourceManager.GetString("wox_plugin_service_restarted_notification", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Running.
+ ///
+ internal static string wox_plugin_service_running {
+ get {
+ return ResourceManager.GetString("wox_plugin_service_running", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Start (Enter).
+ ///
+ internal static string wox_plugin_service_start {
+ get {
+ return ResourceManager.GetString("wox_plugin_service_start", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Automatic.
+ ///
+ internal static string wox_plugin_service_start_mode_automatic {
+ get {
+ return ResourceManager.GetString("wox_plugin_service_start_mode_automatic", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Boot.
+ ///
+ internal static string wox_plugin_service_start_mode_boot {
+ get {
+ return ResourceManager.GetString("wox_plugin_service_start_mode_boot", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Disabled.
+ ///
+ internal static string wox_plugin_service_start_mode_disabled {
+ get {
+ return ResourceManager.GetString("wox_plugin_service_start_mode_disabled", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Manual.
+ ///
+ internal static string wox_plugin_service_start_mode_manual {
+ get {
+ return ResourceManager.GetString("wox_plugin_service_start_mode_manual", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to System.
+ ///
+ internal static string wox_plugin_service_start_mode_system {
+ get {
+ return ResourceManager.GetString("wox_plugin_service_start_mode_system", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Starting.
+ ///
+ internal static string wox_plugin_service_start_pending {
+ get {
+ return ResourceManager.GetString("wox_plugin_service_start_pending", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Started.
+ ///
+ internal static string wox_plugin_service_started {
+ get {
+ return ResourceManager.GetString("wox_plugin_service_started", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to {0} has been started.
+ ///
+ internal static string wox_plugin_service_started_notification {
+ get {
+ return ResourceManager.GetString("wox_plugin_service_started_notification", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Startup.
+ ///
+ internal static string wox_plugin_service_startup {
+ get {
+ return ResourceManager.GetString("wox_plugin_service_startup", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Status.
+ ///
+ internal static string wox_plugin_service_status {
+ get {
+ return ResourceManager.GetString("wox_plugin_service_status", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Stop (Enter).
+ ///
+ internal static string wox_plugin_service_stop {
+ get {
+ return ResourceManager.GetString("wox_plugin_service_stop", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Stopping.
+ ///
+ internal static string wox_plugin_service_stop_pending {
+ get {
+ return ResourceManager.GetString("wox_plugin_service_stop_pending", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Stopped.
+ ///
+ internal static string wox_plugin_service_stopped {
+ get {
+ return ResourceManager.GetString("wox_plugin_service_stopped", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to {0} has been stopped.
+ ///
+ internal static string wox_plugin_service_stopped_notification {
+ get {
+ return ResourceManager.GetString("wox_plugin_service_stopped_notification", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Properties/Resources.resx b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Properties/Resources.resx
new file mode 100644
index 0000000000..c5b6d31c57
--- /dev/null
+++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Properties/Resources.resx
@@ -0,0 +1,192 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Continue
+
+
+ Open services (Ctrl+Shift+O)
+
+
+ Paused
+
+
+ Pausing
+
+
+ Manages Windows services
+
+
+ Service
+
+
+ Restart (Ctrl+Shift+R)
+
+
+ {0} has been restarted
+
+
+ Running
+
+
+ Start (Enter)
+
+
+ Started
+
+
+ {0} has been started
+
+
+ Startup
+
+
+ Automatic
+
+
+ Boot
+
+
+ Disabled
+
+
+ Manual
+
+
+ System
+
+
+ Starting
+
+
+ Status
+
+
+ Stop (Enter)
+
+
+ Stopped
+
+
+ {0} has been stopped
+
+
+ Stopping
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/ServiceResult.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/ServiceResult.cs
new file mode 100644
index 0000000000..2f5d0bd34d
--- /dev/null
+++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/ServiceResult.cs
@@ -0,0 +1,33 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.ServiceProcess;
+
+namespace Microsoft.PowerToys.Run.Plugin.Service
+{
+ public class ServiceResult
+ {
+ public string ServiceName { get; }
+
+ public string DisplayName { get; }
+
+ public ServiceStartMode StartMode { get; }
+
+ public bool IsRunning { get; }
+
+ public ServiceResult(ServiceController serviceController)
+ {
+ if (serviceController == null)
+ {
+ throw new ArgumentNullException(nameof(serviceController));
+ }
+
+ ServiceName = serviceController.ServiceName;
+ DisplayName = serviceController.DisplayName;
+ StartMode = serviceController.StartType;
+ IsRunning = serviceController.Status != ServiceControllerStatus.Stopped && serviceController.Status != ServiceControllerStatus.StopPending;
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/plugin.json b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/plugin.json
new file mode 100644
index 0000000000..e92972f882
--- /dev/null
+++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/plugin.json
@@ -0,0 +1,12 @@
+{
+ "ID": "11A6C36E4E91439CA69F702CBD364EF7",
+ "ActionKeyword": "!",
+ "Name": "Service",
+ "Description": "Manages Windows services",
+ "Author": "davidegiacometti",
+ "Version": "1.0.0",
+ "Language": "csharp",
+ "Website": "https://aka.ms/powertoys",
+ "ExecuteFileName": "Microsoft.PowerToys.Run.Plugin.Service.dll",
+ "IcoPath": "Images\\service.dark.png"
+}
diff --git a/src/modules/launcher/PowerLauncher/Helper/LauncherNotificationActivator.cs b/src/modules/launcher/PowerLauncher/Helper/LauncherNotificationActivator.cs
new file mode 100644
index 0000000000..54036880bd
--- /dev/null
+++ b/src/modules/launcher/PowerLauncher/Helper/LauncherNotificationActivator.cs
@@ -0,0 +1,23 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Runtime.InteropServices;
+using Microsoft.Toolkit.Uwp.Notifications;
+
+namespace PowerLauncher.Helper
+{
+ [ClassInterface(ClassInterfaceType.None)]
+#pragma warning disable CS0618 // Type or member is obsolete
+ [ComSourceInterfaces(typeof(INotificationActivationCallback))]
+#pragma warning restore CS0618 // Type or member is obsolete
+ [Guid("DD5CACDA-7C2E-4997-A62A-04A597B58F76")]
+ [ComVisible(true)]
+ public class LauncherNotificationActivator : NotificationActivator
+ {
+ public override void OnActivated(string invokedArgs, NotificationUserInput userInput, string appUserModelId)
+ {
+ }
+ }
+}
diff --git a/src/modules/launcher/PowerLauncher/Images/icon.ico b/src/modules/launcher/PowerLauncher/Images/icon.ico
new file mode 100644
index 0000000000..6cbba351a1
Binary files /dev/null and b/src/modules/launcher/PowerLauncher/Images/icon.ico differ
diff --git a/src/modules/launcher/PowerLauncher/PowerLauncher.csproj b/src/modules/launcher/PowerLauncher/PowerLauncher.csproj
index 5f7baa5ac5..00ac373f7b 100644
--- a/src/modules/launcher/PowerLauncher/PowerLauncher.csproj
+++ b/src/modules/launcher/PowerLauncher/PowerLauncher.csproj
@@ -1,4 +1,4 @@
-
+
WinExe
@@ -100,6 +100,7 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
+
@@ -113,6 +114,7 @@
+
NU1701
@@ -175,6 +177,9 @@
PreserveNewest
+
+ PreserveNewest
+
PreserveNewest
diff --git a/src/modules/launcher/PowerLauncher/PublicAPIInstance.cs b/src/modules/launcher/PowerLauncher/PublicAPIInstance.cs
index 4184da631e..786cf44c96 100644
--- a/src/modules/launcher/PowerLauncher/PublicAPIInstance.cs
+++ b/src/modules/launcher/PowerLauncher/PublicAPIInstance.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Microsoft Corporation
+// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
@@ -9,9 +9,11 @@ using System.Net;
using System.Windows;
using ManagedCommon;
using Microsoft.PowerToys.Common.UI;
+using Microsoft.Toolkit.Uwp.Notifications;
using PowerLauncher.Helper;
using PowerLauncher.Plugin;
using PowerLauncher.ViewModel;
+using Windows.UI.Notifications;
using Wox.Infrastructure.Image;
using Wox.Plugin;
@@ -33,6 +35,9 @@ namespace Wox
_themeManager = themeManager ?? throw new ArgumentNullException(nameof(themeManager));
_themeManager.ThemeChanged += OnThemeChanged;
WebRequest.RegisterPrefix("data", new DataWebRequestFactory());
+
+ DesktopNotificationManagerCompat.RegisterActivator();
+ DesktopNotificationManagerCompat.RegisterAumidAndComServer("PowerToysRun");
}
public void ChangeQuery(string query, bool requery = false)
@@ -79,6 +84,18 @@ namespace Wox
});
}
+ public void ShowNotification(string text)
+ {
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ ToastContent toastContent = new ToastContentBuilder()
+ .AddText(text)
+ .GetToastContent();
+ var toast = new ToastNotification(toastContent.GetXml());
+ DesktopNotificationManagerCompat.CreateToastNotifier().Show(toast);
+ });
+ }
+
public void InstallPlugin(string path)
{
Application.Current.Dispatcher.Invoke(() => PluginManager.InstallPlugin(path));
diff --git a/src/modules/launcher/Wox.Plugin/IPublicAPI.cs b/src/modules/launcher/Wox.Plugin/IPublicAPI.cs
index 2f8d90efef..13e1daa422 100644
--- a/src/modules/launcher/Wox.Plugin/IPublicAPI.cs
+++ b/src/modules/launcher/Wox.Plugin/IPublicAPI.cs
@@ -74,5 +74,11 @@ namespace Wox.Plugin
/// Get all loaded plugins
///
List GetAllPlugins();
+
+ ///
+ /// Show toast notification
+ ///
+ /// Notification text
+ void ShowNotification(string text);
}
}