Michael Jolley 6acb793184
Some checks failed
Spell checking / Check Spelling (push) Has been cancelled
Spell checking / Report (Push) (push) Has been cancelled
Spell checking / Report (PR) (push) Has been cancelled
Spell checking / Update PR (push) Has been cancelled
CmdPal: Null pattern matching based on is expression rather than overridable operators (#40972)
What the title says. 😄 

Rather than relying on the potentially overloaded `!=` or `==` operators
when checking for null, now we'll use the `is` expression (possibly
combined with the `not` operator) to ensure correct checking. Probably
overkill for many of these classes, but decided to err on the side of
consistency. Would matter more on classes that may be inherited or
extended.

Using `is` and `is not` will provide us a guarantee that no
user-overloaded equality operators (`==`/`!=`) is invoked when a
`expression is null` is evaluated.

In code form, changed all instances of:

```c#
something != null

something == null
```

to:

```c#
something is not null

something is null
```

The one exception was checking null on a `KeyChord`. `KeyChord` is a
struct which is never null so VS will raise an error when trying this
versus just providing a warning when using `keyChord != null`. In
reality, we shouldn't do this check because it can't ever be null. In
the case of a `KeyChord` it **would** be a `KeyChord` equivalent to:

```c#
KeyChord keyChord = new ()
{
    Modifiers = 0,
    Vkey = 0,
    ScanCode = 0
};
```
2025-08-18 06:07:28 -05:00

686 lines
25 KiB
C#

