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); } }