mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-08-29 10:09:43 +02:00
[Keyboard Manager] Build the WinUI 3 editor self-contained to fix launch crash (0xC0000409) (#49524)
## Summary of the Pull Request `PowerToys.KeyboardManagerEditorUI.exe` fail-fasts with `0xC0000409` (`STATUS_STACK_BUFFER_OVERRUN`) during `MainWindow` construction, so the new Keyboard Manager editor never opens. `KeyboardManagerEditorUI.csproj` was the **only WinUI 3 executable in the repo missing `<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>`**, so it was built framework-package-dependent and mixed two Windows App SDK provenances in one process. ## PR Checklist - [x] Closes: #49399 - [ ] **Communication:** I've discussed this with core contributors already. If the work hasn't been agreed, this work might be rejected - [ ] **Tests:** Added/updated and all pass - [ ] **Localization:** All end-user-facing strings can be localized <!-- no user-facing strings added --> - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places <!-- no new binaries --> - [ ] **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 ### Root cause The reporter's WinDbg capture shows a first-chance `Core::ApiException` carrying `0x800704DF` (`ERROR_ALREADY_INITIALIZED`): ``` MainWindow.SetTitleBar -> Microsoft.UI.Xaml.Window.set_ExtendsContentIntoTitleBar -> Microsoft.UI.Input!InputNonClientPointerSourceWinRTStatics::GetForWindowIdHelper -> Microsoft.UI.Windowing.Core!RegisterWindowFeature -> Core::NamedApiObject::Init -> Core::ApiException ``` `ExtendsContentIntoTitleBar` is the **site**, not the cause — it is simply the first user statement that crosses XAML -> Windowing -> Input. `microsoft.windowsappsdk.foundation/*/buildTransitive/Microsoft.WindowsAppSDK.BootstrapCommon.targets` turns the bootstrapper on precisely when this project's shape is hit: ```xml <PropertyGroup Condition="'$(WindowsAppSdkBootstrapInitialize)'=='' and '$(WindowsAppSDKSelfContained)'!='true' and '$(WindowsPackageType)'=='None' and ('$(OutputType)'=='Exe' or '$(OutputType)'=='Winexe')"> <WindowsAppSdkBootstrapInitialize>true</WindowsAppSdkBootstrapInitialize> </PropertyGroup> ``` That compiles in `MddBootstrapAutoInitializer.cs`, which joins the machine-wide `Microsoft.WindowsAppRuntime` MSIX framework package to the process package graph before `Main`. Meanwhile the exe's own directory — `WinUI3Apps` — is first in the Win32 DLL search order and already contains a complete app-local Windows App SDK payload, deployed there by the other 14 self-contained apps. One process, two Windows App SDK provenances, and the one-time feature-type registration in `Microsoft.UI.Input` collides. The omission was easy to miss: the project imports `src\Common.SelfContained.props`, whose name suggests it covers this — but it only sets the **.NET** `<SelfContained>` property, which is unrelated. This also explains why the reporter could not shake it off: the framework package is machine state, so uninstall/reinstall and wiping `%LOCALAPPDATA%\Microsoft\PowerToys` change nothing. The classic C++ editor is unaffected because it uses WinUI 2 XAML Islands and ships no Windows App SDK at all. `PowerToys.Settings.exe` ran healthily in the same elevated session on the same day while doing strictly more title-bar work (it sets `ExtendsContentIntoTitleBar` twice and drives `InputNonClientPointerSource.GetForWindowId` on every `SizeChanged`) — because it *is* self-contained. ### Two additional defects fixed Both were found while investigating why the crash left no diagnostics at all: 1. **`App.xaml.cs` initialized the logger via fire-and-forget `Task.Run`** — the only one of ~30 `Logger.InitializeLogger` call sites in the repo to do so. That races window creation, and `Logger` has no buffering or replay (`Trace.WriteLine` straight through, listener attached in `InitializeLogger`), so anything logged before the listener is attached is lost permanently. This is why the user's bug report bundle contains a `WinUI3Editor` log for the day it worked and **no log file at all** for the day it crashed. Made synchronous, ordered to match `FileLocksmithXAML/App.xaml.cs`, plus a log line before the window is constructed. 2. **`MainWindow` never called `WindowHelpers.ForceTopBorder1PixelInsetOnWindows10`**, unlike the other PowerToys WinUI 3 module windows (AdvancedPaste, EnvironmentVariables, FileLocksmith, Hosts, ImageResizer, Peek, RegistryPreview, Settings). It is a no-op on Windows 11 and fixes the black top border from microsoft/microsoft-ui-xaml#6901 on Windows 10 — the OS this issue was reported against. Happy to drop this hunk if reviewers prefer a minimal diff. Deliberately **not** done: wrapping `new MainWindow()` in `try/catch`. The failure is a WIL `RaiseFailFastException`, which managed code cannot intercept; and swallowing managed exceptions there would leave a windowless zombie process still holding the runner's `m_hEditorProcess` handle, making the runner take its "editor already open" branch and breaking every subsequent launch. That is why #49477 cannot work. ### Repo-wide audit All 15 WinUI 3 executables (`UseWinUI=true` and `OutputType=WinExe`) were checked. **KeyboardManagerEditorUI was the only one missing the property**; the other 14 already set it. Also verified as correct and unchanged: the 7 WinUI class libraries (property is app-level, N/A), `runner.vcxproj` and `PowerRenameUI.vcxproj` (native exes, both already `true`), and `PowerToys.MeasureToolCore.vcxproj` / `FindMyMouse.vcxproj` (deliberately `false` — in-proc module DLLs whose host already establishes the self-contained context). There is no repo-level default or build guard for this property; it is hand-copied into 17 project files, which is how the hole opened. A `Directory.Build.targets` guard that errors when an unpackaged Windows App SDK executable omits it would prevent recurrence, but it would catch nothing today, so I left it out of this PR to keep the diff scoped. Happy to open it separately. ## Validation Steps Performed Built `KeyboardManagerEditorUI.csproj` (x64/Debug) and diffed the build output before and after the change: | | before | after | `PowerToys.Hosts.exe` (reference) | |---|---|---|---| | `obj\x64\Debug\Manifests\` (created only by `CreateWinRTRegistration`) | absent | **present** | present | | `activatableClass` registrations embedded in the exe | **0** | **1912** | 1912 | | assembly references `Microsoft.WindowsAppRuntime.Bootstrap.Net` / `MddBootstrap` | **yes** | **no** | no | The editor now resolves every `Microsoft.UI.*` activation app-locally through registration-free WinRT instead of the machine framework package, which removes the mixing hazard. **Not yet validated on Windows 10.** I do not have a Windows 10 19045 machine, so the crash repro itself is unverified end-to-end. The deployment-mode change is verified from build output as above; confirmation from the issue reporter would be valuable. A useful discriminator if anyone has the reporter's ProcDump dump: `lm v m Microsoft.UI.*` — if `Microsoft.UI.Input.dll` is listed twice from two different paths, the mechanism is confirmed directly. Co-authored-by: Yu Leng (from Dev Box) <yuleng@microsoft.com> Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,11 @@
|
||||
<ApplicationIcon>Assets\KeyboardManagerEditor\Keyboard.ico</ApplicationIcon>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
<!-- Required alongside WindowsPackageType=None. Without it the Windows App SDK targets compile in the
|
||||
MddBootstrap auto-initializer, and this app binds to the machine-wide Microsoft.WindowsAppRuntime
|
||||
framework package instead of the app-local payload PowerToys ships in WinUI3Apps. Note that
|
||||
Common.SelfContained.props above sets the unrelated .NET <SelfContained> property. See #49399. -->
|
||||
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
|
||||
<AssemblyName>PowerToys.KeyboardManagerEditorUI</AssemblyName>
|
||||
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)\WinUI3Apps</OutputPath>
|
||||
<!-- MRT from windows app sdk will search for a pri file with the same name of the module before defaulting to resources.pri -->
|
||||
|
||||
@@ -7,7 +7,6 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using System.Threading.Tasks;
|
||||
using KeyboardManagerEditorUI.Helpers;
|
||||
using KeyboardManagerEditorUI.Settings;
|
||||
using ManagedCommon;
|
||||
@@ -40,12 +39,12 @@ namespace KeyboardManagerEditorUI
|
||||
/// </summary>
|
||||
public App()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
// Initialize the logger synchronously, before anything else can log. Doing this on a
|
||||
// background task races window creation, so a failure during startup leaves no trace
|
||||
// at all - which is exactly what happened in #49399.
|
||||
Logger.InitializeLogger("\\Keyboard Manager\\WinUI3Editor\\Logs");
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
Logger.InitializeLogger("\\Keyboard Manager\\WinUI3Editor\\Logs");
|
||||
});
|
||||
this.InitializeComponent();
|
||||
|
||||
UnhandledException += App_UnhandledException;
|
||||
|
||||
@@ -58,6 +57,8 @@ namespace KeyboardManagerEditorUI
|
||||
/// <param name="args">Details about the launch request and process.</param>
|
||||
protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
|
||||
{
|
||||
Logger.LogInfo("keyboard-manager WinUI3 editor is creating its main window");
|
||||
|
||||
MainWindow = new MainWindow();
|
||||
|
||||
MainWindow.DispatcherQueue.TryEnqueue(() =>
|
||||
|
||||
@@ -10,6 +10,7 @@ using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using KeyboardManagerEditorUI.Helpers;
|
||||
using ManagedCommon;
|
||||
using Microsoft.UI;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
@@ -41,6 +42,7 @@ namespace KeyboardManagerEditorUI
|
||||
this.SetIcon(@"Assets\KeyboardManagerEditor\Keyboard.ico");
|
||||
this.SetTitleBar(titleBar);
|
||||
Title = "Keyboard Manager";
|
||||
WindowHelpers.ForceTopBorder1PixelInsetOnWindows10(this.GetWindowHandle());
|
||||
}
|
||||
|
||||
private void MainWindow_Activated(object sender, WindowActivatedEventArgs args)
|
||||
|
||||
Reference in New Issue
Block a user