From 3a87c4909cbcb0e3a0c4bc97bf35852d367bfdf5 Mon Sep 17 00:00:00 2001 From: Davide Giacometti Date: Wed, 6 Jan 2021 11:40:07 +0100 Subject: [PATCH] [PT Run] Service Plugin (#8076) * PT Run service plugin * icon, localization and fixes * basic toast notification * service start mode * improved keys * fixed setup * improvements * added _ keyword * better shortcuts * action for open services.msc * pt run service plugin dll signing * renamed Microsoft.Plugin.Service * changed output dir * set ! action keyword * launcher dll Co-authored-by: Clint Rutkas --- .pipelines/pipeline.user.windows.yml | 1 + PowerToys.sln | 8 + installer/PowerToysSetup/Product.wxs | 26 +- .../Action.cs | 13 + .../Helpers/ServiceHelper.cs | 215 ++++++++++++++ .../Images/service.dark.png | Bin 0 -> 2478 bytes .../Images/service.light.png | Bin 0 -> 2426 bytes .../Main.cs | 143 +++++++++ ...rosoft.PowerToys.Run.Plugin.Service.csproj | 100 +++++++ .../Properties/Resources.Designer.cs | 277 ++++++++++++++++++ .../Properties/Resources.resx | 192 ++++++++++++ .../ServiceResult.cs | 33 +++ .../plugin.json | 12 + .../Helper/LauncherNotificationActivator.cs | 23 ++ .../launcher/PowerLauncher/Images/icon.ico | Bin 0 -> 53114 bytes .../PowerLauncher/PowerLauncher.csproj | 7 +- .../PowerLauncher/PublicAPIInstance.cs | 19 +- src/modules/launcher/Wox.Plugin/IPublicAPI.cs | 6 + 18 files changed, 1071 insertions(+), 4 deletions(-) create mode 100644 src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Action.cs create mode 100644 src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Helpers/ServiceHelper.cs create mode 100644 src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Images/service.dark.png create mode 100644 src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Images/service.light.png create mode 100644 src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Main.cs create mode 100644 src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Microsoft.PowerToys.Run.Plugin.Service.csproj create mode 100644 src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Properties/Resources.Designer.cs create mode 100644 src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Properties/Resources.resx create mode 100644 src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/ServiceResult.cs create mode 100644 src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/plugin.json create mode 100644 src/modules/launcher/PowerLauncher/Helper/LauncherNotificationActivator.cs create mode 100644 src/modules/launcher/PowerLauncher/Images/icon.ico 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 0000000000000000000000000000000000000000..198dafd7792a6684133900f58ff7ad7b1d20ae97 GIT binary patch literal 2478 zcmcIm3s4kg9A600BT=%k>7moIh`ij}*RjX0Tq1WUXPnYWc^HLzyL)##-ra7y3kR54 zj){~QOcG5tq@y{d`5>Lt9!8^4k@m3Xlrf_rSc>+hhg9Dl2Wpu)&2+PmZ@=&N{rf|56WLCZ)JsgP?H-jXSO-XWUH?l(=6i z^XMK|3CqhNGbhMi(OeT!02+ewXVfShzgW~^ujrS;HstI}uOhG{*pLOeF5IQq#eh^? zr;79I=9ckwi+M&sX50ej*RX&fBZZN^a^$4S`Am2bx+I5TgOT&fRk{rU zN{3tsDI>IDEfyvaFsz1CFv5%*E)4@+uFFG1p%JvE7gYl*F};^0*2-!X5p#>0T&412 zQMDM>(}s;H0a=%|fcy{IMusm^DDWc&l`1tD4UfQMVo(eLZVh0Gs6K_Yt0Je%YMCqt zhZ9vgY!M~_4$fR4g#|gHWf_WM6GR86i#Ei_8j924DkCf`Wo0dR4i3_X4?$g^4g^l; zE&vk*N-$+4&RQrI&&$Dy^I%z!e6?2sb-7q)Skt*MFFGAI1c)+AlECJ1R*JLaaVSH2 zDb&had?-id3aB73ILR{vgU57%`3{+{G9qEjA1Yfx<^jh@G+CY_d6KkpDB&fnC?#45 zl%W7GLz5KFFg^=mp`u;_wt1=qRt^`ukkwEn07ihOyfn|D0z+F-Qm}YY#z*6*7Z-AK zX)8(MKEQL{u3Zkws!J9?bT8_6+U=#P?303EKy%M7gq=lpnqX)eCCvl^3q0$S)er{? zO$u>-5mUl`qwIkUAh1}{K#pr;3u(T1PplP`;AnNS9B(WN8^RmaAPPvVTe?J;ugsw# z+DJeIq?f6ZA($rn^a!VlGyNbX|5}CEKZ)14>dQ;#7@D*ANFHT)tAOHGixuSmtKHZmIu;|U$`3#P5O&7n6a@A zRT%cy;){-5unB`iM!UeyZZt=Z6!0-}D2ZVZClwqyp=(GJ1jRq*bj&OZJkam?ym=#@ zvetZ+TS?l~@nAvHrs%al<=)hzD;E_u*-s8Oc@|>RIwtgGM?Siv{m7WjPuK3A7Pl(W zxntMlyobg=u!zQNo*Jwg4OOhB zy6*Y?W=L+0`_Ol$ent7!P;1k*`%GiM*|5`X-tcJosE?ChH(kGRTu0-iuO{4mw0RH} zU37fs&W!ptI_J~$+&kwfgRU)w?yfg-f-2ll@U>}D#6Nv`LdiLmpOe)P{Gg{RZhZRI z6usdL|B>m%DV6O<>}^vC@~1e5W#ZxQdq*vKbAEhh=B*hkw>_I!H*fs<^y7QJ$Ve$a zn4Y_G(-G61P-n{Fr(Q`IxAnG@`Nw+qCQeLiZ|&&)4a!{EIJxhw$uDZVTvyMim_D#{ zi7B|h`G)$I^0(JCdUgboAFC+cDY{;I7&&mb$?6G*rqYZ>Q)tGueWzjT+xwO z+yCSf%eODAx+Z>2nsz#)|CViw8=%%7`@Lu2-rj-8+ep@ln}4;|sq!BO?l8sgI@GPd zQgXQUxrFuWJ}+5x>RofN^1&@%oNJzR`!lmVSI$AZf$m+ zJi?w;(oXOFV?yD%>a(A9Pwfi}@9gpPJ(s$rbNqP#ZT>bL% zbH3f-9<*(|cE*bCV@o&eUD~=IvwsNxylS6OLv(jrhczK# VVa>hHD%AK#x&+tM+#Hl9jvLv%9%m2n~vM ziVCAli-xE+9xdb54vJPYVZ^JW;!%&OdUO!)_f)@4F7YU8I@7z|+x@=p_x=9A|Kt0< z`zSLbEuvLaD+q!j?CG{F@WhR~WpnVG-Q>FAGmjr0|Z`4=i#8&s|2r8)K%aSyuKoz9a zfaKF-8jSh#6kXBslxJvb9DYopC^Q=M7pR`P@Q4B?c_c63)&Q2M)92?>REgIW)u||+ zhD2pHScFM{gX6PhpQsdS-3>*d36hQ1B`acN4aFI7ISHDhEF6s|;2?c?J=6i}K;(7) z5imiZ1nVSmjwU&?IRPggf)!DA7rhA7;o$5(P3L`rWVcxnAj%}mBF7MkqL~1KNS7$0 zf#$+8qho9lFe`8Gu;Fd|{huP<9s5dcSHG&#YNC|D@Qg0i?vKyis? zQC4(QDD9>!W-~2GyvQ`L4OV5aa(K_9tcEHPFp?Bbxfp>*MHY~eBJDz1H&ErmMYEZ) zkRq=`UaQQ+K)>g7S9 z$zDEJ!u-BmqwIkU;BLCCfgBfw7Sd2@Oz6!c!*$ik@q)1^tcYM#gCruMZuu!;eldsY zX$$itK>D02sfTHbTQB5QDJd7E81pV5C{t7^na8d;ui9FU#d*yY^?Pa zhCNw)bz_fg!XS}#U0`Q7UK@`T&}clABp-;A3XYsoOYXZ6)Wl`CB{}mZUd-9=os370 z*P9L>_44qtnAJ@Zj4PP~Uox3&(s|;9QI@?Y%UkT&fc#v^CkNkfc8Kn+MC@Ol`SpO^)?sNUD9-RzCT*x#GblT5NOafLXZr`XzqT`QCg+oa)D@Q#!voze>^V3t zEGMsaN9_HIu5V_v4Lfn~tMX>+s@8Z~ZrpHabAHpfgFS?HqiT*F>VINM<>YN&yjANx zGjm&qt+g|1$M1jV*!S}utZ6^F#JhImNNm{6#bM51WX;{9Q!Z-h2RfaLoOgL+>mz}f zj>l_VyW%^>EXx0@%bE|!QfAroBPMFvi20s^n~O_E^jdj+_*5rx%kp`zh0iPL+Y07E zi}Ot@3ZeN({ 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 0000000000000000000000000000000000000000..6cbba351a112cc958f19c4db3bb8bd90c68cc74f GIT binary patch literal 53114 zcmeHw2|QJ6_x~|uh=fWKA*4a2G|+%jp+vKJD5Sa2AekFbiBK98ZiCXG(mbG?%ymjh zg>aCm=xCtG{y%G<^BmjZ+~IxS`@Z-7{Hi6QGIF;GY0Z zg|<$UM%#n_HZ){^9zK&sYev_!wb`G~Y0zj2YiP9L!`Yu#DbQ$-!f7-W70&;crqMaMw3S0gXgUhrqN{cXtdA;=Ei+`EA&Q>?qg~) zYZ3fkC}QaQG&_%TD^VrB$;8@|Mw7~bB9OK5oC|s`&2-j`C0?-~?dRwF880-wBhr#=cSp@v7Z`c2;KyxG}GUhZzjjoM6lpGSM?yBEqSsLdwm ztD>bxKL4eU;AnX*t@y1*w#})P%sZZOI&M#WN_H^xK2Kxp@S9IG`NZn!c-FK(%`e$e zbB87soKdk!SMiTl2l`AM{dlb3#}`dP0a*g&<7i&-8p@9pWjD@qTDswaW`DKuU$yQj z`fhPqwD0)gfv+#8*o8m3GGMQ$OzHdq_f=ngnn}AYK~whs6xQ^kHh0)k)kc~_``bii zp@48&y3zFJggDa+5ssffZuxTXMNq46P;uzX8SRg!0IkvAWt!ZA& zhm%tq8b`YJ3YikH`W0akez9NLqPeSH#i%YiM}JkXHR_qw!dP9_h?5a^eM0mC2Bv+h zYY|FU{+fOAgNI{a>(3uCg_U`}rPUoYrt?G1AwE(2`$@agUR)!TZ9FvRwVzE=dsovk zaCGmuw;lxj+xPV+jxM0lTI)p1zAWBmxpdE_t4km5s=sC+ytX;`$yfC!qmyhb8Foqc zyvjWMi(RvK-)gv6bV_?&uNIBT4Kedo$9W7oaq*dA?b|iCuDlU2YwHM}`+Wq|hqpz8VQtdzn-z4eSnd!A}Ma^9qUcy^3e?RTQ%Wt*I(naWi`uh@Ch z>;}$bCMPGx#R}&AbRCnHrg0%8F3SJ-(?!zHf;ndSQ8f*v)6MYd!~8zmv6F zzm(Qi)2`za=Q#Yvsu8I(S7huTdn)Ckq0wYVyOrn5mMp)pY&jl-r|8yuW79nYYzFMnO%*bwc8uF|GBTvDdzNuPI{qK}nW_=rU-T99HzEJd_7AuQo+cbrj(XLe$srdb} zT$j}*>>KAlIHb&eMS_dLtedn&x^H~_ZteB7X>8L^v(+4>yWv%qZg0`@w)UXtu`Mr_ zXm;f0=E>L7kTMYkDjg#q+z(o;=C}Cq`H!D_#Rz368s2}e+*%%NJn>T0Ni3h&Y%bqH45iO^0)6E?|{^2UU_A?hT48&S|XeYI z>o|*28Tlr0x}qWKpwn2Ts=8~2UlM$&^f3OoKrutmB`_(vxAu8GV`-wMSxIWY$%oPn zlZn3RQw3!-uSB*kTBemEs5`USxWM4rjv;>E>PLO5FU=dLwthg(!v!`f)+sboXC;Xd z?Y{o)c9$>6IS##d?Zd{>&r@q>6@8h!>r0FO(e??cgxi>YKXUg3UH1R+K{;}sVnLFL z|Fz`ByMq*`ZqW5uRdiybzk~zv*y7RJj+PO(Z{Kzb9s6E5N$k+;t?RbY9?R7+u1{=s zbac!}w)YkA`_Mdi+_;T{oON|0qYNgi*i61Kc}`KJZQ<(jJ-egLdi}yUsK6S`dX}G2 zH!37TD#IdQ}7lS6qrCByQioKVhrjorKleb1Oy!=(7a8nzO8l z_wPl9yqTV+X)x*H>Cmlo%^S)`)m~2xh&5Y3v_;0CaKPQoiOauT8+KrCT|v9%r}?Hy z3B9GZ=o3QW>D&B2&-=8~k(i9crv?0+RQ@tl*aot$NUUZbw~ z&ASrRE=Uj8p57nhPIw^xaBRXrWyURbK9|u*q3-O^W}I{5Z9=uO0?V_Fs}IigYcOFW6so z!0V*tUN^d1rQP)N7c-q7T-4GrpPi;e`}D!$M7mt!v-zUEnuV-<1`U@OUbs`F=KcFU zm!{Qxnak9?Tl+Hl?A20z5o7JoK`C@uxRNELs!S zx4K$?v~=9GRT~7x5ASC%vqWH%kefigq!&}jd7LDx#^Zc14LW0Ua7oBHjh0=u?yKJ{ z7^?rNDwdgWkWtrL!`giE=%;phC9i%QZY!l*9E;f~u<7-;LMOf2SdkC)^yZU?Pu}xL zrtKTuq7f3VQxv&<-kZ5Y=5-X(-8MhBPOCXNGpyBY%L96HvR1*PH!H4OArfC#iODS( zswH#T@O_@4b^N_u^0}gZ^5a*Ij~JBhFf2#Q#^PB1vcoZ!DxZ_C<@9-RO)c9rvfqQb z?PinXVqRLQbwsDj8iY;`vFQKVcJGmxDFWlPPVemhUEs92kknB9mN`S-30~Cjo77-= zF<57)Q^8*If=4n-Ts1`vla1EDa-8L}=%#dB^0w@pjW-yKhIH?O9kjObf(M)gG}J?t z6TIYCU5$`0I36&3biY{QXr(yIRN8j7Q-SV-(5qh~3kqb`y{XZ3Hwi7Vp0w?{S8laW z@&^wCPla8rnP>B6?rfFG4Hx*T@KomRn8;|De(Kz!A@E8a?$HKZ0GfvioY+>R#DgbWDd3VQO0M z`}RGP?r19)<-yWdJ~O(crsl4xn$XZ0TZR`+f0dUsMmlch$2sLCy={%vYH2?dHWW|K zHja{w6V@?OSsnXs40ExPF;nclbBb=t&GwkLHCZN3xrbN!zW0{;eOI@%y-&|-$baiENep!S_;VPi8$nHsbrfS35tN9c`W0Ji&Epc23jhg>U!z z>L-qxWglyBta+qSOn<8shH;^G?iTIHcAA`pHF)O3K}h8 z_r^s#D?%9;azYsoPI&b{K>H@;^t|ETg^=*v37h9VNIf2HZ|c)%7Bq57edM|WB{6|} z#ZSpJ3%nN@Fjy{XPXF|WN50>UT=GqPwwqn?>UDk>i)tnuLqqrAuoo=VpJQa>vX`}d zzZKFd<6#@{_|l8iyT&FyHjQG64=Y@c%jsR+tWp$0pPafZc3*;w%MV!-y|b^QOFgs~ zAGa;Cki8H(_Qmj>7CYC=I`qvo&G7X**s2jJwM97kzSXCQqS@_cLjsDYL^BhtZqD%; zn)Su=t4CZ*!`bYHnDqyruQqgeO~0zp__@X7<&U}EM_a{=BO=sJ3qHy6zU>&2-OJv` zy0Ifeb<9mhqHBz_f55=<3(*@xo+k?y%NU$LGoeyo;x5@4)&3S&2018{X$`KYXL&3h zawv>7jCFp@T)&~kpRK|*zSuBRw!BR}v2k|0*yqahl><|>^Q!k4j$W-?(VBPA|Bbuz zoW$WrEu&o>dYPN_FkLG4jQsYOR&N%{ zq{(e~JizsZPD^zA+R(WAq>rDACl48P==#Syx@+d@A1?IuyJWufwXDI+YF+JO^To|C z9#&>2-!EZo9#~*JHAsQAaGHjn{iMtb$pbWt?%4Hha6Ud(vf{_lVg0Iol+J`qn6hQm z#0npVptf+BfG1OQm@o9l^*J*Q*H`5njcnFloOo?Cb6~DH%g`n7wAmm=o7ecm6PYir z<-ZOaU-4Raer%Io~c2TQKB$}DU9zDeQvP`{1VSBtg@c}{=ps`vdv!N3z^=D&~C zJrM8r{MxPktui$`#QC{vNXLx;+y^c=y9ZBP71^X9i`b8G%`fERu z`Oy2CZ(8qIIlG@$;m>t%pS~29MVnha;?9pMvS1EYoZ%Z+mODy1F7no=r~c(GwrCo8p*>o( zZ`YjE)W2_`kACvL){+b9M_R3mRR_47$ehIV9Clh_YO~OAMbVYTy1UvNLgHK|-jq4- zzKLFDyz-92qeoPWH1t26da&{i!#?`ZuF_OSa`rHF@$(Vy^_4gF zJ*Va*lDQ&|kt`)5pCZ6gbFmN_BE-^Ej~;!`&t;Y?li?vtPaHpyIX1%nm&swlQ&@>! zi>{PUI4t8oGD9!Yf$4ms&R|_iUw<*yixqmT(I4MNw9|~zBu|G4eov{AonbW6_|mT0 zCpl>+TWLXgKhJ8dC^=iStzlN8tL?^tE2VDD@mW-M*w^oC(Vhv*&DujA1_$y}YaS`o&&C zgRGWKr3<{?tSxO6Fp}kVM})O~VjrdsQ)W_IyT`lbukx${cfFGJABj%scdiANc;}f| ztLQIF+uf=j-#*?c$aU%ZjYrN4X>U1~(^@0^SWf211eJ$7Sp!Guoj&|jIl|?huivO- zL-YQ9?fg!y^SxtHsogIwGQzX`L2}c3XOY$S_R=@%x-ZvCHa)w3m{h>Vq{he9D+7)8 z$guQEwO7PBMx7XLsam5iUl267<@*xL@k2Kc7h7JpKz(#|(u`}qc|X%PjF@ocSnJI7 zi?-3WPn|a_>hclSRW_p=zV#K@@U1+%Y{#cnGOX2h^KY7c7{;>xuKz~9?>c>>_6hgQ z+b@QX3XQukL`>=3IXzlx*tK^T2BUpfo0yxTeTshNMBP*M2QI`sajVy7Cb|wB-STEo z+UW}uqBH~Tp2XU#=!=(3Vj71`4X7O2kx-C+T0nemKZ9{gF6)mrzLLFKnyLA7z4V>q zt-~)he0(H5J7uf#%R{kKo|=C=(9-7OQ@6=8_enfsaP?TBI`8C>udDTq-}(k9*Tl9T z_j8M%RdDbCEy_Qm;hy62)zc<4yW4&EYHxRVO4|yh)dD+yG@vs;U7n#(sKtfq(hfy! zRsCY^Vfuz!-y{nXwadsFW9nd@^ld}y1`T%W~l_L+l2&*u0K%6_(E;zQ~F z#=4c|Z%6-3S+Tt%XxeJ)_e;z5q9Qy?_0x>x9t({w7^)w^N{CUI+NQud=&^%&s&^O~ zip2I$&MNwbYw{CkwyR!auZF$V9mdXUl!(q|$m%}G)MIH5dU{La+Vr$Zrvt~)!y|r9 zYrPtwEX1IhDvG~c89mG5Z4o_Nr!-+rv{4A-(8qT*MXt{GP1@DX>vNbAA>oBDIv(z+ z_A3ycozb}DQen{NpA+Plm(~|;L+6r)f}t&ItoOuTvUYTe^N-g)b#GR!0?X7XPv1cJ z@-z8Wmj0=U0s`gQ_fD_2J3ik}za+Lq>DsQ?c`e_At&DEY_DKrYaII5tzEf~y`I`X- zW7bGs@D4s9^0LvXqqsSU&X_fGL-fQ3y^+ROS_F$7%RYK4B*c#jOZ@m@>e=H4uSUtH zy_~otA}S@w_0W5%^oyQ-n)`m=5TJLb_*$K_VXaDHzJ8khq=dEJC)U#A@@6hm8FgGz zT<36Rm2&yo4~pp_PYp4wwtThW$GM+vg8hf(uUh7~6g45B~{esIc z7L2KlSfXTj`Mv7u*v66t2OZ{lY|9aOnJULRm|yR!?p?la6C zf7go1SbeFwLxfqm=IFRw<*6bge-Uvu5TdztxW=nT3_?e@lvrt&QT!pD>OFcvB522> zDN~vE?fU{%gEhv7q61IlxNpmSI)o7S85p(Zi2LGnF{Ux>Kdn3`?a9yIzkita>RAGx zs@%Q^4mG%tD1&TyH`g@}oo}Ku5Dg*Sun`XG`UA5}&m9}B4o(D19-?D!GPUepNljL} zSh{`0A?+XI7nquSub+EAH7`j9YH?RC7LI=>~<&%V-01! z(RH+NC(JLeZP7OveKVvr){(Jp{@~v9aAbr{@2#+7@d&2dYDt#FUA>z)lEV=%*2FnJt$DfIOg&2NXhX~Q_TIwjUhk7X&51PimP(Iky;r(n zSoMZmJ1<&;&bl#Z*5 z`;y$-Q`%EF6Kxsw`yA=@GQiFESZeXd^5dIC9h4k`O6GnO@U!!5o=04K9buh1@;Kw= zy8B;$Y)DwQsw(B~6_dmHVKN`fa((r`7DqAL9KXL>XUQBsU)I`8bl^}M!T1ZsW$VJE z8n;eG*3p2p2j23LGAH_sR}W8h3p89AE!}EuuzkajeP>pmKd5SyVIJ_-XVm3kgNx;* z1kGYK{qL-64`~vQ?jwo1E$%oG)k-;ZP^#7`|8pfbfa*T$?pB56fvoWrJ*t=}CCG&^2a9;YR@ zWg0x{`?GBECU+g7jl zhnfN%Z+{=}v(Za$z4)m)Kko@l3>v>C-^H@&bK>>7DZOOTN%{UV#R%aC?MJ;#n5L8a zF%!LI3(q|m*J?l3PyAhTiQ}WSkBTblpPq2(wLq=ICeg{%dw*2>cm-|^qFrB_T$%%<(zt<{NF>(htr>!)owQPgQhd*r}Oa}}xDCr{?z6ioM5y45fG zrTf`%a~h+)WO|~i^n^D8jv@u;+{15AOg|mI)ePDaclt5MK zNsBYa3gx^EG#x?|w2OTZzI`Tbqre7Dn^DG3#iKhCs|(XhugfhM#iYIJ=XiKi?6&vW zO*(06L6?QSb2jW5$+}g1`Q2*6+02+_0wsF`M%Cz~2?aQcN530A;v-S0xN|&9Ejm^5 z`V`NJw*}I7HQo7MbD(@{yvwLB8=nk17qmm^O!OU@xo!e-Zev93d(kHxv&cwo-?kun zgIH$TnW+RbCGGjeaMeoMGFiu@c^aCApEvofy;pwQewAL};rwBeDt^1tV*=&e&dH=z z32#z3#9EsEarP7QO}D?DUacb;)20~hAY7p7FKpa?Oszb3s-jG+P;J=8z=OMl+-GR+6YM;_f2i^}w1kbE5nQ=7VeK7%;jBO%#pfr> zJEpcgs%-jh>h(ow<4L~(lMKZA7x=H#(>maPVp1PQFBMw)VxnVq>v2s3p|KCz_u9ni zdKoaz#o4E%D*9I`rpw4EJ8jfTYw2(dZfh#BohBeJZ=|=Y#a-X*i`njX-_Jjc6RUPZ3AsX9sL*<=_lG6XJiEgid!hB8m7>Z>r6=8 zFeBJ{s9En7txtVh(iv%OGLKJgH((^iNy>O_sMVhHd53Yo<~_C-g@*imKDnvkQKrBI z$;G;UW3)bH%3N{Zv`5giRcWLD>-gYz49|u{4VGH}IKfZ$S9fKmYKvFuzFIBWq`Brr z=r_;)w*3{;k-=WYdAVe@^t-HmZI2u>awMN!&U(jq(WsgHrtxml5YZts+V3<*y65Mt z4Lh}Ac;@|2e#}WHd<+IIQ-3z*MrFGF`o#~fR0rDKn6^&d*l_N;fdLw}XJ|B0S>OU> zqlTIir?b@P;X>h>9ZP!YI6GCQ>xfK}QZxwJGxNvAi4&tA>YQrnXMQeDB6`$yqjeI` zN+;=@I{%?pze?%YakU?o3pUkk{~9H*_`#BW>!gH6%SO*=d#ZhL!hr*4m;pzhw~4zw zXGkU}luCYO>m+n;vgf_wzazBybTza;(~2v z&$5x;M#~5_NZ667lIfl;6&$EJ`9x&0q-ut^)g77pahsO?{BYUgoOKWy{x?n9?s{z7 z6PggOcxRBB{PF!QpZEWGW6&_~hG}h{p~Nxg%@c*zN-Jc#*FKx(EI&R2;roo?$J{n| zn-Lm7tb+#2w#pj_7zvmh-s`pfNbu;2pVL12O&YmisNb#}XG2@2#Zu;=oqa5b%+m#V z8rHX`OISx{wCPQjc#4i1k*Rg%>kRswO||elC3cEW9CvGH>W78ilDi~>Cm$F$Z#II* z*r1P_ujC7hokzEtJ7*et&m6gbf4-O4gE;hC%s{_l+XSmI1=gE1^jp5fw9Jtf^^wZx zFJ&fw_LGaSr*-xhy8bSD$hU;VxbI0eY4_u53K?IuA}YdvBtC3iS>JXqt?lLFPak6( z&&&5#d=wzrE5hB-=@IR@E;CZre_P|37{|bSM7whR&(D4*-3;1PvXm!^E*O^jWJS%H zm^2~(W;g#nac%7>=M!jVc}6aZ?$>KRxhSU0psiJ$(EdfoF|e=yhp11@qsqqBsRgv? zY;CmrVI4fpf2-|)c832?--4m3+pFgg4}&{=*7j385cRmJ<6H7(lSAJ>AKuo%9456- zznA>gm)mro4jv#S{%l^YmZ5+BA{jKp{W9N2{Ks$~d3tEmri;OSSZczzXeXX{ATPe; zkHbF-zQ_=|wadl@SAKiA6>$-YEq3ZDCI$-5h(^xc)Ho4OU2~Z^^l*u-u{!VSy>)euGx|Ue2gE7pUj1HfzN048s$58j)rqUqX}< zHurmm?pe39-6L*i5QlPL)Gl_sG95knl)%qwebLg&bn@$&(grKCm#b)|WgN_iY?V3g zKcICK@m>AR^=Q|auN&X6&J`A2_9&d4spcY}_j7;Hg1r7OO3h2pdb%nd?^|f~b7XkQ zMdFpQU!95H0ISV+$_ygSeU7O=`>`PG@`c2CHM!m!R9R=@z2!}!S{uG(1?{au7SB_s zcYMDwb8_7Eu-2anhqwFBZ;n%AslAjED!p6nH+;_1U9A!gdONFAC5_iBH;mqWTj*1D z>Xi=L6h>3@m+I8v`=1IfncM1-mT-Jtvv0?g*cTp-bIvzg2h0skQSrO{aoXXwR7T7> zqvNev9txj&Ar8{$_igR2KUNp$jc)OcOFJWVn%I5GM#Haf`375I`;VEA>!e5YmwY@b z%|>(SqG!W?Iezr3(TK%n+os+~+P1XLyh-`ym-~xkU32n(Y^`!LYj0fkL{_T2vihQ{ z+V<-yekOw3U#%>)iwr0(7>a~%lbAH?y4%n7mWMS{r+shvx@5EE?V{i(8wCYv(^|f3 zrb*KJDlIx3#(XZD7C+zl&R83xq5_}gO2?3uOJpGPLrhzLM(W4sE&BIw3_BkuykiC7 zc2uIJTEB4hosyVWZx&ure>L{O#gk8LL%pYG=K1c`RA1B2ot8{g#NCmQ{?^ClW`V7U z1hdV|287g{?4Y;N%GD+wt#oMI z=rG)Jv!|)xvMOYVoVTY{efIXuCVIb(JH9+nK9pqt5)sy>wS3VWvhzVdFR?`B$t=(H zL!IsI>Lp%oT0=Kpku|^FF7MzphQFM!TZfptsPwo?B18Jp!e??HV&N_03Mw<^nd|B95@(Y zJ^9u5Uoi>LAD2!XIx&EL!4C&^Uw}SA=?m~T>JQ!e0`!gF=nve!0DXee7q~e5fxZC! zgP-2``u!r%p9h03{h=ouc*X=8BmBoi4>)iz__cAt#o^x^6If4v^#jnKivg2ZxpL*- zy1+a^og4V+jjul_swB7=ARPWL=+DIfVS)AJSF`nJV*oh(U(p}07yR_b*B`t_xmZuzVQoYE z8JMG?pCxBK=i6`o+xJn|OxQQ@``z*N)HhgvsQLGQz=)hD$+fYZT<>9hhke~mv_|p6 z?Z15=$U?;c>&dSs^#=?%IPlXWz4$$m_NJQbD}V#+lVDE-`yRmIuW}#nzJZ_K_&VrB z>czo;pB|4&>xhRXHAMPbHV!an!oCTQiNDHy_*{W;z zypAz|nx7tjm+Ra!H|LDWPj7r3#xRKi2Zz6lp8VwG>d&z?U_JTOFqgA2VB^5AT~}9E z0v8t-7vg_W@bjVOCnv8Bj-TH6I;`n{0mh-Hc0tz_D^?K8moF!roSgor1!MqOK&GB# zCGAiYTO`(#U;VHIt>=IN2M69Z;W_Y3dwYAr#>R%QvaR zYH~kE>dwZ1+8&HE=xf&2)$Q0gvvuy(oso6B^8wisN6^89zm$2TN6XltO>GQRpo|+}$Ka_Cb)1cP_ZgW1r`z zH@*(~lZAspPx?cj7oS~9h|ek^#4rXtI0zveXpfPOfI$W-d~i^IWJ3&nXx#+|1%!h< z!a)w<&>!LOW|tVk0F^+5LjW6xSAGZs3Jyz`E+v1PZu(5Z><>d(fZC;btBl8`ichcI|YVnD@# zIwoj2NcU`1_~C%Y#K=rrV)$blVi>{z`hpt5Kow!2j4)8*jET3w62x1;U^g3w*IXP> zU*PnIB}vApqByh=xQQ^gbGA735*b!s1Z3)0(6vANy!hv^8l*A!J%_Zptbfn()}4KfCCi+%9xn^ z#GaU#Z%^p(!2!lZX}AngdO(_B>`0CH&yTf|&tKkewVgPmxgag0dUv>eL zkg=%f#DUWnaDMjy;!hnhc z#sJL?Fedov&+EtKr#HUN)*monuYXrxF4mJ@ z4YnLA2K@9Nu(*=wzo?Rf0m1?L0*OO8dt4wKXsI*&N zP=06tQ5M~wD2qZE01god2WnqHIPle<>&s>P^!e$Huah=C3I_c2ms?sz3|N9NAaTHb zfrA65FVJ2i-HTB96&z;0bR>*kI1)1o9J=5@9TVj-azy!IIid{X!08KA9Qf(a_2rU2 zedcfO|3QB?2FRX;aNwu^z-3hgU?8^y;Q$zP!GYZuXm643Z&2Zf0~!-1ubhZEg^mQ^ zFcaZmh;Sgs1nLh{ICDeAQF)@`$UuU`fy4my2g;a$xq-j_Y+o+v(?@*)|IYkstUs># z=`U|pO~_kT6P-9zvT@*y378vbB}n&TRC>aJGbZM}b|OrRoCsrt!)%0u5yk!WiZuoWm*%&}y;HNjf4t7kkaQXp1{S~a=6N5MyP;r1U z!JZq?v)&=yOHtv)fifn{-!3Q2-Z&8?4hREdgu!g?m|)Khl_v%h%vc42c?{ve?F(IT z;Hy9O=W_hnSWkX6Y1cm>a5pQ6{Prh7fEV5C%Lrz}&!Be~up)`}Fzgjjw|* zgI(Ad@Y7$}v5pu57<9pb-52oOKzomL#~4ui0tE-V3TMKm+_?)5i%QU#K>dN78&F?( ze_Dm8K7}wqI8=4P0gVZm8_<~eRsA`>T)+V9$*%^V7U|dG;Lwx)VE<5AUPmZ9q2gG} z#=*Xp>M{CVuc#-S(u!RD;y(m)J#?!-U^`T{C|10EB!2BdpED*SLjW5V^lE8&7LSW(p# z2kMxpO;RUn&#Mz%aey&_3TI5fnY2g!dHmV@^v2ga^=IQi)gS%_8#~zB)mJtUYOV+a z9vrA+g7y*V{sEPqaNvxI)wQmKTMhChAsn0$4$Bb^)G+}#)Lk4#)Lzga0Ee3Ms4qZ& z;29I}oAhM=r~0zNuaEWQSA#zn3dVs~f3N`n&f2a82!juVI>Lb47byJ!aG*6I-5XKi z#ep&=*43{h*3`KYs}K$=5e_czT_`v>lXFA;Wlf^)(y*>LaL2?@;>i)M9)Ej~9s6=I z4*c}S*TJu!UB$)#aNyM+J_81THQ?}9C?E^3-51Nv>(9n|@~heU0|smya65n3n%AGr zPj7skW$^beq4+L){|eIQ_UiI9T*4P&fm4heq4+LKfUpFQh&gJgTvod zZ!9qvKPE(U)Xht7Qiw;zyush)7?dd_B#iB7u?5XZ5i_G z|7QFf#^Im(9|ZnE;Qu}X#Bcoj#D<{nKQ#%&-wuc0_<0G)69J2Y6Dl}QL;!mK(?9?I zLEz6q0C5rmc;m`@iYMMfF1a2^ zJt#5#6izi)7e2oSj=iGB;`NlnfL9NG@>6~K_?-J!skyP%zk)xGF$X>VDsp2vaGW&M zeDUYR27?~>zQ0OM9UJ`Qr}~8dDsp2vxbey;W}CnKojzTBAFgqHG0Za%`vha}N_Ihy zztf-bT|C!9%~yVkKN8E0Yw%OCV@W&XX(5IU|9@)zXMDGFuHr90_NjqSktz@T{Rr6^ zz~-KY{vV7Fluw7um&5zLKkK{HcvpV-|5|(}*cO2Q3^pV1ov>qCd!DEMjPH{1ioX%7 ziG4Ys2QRK*cLdua$OiU6uzU9OJ-F}B_%1iz@>gOlsXi%Q`7@Bc4s45HD+AjP*!lRy zQ~nv>rN&G0>py?x_(1AjgrEG_7XW%t@dqCo_U(aB?awawc~lvw@p^pa=ftLgPY2%% zZGkTm$E87x(w|vy+!;I*o{i<8?r-_a&yFpFv5D`4HbC|T3lQ`dLV2;N&bu2&h6~J3^)(bK&Ok^gC2}7|!)R-`CEEJ9h!?9o}4vdWb zK`}272c|&AfLW0-V6vR}uh*RTFEZ|l6Z@6FwHL|7hi79ssAndA_)}xN@O{t*a46WZ zJf&nz6U4AkW826$9vsVr*#;v$AeIecxZZH%G$`>~oY*ahBc#fY z&m5o!fBCUpyOygrmY*G$gyT>s@oDI{;Kt$L_!AtL260G~xDyTh7yOy zPkzvYpZq<=7h(CS@iY)eLXEQ{V`*^g1dgfV#Mj~25gb2*^Pv#SSTvbpBw)}>LJ#n z{5am{x8?7Q@4)zT<60=OJvi2ljAy|yJvgq36VF4&Z*XE*#-dmrh-V^W+o0=1NEs{9Ns&Q1;Oz!5F6FW`=`XgaN<9@$f>w!29E@vI{_Hpb2ieurGA8aDnzQYfH zEI-t|_+w5!uO4JP0_Ngl4m#%Hqu&B}*O+rX8{!afECz}}=)%3{#2=JJvtuy2%MY;z z?05sd@?-u(5Au_Ia?GQq#8*H}K^Go5=8$t@OE7;NbLh!<3QnKG99b@R9>r0VgB~cB zq?`PdcmaO$Q~mb5_$wk#IPl`TapI|bc+7vsaS^Cb0S6rO;BhPf85e?n3zF-L`R}p7 zckYaX;FTZqyupURSAO6&^YGho|0nsY94<3e4~TamdCb6P2QD{`ZQ%4N;Avv+GRfbj zaQV4BRg%XK{B=$|2hyXP{9qg518gWQfme+H+#3aX_6K6nj zi7|H+bILiqdf?7t9yQ4&$8iNHwu0o$V*WHo56mA2-aKH3<)GSj_{-1X$5P~nGX#1I zJckq9$&03PqPbjP%!kI@c+{uZ9NIo+A#FRrc}8|L ze)99<_rh6j_{vH)x48#iGs)-0TwcuU=5X8BA|5YrnMn>Wg~tq>_B!D2Vh%IraU=e6 zX}mfaPmHmL8f+ujzQb333g3u&FW>+k~tTzD20ds2Q*-XzfDSLFvCP@&5ISNI$A zM8P(~Pkzh|{VU4fY1^scFF)q9{RQzSd72#i4qyDS{J7@yCH|a5HvgIUW~z&yweRl3#-C?4U<@Y(kgs{8k ?VJPSV#OqUiqkY5bCqxZ#V;BUIp0M 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 + 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); } }