From 57d32bcb6b8f421ffbd18e5b444ca02ee34f7a02 Mon Sep 17 00:00:00 2001 From: Niels Laute Date: Fri, 7 Aug 2026 17:05:47 +0200 Subject: [PATCH] Fix Keyboard Manager editor file picker not opening when elevated (#48876) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Fixes #48845. In the new WinUI 3 Keyboard Manager editor, clicking the **browse icon** to select a program path (or "start in" folder) for the *Run Program* action did nothing — no dialog appeared. ### Root cause The editor (`PowerToys.KeyboardManagerEditorUI.exe`) is launched by the Keyboard Manager module DLL via `ShellExecuteExW` from inside the PowerToys runner (`src/modules/keyboardmanager/dll/dllmain.cpp`). When PowerToys runs elevated, the editor **inherits that elevation**. The browse buttons used the legacy **`Windows.Storage.Pickers`** (`FileOpenPicker` / `FolderPicker` + `InitializeWithWindow`). Those pickers activate through the UWP runtime broker, which fails with `E_ACCESSDENIED` in an elevated process. The handlers were `async void` with no `try/catch`, so the exception was swallowed and no dialog ever opened. Typing/pasting a path into the field still worked — matching the bug report. ### Fix Switch both handlers to the Windows App SDK **`Microsoft.Windows.Storage.Pickers`** API, constructed with a `WindowId`. Those pickers are a thin wrapper over the in-process Win32 Common Item Dialog (`IFileOpenDialog`, `CLSCTX_INPROC_SERVER`) and work correctly in elevated processes — the same mechanism already used elsewhere in PowerToys (e.g. Settings UI `IFileDialog`/`GetOpenFileName`, and CmdPal which already uses this exact namespace). Also wrapped the handlers in `try/catch` with `Logger.LogError` so any future failure is logged instead of silently swallowed. ### Verification - Built `KeyboardManagerEditorUI.csproj` (Release / x64) with all native dependencies — exit code 0. - Confirmed against the Windows App SDK source that `Microsoft.Windows.Storage.Pickers.FileOpenPicker` uses `create_instance(CLSID_FileOpenDialog, CLSCTX_INPROC_SERVER)` and `dialog->Show(hwnd)`, i.e. the elevation-safe in-process dialog. ### Notes / out of scope The report also mentions some apps (e.g. `visio.exe`) not launching while others (`winword.exe`) do. That's a separate issue in the launch path (`run_non_elevated` uses `CreateProcessW`, which ignores registry App Paths / shell activation, unlike `ShellExecute` used by the *Open URI* action) and is **not** addressed here. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Copilot-Session: 1f00def4-e790-4071-96c6-a81c9c2adba5 --- .../Controls/UnifiedMappingControl.xaml.cs | 68 ++++++++++++------- 1 file changed, 43 insertions(+), 25 deletions(-) diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorUI/Controls/UnifiedMappingControl.xaml.cs b/src/modules/keyboardmanager/KeyboardManagerEditorUI/Controls/UnifiedMappingControl.xaml.cs index ebea842d7c..55fcd86d59 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorUI/Controls/UnifiedMappingControl.xaml.cs +++ b/src/modules/keyboardmanager/KeyboardManagerEditorUI/Controls/UnifiedMappingControl.xaml.cs @@ -8,10 +8,10 @@ using System.Collections.ObjectModel; using System.Linq; using KeyboardManagerEditorUI.Helpers; using KeyboardManagerEditorUI.Interop; +using ManagedCommon; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; -using Windows.Storage; -using Windows.Storage.Pickers; +using Microsoft.Windows.Storage.Pickers; using Windows.System; using WinRT.Interop; using static KeyboardManagerEditorUI.Interop.ShortcutKeyMapping; @@ -632,36 +632,54 @@ namespace KeyboardManagerEditorUI.Controls private async void ProgramPathSelectButton_Click(object sender, RoutedEventArgs e) { - var picker = new FileOpenPicker(); - - var hwnd = WindowNative.GetWindowHandle(App.MainWindow); - InitializeWithWindow.Initialize(picker, hwnd); - - picker.FileTypeFilter.Add(".exe"); - - StorageFile file = await picker.PickSingleFileAsync(); - - if (file != null) + try { - ProgramPathInput.Text = file.Path; - RaiseValidationStateChanged(); + var hwnd = WindowNative.GetWindowHandle(App.MainWindow); + var windowId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(hwnd); + + var picker = new FileOpenPicker(windowId) + { + SuggestedStartLocation = PickerLocationId.ComputerFolder, + }; + + picker.FileTypeFilter.Add(".exe"); + + var file = await picker.PickSingleFileAsync(); + + if (file != null) + { + ProgramPathInput.Text = file.Path; + RaiseValidationStateChanged(); + } + } + catch (Exception ex) + { + Logger.LogError("Failed to pick program path", ex); } } private async void StartInSelectButton_Click(object sender, RoutedEventArgs e) { - var picker = new FolderPicker(); - - var hwnd = WindowNative.GetWindowHandle(App.MainWindow); - InitializeWithWindow.Initialize(picker, hwnd); - - picker.FileTypeFilter.Add("*"); - - StorageFolder folder = await picker.PickSingleFolderAsync(); - - if (folder != null) + try { - StartInPathInput.Text = folder.Path; + var hwnd = WindowNative.GetWindowHandle(App.MainWindow); + var windowId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(hwnd); + + var picker = new FolderPicker(windowId) + { + SuggestedStartLocation = PickerLocationId.ComputerFolder, + }; + + var folder = await picker.PickSingleFolderAsync(); + + if (folder != null) + { + StartInPathInput.Text = folder.Path; + } + } + catch (Exception ex) + { + Logger.LogError("Failed to pick start-in folder", ex); } }