mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-05 02:36:19 +02:00
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 https://github.com/zadjii-msft/PowerToys/issues/546 Co-authored-by: Manodasan Wignarajah <mawign@microsoft.com>
This commit is contained in:
@@ -2,6 +2,8 @@
|
|||||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
using CommunityToolkit.Common;
|
using CommunityToolkit.Common;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.Input;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
@@ -13,7 +15,7 @@ using Microsoft.CmdPal.UI.ViewModels.Messages;
|
|||||||
using Microsoft.CmdPal.UI.ViewModels.Models;
|
using Microsoft.CmdPal.UI.ViewModels.Models;
|
||||||
using Microsoft.CommandPalette.Extensions;
|
using Microsoft.CommandPalette.Extensions;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Windows.Win32;
|
using WinRT;
|
||||||
|
|
||||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||||
|
|
||||||
@@ -148,14 +150,16 @@ public partial class ShellViewModel(IServiceProvider _serviceProvider, TaskSched
|
|||||||
// need to handle that
|
// need to handle that
|
||||||
_activeExtension = extension;
|
_activeExtension = extension;
|
||||||
|
|
||||||
var extensionComObject = _activeExtension?.GetExtensionObject();
|
var extensionWinRtObject = _activeExtension?.GetExtensionObject();
|
||||||
if (extensionComObject != null)
|
if (extensionWinRtObject != null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
var hr = PInvoke.CoAllowSetForegroundWindow(extensionComObject);
|
var winrtObj = (IWinRTObject)extensionWinRtObject;
|
||||||
|
var intPtr = winrtObj.NativeObject.ThisPtr;
|
||||||
|
var hr = Native.CoAllowSetForegroundWindow(intPtr);
|
||||||
if (hr != 0)
|
if (hr != 0)
|
||||||
{
|
{
|
||||||
Logger.LogWarning($"Error giving foreground rights: 0x{hr.Value:X8}");
|
Logger.LogWarning($"Error giving foreground rights: 0x{hr.Value:X8}");
|
||||||
@@ -174,4 +178,18 @@ public partial class ShellViewModel(IServiceProvider _serviceProvider, TaskSched
|
|||||||
{
|
{
|
||||||
SetActiveExtension(null);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -130,14 +130,36 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
|
|||||||
// Or the command may be a stub. Future us problem.
|
// Or the command may be a stub. Future us problem.
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// In the case that we're coming from a top-level command, the
|
||||||
|
// current page's host is the global instance. We only really want
|
||||||
|
// to use that as the host of last resort.
|
||||||
var pageHost = ViewModel.CurrentPage?.ExtensionHost;
|
var pageHost = ViewModel.CurrentPage?.ExtensionHost;
|
||||||
|
if (pageHost == CommandPaletteHost.Instance)
|
||||||
|
{
|
||||||
|
pageHost = null;
|
||||||
|
}
|
||||||
|
|
||||||
var messageHost = message.ExtensionHost;
|
var messageHost = message.ExtensionHost;
|
||||||
|
|
||||||
// Use the host from the current page if it has one, else use the
|
// Use the host from the current page if it has one, else use the
|
||||||
// one specified in the PerformMessage for a top-level command,
|
// one specified in the PerformMessage for a top-level command,
|
||||||
// else just use the global one.
|
// else just use the global one.
|
||||||
var host = pageHost ?? messageHost ?? CommandPaletteHost.Instance;
|
CommandPaletteHost host;
|
||||||
|
|
||||||
|
// Top level items can come through without a Extension set on the
|
||||||
|
// message. In that case, the `Context` is actually the
|
||||||
|
// TopLevelViewModel itself, and we can use that to get at the
|
||||||
|
// extension object.
|
||||||
extension = pageHost?.Extension ?? messageHost?.Extension ?? null;
|
extension = pageHost?.Extension ?? messageHost?.Extension ?? null;
|
||||||
|
if (extension == null && message.Context is TopLevelViewModel topLevelViewModel)
|
||||||
|
{
|
||||||
|
extension = topLevelViewModel.ExtensionHost?.Extension;
|
||||||
|
host = pageHost ?? messageHost ?? topLevelViewModel?.ExtensionHost ?? CommandPaletteHost.Instance;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
host = pageHost ?? messageHost ?? CommandPaletteHost.Instance;
|
||||||
|
}
|
||||||
|
|
||||||
if (extension != null)
|
if (extension != null)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user