Skip to content

Commit 4e7bd34

Browse files
Pass FG rights to extensions when we access them (#38068)
Calling Win32 APIs from C# is usually easy! but mixing C#+WinRT+COM is dark and full of terrors Closes zadjii-msft#546 Co-authored-by: Manodasan Wignarajah <[email protected]>
1 parent 43783d2 commit 4e7bd34

File tree

2 files changed

+45
-5
lines changed

2 files changed

+45
-5
lines changed

src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ShellViewModel.cs

+22-4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// The Microsoft Corporation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5+
using System.Runtime.InteropServices;
6+
using System.Runtime.Versioning;
57
using CommunityToolkit.Common;
68
using CommunityToolkit.Mvvm.ComponentModel;
79
using CommunityToolkit.Mvvm.Input;
@@ -13,7 +15,7 @@
1315
using Microsoft.CmdPal.UI.ViewModels.Models;
1416
using Microsoft.CommandPalette.Extensions;
1517
using Microsoft.Extensions.DependencyInjection;
16-
using Windows.Win32;
18+
using WinRT;
1719

1820
namespace Microsoft.CmdPal.UI.ViewModels;
1921

@@ -148,14 +150,16 @@ public void SetActiveExtension(IExtensionWrapper? extension)
148150
// need to handle that
149151
_activeExtension = extension;
150152

151-
var extensionComObject = _activeExtension?.GetExtensionObject();
152-
if (extensionComObject != null)
153+
var extensionWinRtObject = _activeExtension?.GetExtensionObject();
154+
if (extensionWinRtObject != null)
153155
{
154156
try
155157
{
156158
unsafe
157159
{
158-
var hr = PInvoke.CoAllowSetForegroundWindow(extensionComObject);
160+
var winrtObj = (IWinRTObject)extensionWinRtObject;
161+
var intPtr = winrtObj.NativeObject.ThisPtr;
162+
var hr = Native.CoAllowSetForegroundWindow(intPtr);
159163
if (hr != 0)
160164
{
161165
Logger.LogWarning($"Error giving foreground rights: 0x{hr.Value:X8}");
@@ -174,4 +178,18 @@ public void GoHome()
174178
{
175179
SetActiveExtension(null);
176180
}
181+
182+
// You may ask yourself, why aren't we using CsWin32 for this?
183+
// The CsWin32 projected version includes some object marshalling, like so:
184+
//
185+
// HRESULT CoAllowSetForegroundWindow([MarshalAs(UnmanagedType.IUnknown)] object pUnk,...)
186+
//
187+
// And if you do it like that, then the IForegroundTransfer interface isn't marshalled correctly
188+
internal sealed class Native
189+
{
190+
[DllImport("OLE32.dll", ExactSpelling = true)]
191+
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
192+
[SupportedOSPlatform("windows5.0")]
193+
internal static extern unsafe global::Windows.Win32.Foundation.HRESULT CoAllowSetForegroundWindow(nint pUnk, [Optional] void* lpvReserved);
194+
}
177195
}

src/modules/cmdpal/Microsoft.CmdPal.UI/Pages/ShellPage.xaml.cs

+23-1
Original file line numberDiff line numberDiff line change
@@ -130,14 +130,36 @@ private void PerformCommand(PerformCommandMessage message)
130130
// Or the command may be a stub. Future us problem.
131131
try
132132
{
133+
// In the case that we're coming from a top-level command, the
134+
// current page's host is the global instance. We only really want
135+
// to use that as the host of last resort.
133136
var pageHost = ViewModel.CurrentPage?.ExtensionHost;
137+
if (pageHost == CommandPaletteHost.Instance)
138+
{
139+
pageHost = null;
140+
}
141+
134142
var messageHost = message.ExtensionHost;
135143

136144
// Use the host from the current page if it has one, else use the
137145
// one specified in the PerformMessage for a top-level command,
138146
// else just use the global one.
139-
var host = pageHost ?? messageHost ?? CommandPaletteHost.Instance;
147+
CommandPaletteHost host;
148+
149+
// Top level items can come through without a Extension set on the
150+
// message. In that case, the `Context` is actually the
151+
// TopLevelViewModel itself, and we can use that to get at the
152+
// extension object.
140153
extension = pageHost?.Extension ?? messageHost?.Extension ?? null;
154+
if (extension == null && message.Context is TopLevelViewModel topLevelViewModel)
155+
{
156+
extension = topLevelViewModel.ExtensionHost?.Extension;
157+
host = pageHost ?? messageHost ?? topLevelViewModel?.ExtensionHost ?? CommandPaletteHost.Instance;
158+
}
159+
else
160+
{
161+
host = pageHost ?? messageHost ?? CommandPaletteHost.Instance;
162+
}
141163

142164
if (extension != null)
143165
{

0 commit comments

Comments
 (0)