PowerToys/src/modules/cmdpal/Microsoft.CmdPal.UI/PowerToysRootPageService.cs
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

145 lines
4.7 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.Runtime.InteropServices;
using System.Runtime.Versioning;
using ManagedCommon;
using Microsoft.CmdPal.Common.Services;
using Microsoft.CmdPal.Core.ViewModels;
using Microsoft.CmdPal.UI.ViewModels;
using Microsoft.CmdPal.UI.ViewModels.MainPage;
using Microsoft.CommandPalette.Extensions;
using Microsoft.Extensions.DependencyInjection;
using WinRT;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace Microsoft.CmdPal.UI;
internal sealed class PowerToysRootPageService : IRootPageService
{
private readonly IServiceProvider _serviceProvider;
private IExtensionWrapper? _activeExtension;
private Lazy<MainListPage> _mainListPage;
public PowerToysRootPageService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
_mainListPage = new Lazy<MainListPage>(() =>
{
return new MainListPage(_serviceProvider);
});
}
public async Task PreLoadAsync()
{
var tlcManager = _serviceProvider.GetService<TopLevelCommandManager>()!;
await tlcManager.LoadBuiltinsAsync();
}
public Microsoft.CommandPalette.Extensions.IPage GetRootPage()
{
return _mainListPage.Value;
}
public async Task PostLoadRootPageAsync()
{
var tlcManager = _serviceProvider.GetService<TopLevelCommandManager>()!;
// After loading built-ins, and starting navigation, kick off a thread to load extensions.
tlcManager.LoadExtensionsCommand.Execute(null);
await tlcManager.LoadExtensionsCommand.ExecutionTask!;
if (tlcManager.LoadExtensionsCommand.ExecutionTask.Status != TaskStatus.RanToCompletion)
{
// TODO: Handle failure case
}
}
private void OnPerformTopLevelCommand(object? context)
{
try
{
if (context is IListItem listItem)
{
_mainListPage.Value.UpdateHistory(listItem);
}
}
catch (Exception ex)
{
Logger.LogError("Failed to update history in PowerToysRootPageService");
Logger.LogError(ex.ToString());
}
}
public void OnPerformCommand(object? context, bool topLevel, AppExtensionHost? currentHost)
{
if (topLevel)
{
OnPerformTopLevelCommand(context);
}
if (currentHost is CommandPaletteHost host)
{
SetActiveExtension(host.Extension);
}
else
{
throw new InvalidOperationException("This must be a programming error - everything in Command Palette should have a CommandPaletteHost");
}
}
public void SetActiveExtension(IExtensionWrapper? extension)
{
if (extension != _activeExtension)
{
// There's not really a CoDisallowSetForegroundWindow, so we don't
// need to handle that
_activeExtension = extension;
var extensionWinRtObject = _activeExtension?.GetExtensionObject();
if (extensionWinRtObject is not null)
{
try
{
unsafe
{
var winrtObj = (IWinRTObject)extensionWinRtObject;
var intPtr = winrtObj.NativeObject.ThisPtr;
var hr = Native.CoAllowSetForegroundWindow(intPtr);
if (hr != 0)
{
Logger.LogWarning($"Error giving foreground rights: 0x{hr.Value:X8}");
}
}
}
catch (Exception ex)
{
Logger.LogError(ex.ToString());
}
}
}
}
public void GoHome()
{
SetActiveExtension(null);
}
// You may ask yourself, why aren't we using CsWin32 for this?
// The CsWin32 projected version includes some object marshalling, like so:
//
// HRESULT CoAllowSetForegroundWindow([MarshalAs(UnmanagedType.IUnknown)] object pUnk,...)
//
// And if you do it like that, then the IForegroundTransfer interface isn't marshalled correctly
internal sealed class Native
{
[DllImport("OLE32.dll", ExactSpelling = true)]
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
[SupportedOSPlatform("windows5.0")]
internal static extern unsafe global::Windows.Win32.Foundation.HRESULT CoAllowSetForegroundWindow(nint pUnk, [Optional] void* lpvReserved);
}
}