Skip to content

Fix PowerLauncher "Something went wrong" dialog on DWM composition change#47400

Closed
Copilot wants to merge 3 commits into
mainfrom
copilot/fix-reported-exception
Closed

Fix PowerLauncher "Something went wrong" dialog on DWM composition change#47400
Copilot wants to merge 3 commits into
mainfrom
copilot/fix-reported-exception

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 29, 2026

Summary of the Pull Request

When DWM composition changes (e.g., navigating wallpapers while Bing Desktop Wallpaper is active), PowerLauncher's SetSystemTheme triggers WPF's internal ThemeManager via the Application.Current.ThemeMode setter. WPF invokes theme-change handlers via reflection, wrapping any thrown COMException (0x80263001 DWM_E_COMPOSITIONDISABLED) in a TargetInvocationException. The existing recovery code only matched bare COMException, so the retry logic was bypassed and the "Report problem UI" appeared instead.

PR Checklist

  • 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
  • Dev docs: Added/updated
  • New binaries: Added on the required places
  • Documentation updated: If checked, please file a pull request on our docs repo and link it here: #xxx

Detailed Description of the Pull Request / Additional comments

Three call sites failed to account for TargetInvocationException wrapping:

  • ExceptionHelper.IsRecoverableDwmCompositionException — now unwraps TargetInvocationException before inspecting the inner COMException:

    // Before: returned false immediately for TargetInvocationException
    if (exception is not COMException comException) { return false; }
    
    // After: unwrap first
    if (exception is TargetInvocationException && exception.InnerException != null)
        exception = exception.InnerException;
    if (exception is not COMException comException) { return false; }
  • ErrorReporting.HandleException — removed the silent e as COMException pre-cast (always null for a TargetInvocationException) and passes e directly to IsRecoverableDwmCompositionException.

  • ThemeManager.UpdateThemeWithRetryAsync — changed catch (COMException ex) when (...) to catch (Exception ex) when (IsRecoverableDwmCompositionException(ex)) so the retry loop fires for wrapped exceptions too. The warning log now extracts and uses the inner exception's HResult when the outer is a TargetInvocationException, ensuring accurate HRESULT values (0x80263001/0xD0000701) are recorded rather than the generic TargetInvocationException HRESULT (0x80131604). Removed now-unused using System.Runtime.InteropServices.

