Don't use Environment.Exit (#20532)

* [Awake] Don't use Process.Exit and move to CsWin32

* [PowerLauncher] Remove unused API

* [ColorPicker] Use cancellable NativeEventWaiter + cleanup using

* [TextExtractor] Don't use Environment.Exit

* [MeasureTool] Don't use Environment.Exit(0);

* [FZE] don't use Environment.Exit and fix WaitForPowerToysRunner
This commit is contained in:
Andrey Nekrasov
2022-09-13 19:25:19 +03:00
committed by GitHub
parent cba6507d2b
commit 09f4dead7f
36 changed files with 258 additions and 480 deletions

View File

@@ -60,7 +60,6 @@ APeriod
api
APIENTRY
APIIs
Apm
APPBARDATA
appdata
APPICON
@@ -130,7 +129,6 @@ Avanc
Awaitable
awakeness
awakeversion
AWAYMODE
AYUV
backend
backtracer
@@ -498,7 +496,6 @@ dvr
DVSD
DVSL
DVTARGETDEVICE
dwhkl
DWINRT
dwl
dwm
@@ -728,8 +725,6 @@ hhk
HHmmss
HHOOK
hhx
Hiber
Hiberboot
HIBYTE
HICON
HIDEWINDOW
@@ -907,7 +902,6 @@ inheritdoc
initguid
Inkscape
Inlines
Inlining
inorder
INotification
INotify
@@ -1037,7 +1031,6 @@ jxr
jyuwono
KBDLLHOOKSTRUCT
kbm
KCode
KEYBDINPUT
keybindings
keyboardeventhandlers
@@ -1161,7 +1154,6 @@ lpsz
lpt
LPTHREAD
LPTOP
lptpm
LPTSTR
LPVOID
LPW
@@ -1226,7 +1218,6 @@ Melman
memcmp
memcpy
memset
MENUBREAK
MENUITEMINFO
MENUITEMINFOW
Metadatas
@@ -1432,7 +1423,6 @@ ntdll
NTFS
NTSTATUS
nuget
nuint
nullonfailure
nullopt
nullptr
@@ -1494,7 +1484,6 @@ overlaywindow
Overridable
Oversampling
OWNDC
OWNERDRAW
PACL
pagos
PAINTSTRUCT
@@ -1536,6 +1525,7 @@ pfo
pft
pgp
pguid
PHANDLER
phbm
phbmp
phwnd
@@ -1938,6 +1928,7 @@ sse
ssf
ssh
sstream
stackalloc
STACKFRAME
stackoverflow
stackpanel
@@ -2237,14 +2228,12 @@ VIDEOINFOHEADER
viewbox
viewmodel
vih
Virt
virtualization
Virtualizing
visiblecolorformats
Visibletrue
visualbrush
visualstudio
viter
VKey
VKTAB
vmovl

View File

@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using ControlzEx.Theming;
namespace Common.UI

View File