// 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.Diagnostics;
using System.Runtime.InteropServices;
using CmdPalKeyboardService;
using CommunityToolkit.Mvvm.Messaging;
using ManagedCommon;
using Microsoft.CmdPal.Common.Helpers;
using Microsoft.CmdPal.Common.Messages;
using Microsoft.CmdPal.Common.Services;
using Microsoft.CmdPal.Core.ViewModels.Messages;
using Microsoft.CmdPal.UI.Events;
using Microsoft.CmdPal.UI.Helpers;
using Microsoft.CmdPal.UI.Messages;
using Microsoft.CmdPal.UI.ViewModels;
using Microsoft.CmdPal.UI.ViewModels.Messages;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.PowerToys.Telemetry;
using Microsoft.UI.Composition;
using Microsoft.UI.Composition.SystemBackdrops;
using Microsoft.UI.Input;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.Windows.AppLifecycle;
using Windows.ApplicationModel.Activation;
using Windows.Foundation;
using Windows.Graphics;
using Windows.UI;
using Windows.UI.WindowManagement;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.Graphics.Dwm;
using Windows.Win32.UI.Input.KeyboardAndMouse;
using Windows.Win32.UI.WindowsAndMessaging;
using WinRT;
using WinUIEx;
using RS_ = Microsoft.CmdPal.UI.Helpers.ResourceLoaderInstance;
namespace Microsoft.CmdPal.UI;
public sealed partial class MainWindow : WindowEx,
IRecipient<DismissMessage>,
IRecipient<ShowWindowMessage>,
IRecipient<HideWindowMessage>,
IRecipient<QuitMessage>
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "Stylistically, window messages are WM_")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1306:Field names should begin with lower-case letter", Justification = "Stylistically, window messages are WM_")]
private readonly uint WM_TASKBAR_RESTART;
private readonly HWND _hwnd;
private readonly WNDPROC? _hotkeyWndProc;
private readonly WNDPROC? _originalWndProc;
private readonly List<TopLevelHotkey> _hotkeys = [];
private readonly KeyboardListener _keyboardListener;
private bool _ignoreHotKeyWhenFullScreen = true;
private DesktopAcrylicController? _acrylicController;
private SystemBackdropConfiguration? _configurationSource;
public MainWindow()
{
InitializeComponent();
_hwnd = new HWND(WinRT.Interop.WindowNative.GetWindowHandle(this).ToInt32());
unsafe
{
CommandPaletteHost.SetHostHwnd((ulong)_hwnd.Value);
}
_keyboardListener = new KeyboardListener();
_keyboardListener.Start();
_keyboardListener.SetProcessCommand(new CmdPalKeyboardService.ProcessCommand(HandleSummon));
this.SetIcon();
AppWindow.Title = RS_.GetString("AppName");
PositionCentered();
SetAcrylic();
WeakReferenceMessenger.Default.Register<DismissMessage>(this);
WeakReferenceMessenger.Default.Register<QuitMessage>(this);
WeakReferenceMessenger.Default.Register<ShowWindowMessage>(this);
WeakReferenceMessenger.Default.Register<HideWindowMessage>(this);
// Hide our titlebar.
// We need to both ExtendsContentIntoTitleBar, then set the height to Collapsed
// to hide the old caption buttons. Then, in UpdateRegionsForCustomTitleBar,
// we'll make the top drag-able again. (after our content loads)
ExtendsContentIntoTitleBar = true;
AppWindow.TitleBar.PreferredHeightOption = TitleBarHeightOption.Collapsed;
SizeChanged += WindowSizeChanged;
RootShellPage.Loaded += RootShellPage_Loaded;
WM_TASKBAR_RESTART = PInvoke.RegisterWindowMessage("TaskbarCreated");
// LOAD BEARING: If you don't stick the pointer to HotKeyPrc into a
// member (and instead like, use a local), then the pointer we marshal
// into the WindowLongPtr will be useless after we leave this function,
// and our **WindProc will explode**.
_hotkeyWndProc = HotKeyPrc;
var hotKeyPrcPointer = Marshal.GetFunctionPointerForDelegate(_hotkeyWndProc);
_originalWndProc = Marshal.GetDelegateForFunctionPointer<WNDPROC>(PInvoke.SetWindowLongPtr(_hwnd, WINDOW_LONG_PTR_INDEX.GWL_WNDPROC, hotKeyPrcPointer));
// Load our settings, and then also wire up a settings changed handler
HotReloadSettings();
App.Current.Services.GetService<SettingsModel>()!.SettingsChanged += SettingsChangedHandler;
// Make sure that we update the acrylic theme when the OS theme changes
RootShellPage.ActualThemeChanged += (s, e) => DispatcherQueue.TryEnqueue(UpdateAcrylic);
// Hardcoding event name to avoid bringing in the PowerToys.interop dependency. Event name must match CMDPAL_SHOW_EVENT from shared_constants.h
NativeEventWaiter.WaitForEventLoop("Local\\PowerToysCmdPal-ShowEvent-62336fcd-8611-4023-9b30-091a6af4cc5a", () =>
{
Summon(string.Empty);
});
}
private void SettingsChangedHandler(SettingsModel sender, object? args) => HotReloadSettings();
private void RootShellPage_Loaded(object sender, RoutedEventArgs e) =>
// Now that our content has loaded, we can update our draggable regions
UpdateRegionsForCustomTitleBar();
private void WindowSizeChanged(object sender, WindowSizeChangedEventArgs args) => UpdateRegionsForCustomTitleBar();
private void PositionCentered()
{
var displayArea = DisplayArea.GetFromWindowId(AppWindow.Id, DisplayAreaFallback.Nearest);
PositionCentered(displayArea);
}
private void PositionCentered(DisplayArea displayArea)
{
if (displayArea is not null)
{
var centeredPosition = AppWindow.Position;
centeredPosition.X = (displayArea.WorkArea.Width - AppWindow.Size.Width) / 2;
centeredPosition.Y = (displayArea.WorkArea.Height - AppWindow.Size.Height) / 2;
centeredPosition.X += displayArea.WorkArea.X;
centeredPosition.Y += displayArea.WorkArea.Y;
AppWindow.Move(centeredPosition);
}
}
private void HotReloadSettings()
{
var settings = App.Current.Services.GetService<SettingsModel>()!;
SetupHotkey(settings);
App.Current.Services.GetService<TrayIconService>()!.SetupTrayIcon(settings.ShowSystemTrayIcon);
_ignoreHotKeyWhenFullScreen = settings.IgnoreShortcutWhenFullscreen;
this.SetVisibilityInSwitchers(Debugger.IsAttached);
}
// We want to use DesktopAcrylicKind.Thin and custom colors as this is the default material
// other Shell surfaces are using, this cannot be set in XAML however.
private void SetAcrylic()
{
if (DesktopAcrylicController.IsSupported())
{
// Hooking up the policy object.
_configurationSource = new SystemBackdropConfiguration
{
// Initial configuration state.
IsInputActive = true,
};
UpdateAcrylic();
}
}
private void UpdateAcrylic()
{
if (_acrylicController != null)
{
_acrylicController.RemoveAllSystemBackdropTargets();
_acrylicController.Dispose();
}
_acrylicController = GetAcrylicConfig(Content);
// Enable the system backdrop.
// Note: Be sure to have "using WinRT;" to support the Window.As<...>() call.
_acrylicController.AddSystemBackdropTarget(this.As<ICompositionSupportsSystemBackdrop>());
_acrylicController.SetSystemBackdropConfiguration(_configurationSource);
}
private static DesktopAcrylicController GetAcrylicConfig(UIElement content)
{
var feContent = content as FrameworkElement;
return feContent?.ActualTheme == ElementTheme.Light
? new DesktopAcrylicController()
{
Kind = DesktopAcrylicKind.Thin,
TintColor = Color.FromArgb(255, 243, 243, 243),
LuminosityOpacity = 0.90f,
TintOpacity = 0.0f,
FallbackColor = Color.FromArgb(255, 238, 238, 238),
}
: new DesktopAcrylicController()
{
Kind = DesktopAcrylicKind.Thin,
TintColor = Color.FromArgb(255, 32, 32, 32),
LuminosityOpacity = 0.96f,
TintOpacity = 0.5f,
FallbackColor = Color.FromArgb(255, 28, 28, 28),
};
}
private void ShowHwnd(IntPtr hwndValue, MonitorBehavior target)
{
var hwnd = new HWND(hwndValue != 0 ? hwndValue : _hwnd);
// Make sure our HWND is cloaked before any possible window manipulations
Cloak();
// Remember, IsIconic == "minimized", which is entirely different state
// from "show/hide"
// If we're currently minimized, restore us first, before we reveal
// our window. Otherwise, we'd just be showing a minimized window -
// which would remain not visible to the user.
if (PInvoke.IsIconic(hwnd))
{
PInvoke.ShowWindow(hwnd, SHOW_WINDOW_CMD.SW_RESTORE);
}
var display = GetScreen(hwnd, target);
PositionCentered(display);
// Just to be sure, SHOW our hwnd.
PInvoke.ShowWindow(hwnd, SHOW_WINDOW_CMD.SW_SHOW);
// Once we're done, uncloak to avoid all animations
Uncloak();
PInvoke.SetForegroundWindow(hwnd);
PInvoke.SetActiveWindow(hwnd);
// Push our window to the top of the Z-order and make it the topmost, so that it appears above all other windows.
// We want to remove the topmost status when we hide the window (because we cloak it instead of hiding it).
PInvoke.SetWindowPos(hwnd, HWND.HWND_TOPMOST, 0, 0, 0, 0, SET_WINDOW_POS_FLAGS.SWP_NOMOVE | SET_WINDOW_POS_FLAGS.SWP_NOSIZE);
}
private DisplayArea GetScreen(HWND currentHwnd, MonitorBehavior target)
{
// Leaving a note here, in case we ever need it:
// https://github.com/microsoft/microsoft-ui-xaml/issues/6454
// If we need to ever FindAll, we'll need to iterate manually
var displayAreas = Microsoft.UI.Windowing.DisplayArea.FindAll();
switch (target)
{
case MonitorBehavior.InPlace:
if (PInvoke.GetWindowRect(currentHwnd, out var bounds))
{
RectInt32 converted = new(bounds.X, bounds.Y, bounds.Width, bounds.Height);
return DisplayArea.GetFromRect(converted, DisplayAreaFallback.Nearest);
}
break;
case MonitorBehavior.ToFocusedWindow:
var foregroundWindowHandle = PInvoke.GetForegroundWindow();
if (foregroundWindowHandle != IntPtr.Zero)
{
if (PInvoke.GetWindowRect(foregroundWindowHandle, out var fgBounds))
{
RectInt32 converted = new(fgBounds.X, fgBounds.Y, fgBounds.Width, fgBounds.Height);
return DisplayArea.GetFromRect(converted, DisplayAreaFallback.Nearest);
}
}
break;
case MonitorBehavior.ToPrimary:
return DisplayArea.Primary;
case MonitorBehavior.ToMouse:
default:
if (PInvoke.GetCursorPos(out var cursorPos))
{
return DisplayArea.GetFromPoint(new PointInt32(cursorPos.X, cursorPos.Y), DisplayAreaFallback.Nearest);
}
break;
}
return DisplayArea.Primary;
}
public void Receive(ShowWindowMessage message)
{
var settings = App.Current.Services.GetService<SettingsModel>()!;
ShowHwnd(message.Hwnd, settings.SummonOn);
}
public void Receive(HideWindowMessage message)
{
// This might come in off the UI thread. Make sure to hop back.
DispatcherQueue.TryEnqueue(() =>
{
HideWindow();
});
}
public void Receive(QuitMessage message) =>
// This might come in on a background thread
DispatcherQueue.TryEnqueue(() => Close());
public void Receive(DismissMessage message)
{
// This might come in off the UI thread. Make sure to hop back.
DispatcherQueue.TryEnqueue(() =>
{
HideWindow();
});
}
private void HideWindow()
{
// Cloak our HWND to avoid all animations.
Cloak();
// Then hide our HWND, to make sure that the OS gives the FG / focus back to another app
// (there's no way for us to guess what the right hwnd might be, only the OS can do it right)
PInvoke.ShowWindow(_hwnd, SHOW_WINDOW_CMD.SW_HIDE);
// TRICKY: show our HWND again. This will trick XAML into painting our
// HWND again, so that we avoid the "flicker" caused by a WinUI3 app
// window being first shown
// SW_SHOWNA will prevent us for trying to fight the focus back
PInvoke.ShowWindow(_hwnd, SHOW_WINDOW_CMD.SW_SHOWNA);
// Intentionally leave the window cloaked. So our window is "visible",
// but also cloaked, so you can't see it.
}
private void Cloak()
{
unsafe
{
BOOL value = true;
PInvoke.DwmSetWindowAttribute(_hwnd, DWMWINDOWATTRIBUTE.DWMWA_CLOAK, &value, (uint)sizeof(BOOL));
}
// Because we're only cloaking the window, bury it at the bottom in case something can
// see it - e.g. some accessibility helper (note: this also removes the top-most status).
PInvoke.SetWindowPos(_hwnd, HWND.HWND_BOTTOM, 0, 0, 0, 0, SET_WINDOW_POS_FLAGS.SWP_NOMOVE | SET_WINDOW_POS_FLAGS.SWP_NOSIZE);
}
private void Uncloak()
{
unsafe
{
BOOL value = false;
PInvoke.DwmSetWindowAttribute(_hwnd, DWMWINDOWATTRIBUTE.DWMWA_CLOAK, &value, (uint)sizeof(BOOL));
}
}
internal void MainWindow_Closed(object sender, WindowEventArgs args)
{
var serviceProvider = App.Current.Services;
var extensionService = serviceProvider.GetService<IExtensionService>()!;
extensionService.SignalStopExtensionsAsync();
App.Current.Services.GetService<TrayIconService>()!.Destroy();
// WinUI bug is causing a crash on shutdown when FailFastOnErrors is set to true (#51773592).
// Workaround by turning it off before shutdown.
App.Current.DebugSettings.FailFastOnErrors = false;
DisposeAcrylic();
_keyboardListener.Stop();
Environment.Exit(0);
}
private void DisposeAcrylic()
{
if (_acrylicController is not null)
{
_acrylicController.Dispose();
_acrylicController = null!;
_configurationSource = null!;
}
}
// Updates our window s.t. the top of the window is draggable.
private void UpdateRegionsForCustomTitleBar()
{
// Specify the interactive regions of the title bar.
var scaleAdjustment = RootShellPage.XamlRoot.RasterizationScale;
// Get the rectangle around our XAML content. We're going to mark this
// rectangle as "Passthrough", so that the normal window operations
// (resizing, dragging) don't apply in this space.
var transform = RootShellPage.TransformToVisual(null);
// Reserve 16px of space at the top for dragging.
var topHeight = 16;
var bounds = transform.TransformBounds(new Rect(
0,
topHeight,
RootShellPage.ActualWidth,
RootShellPage.ActualHeight));
var contentRect = GetRect(bounds, scaleAdjustment);
var rectArray = new RectInt32[] { contentRect };
var nonClientInputSrc = InputNonClientPointerSource.GetForWindowId(this.AppWindow.Id);
nonClientInputSrc.SetRegionRects(NonClientRegionKind.Passthrough, rectArray);
// Add a drag-able region on top
var w = RootShellPage.ActualWidth;
_ = RootShellPage.ActualHeight;
var dragSides = new RectInt32[]
{
GetRect(new Rect(0, 0, w, topHeight), scaleAdjustment), // the top, {topHeight=16} tall
};
nonClientInputSrc.SetRegionRects(NonClientRegionKind.Caption, dragSides);
}
private static RectInt32 GetRect(Rect bounds, double scale)
{
return new RectInt32(
_X: (int)Math.Round(bounds.X * scale),
_Y: (int)Math.Round(bounds.Y * scale),
_Width: (int)Math.Round(bounds.Width * scale),
_Height: (int)Math.Round(bounds.Height * scale));
}
internal void MainWindow_Activated(object sender, WindowActivatedEventArgs args)
{
if (args.WindowActivationState == WindowActivationState.Deactivated)
{
// If there's a debugger attached...
if (System.Diagnostics.Debugger.IsAttached)
{
// ... then don't hide the window when it loses focus.
return;
}
// Are we disabled? If we are, then we don't want to dismiss on focus lost.
// This can happen if an extension wanted to show a modal dialog on top of our
// window i.e. in the case of an MSAL auth window.
if (PInvoke.IsWindowEnabled(_hwnd) == 0)
{
return;
}
// This will DWM cloak our window:
HideWindow();
PowerToysTelemetry.Log.WriteEvent(new CmdPalDismissedOnLostFocus());
}
if (_configurationSource is not null)
{
_configurationSource.IsInputActive = args.WindowActivationState != WindowActivationState.Deactivated;
}
}
public void HandleLaunch(AppActivationArguments? activatedEventArgs)
{
if (activatedEventArgs is null)
{
Summon(string.Empty);
return;
}
try
{
if (activatedEventArgs.Kind == ExtendedActivationKind.StartupTask)
{
return;
}
if (activatedEventArgs.Kind == ExtendedActivationKind.Protocol)
{
if (activatedEventArgs.Data is IProtocolActivatedEventArgs protocolArgs)
{
if (protocolArgs.Uri.ToString() is string uri)
{
// was the URI "x-cmdpal://background" ?
if (uri.StartsWith("x-cmdpal://background", StringComparison.OrdinalIgnoreCase))
{
// we're running, we don't want to activate our window. bail
return;
}
else if (uri.StartsWith("x-cmdpal://settings", StringComparison.OrdinalIgnoreCase))
{
WeakReferenceMessenger.Default.Send<OpenSettingsMessage>(new());
return;
}
}
}
}
}
catch (COMException ex)
{
// Accessing properties activatedEventArgs.Kind and activatedEventArgs.Data might cause COMException
// if the args are not valid or not passed correctly.
Logger.LogError("COM exception when activating the application", ex);
}
Summon(string.Empty);
}
public void Summon(string commandId) =>
// The actual showing and hiding of the window will be done by the
// ShellPage. This is because we don't want to show the window if the
// user bound a hotkey to just an invokable command, which we can't
// know till the message is being handled.
WeakReferenceMessenger.Default.Send<HotkeySummonMessage>(new(commandId, _hwnd));
private void UnregisterHotkeys()
{
_keyboardListener.ClearHotkeys();
while (_hotkeys.Count > 0)
{
PInvoke.UnregisterHotKey(_hwnd, _hotkeys.Count - 1);
_hotkeys.RemoveAt(_hotkeys.Count - 1);
}
}
private void SetupHotkey(SettingsModel settings)
{
UnregisterHotkeys();
var globalHotkey = settings.Hotkey;
if (globalHotkey is not null)
{
if (settings.UseLowLevelGlobalHotkey)
{
_keyboardListener.SetHotkeyAction(globalHotkey.Win, globalHotkey.Ctrl, globalHotkey.Shift, globalHotkey.Alt, (byte)globalHotkey.Code, string.Empty);
_hotkeys.Add(new(globalHotkey, string.Empty));
}
else
{
var vk = globalHotkey.Code;
var modifiers =
(globalHotkey.Alt ? HOT_KEY_MODIFIERS.MOD_ALT : 0) |
(globalHotkey.Ctrl ? HOT_KEY_MODIFIERS.MOD_CONTROL : 0) |
(globalHotkey.Shift ? HOT_KEY_MODIFIERS.MOD_SHIFT : 0) |
(globalHotkey.Win ? HOT_KEY_MODIFIERS.MOD_WIN : 0)
;
var success = PInvoke.RegisterHotKey(_hwnd, _hotkeys.Count, modifiers, (uint)vk);
if (success)
{
_hotkeys.Add(new(globalHotkey, string.Empty));
}
}
}
foreach (var commandHotkey in settings.CommandHotkeys)
{
var key = commandHotkey.Hotkey;
if (key is not null)
{
if (settings.UseLowLevelGlobalHotkey)
{
_keyboardListener.SetHotkeyAction(key.Win, key.Ctrl, key.Shift, key.Alt, (byte)key.Code, commandHotkey.CommandId);
_hotkeys.Add(new(globalHotkey, string.Empty));
}
else
{
var vk = key.Code;
var modifiers =
(key.Alt ? HOT_KEY_MODIFIERS.MOD_ALT : 0) |
(key.Ctrl ? HOT_KEY_MODIFIERS.MOD_CONTROL : 0) |
(key.Shift ? HOT_KEY_MODIFIERS.MOD_SHIFT : 0) |
(key.Win ? HOT_KEY_MODIFIERS.MOD_WIN : 0)
;
var success = PInvoke.RegisterHotKey(_hwnd, _hotkeys.Count, modifiers, (uint)vk);
if (success)
{
_hotkeys.Add(commandHotkey);
}
}
}
}
}
private void HandleSummon(string commandId)
{
var isRootHotkey = string.IsNullOrEmpty(commandId);
PowerToysTelemetry.Log.WriteEvent(new CmdPalHotkeySummoned(isRootHotkey));
var isVisible = this.Visible;
unsafe
{
// We need to check if our window is cloaked or not. A cloaked window is still
// technically visible, because SHOW/HIDE != iconic (minimized) != cloaked
// (these are all separate states)
long attr = 0;
PInvoke.DwmGetWindowAttribute(_hwnd, DWMWINDOWATTRIBUTE.DWMWA_CLOAKED, &attr, sizeof(long));
if (attr == 1 /* DWM_CLOAKED_APP */)
{
isVisible = false;
}
}
// Note to future us: the wParam will have the index of the hotkey we registered.
// We can use that in the future to differentiate the hotkeys we've pressed
// so that we can bind hotkeys to individual commands
if (!isVisible || !isRootHotkey)
{
Activate();
Summon(commandId);
}
else if (isRootHotkey)
{
// If there's a debugger attached...
if (System.Diagnostics.Debugger.IsAttached)
{
// ... then manually hide our window. When debugged, we won't get the cool cloaking,
// but that's the price to pay for having the HWND not light-dismiss while we're debugging.
Cloak();
this.Hide();
return;
}
HideWindow();
}
}
private LRESULT HotKeyPrc(
HWND hwnd,
uint uMsg,
WPARAM wParam,
LPARAM lParam)
{
switch (uMsg)
{
// Prevent the window from maximizing when double-clicking the title bar area
case PInvoke.WM_NCLBUTTONDBLCLK:
return (LRESULT)IntPtr.Zero;
case PInvoke.WM_HOTKEY:
{
var hotkeyIndex = (int)wParam.Value;
if (hotkeyIndex < _hotkeys.Count)
{
if (_ignoreHotKeyWhenFullScreen)
{
// If we're in full screen mode, ignore the hotkey
if (WindowHelper.IsWindowFullscreen())
{
return (LRESULT)IntPtr.Zero;
}
}
var hotkey = _hotkeys[hotkeyIndex];
HandleSummon(hotkey.CommandId);
}
return (LRESULT)IntPtr.Zero;
}
default:
if (uMsg == WM_TASKBAR_RESTART)
{
HotReloadSettings();
}
break;
}
return PInvoke.CallWindowProc(_originalWndProc, hwnd, uMsg, wParam, lParam);
}
}