PowerToysRootPageService.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.Runtime.InteropServices; 6 using System.Runtime.Versioning; 7 using ManagedCommon; 8 using Microsoft.CmdPal.Core.Common.Services; 9 using Microsoft.CmdPal.Core.ViewModels; 10 using Microsoft.CmdPal.UI.ViewModels; 11 using Microsoft.CmdPal.UI.ViewModels.MainPage; 12 using Microsoft.CommandPalette.Extensions; 13 using WinRT; 14 15 // To learn more about WinUI, the WinUI project structure, 16 // and more about our project templates, see: http://aka.ms/winui-project-info. 17 namespace Microsoft.CmdPal.UI; 18 19 internal sealed class PowerToysRootPageService : IRootPageService 20 { 21 private readonly TopLevelCommandManager _tlcManager; 22 23 private IExtensionWrapper? _activeExtension; 24 private Lazy<MainListPage> _mainListPage; 25 26 public PowerToysRootPageService(TopLevelCommandManager topLevelCommandManager, SettingsModel settings, AliasManager aliasManager, AppStateModel appStateModel) 27 { 28 _tlcManager = topLevelCommandManager; 29 30 _mainListPage = new Lazy<MainListPage>(() => 31 { 32 return new MainListPage(_tlcManager, settings, aliasManager, appStateModel); 33 }); 34 } 35 36 public async Task PreLoadAsync() 37 { 38 await _tlcManager.LoadBuiltinsAsync(); 39 } 40 41 public Microsoft.CommandPalette.Extensions.IPage GetRootPage() 42 { 43 return _mainListPage.Value; 44 } 45 46 public async Task PostLoadRootPageAsync() 47 { 48 // After loading built-ins, and starting navigation, kick off a thread to load extensions. 49 _tlcManager.LoadExtensionsCommand.Execute(null); 50 51 await _tlcManager.LoadExtensionsCommand.ExecutionTask!; 52 if (_tlcManager.LoadExtensionsCommand.ExecutionTask.Status != TaskStatus.RanToCompletion) 53 { 54 // TODO: Handle failure case 55 } 56 } 57 58 private void OnPerformTopLevelCommand(object? context) 59 { 60 try 61 { 62 if (context is IListItem listItem) 63 { 64 _mainListPage.Value.UpdateHistory(listItem); 65 } 66 } 67 catch (Exception ex) 68 { 69 Logger.LogError("Failed to update history in PowerToysRootPageService"); 70 Logger.LogError(ex.ToString()); 71 } 72 } 73 74 public void OnPerformCommand(object? context, bool topLevel, AppExtensionHost? currentHost) 75 { 76 if (topLevel) 77 { 78 OnPerformTopLevelCommand(context); 79 } 80 81 if (currentHost is CommandPaletteHost host) 82 { 83 SetActiveExtension(host.Extension); 84 } 85 else 86 { 87 throw new InvalidOperationException("This must be a programming error - everything in Command Palette should have a CommandPaletteHost"); 88 } 89 } 90 91 public void SetActiveExtension(IExtensionWrapper? extension) 92 { 93 if (extension != _activeExtension) 94 { 95 // There's not really a CoDisallowSetForegroundWindow, so we don't 96 // need to handle that 97 _activeExtension = extension; 98 99 var extensionWinRtObject = _activeExtension?.GetExtensionObject(); 100 if (extensionWinRtObject is not null) 101 { 102 try 103 { 104 unsafe 105 { 106 var winrtObj = (IWinRTObject)extensionWinRtObject; 107 var intPtr = winrtObj.NativeObject.ThisPtr; 108 var hr = Native.CoAllowSetForegroundWindow(intPtr); 109 if (hr != 0) 110 { 111 Logger.LogWarning($"Error giving foreground rights: 0x{hr.Value:X8}"); 112 } 113 } 114 } 115 catch (Exception ex) 116 { 117 ManagedCommon.Logger.LogError(ex.ToString()); 118 } 119 } 120 } 121 } 122 123 public void GoHome() 124 { 125 SetActiveExtension(null); 126 } 127 128 // You may ask yourself, why aren't we using CsWin32 for this? 129 // The CsWin32 projected version includes some object marshalling, like so: 130 // 131 // HRESULT CoAllowSetForegroundWindow([MarshalAs(UnmanagedType.IUnknown)] object pUnk,...) 132 // 133 // And if you do it like that, then the IForegroundTransfer interface isn't marshalled correctly 134 internal sealed class Native 135 { 136 [DllImport("OLE32.dll", ExactSpelling = true)] 137 [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] 138 [SupportedOSPlatform("windows5.0")] 139 internal static extern unsafe global::Windows.Win32.Foundation.HRESULT CoAllowSetForegroundWindow(nint pUnk, [Optional] void* lpvReserved); 140 } 141 }