Validation Steps Performed

  • Added ExceptionHelperTest.cs with MSTest coverage for:
    • null and non-COM exceptions (return false)
    • Direct COMException (0x80263001) (returns true)
    • TargetInvocationException wrapping COMException (0x80263001) (returns true)
    • TargetInvocationException wrapping COMException (0xD0000701) from PresentationFramework source (returns true)
    • TargetInvocationException wrapping a COMException with a DwmCompositionChanged stack trace (returns true)
    • TargetInvocationException wrapping an unrelated COMException or non-COM exception (returns false)
    • TargetInvocationException with null inner exception (returns false)
    • [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:...")] applied on the test class to match the production helper pattern and satisfy TreatWarningsAsErrors

Copilot AI linked an issue Apr 29, 2026 that may be closed by this pull request
…on errors in PowerLauncher ThemeManager

Agent-Logs-Url: https://github.com/microsoft/PowerToys/sessions/5d22a198-4d58-47bc-95b1-60c65d651a58

Co-authored-by: MuyuanMS <116717757+MuyuanMS@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix reported exception in PowerToys when using bing desktop wallpaper Fix PowerLauncher "Something went wrong" dialog on DWM composition change Apr 29, 2026
Copilot AI requested a review from MuyuanMS April 29, 2026 09:40
@niels9001 niels9001 added the Product-PowerToys Run Improved app launch PT Run (Win+R) Window label Apr 29, 2026
@MuyuanMS MuyuanMS requested a review from Copilot May 14, 2026 03:09
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates PowerLauncher’s DWM composition exception recovery so WPF TargetInvocationException wrappers are recognized instead of showing the “Something went wrong” report UI.

Changes:

  • Unwraps TargetInvocationException in ExceptionHelper.
  • Passes original exceptions into recovery detection from error reporting.
  • Broadens theme retry handling and adds unit tests for recoverable DWM exception detection.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
src/modules/launcher/PowerLauncher/Helper/ExceptionHelper.cs Adds unwrapping for reflection-wrapped exceptions before COM HRESULT checks.
src/modules/launcher/PowerLauncher/Helper/ErrorReporting.cs Passes the full exception to recoverable DWM detection.
src/modules/launcher/PowerLauncher/Helper/ThemeManager.cs Allows retry logic to catch wrapped recoverable exceptions.
src/modules/launcher/Wox.Test/ExceptionHelperTest.cs Adds MSTest coverage for direct and wrapped exception cases.
Comments suppressed due to low confidence (1)

src/modules/launcher/Wox.Test/ExceptionHelperTest.cs:18

  • These two constants are never referenced by the tests. With the repository-wide TreatWarningsAsErrors setting, the unused private fields can break the Wox.Test build; either remove them or add the intended coverage for the STATUS_MESSAGE_LOST_HR/PresentationFramework path.
    private const int STATUS_MESSAGE_LOST_HR = unchecked((int)0xD0000701);
    private const string PresentationFrameworkSource = "PresentationFramework";

Comment on lines +16 to +17
private const int DWM_E_COMPOSITIONDISABLED = unchecked((int)0x80263001);
private const int STATUS_MESSAGE_LOST_HR = unchecked((int)0xD0000701);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: Added SA1310 suppression attribute on the test class, matching the pattern used in production code.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "Win32 naming conventions")] on the test class, matching the pattern used in ExceptionHelper.cs. Addressed in commit 0c5d723.

Comment on lines +28 to +30
if (exception is TargetInvocationException && exception.InnerException != null)
{
exception = exception.InnerException;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: Added test cases for wrapped COMException with 0xD0000701 HResult and PresentationFramework source, for the DwmCompositionChanged stack-trace path, and retained wrapped non-recoverable coverage.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added two more test cases in commit 0c5d723:

  • IsRecoverableDwmCompositionException_TargetInvocationException_WrappingMessageLostFromPresentationFramework_ReturnsTrue — covers 0xD0000701 + PresentationFramework source wrapped in TargetInvocationException
  • IsRecoverableDwmCompositionException_TargetInvocationException_WrappingDwmCompositionChangedStackTrace_ReturnsTrue — covers the stack-trace-based path, using a helper method named ThrowDwmCompositionChangedComException (with [MethodImpl(MethodImplOptions.NoInlining)] to preserve the frame name in the stack trace)

return;
}
catch (COMException ex) when (ExceptionHelper.IsRecoverableDwmCompositionException(ex))
catch (Exception ex) when (ExceptionHelper.IsRecoverableDwmCompositionException(ex))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: Now extracting and logging the inner exception's HResult when the outer is a TargetInvocationException wrapper.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in commit 0c5d723. Now extracts the inner exception's HResult when a TargetInvocationException wrapper is present:

var logHResult = (ex is System.Reflection.TargetInvocationException tie && tie.InnerException != null)
    ? tie.InnerException.HResult
    : ex.HResult;
```This ensures the warning logs `0x80263001`/`0xD0000701` (the actual DWM error) rather than `0x80131604` (TargetInvocationException's generic HRESULT).

…esult

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@MuyuanMS
Copy link
Copy Markdown
Contributor

@copilot please re-review the latest changes.

@niels9001
Copy link
Copy Markdown
Collaborator

Same as the other DWM PR. This issue has been surpressed in 0.97, and the actual fix will be part of an upcoming .NET release

@niels9001 niels9001 closed this May 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Product-PowerToys Run Improved app launch PT Run (Win+R) Window

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Reported exception

4 participants