RunnerHelper.cs
1 // Copyright (c) Microsoft Corporation 2 // The Microsoft Corporation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System; 6 using System.Diagnostics; 7 using System.Reflection; 8 using System.Threading.Tasks; 9 10 using Microsoft.PowerToys.Telemetry; 11 using Microsoft.PowerToys.Telemetry.Events; 12 13 namespace ManagedCommon 14 { 15 public static class RunnerHelper 16 { 17 public static void WaitForPowerToysRunner(int powerToysPID, Action act, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "") 18 { 19 var stackTrace = new StackTrace(); 20 var assembly = Assembly.GetCallingAssembly().GetName(); 21 PowerToysTelemetry.Log.WriteEvent(new DebugEvent() { Message = $"[{assembly}][{memberName}]WaitForPowerToysRunner waiting for Event powerToysPID={powerToysPID}" }); 22 Task.Run(() => 23 { 24 const uint INFINITE = 0xFFFFFFFF; 25 const uint WAIT_OBJECT_0 = 0x00000000; 26 const uint SYNCHRONIZE = 0x00100000; 27 28 IntPtr powerToysProcHandle = NativeMethods.OpenProcess(SYNCHRONIZE, false, powerToysPID); 29 if (NativeMethods.WaitForSingleObject(powerToysProcHandle, INFINITE) == WAIT_OBJECT_0) 30 { 31 PowerToysTelemetry.Log.WriteEvent(new DebugEvent() { Message = $"[{assembly}][{memberName}]WaitForPowerToysRunner Event Notified powerToysPID={powerToysPID}" }); 32 act.Invoke(); 33 } 34 }); 35 } 36 37 private static readonly string PowerToysRunnerProcessName = "PowerToys.exe"; 38 39 // In case we don't have a permission to open user's processes with a SYNCHRONIZE access right, e.g. LocalSystem processes, we could use GetExitCodeProcess to check the process' exit code periodically. 40 public static void WaitForPowerToysRunnerExitFallback(Action act) 41 { 42 int[] processIds = new int[1024]; 43 uint bytesCopied; 44 45 NativeMethods.EnumProcesses(processIds, (uint)processIds.Length * sizeof(uint), out bytesCopied); 46 47 const uint PROCESS_QUERY_LIMITED_INFORMATION = 0x1000; 48 var handleAccess = PROCESS_QUERY_LIMITED_INFORMATION; 49 50 IntPtr runnerHandle = IntPtr.Zero; 51 foreach (var processId in processIds) 52 { 53 IntPtr hProcess = NativeMethods.OpenProcess(handleAccess, false, processId); 54 System.Text.StringBuilder name = new System.Text.StringBuilder(1024); 55 uint length = 1024; 56 if (hProcess == IntPtr.Zero || !NativeMethods.QueryFullProcessImageName(hProcess, 0, name, ref length)) 57 { 58 continue; 59 } 60 61 if (System.IO.Path.GetFileName(name.ToString()) == PowerToysRunnerProcessName) 62 { 63 runnerHandle = hProcess; 64 break; 65 } 66 } 67 68 if (runnerHandle == IntPtr.Zero) 69 { 70 Logger.LogError("Couldn't determine PowerToys.exe pid"); 71 return; 72 } 73 74 Task.Run(() => 75 { 76 const int STILL_ACTIVE = 0x103; 77 uint exit_status; 78 do 79 { 80 System.Threading.Thread.Sleep(1000); 81 NativeMethods.GetExitCodeProcess(runnerHandle, out exit_status); 82 } 83 while (exit_status == STILL_ACTIVE); 84 85 NativeMethods.CloseHandle(runnerHandle); 86 87 act.Invoke(); 88 }); 89 } 90 } 91 }