@@ -3,26 +3,24 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Reflection;
using System.Threading;
using System.Windows;
using Wox.Plugin.Logger;
namespace PowerLauncher.Helper
using Dispatcher = System.Windows.Threading.Dispatcher;
namespace Common.UI
{
public static class NativeEventWaiter
{
public static void WaitForEventLoop(string eventName, Action callback, CancellationToken cancel)
public static void WaitForEventLoop(string eventName, Action callback, Dispatcher dispatcher, CancellationToken cancel)
{
new Thread(() =>
{
var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, eventName);
var eventHandle = new EventWaitHandle(false, EventResetMode.ManualReset, eventName);
while (true)
{
if (WaitHandle.WaitAny(new WaitHandle[] { cancel.WaitHandle, eventHandle }) == 1)
{
Log.Info($"Successfully waited for {eventName}", MethodBase.GetCurrentMethod().DeclaringType);
Application.Current.Dispatcher.Invoke(callback);
dispatcher.BeginInvoke(callback);
}
else
{

View File

@@ -5,6 +5,7 @@
using System;
using ManagedCommon;
using MeasureToolUI.Helpers;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
namespace MeasureToolUI
@@ -36,9 +37,10 @@ namespace MeasureToolUI
{
if (int.TryParse(cmdArgs[cmdArgs.Length - 1], out int powerToysRunnerPid))
{
var dispatcher = DispatcherQueue.GetForCurrentThread();
RunnerHelper.WaitForPowerToysRunner(powerToysRunnerPid, () =>
{
Environment.Exit(0);
dispatcher.TryEnqueue(App.Current.Exit);
});
}
}
@@ -51,7 +53,8 @@ namespace MeasureToolUI
catch (Exception ex)
{
Logger.LogError($"MeasureToolCore failed to initialize: {ex}");
Environment.Exit(1);
App.Current.Exit();
return;
}
_window = new MainWindow(core);

View File

@@ -3,7 +3,6 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Diagnostics;
using System.Threading;
using System.Windows;
using ManagedCommon;
@@ -23,6 +22,13 @@ public partial class App : Application, IDisposable
private Mutex? _instanceMutex;
private int _powerToysRunnerPid;
private CancellationTokenSource NativeThreadCTS { get; set; }
public App()
{
NativeThreadCTS = new CancellationTokenSource();
}
public void Dispose()
{
GC.SuppressFinalize(this);
@@ -37,7 +43,7 @@ public partial class App : Application, IDisposable
{
Logger.LogWarning("Another running TextExtractor instance was detected. Exiting TextExtractor");
_instanceMutex = null;
Environment.Exit(0);
Shutdown();
return;
}
@@ -51,10 +57,11 @@ public partial class App : Application, IDisposable
RunnerHelper.WaitForPowerToysRunner(_powerToysRunnerPid, () =>
{
Logger.LogInfo("PowerToys Runner exited. Exiting TextExtractor");
Environment.Exit(0);
NativeThreadCTS.Cancel();
Application.Current.Dispatcher.Invoke(() => Shutdown());
});
var userSettings = new UserSettings(new Helpers.ThrottledActionInvoker());
eventMonitor = new EventMonitor();
eventMonitor = new EventMonitor(Application.Current.Dispatcher, NativeThreadCTS.Token);
}
catch (Exception ex)
{

View File

@@ -1,29 +0,0 @@
// 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.Threading;
using System.Windows;
namespace PowerOCR.Helpers
{
public static class NativeEventWaiter
{
public static void WaitForEventLoop(string eventName, Action callback)
{
new Thread(() =>
{
var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, eventName);
while (true)
{
if (eventHandle.WaitOne())
{
Logger.LogInfo($"Successfully waited for {eventName}");
Application.Current.Dispatcher.Invoke(callback);
}
}
}).Start();
}
}
}

View File

@@ -4,6 +4,7 @@
using System;
using System.Windows.Interop;
using Common.UI;
using interop;
using PowerOCR.Helpers;
using PowerOCR.Utilities;
@@ -16,9 +17,9 @@ namespace PowerOCR.Keyboard
/// </summary>
internal class EventMonitor
{
public EventMonitor()
public EventMonitor(System.Windows.Threading.Dispatcher dispatcher, System.Threading.CancellationToken exitToken)
{
NativeEventWaiter.WaitForEventLoop(Constants.ShowPowerOCRSharedEvent(), StartOCRSession);
NativeEventWaiter.WaitForEventLoop(Constants.ShowPowerOCRSharedEvent(), StartOCRSession, dispatcher, exitToken);
}
public void StartOCRSession()

View File

@@ -43,6 +43,9 @@
<ItemGroup>
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.2.46-beta">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="NLog" Version="4.7.13" />
<PackageReference Include="System.CommandLine" Version="2.0.0-beta1.20071.2" />
<PackageReference Include="System.Reactive" Version="5.0.0" />

View File

@@ -7,17 +7,20 @@ using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using Awake.Core.Models;
using Microsoft.Win32;
using NLog;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.Storage.FileSystem;
using Windows.Win32.System.Console;
using Windows.Win32.System.Power;
namespace Awake.Core
{
public delegate bool ConsoleEventHandler(ControlType ctrlType);
/// <summary>
/// Helper class that allows talking to Win32 APIs without having to rely on PInvoke in other parts
/// of the codebase.
@@ -25,9 +28,6 @@ namespace Awake.Core
public class APIHelper
{
private const string BuildRegistryLocation = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion";
private const int StdOutputHandle = -11;
private const uint GenericWrite = 0x40000000;
private const uint GenericRead = 0x80000000;
private static readonly Logger _log;
private static CancellationTokenSource _tokenSource;
@@ -43,21 +43,21 @@ namespace Awake.Core
_tokenSource = new CancellationTokenSource();
}
public static void SetConsoleControlHandler(ConsoleEventHandler handler, bool addHandler)
internal static void SetConsoleControlHandler(PHANDLER_ROUTINE handler, bool addHandler)
{
NativeMethods.SetConsoleCtrlHandler(handler, addHandler);
PInvoke.SetConsoleCtrlHandler(handler, addHandler);
}
public static void AllocateConsole()
{
_log.Debug("Bootstrapping the console allocation routine.");
NativeMethods.AllocConsole();
PInvoke.AllocConsole();
_log.Debug($"Console allocation result: {Marshal.GetLastWin32Error()}");
var outputFilePointer = NativeMethods.CreateFile("CONOUT$", GenericRead | GenericWrite, FileShare.Write, IntPtr.Zero, FileMode.OpenOrCreate, 0, IntPtr.Zero);
var outputFilePointer = PInvoke.CreateFile("CONOUT$", FILE_ACCESS_FLAGS.FILE_GENERIC_READ | FILE_ACCESS_FLAGS.FILE_GENERIC_WRITE, FILE_SHARE_MODE.FILE_SHARE_WRITE, null, FILE_CREATION_DISPOSITION.OPEN_EXISTING, 0, null);
_log.Debug($"CONOUT creation result: {Marshal.GetLastWin32Error()}");
NativeMethods.SetStdHandle(StdOutputHandle, outputFilePointer);
PInvoke.SetStdHandle(Windows.Win32.System.Console.STD_HANDLE.STD_OUTPUT_HANDLE, outputFilePointer);
_log.Debug($"SetStdHandle result: {Marshal.GetLastWin32Error()}");
Console.SetOut(new StreamWriter(Console.OpenStandardOutput(), Console.OutputEncoding) { AutoFlush = true });
@@ -70,11 +70,11 @@ namespace Awake.Core
/// </summary>
/// <param name="state">Single or multiple EXECUTION_STATE entries.</param>
/// <returns>true if successful, false if failed</returns>
private static bool SetAwakeState(ExecutionState state)
private static bool SetAwakeState(EXECUTION_STATE state)
{
try
{
var stateResult = NativeMethods.SetThreadExecutionState(state);
var stateResult = PInvoke.SetThreadExecutionState(state);
return stateResult != 0;
}
catch
@@ -160,18 +160,18 @@ namespace Awake.Core
bool success;
if (keepDisplayOn)
{
success = SetAwakeState(ExecutionState.ES_SYSTEM_REQUIRED | ExecutionState.ES_DISPLAY_REQUIRED | ExecutionState.ES_CONTINUOUS);
success = SetAwakeState(EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_DISPLAY_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS);
}
else
{
success = SetAwakeState(ExecutionState.ES_SYSTEM_REQUIRED | ExecutionState.ES_CONTINUOUS);
success = SetAwakeState(EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS);
}
try
{
if (success)
{
_log.Info($"Initiated indefinite keep awake in background thread: {NativeMethods.GetCurrentThreadId()}. Screen on: {keepDisplayOn}");
_log.Info($"Initiated indefinite keep awake in background thread: {PInvoke.GetCurrentThreadId()}. Screen on: {keepDisplayOn}");
WaitHandle.WaitAny(new[] { _threadToken.WaitHandle });
@@ -186,28 +186,35 @@ namespace Awake.Core
catch (OperationCanceledException ex)
{
// Task was clearly cancelled.
_log.Info($"Background thread termination: {NativeMethods.GetCurrentThreadId()}. Message: {ex.Message}");
_log.Info($"Background thread termination: {PInvoke.GetCurrentThreadId()}. Message: {ex.Message}");
return success;
}
}
internal static void CompleteExit(int exitCode, bool force = false)
internal static void CompleteExit(int exitCode, ManualResetEvent? exitSignal, bool force = false)
{
APIHelper.SetNoKeepAwake();
TrayHelper.ClearTray();
SetNoKeepAwake();
// Because we are running a message loop for the tray, we can't just use Environment.Exit,
// but have to make sure that we properly send the termination message.
IntPtr windowHandle = APIHelper.GetHiddenWindow();
HWND windowHandle = GetHiddenWindow();
if (windowHandle != IntPtr.Zero)
if (windowHandle != HWND.Null)
{
NativeMethods.SendMessage(windowHandle, NativeConstants.WM_CLOSE, 0, string.Empty);
PInvoke.SendMessage(windowHandle, PInvoke.WM_CLOSE, 0, 0);
}
if (force)
{
Environment.Exit(exitCode);
PInvoke.PostQuitMessage(0);
}
try
{
exitSignal?.Set();
PInvoke.DestroyWindow(windowHandle);
}
catch (Exception ex)
{
_log.Info($"Exit signal error ${ex}");
}
}
@@ -221,18 +228,18 @@ namespace Awake.Core
{
if (keepDisplayOn)
{
success = SetAwakeState(ExecutionState.ES_SYSTEM_REQUIRED | ExecutionState.ES_DISPLAY_REQUIRED | ExecutionState.ES_CONTINUOUS);
success = SetAwakeState(EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_DISPLAY_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS);
}
else
{
success = SetAwakeState(ExecutionState.ES_SYSTEM_REQUIRED | ExecutionState.ES_CONTINUOUS);
success = SetAwakeState(EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS);
}
if (success)
{
_log.Info($"Initiated temporary keep awake in background thread: {NativeMethods.GetCurrentThreadId()}. Screen on: {keepDisplayOn}");
_log.Info($"Initiated temporary keep awake in background thread: {PInvoke.GetCurrentThreadId()}. Screen on: {keepDisplayOn}");
_timedLoopTimer = new System.Timers.Timer(seconds * 1000);
_timedLoopTimer = new System.Timers.Timer((seconds * 1000) + 1);
_timedLoopTimer.Elapsed += (s, e) =>
{
_tokenSource.Cancel();
@@ -262,7 +269,7 @@ namespace Awake.Core
catch (OperationCanceledException ex)
{
// Task was clearly cancelled.
_log.Info($"Background thread termination: {NativeMethods.GetCurrentThreadId()}. Message: {ex.Message}");
_log.Info($"Background thread termination: {PInvoke.GetCurrentThreadId()}. Message: {ex.Message}");
return success;
}
}
@@ -294,15 +301,20 @@ namespace Awake.Core
}
[SuppressMessage("Performance", "CA1806:Do not ignore method results", Justification = "Function returns DWORD value that identifies the current thread, but we do not need it.")]
public static IEnumerable<IntPtr> EnumerateWindowsForProcess(int processId)
internal static IEnumerable<HWND> EnumerateWindowsForProcess(int processId)
{
var handles = new List<IntPtr>();
IntPtr hCurrentWnd = IntPtr.Zero;
var handles = new List<HWND>();
var hCurrentWnd = HWND.Null;
do
{
hCurrentWnd = NativeMethods.FindWindowEx(IntPtr.Zero, hCurrentWnd, null, null);
NativeMethods.GetWindowThreadProcessId(hCurrentWnd, out uint targetProcessId);
hCurrentWnd = PInvoke.FindWindowEx(HWND.Null, hCurrentWnd, null as string, null);
uint targetProcessId = 0;
unsafe
{
PInvoke.GetWindowThreadProcessId(hCurrentWnd, &targetProcessId);
}
if (targetProcessId == processId)
{
handles.Add(hCurrentWnd);
@@ -314,23 +326,30 @@ namespace Awake.Core
}
[SuppressMessage("Globalization", "CA1305:Specify IFormatProvider", Justification = "In this context, the string is only converted to a hex value.")]
public static IntPtr GetHiddenWindow()
internal static HWND GetHiddenWindow()
{
IEnumerable<IntPtr> windowHandles = EnumerateWindowsForProcess(Environment.ProcessId);
IEnumerable<HWND> windowHandles = EnumerateWindowsForProcess(Environment.ProcessId);
var domain = AppDomain.CurrentDomain.GetHashCode().ToString("x");
string targetClass = $"{InternalConstants.TrayWindowId}{domain}";
foreach (var handle in windowHandles)
unsafe
{
StringBuilder className = new (256);
int classQueryResult = NativeMethods.GetClassName(handle, className, className.Capacity);
if (classQueryResult != 0 && className.ToString().StartsWith(targetClass, StringComparison.InvariantCultureIgnoreCase))
var classNameLen = 256;
Span<char> className = stackalloc char[classNameLen];
foreach (var handle in windowHandles)
{
return handle;
fixed (char* ptr = className)
{
int classQueryResult = PInvoke.GetClassName(handle, ptr, classNameLen);
if (classQueryResult != 0 && className.ToString().StartsWith(targetClass, StringComparison.InvariantCultureIgnoreCase))
{
return handle;
}
}
}
}
return IntPtr.Zero;
return HWND.Null;
}
public static Dictionary<string, int> GetDefaultTrayOptions()

View File

@@ -1,17 +0,0 @@
// 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;
namespace Awake.Core.Models
{
[Flags]
public enum ExecutionState : uint
{
ES_AWAYMODE_REQUIRED = 0x00000040,
ES_CONTINUOUS = 0x80000000,
ES_DISPLAY_REQUIRED = 0x00000002,
ES_SYSTEM_REQUIRED = 0x00000001,
}
}

View File

@@ -1,70 +0,0 @@
// 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.Runtime.InteropServices;
namespace Awake.Core.Models
{
public struct SystemPowerCapabilities
{
[MarshalAs(UnmanagedType.U1)]
public bool PowerButtonPresent;
[MarshalAs(UnmanagedType.U1)]
public bool SleepButtonPresent;
[MarshalAs(UnmanagedType.U1)]
public bool LidPresent;
[MarshalAs(UnmanagedType.U1)]
public bool SystemS1;
[MarshalAs(UnmanagedType.U1)]
public bool SystemS2;
[MarshalAs(UnmanagedType.U1)]
public bool SystemS3;
[MarshalAs(UnmanagedType.U1)]
public bool SystemS4;
[MarshalAs(UnmanagedType.U1)]
public bool SystemS5;
[MarshalAs(UnmanagedType.U1)]
public bool HiberFilePresent;
[MarshalAs(UnmanagedType.U1)]
public bool FullWake;
[MarshalAs(UnmanagedType.U1)]
public bool VideoDimPresent;
[MarshalAs(UnmanagedType.U1)]
public bool ApmPresent;
[MarshalAs(UnmanagedType.U1)]
public bool UpsPresent;
[MarshalAs(UnmanagedType.U1)]
public bool ThermalControl;
[MarshalAs(UnmanagedType.U1)]
public bool ProcessorThrottle;
public byte ProcessorMinThrottle;
public byte ProcessorMaxThrottle;
[MarshalAs(UnmanagedType.U1)]
public bool FastSystemS4;
[MarshalAs(UnmanagedType.U1)]
public bool Hiberboot;
[MarshalAs(UnmanagedType.U1)]
public bool WakeAlarmPresent;
[MarshalAs(UnmanagedType.U1)]
public bool AoAc;
[MarshalAs(UnmanagedType.U1)]
public bool DiskSpinDown;
public byte HiberFileType;
[MarshalAs(UnmanagedType.U1)]
public bool AoAcConnectivitySupported;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
private readonly byte[] spare3;
[MarshalAs(UnmanagedType.U1)]
public bool SystemBatteriesPresent;
[MarshalAs(UnmanagedType.U1)]
public bool BatteriesAreShortTerm;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public BatteryReportingScale[] BatteryScale;
public SystemPowerState AcOnLineWake;
public SystemPowerState SoftLidWake;
public SystemPowerState RtcWake;
public SystemPowerState MinDeviceWakeState;
public SystemPowerState DefaultLowLatencyWake;
}
}

View File

@@ -1,20 +0,0 @@
// 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 Awake.Core.Models
{
// Maps to the OS power state.
// See documentation: https://docs.microsoft.com/windows/win32/power/system-power-states
public enum SystemPowerState
{
PowerSystemUnspecified = 0,
PowerSystemWorking = 1,
PowerSystemSleeping1 = 2,
PowerSystemSleeping2 = 3,
PowerSystemSleeping3 = 4,
PowerSystemHibernate = 5,
PowerSystemShutdown = 6,
PowerSystemMaximum = 7,
}
}

View File

@@ -2,14 +2,16 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Windows.Win32;
namespace Awake.Core.Models
{
internal enum TrayCommands : uint
{
TC_DISPLAY_SETTING = NativeConstants.WM_USER + 1,
TC_MODE_PASSIVE = NativeConstants.WM_USER + 2,
TC_MODE_INDEFINITE = NativeConstants.WM_USER + 3,
TC_EXIT = NativeConstants.WM_USER + 4,
TC_TIME = NativeConstants.WM_USER + 5,
TC_DISPLAY_SETTING = PInvoke.WM_USER + 1,
TC_MODE_PASSIVE = PInvoke.WM_USER + 2,
TC_MODE_INDEFINITE = PInvoke.WM_USER + 3,
TC_EXIT = PInvoke.WM_USER + 4,
TC_TIME = PInvoke.WM_USER + 5,
}
}

View File

@@ -1,26 +0,0 @@
// 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.
#pragma warning disable SA1310 // Field names should not contain underscore
namespace Awake.Core
{
internal class NativeConstants
{
internal const uint WM_COMMAND = 0x111;
internal const uint WM_USER = 0x400;
internal const uint WM_GETTEXT = 0x000D;
internal const uint WM_CLOSE = 0x0010;
// Popup menu constants.
internal const uint MF_BYPOSITION = 1024;
internal const uint MF_STRING = 0;
internal const uint MF_MENUBREAK = 0x00000040;
internal const uint MF_SEPARATOR = 0x00000800;
internal const uint MF_POPUP = 0x00000010;
internal const uint MF_UNCHECKED = 0x00000000;
internal const uint MF_CHECKED = 0x00000008;
internal const uint MF_OWNERDRAW = 0x00000100;
}
}

View File

@@ -1,74 +0,0 @@
// 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.IO;
using System.Runtime.InteropServices;
using System.Text;
using Awake.Core.Models;
namespace Awake.Core
{
internal static class NativeMethods
{
internal delegate bool EnumThreadDelegate(IntPtr hWnd, IntPtr lParam);
[DllImport("Powrprof.dll", SetLastError = true)]
internal static extern bool GetPwrCapabilities(out SystemPowerCapabilities lpSystemPowerCapabilities);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool SetConsoleCtrlHandler(ConsoleEventHandler handler, bool add);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
internal static extern ExecutionState SetThreadExecutionState(ExecutionState esFlags);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool AllocConsole();
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool SetStdHandle(int nStdHandle, IntPtr hHandle);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern uint GetCurrentThreadId();
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
internal static extern IntPtr CreateFile(
[MarshalAs(UnmanagedType.LPWStr)] string filename,
[MarshalAs(UnmanagedType.U4)] uint access,
[MarshalAs(UnmanagedType.U4)] FileShare share,
IntPtr securityAttributes,
[MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
[MarshalAs(UnmanagedType.U4)] FileAttributes flagsAndAttributes,
IntPtr templateFile);
[DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr CreatePopupMenu();
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool InsertMenu(IntPtr hMenu, uint uPosition, uint uFlags, uint uIDNewItem, string lpNewItem);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool TrackPopupMenuEx(IntPtr hmenu, uint fuFlags, int x, int y, IntPtr hwnd, IntPtr lptpm);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr hWndChildAfter, string? className, string? windowTitle);
[DllImport("user32.dll", SetLastError = true)]
internal static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, nuint wParam, string lParam);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool DestroyMenu(IntPtr hMenu);
}
}

View File

@@ -7,11 +7,16 @@ using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Awake.Core.Models;
using Microsoft.PowerToys.Settings.UI.Library;
using NLog;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.UI.WindowsAndMessaging;
#pragma warning disable CS8602 // Dereference of a possibly null reference.
#pragma warning disable CS8603 // Possible null reference return.
@@ -22,21 +27,18 @@ namespace Awake.Core
{
private static readonly Logger _log;
private static IntPtr _trayMenu;
private static DestroyMenuSafeHandle TrayMenu { get; set; }
private static IntPtr TrayMenu { get => _trayMenu; set => _trayMenu = value; }
private static NotifyIcon? _trayIcon;
private static NotifyIcon TrayIcon { get => _trayIcon; set => _trayIcon = value; }
private static NotifyIcon TrayIcon { get; set; }
static TrayHelper()
{
_log = LogManager.GetCurrentClassLogger();
TrayMenu = new DestroyMenuSafeHandle();
TrayIcon = new NotifyIcon();
}
public static void InitializeTray(string text, Icon icon, ContextMenuStrip? contextMenu = null)
public static void InitializeTray(string text, Icon icon, ManualResetEvent? exitSignal, ContextMenuStrip? contextMenu = null)
{
Task.Factory.StartNew(
(tray) =>
@@ -49,7 +51,7 @@ namespace Awake.Core
((NotifyIcon?)tray).ContextMenuStrip = contextMenu;
((NotifyIcon?)tray).Visible = true;
((NotifyIcon?)tray).MouseClick += TrayClickHandler;
Application.AddMessageFilter(new TrayMessageFilter());
Application.AddMessageFilter(new TrayMessageFilter(exitSignal));
Application.Run();
_log.Info("Tray setup complete.");
}
@@ -74,21 +76,15 @@ namespace Awake.Core
/// <param name="e">MouseEventArgs instance containing mouse click event information.</param>
private static void TrayClickHandler(object? sender, MouseEventArgs e)
{
IntPtr windowHandle = APIHelper.GetHiddenWindow();
HWND windowHandle = APIHelper.GetHiddenWindow();
if (windowHandle != IntPtr.Zero)
if (windowHandle != HWND.Null)
{
NativeMethods.SetForegroundWindow(windowHandle);
NativeMethods.TrackPopupMenuEx(TrayMenu, 0, Cursor.Position.X, Cursor.Position.Y, windowHandle, IntPtr.Zero);
PInvoke.SetForegroundWindow(windowHandle);
PInvoke.TrackPopupMenuEx(TrayMenu, 0, Cursor.Position.X, Cursor.Position.Y, windowHandle, null);
}
}
public static void ClearTray()
{
TrayIcon.Icon = null;
TrayIcon.Dispose();
}
internal static void SetTray(string text, AwakeSettings settings)
{
SetTray(
@@ -101,19 +97,14 @@ namespace Awake.Core
[SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1005:Single line comments should begin with single space", Justification = "For debugging purposes - will remove later.")]
public static void SetTray(string text, bool keepDisplayOn, AwakeMode mode, Dictionary<string, int> trayTimeShortcuts)
{
if (TrayMenu != IntPtr.Zero)
{
var destructionStatus = NativeMethods.DestroyMenu(TrayMenu);
if (destructionStatus != true)
{
_log.Error("Failed to destroy tray menu and free up memory.");
}
}
TrayMenu = new DestroyMenuSafeHandle(PInvoke.CreatePopupMenu());
TrayMenu = NativeMethods.CreatePopupMenu();
NativeMethods.InsertMenu(TrayMenu, 0, NativeConstants.MF_BYPOSITION | NativeConstants.MF_STRING, (uint)TrayCommands.TC_EXIT, "Exit");
NativeMethods.InsertMenu(TrayMenu, 0, NativeConstants.MF_BYPOSITION | NativeConstants.MF_SEPARATOR, 0, string.Empty);
NativeMethods.InsertMenu(TrayMenu, 0, NativeConstants.MF_BYPOSITION | NativeConstants.MF_STRING | (keepDisplayOn ? NativeConstants.MF_CHECKED : NativeConstants.MF_UNCHECKED), (uint)TrayCommands.TC_DISPLAY_SETTING, "Keep screen on");
if (!TrayMenu.IsInvalid)
{
PInvoke.InsertMenu(TrayMenu, 0, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_STRING, (uint)TrayCommands.TC_EXIT, "Exit");
PInvoke.InsertMenu(TrayMenu, 0, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_SEPARATOR, 0, string.Empty);
PInvoke.InsertMenu(TrayMenu, 0, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_STRING | (keepDisplayOn ? MENU_ITEM_FLAGS.MF_CHECKED : MENU_ITEM_FLAGS.MF_UNCHECKED), (uint)TrayCommands.TC_DISPLAY_SETTING, "Keep screen on");
}
// In case there are no tray shortcuts defined for the application default to a
// reasonable initial set.
@@ -123,18 +114,18 @@ namespace Awake.Core
}
// TODO: Make sure that this loads from JSON instead of being hard-coded.
var awakeTimeMenu = NativeMethods.CreatePopupMenu();
var awakeTimeMenu = new DestroyMenuSafeHandle(PInvoke.CreatePopupMenu(), false);
for (int i = 0; i < trayTimeShortcuts.Count; i++)
{
NativeMethods.InsertMenu(awakeTimeMenu, (uint)i, NativeConstants.MF_BYPOSITION | NativeConstants.MF_STRING, (uint)TrayCommands.TC_TIME + (uint)i, trayTimeShortcuts.ElementAt(i).Key);
PInvoke.InsertMenu(awakeTimeMenu, (uint)i, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_STRING, (uint)TrayCommands.TC_TIME + (uint)i, trayTimeShortcuts.ElementAt(i).Key);
}
var modeMenu = NativeMethods.CreatePopupMenu();
NativeMethods.InsertMenu(modeMenu, 0, NativeConstants.MF_BYPOSITION | NativeConstants.MF_STRING | (mode == AwakeMode.PASSIVE ? NativeConstants.MF_CHECKED : NativeConstants.MF_UNCHECKED), (uint)TrayCommands.TC_MODE_PASSIVE, "Off (keep using the selected power plan)");
NativeMethods.InsertMenu(modeMenu, 1, NativeConstants.MF_BYPOSITION | NativeConstants.MF_STRING | (mode == AwakeMode.INDEFINITE ? NativeConstants.MF_CHECKED : NativeConstants.MF_UNCHECKED), (uint)TrayCommands.TC_MODE_INDEFINITE, "Keep awake indefinitely");
var modeMenu = new DestroyMenuSafeHandle(PInvoke.CreatePopupMenu(), false);
PInvoke.InsertMenu(modeMenu, 0, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_STRING | (mode == AwakeMode.PASSIVE ? MENU_ITEM_FLAGS.MF_CHECKED : MENU_ITEM_FLAGS.MF_UNCHECKED), (uint)TrayCommands.TC_MODE_PASSIVE, "Off (keep using the selected power plan)");
PInvoke.InsertMenu(modeMenu, 1, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_STRING | (mode == AwakeMode.INDEFINITE ? MENU_ITEM_FLAGS.MF_CHECKED : MENU_ITEM_FLAGS.MF_UNCHECKED), (uint)TrayCommands.TC_MODE_INDEFINITE, "Keep awake indefinitely");
NativeMethods.InsertMenu(modeMenu, 2, NativeConstants.MF_BYPOSITION | NativeConstants.MF_POPUP | (mode == AwakeMode.TIMED ? NativeConstants.MF_CHECKED : NativeConstants.MF_UNCHECKED), (uint)awakeTimeMenu, "Keep awake temporarily");
NativeMethods.InsertMenu(TrayMenu, 0, NativeConstants.MF_BYPOSITION | NativeConstants.MF_POPUP, (uint)modeMenu, "Mode");
PInvoke.InsertMenu(modeMenu, 2, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_POPUP | (mode == AwakeMode.TIMED ? MENU_ITEM_FLAGS.MF_CHECKED : MENU_ITEM_FLAGS.MF_UNCHECKED), (uint)awakeTimeMenu.DangerousGetHandle(), "Keep awake temporarily");
PInvoke.InsertMenu(TrayMenu, 0, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_POPUP, (uint)modeMenu.DangerousGetHandle(), "Mode");
TrayIcon.Text = text;
}

View File

@@ -6,9 +6,11 @@ using System;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Threading;
using System.Windows.Forms;
using Awake.Core.Models;
using Microsoft.PowerToys.Settings.UI.Library;
using Windows.Win32;
#pragma warning disable CS8603 // Possible null reference return.
@@ -20,8 +22,11 @@ namespace Awake.Core
private static SettingsUtils ModuleSettings { get => _moduleSettings; set => _moduleSettings = value; }
public TrayMessageFilter()
private static ManualResetEvent? _exitSignal;
public TrayMessageFilter(ManualResetEvent? exitSignal)
{
_exitSignal = exitSignal;
ModuleSettings = new SettingsUtils();
}
@@ -31,12 +36,12 @@ namespace Awake.Core
switch (m.Msg)
{
case (int)NativeConstants.WM_COMMAND:
case (int)PInvoke.WM_COMMAND:
var targetCommandIndex = m.WParam.ToInt64() & 0xFFFF;
switch (targetCommandIndex)
{
case (long)TrayCommands.TC_EXIT:
ExitCommandHandler();
ExitCommandHandler(_exitSignal);
break;
case (long)TrayCommands.TC_DISPLAY_SETTING:
DisplaySettingCommandHandler(InternalConstants.AppName);
@@ -68,9 +73,9 @@ namespace Awake.Core
return false;
}
private static void ExitCommandHandler()
private static void ExitCommandHandler(ManualResetEvent? exitSignal)
{
APIHelper.CompleteExit(0, true);
APIHelper.CompleteExit(0, exitSignal, true);
}
private static void DisplaySettingCommandHandler(string moduleName)

View File

@@ -0,0 +1,23 @@
AllocConsole
CreateFile
CreatePopupMenu
DestroyMenu
DestroyWindow
FindWindowEx
GetClassName
GetCurrentThreadId
GetPwrCapabilities
GetWindowThreadProcessId
HMENU
InsertMenu
PostQuitMessage
SendMessage
SetConsoleCtrlHandler
SetForegroundWindow
SetStdHandle
SetThreadExecutionState
TrackPopupMenuEx
WM_CLOSE
WM_COMMAND
WM_GETTEXT
WM_USER

View File

@@ -3,7 +3,6 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.CommandLine;
using System.CommandLine.Invocation;
using System.Diagnostics;
@@ -17,11 +16,15 @@ using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Awake.Core;
using Awake.Core.Models;
using interop;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Telemetry.Events;
using NLog;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.System.Console;
using Windows.Win32.System.Power;
#pragma warning disable CS8602 // Dereference of a possibly null reference.
#pragma warning disable CS8603 // Possible null reference return.
@@ -47,8 +50,8 @@ namespace Awake
private static Logger? _log;
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
private static ConsoleEventHandler _handler;
private static SystemPowerCapabilities _powerCapabilities;
private static PHANDLER_ROUTINE _handler;
private static SYSTEM_POWER_CAPABILITIES _powerCapabilities;
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
private static ManualResetEvent _exitSignal = new ManualResetEvent(false);
@@ -63,7 +66,7 @@ namespace Awake
if (!instantiated)
{
Exit(InternalConstants.AppName + " is already running! Exiting the application.", 1, true);
Exit(InternalConstants.AppName + " is already running! Exiting the application.", 1, _exitSignal, true);
}
_settingsUtils = new SettingsUtils();
@@ -82,12 +85,12 @@ namespace Awake
// To make it easier to diagnose future issues, let's get the
// system power capabilities and aggregate them in the log.
NativeMethods.GetPwrCapabilities(out _powerCapabilities);
PInvoke.GetPwrCapabilities(out _powerCapabilities);
_log.Info(JsonSerializer.Serialize(_powerCapabilities));
_log.Info("Parsing parameters...");
Option<bool>? configOption = new (
var configOption = new Option<bool>(
aliases: new[] { "--use-pt-config", "-c" },
getDefaultValue: () => false,
description: $"Specifies whether {InternalConstants.AppName} will be using the PowerToys configuration file for managing the state.")
@@ -100,7 +103,7 @@ namespace Awake
configOption.Required = false;
Option<bool>? displayOption = new (
var displayOption = new Option<bool>(
aliases: new[] { "--display-on", "-d" },
getDefaultValue: () => true,
description: "Determines whether the display should be kept awake.")
@@ -113,7 +116,7 @@ namespace Awake
displayOption.Required = false;
Option<uint>? timeOption = new (
var timeOption = new Option<uint>(
aliases: new[] { "--time-limit", "-t" },
getDefaultValue: () => 0,
description: "Determines the interval, in seconds, during which the computer is kept awake.")
@@ -126,7 +129,7 @@ namespace Awake
timeOption.Required = false;
Option<int>? pidOption = new (
var pidOption = new Option<int>(
aliases: new[] { "--pid", "-p" },
getDefaultValue: () => 0,
description: $"Bind the execution of {InternalConstants.AppName} to another process.")
@@ -156,23 +159,23 @@ namespace Awake
return rootCommand.InvokeAsync(args).Result;
}
private static bool ExitHandler(ControlType ctrlType)
private static BOOL ExitHandler(uint ctrlType)
{
_log.Info($"Exited through handler with control type: {ctrlType}");
Exit("Exiting from the internal termination handler.", Environment.ExitCode);
Exit("Exiting from the internal termination handler.", Environment.ExitCode, _exitSignal);
return false;
}
private static void Exit(string message, int exitCode, bool force = false)
private static void Exit(string message, int exitCode, ManualResetEvent exitSignal, bool force = false)
{
_log.Info(message);
APIHelper.CompleteExit(exitCode, force);
APIHelper.CompleteExit(exitCode, exitSignal, force);
}
private static void HandleCommandLineArguments(bool usePtConfig, bool displayOn, uint timeLimit, int pid)
{
_handler += new ConsoleEventHandler(ExitHandler);
_handler += ExitHandler;
APIHelper.SetConsoleControlHandler(_handler, true);
if (pid == 0)
@@ -192,16 +195,15 @@ namespace Awake
// and instead watch for changes in the file.
try
{
var eventHandle = new EventWaitHandle(false, EventResetMode.ManualReset, Constants.AwakeExitEvent());
new Thread(() =>
{
EventWaitHandle? eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.AwakeExitEvent());
if (eventHandle.WaitOne())
if (WaitHandle.WaitAny(new WaitHandle[] { _exitSignal, eventHandle }) == 1)
{
Exit("Received a signal to end the process. Making sure we quit...", 0, true);
Exit("Received a signal to end the process. Making sure we quit...", 0, _exitSignal, true);
}
}).Start();
TrayHelper.InitializeTray(InternalConstants.FullAppName, new Icon("modules/awake/images/awake.ico"));
TrayHelper.InitializeTray(InternalConstants.FullAppName, new Icon("modules/awake/images/awake.ico"), _exitSignal);
string? settingsPath = _settingsUtils.GetSettingsFilePath(InternalConstants.AppName);
_log.Info($"Reading configuration file: {settingsPath}");
@@ -263,7 +265,7 @@ namespace Awake
RunnerHelper.WaitForPowerToysRunner(pid, () =>
{
_log.Info($"Triggered PID-based exit handler for PID {pid}.");
Exit("Terminating from process binding hook.", 0, true);
Exit("Terminating from process binding hook.", 0, _exitSignal, true);
});
}

View File

@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
using System.ComponentModel.Composition;
using System.Threading;
using System.Windows;
using ColorPicker.Helpers;
@@ -23,8 +24,16 @@ namespace ColorPickerUI
private bool disposedValue;
private ThemeManager _themeManager;
private CancellationTokenSource NativeThreadCTS { get; set; }
[Export]
private static CancellationToken ExitToken { get; set; }
protected override void OnStartup(StartupEventArgs e)
{
NativeThreadCTS = new CancellationTokenSource();
ExitToken = NativeThreadCTS.Token;
_args = e?.Args;
// allow only one instance of color picker
@@ -33,7 +42,7 @@ namespace ColorPickerUI
{
Logger.LogWarning("There is ColorPicker instance running. Exiting Color Picker");
_instanceMutex = null;
Environment.Exit(0);
Shutdown(0);
return;
}
@@ -45,7 +54,8 @@ namespace ColorPickerUI
RunnerHelper.WaitForPowerToysRunner(_powerToysRunnerPid, () =>
{
Logger.LogInfo("PowerToys Runner exited. Exiting ColorPicker");
Environment.Exit(0);
NativeThreadCTS.Cancel();
Dispatcher.Invoke(Shutdown);
});
}
else

View File

@@ -7,7 +7,6 @@ using System.Globalization;
using System.Windows;
using System.Windows.Automation.Peers;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using ColorPicker.Helpers;

View File

@@ -3,11 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
namespace ColorPicker.Controls
{

View File

@@ -4,7 +4,6 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;

View File

@@ -1,29 +0,0 @@
// 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.Threading;
using System.Windows;
namespace ColorPicker.Helpers
{
public static class NativeEventWaiter
{
public static void WaitForEventLoop(string eventName, Action callback)
{
new Thread(() =>
{
var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, eventName);
while (true)
{
if (eventHandle.WaitOne())
{
Logger.LogInfo($"Successfully waited for {eventName}");
Application.Current.Dispatcher.Invoke(callback);
}
}
}).Start();
}
}
}

View File

@@ -7,11 +7,8 @@ using System.ComponentModel.Composition;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Windows;
using System.Windows.Media.Imaging;
using ColorPicker.Telemetry;
using ColorPicker.ViewModelContracts;
using Microsoft.PowerToys.Telemetry;
namespace ColorPicker.Helpers
{

View File

@@ -8,10 +8,7 @@ using System.ComponentModel.Composition;
using System.Windows.Input;
using ColorPicker.Helpers;
using ColorPicker.Settings;
using ColorPicker.Telemetry;
using Microsoft.PowerToys.Settings.UI.Library.Enumerations;
using Microsoft.PowerToys.Settings.UI.Library.Utilities;
using Microsoft.PowerToys.Telemetry;
using static ColorPicker.NativeMethods;
namespace ColorPicker.Keyboard

View File

@@ -5,7 +5,6 @@
using System;
using ColorPicker.Helpers;
using ColorPicker.Mouse;
using ColorPickerUI;
namespace ColorPicker

View File

@@ -2,7 +2,6 @@
// 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.Diagnostics.Tracing;
using Microsoft.PowerToys.Telemetry;
using Microsoft.PowerToys.Telemetry.Events;

View File

@@ -4,7 +4,6 @@
using System;
using System.ComponentModel.Composition;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows;
using System.Windows.Media;
@@ -13,11 +12,9 @@ using ColorPicker.Helpers;
using ColorPicker.Keyboard;
using ColorPicker.Mouse;
using ColorPicker.Settings;
using ColorPicker.Telemetry;
using ColorPicker.ViewModelContracts;
using Common.UI;
using interop;
using Microsoft.PowerToys.Settings.UI.Library.Enumerations;
using Microsoft.PowerToys.Telemetry;
namespace ColorPicker.ViewModels
{
@@ -49,13 +46,24 @@ namespace ColorPicker.ViewModels
ZoomWindowHelper zoomWindowHelper,
AppStateHandler appStateHandler,
KeyboardMonitor keyboardMonitor,
IUserSettings userSettings)
IUserSettings userSettings,
CancellationToken exitToken)
{
_zoomWindowHelper = zoomWindowHelper;
_appStateHandler = appStateHandler;
_userSettings = userSettings;
NativeEventWaiter.WaitForEventLoop(Constants.ShowColorPickerSharedEvent(), _appStateHandler.StartUserSession);
NativeEventWaiter.WaitForEventLoop(Constants.ColorPickerSendSettingsTelemetryEvent(), _userSettings.SendSettingsTelemetry);
NativeEventWaiter.WaitForEventLoop(
Constants.ShowColorPickerSharedEvent(),
_appStateHandler.StartUserSession,
Application.Current.Dispatcher,
exitToken);
NativeEventWaiter.WaitForEventLoop(
Constants.ColorPickerSendSettingsTelemetryEvent(),
_userSettings.SendSettingsTelemetry,
Application.Current.Dispatcher,
exitToken);
if (mouseInfoProvider != null)
{

View File

@@ -2,7 +2,6 @@
// 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.ComponentModel;
using System.Windows;
namespace ColorPicker

View File

@@ -5,9 +5,9 @@
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
using Common.UI;
using FancyZonesEditor.Logs;
using FancyZonesEditor.Utils;
@@ -35,10 +35,6 @@ namespace FancyZonesEditor
private ThemeManager _themeManager;
private EventWaitHandle _eventHandle;
private Thread _exitWaitThread;
public static bool DebugMode
{
get
@@ -50,6 +46,8 @@ namespace FancyZonesEditor
private static bool _debugMode;
private bool _isDisposed;
private CancellationTokenSource NativeThreadCTS { get; set; }
[Conditional("DEBUG")]
private void DebugModeCheck()
{
@@ -59,27 +57,29 @@ namespace FancyZonesEditor
public App()
{
// DebugModeCheck();
NativeThreadCTS = new CancellationTokenSource();
FancyZonesEditorIO = new FancyZonesEditorIO();
Overlay = new Overlay();
MainWindowSettings = new MainWindowSettingsModel();
_exitWaitThread = new Thread(App_WaitExit);
_exitWaitThread.Start();
App_WaitExit();
}
private void OnStartup(object sender, StartupEventArgs e)
{
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
RunnerHelper.WaitForPowerToysRunner(PowerToysPID, () =>
{
Logger.LogInfo("Runner exited");
Environment.Exit(0);
});
_themeManager = new ThemeManager(this);
var parseResult = FancyZonesEditorIO.ParseParams();
RunnerHelper.WaitForPowerToysRunner(PowerToysPID, () =>
{
Logger.LogInfo("Runner exited");
NativeThreadCTS.Cancel();
Application.Current.Dispatcher.Invoke(Application.Current.Shutdown);
});
if (!parseResult.Result)
{
Logger.LogError(ParsingErrorReportTag + ": " + parseResult.Message + "; " + ParsingErrorDataTag + ": " + parseResult.MalformedData);
@@ -122,26 +122,23 @@ namespace FancyZonesEditor
private void OnExit(object sender, ExitEventArgs e)
{
NativeThreadCTS.Cancel();
Dispose();
if (_eventHandle != null)
{
_eventHandle.Set();
}
_exitWaitThread.Join();
Logger.LogInfo("FancyZones Editor exited");
}
private void App_WaitExit()
{
_eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, interop.Constants.FZEExitEvent());
if (_eventHandle.WaitOne())
NativeEventWaiter.WaitForEventLoop(
interop.Constants.FZEExitEvent(),
() =>
{
Logger.LogInfo("Exit event triggered");
Environment.Exit(0);
}
Application.Current.Shutdown();
},
Current.Dispatcher,
NativeThreadCTS.Token);
}
public void App_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)

View File

@@ -72,13 +72,16 @@ namespace PowerLauncher
using (var application = new App())
{
application.InitializeComponent();
NativeEventWaiter.WaitForEventLoop(
Constants.RunExitEvent(),
() =>
{
Log.Warn("RunExitEvent was signaled. Exiting PowerToys", typeof(App));
ExitPowerToys(application);
}, NativeThreadCTS.Token);
{
Log.Warn("RunExitEvent was signaled. Exiting PowerToys", typeof(App));
ExitPowerToys(application);
},
Application.Current.Dispatcher,
NativeThreadCTS.Token);
if (powerToysPid != 0)
{

View File

@@ -12,6 +12,7 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interop;
using Common.UI;
using interop;
using Microsoft.PowerLauncher.Telemetry;
using Microsoft.PowerToys.Telemetry;
@@ -52,7 +53,11 @@ namespace PowerLauncher
_firstDeleteTimer.Elapsed += CheckForFirstDelete;
_firstDeleteTimer.Interval = 1000;
NativeEventWaiter.WaitForEventLoop(Constants.RunSendSettingsTelemetryEvent(), SendSettingsTelemetry, _nativeWaiterCancelToken);
NativeEventWaiter.WaitForEventLoop(
Constants.RunSendSettingsTelemetryEvent(),
SendSettingsTelemetry,
Application.Current.Dispatcher,
_nativeWaiterCancelToken);
}
private void SendSettingsTelemetry()
@@ -701,7 +706,15 @@ namespace PowerLauncher
private void OnClosed(object sender, EventArgs e)
{
_hwndSource.RemoveHook(ProcessWindowMessages);
try
{
_hwndSource.RemoveHook(ProcessWindowMessages);
}
catch (Exception ex)
{
Log.Exception($"Exception when trying to Remove hook", ex, ex.GetType());
}
_hwndSource = null;
}
}

View File

@@ -49,19 +49,6 @@ namespace Wox
_mainVM.ChangeQueryText(query, requery);
}
public void RestartApp()
{
_mainVM.MainWindowVisibility = Visibility.Hidden;
// we must manually save
// UpdateManager.RestartApp() will call Environment.Exit(0)
// which will cause ungraceful exit
SaveAppAllSettings();
// Todo : Implement logic to restart this app.
Environment.Exit(0);
}
public void CheckForNewUpdate()
{
// _settingsVM.UpdateApp();

View File

@@ -12,6 +12,7 @@ using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
using Common.UI;
using interop;
using Microsoft.PowerLauncher.Telemetry;
using Microsoft.PowerToys.Telemetry;
@@ -93,12 +94,12 @@ namespace PowerLauncher.ViewModel
Log.Info("RegisterHotkey()", GetType());
// Allow OOBE to call PowerToys Run.
NativeEventWaiter.WaitForEventLoop(Constants.PowerLauncherSharedEvent(), OnHotkey, _nativeWaiterCancelToken);
NativeEventWaiter.WaitForEventLoop(Constants.PowerLauncherSharedEvent(), OnHotkey, Application.Current.Dispatcher, _nativeWaiterCancelToken);
if (_settings.StartedFromPowerToysRunner)
{
// Allow runner to call PowerToys Run from the centralized keyboard hook.
NativeEventWaiter.WaitForEventLoop(Constants.PowerLauncherCentralizedHookSharedEvent(), OnCentralizedKeyboardHookHotKey, _nativeWaiterCancelToken);
NativeEventWaiter.WaitForEventLoop(Constants.PowerLauncherCentralizedHookSharedEvent(), OnCentralizedKeyboardHookHotKey, Application.Current.Dispatcher, _nativeWaiterCancelToken);
}
_settings.PropertyChanged += (s, e) =>

View File

@@ -23,11 +23,6 @@ namespace Wox.Plugin
/// </param>
void ChangeQuery(string query, bool requery = false);
/// <summary>
/// Restart Wox
/// </summary>
void RestartApp();
/// <summary>
/// Remove user selected history item and refresh/requery
/// </summary>