mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-05 10:46:33 +02:00
Added basic support for Windows App Actions. (#39927)
## Summary of the Pull Request Adds basic support for finding, listing, and executing Windows App Actions on files found by the Microsoft.CmdPal.Ext.Indexer extension. ## PR Checklist - [X] **Closes:** #39926 - [X] **Communication:** I've discussed this with core contributors already. If work hasn't been agreed, this work might be rejected - [ ] **Tests:** Added/updated and all pass - [X] **Localization:** All end user facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx ## Detailed Description of the Pull Request / Additional comments We also update cswin32 to stable version. <!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well --> ## Validation Steps Performed Validated that it doesn't show on older versions of Windows (<26100 insiders) and that it does work on newer version that have the App Actions runtime. --------- Co-authored-by: Gordon Lam (SH) <yeelam@microsoft.com> Co-authored-by: Mike Griese <migrie@microsoft.com> Co-authored-by: Leilei Zhang <leilzh@microsoft.com>
This commit is contained in:
committed by
GitHub
parent
c83be3e74c
commit
4737ec987e
@@ -4,7 +4,6 @@
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Microsoft.UI;
|
||||
using Microsoft.UI.Xaml;
|
||||
@@ -24,8 +23,8 @@ namespace Peek.FilePreviewer.Controls
|
||||
private static readonly COLORREF LightThemeBgColor = new(0x00f3f3f3);
|
||||
private static readonly COLORREF DarkThemeBgColor = new(0x00202020);
|
||||
|
||||
private static readonly HBRUSH LightThemeBgBrush = PInvoke.CreateSolidBrush(LightThemeBgColor);
|
||||
private static readonly HBRUSH DarkThemeBgBrush = PInvoke.CreateSolidBrush(DarkThemeBgColor);
|
||||
private static readonly HBRUSH LightThemeBgBrush = PInvoke_FilePreviewer.CreateSolidBrush(LightThemeBgColor);
|
||||
private static readonly HBRUSH DarkThemeBgBrush = PInvoke_FilePreviewer.CreateSolidBrush(DarkThemeBgColor);
|
||||
|
||||
[ObservableProperty]
|
||||
private IPreviewHandler? source;
|
||||
@@ -88,19 +87,19 @@ namespace Peek.FilePreviewer.Controls
|
||||
|
||||
if (HandlerVisibility == Visibility.Visible)
|
||||
{
|
||||
PInvoke.ShowWindow(containerHwnd, SHOW_WINDOW_CMD.SW_SHOW);
|
||||
PInvoke_FilePreviewer.ShowWindow(containerHwnd, SHOW_WINDOW_CMD.SW_SHOW);
|
||||
IsEnabled = true;
|
||||
|
||||
// Clears the background from the last previewer
|
||||
// The brush can only be drawn here because flashes will occur during resize
|
||||
PInvoke.SetClassLongPtr(containerHwnd, GET_CLASS_LONG_INDEX.GCLP_HBRBACKGROUND, containerBgBrush);
|
||||
PInvoke.UpdateWindow(containerHwnd);
|
||||
PInvoke.SetClassLongPtr(containerHwnd, GET_CLASS_LONG_INDEX.GCLP_HBRBACKGROUND, IntPtr.Zero);
|
||||
PInvoke.InvalidateRect(containerHwnd, (RECT*)null, true);
|
||||
PInvoke_FilePreviewer.SetClassLongPtr(containerHwnd, GET_CLASS_LONG_INDEX.GCLP_HBRBACKGROUND, containerBgBrush);
|
||||
PInvoke_FilePreviewer.UpdateWindow(containerHwnd);
|
||||
PInvoke_FilePreviewer.SetClassLongPtr(containerHwnd, GET_CLASS_LONG_INDEX.GCLP_HBRBACKGROUND, IntPtr.Zero);
|
||||
PInvoke_FilePreviewer.InvalidateRect(containerHwnd, (RECT*)null, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
PInvoke.ShowWindow(containerHwnd, SHOW_WINDOW_CMD.SW_HIDE);
|
||||
PInvoke_FilePreviewer.ShowWindow(containerHwnd, SHOW_WINDOW_CMD.SW_HIDE);
|
||||
IsEnabled = false;
|
||||
}
|
||||
}
|
||||
@@ -132,14 +131,14 @@ namespace Peek.FilePreviewer.Controls
|
||||
visuals.SetTextColor(fgColor);
|
||||
|
||||
// Changing the previewer colors might not always redraw itself
|
||||
PInvoke.InvalidateRect(containerHwnd, (RECT*)null, true);
|
||||
PInvoke_FilePreviewer.InvalidateRect(containerHwnd, (RECT*)null, true);
|
||||
}
|
||||
}
|
||||
|
||||
private LRESULT ContainerWndProc(HWND hWnd, uint msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
// Here for future use :)
|
||||
return PInvoke.DefWindowProc(hWnd, msg, wParam, lParam);
|
||||
return PInvoke_FilePreviewer.DefWindowProc(hWnd, msg, wParam, lParam);
|
||||
}
|
||||
|
||||
private void EnsureContainerHwndCreated()
|
||||
@@ -157,14 +156,14 @@ namespace Peek.FilePreviewer.Controls
|
||||
|
||||
fixed (char* pContainerClassName = "PeekShellPreviewHandlerContainer")
|
||||
{
|
||||
PInvoke.RegisterClass(new WNDCLASSW()
|
||||
PInvoke_FilePreviewer.RegisterClass(new WNDCLASSW()
|
||||
{
|
||||
lpfnWndProc = containerWndProc,
|
||||
lpszClassName = pContainerClassName,
|
||||
});
|
||||
|
||||
// Create the container window to host the preview handler
|
||||
containerHwnd = PInvoke.CreateWindowEx(
|
||||
containerHwnd = PInvoke_FilePreviewer.CreateWindowEx(
|
||||
WINDOW_EX_STYLE.WS_EX_LAYERED,
|
||||
pContainerClassName,
|
||||
null,
|
||||
@@ -178,7 +177,7 @@ namespace Peek.FilePreviewer.Controls
|
||||
HINSTANCE.Null);
|
||||
|
||||
// Allows the preview handlers to display properly
|
||||
PInvoke.SetLayeredWindowAttributes(containerHwnd, default, byte.MaxValue, LAYERED_WINDOW_ATTRIBUTES_FLAGS.LWA_ALPHA);
|
||||
PInvoke_FilePreviewer.SetLayeredWindowAttributes(containerHwnd, default, byte.MaxValue, LAYERED_WINDOW_ATTRIBUTES_FLAGS.LWA_ALPHA);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,12 +185,12 @@ namespace Peek.FilePreviewer.Controls
|
||||
{
|
||||
EnsureContainerHwndCreated();
|
||||
|
||||
var dpi = (float)PInvoke.GetDpiForWindow(containerHwnd) / 96;
|
||||
var dpi = (float)PInvoke_FilePreviewer.GetDpiForWindow(containerHwnd) / 96;
|
||||
|
||||
// Resize the container window
|
||||
PInvoke.SetWindowPos(
|
||||
PInvoke_FilePreviewer.SetWindowPos(
|
||||
containerHwnd,
|
||||
(HWND)0, // HWND_TOP
|
||||
(HWND)(nint)0, // HWND_TOP
|
||||
(int)(Math.Abs(args.EffectiveViewport.X) * dpi),
|
||||
(int)(Math.Abs(args.EffectiveViewport.Y) * dpi),
|
||||
(int)(ActualWidth * dpi),
|
||||
@@ -210,7 +209,7 @@ namespace Peek.FilePreviewer.Controls
|
||||
}
|
||||
|
||||
// Resizing the previewer might not always redraw itself
|
||||
PInvoke.InvalidateRect(containerHwnd, (RECT*)null, false);
|
||||
PInvoke_FilePreviewer.InvalidateRect(containerHwnd, (RECT*)null, false);
|
||||
}
|
||||
|
||||
private void UserControl_GotFocus(object sender, RoutedEventArgs e)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"$schema": "https://aka.ms/CsWin32.schema.json",
|
||||
"public": false
|
||||
"$schema": "https://aka.ms/CsWin32.schema.json",
|
||||
"public": false,
|
||||
"className": "PInvoke_FilePreviewer"
|
||||
}
|
||||
@@ -53,9 +53,9 @@ namespace Peek.FilePreviewer.Previewers.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
public void Seek(long dlibMove, STREAM_SEEK dwOrigin, [Optional] ulong* plibNewPosition)
|
||||
public void Seek(long dlibMove, SeekOrigin dwOrigin, [Optional] ulong* plibNewPosition)
|
||||
{
|
||||
long position = Stream.Seek(dlibMove, (SeekOrigin)dwOrigin);
|
||||
long position = Stream.Seek(dlibMove, dwOrigin);
|
||||
if (plibNewPosition != null)
|
||||
{
|
||||
*plibNewPosition = (ulong)position;
|
||||
@@ -82,7 +82,7 @@ namespace Peek.FilePreviewer.Previewers.Helpers
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void LockRegion(ulong libOffset, ulong cb, uint dwLockType)
|
||||
public void LockRegion(ulong libOffset, ulong cb, LOCKTYPE dwLockType)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
@@ -92,7 +92,7 @@ namespace Peek.FilePreviewer.Previewers.Helpers
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void Stat(STATSTG* pstatstg, uint grfStatFlag)
|
||||
public void Stat(STATSTG* pstatstg, STATFLAG grfStatFlag)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ namespace Peek.FilePreviewer.Previewers
|
||||
// TODO: Figure out how to get it to run in a low integrity level
|
||||
if (!HandlerFactories.TryGetValue(clsid, out var factory))
|
||||
{
|
||||
var hr = PInvoke.CoGetClassObject(clsid, CLSCTX.CLSCTX_LOCAL_SERVER, null, typeof(IClassFactory).GUID, out var pFactory);
|
||||
var hr = PInvoke_FilePreviewer.CoGetClassObject(clsid, CLSCTX.CLSCTX_LOCAL_SERVER, null, typeof(IClassFactory).GUID, out var pFactory);
|
||||
Marshal.ThrowExceptionForHR(hr);
|
||||
|
||||
// Storing the factory in memory helps makes the handlers load faster
|
||||
@@ -149,7 +149,7 @@ namespace Peek.FilePreviewer.Previewers
|
||||
}
|
||||
else if (previewHandler is IInitializeWithItem initWithItem)
|
||||
{
|
||||
var hr = PInvoke.SHCreateItemFromParsingName(FileItem.Path, null, typeof(IShellItem).GUID, out var item);
|
||||
var hr = PInvoke_FilePreviewer.SHCreateItemFromParsingName(FileItem.Path, null, typeof(IShellItem).GUID, out var item);
|
||||
Marshal.ThrowExceptionForHR(hr);
|
||||
|
||||
initWithItem.Initialize((IShellItem)item, STGM_READ);
|
||||
|
||||
@@ -48,21 +48,21 @@ namespace Peek.UI.Extensions
|
||||
|
||||
internal static HWND FindChildWindow(this HWND windowHandle, string className)
|
||||
{
|
||||
return PInvoke.FindWindowEx(windowHandle, HWND.Null, className, null);
|
||||
return PInvoke_PeekUI.FindWindowEx(windowHandle, HWND.Null, className, null);
|
||||
}
|
||||
|
||||
internal static Size GetMonitorSize(this HWND hwnd)
|
||||
{
|
||||
var monitor = PInvoke.MonitorFromWindow(hwnd, MONITOR_FROM_FLAGS.MONITOR_DEFAULTTONEAREST);
|
||||
var monitor = PInvoke_PeekUI.MonitorFromWindow(hwnd, MONITOR_FROM_FLAGS.MONITOR_DEFAULTTONEAREST);
|
||||
MONITORINFO info = default(MONITORINFO);
|
||||
info.cbSize = 40;
|
||||
PInvoke.GetMonitorInfo(monitor, ref info);
|
||||
PInvoke_PeekUI.GetMonitorInfo(monitor, ref info);
|
||||
return new Size(info.rcMonitor.Size.Width, info.rcMonitor.Size.Height);
|
||||
}
|
||||
|
||||
internal static double GetMonitorScale(this HWND hwnd)
|
||||
{
|
||||
var dpi = PInvoke.GetDpiForWindow(hwnd);
|
||||
var dpi = PInvoke_PeekUI.GetDpiForWindow(hwnd);
|
||||
var scalingFactor = dpi / 96d;
|
||||
return scalingFactor;
|
||||
}
|
||||
|
||||
@@ -28,12 +28,12 @@ namespace Peek.UI.Extensions
|
||||
|
||||
// If the window is maximized, restore to normal state before change its size
|
||||
var placement = default(WINDOWPLACEMENT);
|
||||
if (PInvoke.GetWindowPlacement(hwndToCenter, ref placement))
|
||||
if (PInvoke_PeekUI.GetWindowPlacement(hwndToCenter, ref placement))
|
||||
{
|
||||
if (placement.showCmd == SHOW_WINDOW_CMD.SW_MAXIMIZE)
|
||||
{
|
||||
placement.showCmd = SHOW_WINDOW_CMD.SW_SHOWNORMAL;
|
||||
if (!PInvoke.SetWindowPlacement(hwndToCenter, in placement))
|
||||
if (!PInvoke_PeekUI.SetWindowPlacement(hwndToCenter, in placement))
|
||||
{
|
||||
Logger.LogError($"SetWindowPlacement failed with error {Marshal.GetLastWin32Error()}");
|
||||
}
|
||||
@@ -44,12 +44,12 @@ namespace Peek.UI.Extensions
|
||||
Logger.LogError($"GetWindowPlacement failed with error {Marshal.GetLastWin32Error()}");
|
||||
}
|
||||
|
||||
var monitor = PInvoke.MonitorFromWindow(hwndDesktop, MONITOR_FROM_FLAGS.MONITOR_DEFAULTTONEAREST);
|
||||
var monitor = PInvoke_PeekUI.MonitorFromWindow(hwndDesktop, MONITOR_FROM_FLAGS.MONITOR_DEFAULTTONEAREST);
|
||||
MONITORINFO info = default(MONITORINFO);
|
||||
info.cbSize = 40;
|
||||
PInvoke.GetMonitorInfo(monitor, ref info);
|
||||
var dpi = PInvoke.GetDpiForWindow(new HWND(hwndDesktop));
|
||||
PInvoke.GetWindowRect(hwndToCenter, out RECT windowRect);
|
||||
PInvoke_PeekUI.GetMonitorInfo(monitor, ref info);
|
||||
var dpi = PInvoke_PeekUI.GetDpiForWindow(new HWND((nint)hwndDesktop));
|
||||
PInvoke_PeekUI.GetWindowRect(hwndToCenter, out RECT windowRect);
|
||||
var scalingFactor = dpi / 96d;
|
||||
var w = width.HasValue ? (int)(width * scalingFactor) : windowRect.right - windowRect.left;
|
||||
var h = height.HasValue ? (int)(height * scalingFactor) : windowRect.bottom - windowRect.top;
|
||||
@@ -63,7 +63,7 @@ namespace Peek.UI.Extensions
|
||||
|
||||
private static void SetWindowPosOrThrow(HWND hWnd, HWND hWndInsertAfter, int x, int y, int cx, int cy, SET_WINDOW_POS_FLAGS uFlags)
|
||||
{
|
||||
bool result = PInvoke.SetWindowPos(hWnd, hWndInsertAfter, x, y, cx, cy, uFlags);
|
||||
bool result = PInvoke_PeekUI.SetWindowPos(hWnd, hWndInsertAfter, x, y, cx, cy, uFlags);
|
||||
if (!result)
|
||||
{
|
||||
Marshal.ThrowExceptionForHR(Marshal.GetLastWin32Error());
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace Peek.UI.Helpers
|
||||
object? oNull2 = null;
|
||||
|
||||
var serviceProvider = (IServiceProvider)shellWindows.FindWindowSW(ref oNull1, ref oNull2, SWC_DESKTOP, out int pHWND, SWFO_NEEDDISPATCH);
|
||||
var shellBrowser = (IShellBrowser)serviceProvider.QueryService(PInvoke.SID_STopLevelBrowser, typeof(IShellBrowser).GUID);
|
||||
var shellBrowser = (IShellBrowser)serviceProvider.QueryService(PInvoke_PeekUI.SID_STopLevelBrowser, typeof(IShellBrowser).GUID);
|
||||
|
||||
IShellItemArray? shellItemArray = GetShellItemArray(shellBrowser, onlySelectedFiles);
|
||||
return shellItemArray;
|
||||
@@ -81,7 +81,7 @@ namespace Peek.UI.Helpers
|
||||
if (webBrowserApp.HWND == foregroundWindowHandle)
|
||||
{
|
||||
var serviceProvider = (IServiceProvider)webBrowserApp;
|
||||
var shellBrowser = (IShellBrowser)serviceProvider.QueryService(PInvoke.SID_STopLevelBrowser, typeof(IShellBrowser).GUID);
|
||||
var shellBrowser = (IShellBrowser)serviceProvider.QueryService(PInvoke_PeekUI.SID_STopLevelBrowser, typeof(IShellBrowser).GUID);
|
||||
shellBrowser.GetWindow(out IntPtr shellBrowserHandle);
|
||||
|
||||
if (activeTab == shellBrowserHandle)
|
||||
@@ -122,7 +122,7 @@ namespace Peek.UI.Helpers
|
||||
GUITHREADINFO guiThreadInfo = new() { cbSize = (uint)Marshal.SizeOf<GUITHREADINFO>() };
|
||||
|
||||
// Get information for the foreground thread
|
||||
if (PInvoke.GetGUIThreadInfo(0, ref guiThreadInfo))
|
||||
if (PInvoke_PeekUI.GetGUIThreadInfo(0, ref guiThreadInfo))
|
||||
{
|
||||
return guiThreadInfo.hwndActive == hwnd && (guiThreadInfo.flags & GUITHREADINFO_FLAGS.GUI_CARETBLINKING) != 0;
|
||||
}
|
||||
|
||||
5
src/modules/peek/Peek.UI/NativeMethods.json
Normal file
5
src/modules/peek/Peek.UI/NativeMethods.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"$schema": "https://aka.ms/CsWin32.schema.json",
|
||||
"public": false,
|
||||
"className": "PInvoke_PeekUI"
|
||||
}
|
||||
@@ -128,7 +128,7 @@ namespace Peek.UI
|
||||
{
|
||||
// Need to read the foreground HWND before activating Peek to avoid focus stealing
|
||||
// Foreground HWND must always be Explorer or Desktop
|
||||
var foregroundWindowHandle = Windows.Win32.PInvoke.GetForegroundWindow();
|
||||
var foregroundWindowHandle = Windows.Win32.PInvoke_PeekUI.GetForegroundWindow();
|
||||
|
||||
bool firstActivation = false;
|
||||
|
||||
|
||||
@@ -213,7 +213,7 @@ namespace Peek.UI
|
||||
/// <param name="e">PreviewSizeChangedArgs</param>
|
||||
private void FilePreviewer_PreviewSizeChanged(object sender, PreviewSizeChangedArgs e)
|
||||
{
|
||||
var foregroundWindowHandle = Windows.Win32.PInvoke.GetForegroundWindow();
|
||||
var foregroundWindowHandle = Windows.Win32.PInvoke_PeekUI.GetForegroundWindow();
|
||||
|
||||
var monitorSize = foregroundWindowHandle.GetMonitorSize();
|
||||
var monitorScale = foregroundWindowHandle.GetMonitorScale();
|
||||
|
||||
Reference in New Issue
Block a user