mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-07-08 19:40:01 +02:00
Compare commits
3 Commits
copilot/fi
...
copilot/fi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f822522bc | ||
|
|
ee04f97d13 | ||
|
|
ca96bee958 |
@@ -211,23 +211,15 @@ void Highlighter::AddDrawingPoint(MouseButton button)
|
||||
else
|
||||
{
|
||||
circleShape.FillBrush(m_compositor.CreateColorBrush(m_alwaysColor));
|
||||
// Remove the previous always-pointer shape from the collection before replacing it.
|
||||
// It has already been made transparent by ClearDrawingPoint(), so removing it
|
||||
// causes no visual glitch and prevents an unbounded accumulation of shape objects.
|
||||
if (m_alwaysPointer)
|
||||
{
|
||||
uint32_t index;
|
||||
if (m_shape.Shapes().IndexOf(m_alwaysPointer, index))
|
||||
{
|
||||
m_shape.Shapes().RemoveAt(index);
|
||||
}
|
||||
}
|
||||
m_alwaysPointer = circleShape;
|
||||
}
|
||||
}
|
||||
|
||||
m_shape.Shapes().Append(circleShape);
|
||||
|
||||
// TODO: We're leaking shapes for long drawing sessions.
|
||||
// Perhaps add a task to the Dispatcher every X circles to clean up.
|
||||
|
||||
// Get back on top in case other Window is now the topmost.
|
||||
// HACK: Draw with 1 pixel off. Otherwise, Windows glitches the task bar transparency when a transparent window fill the whole screen.
|
||||
SetWindowPos(m_hwnd, HWND_TOPMOST, GetSystemMetrics(SM_XVIRTUALSCREEN) + 1, GetSystemMetrics(SM_YVIRTUALSCREEN) + 1, GetSystemMetrics(SM_CXVIRTUALSCREEN) - 2, GetSystemMetrics(SM_CYVIRTUALSCREEN) - 2, 0);
|
||||
@@ -297,19 +289,7 @@ void Highlighter::StartDrawingPointFading(MouseButton button)
|
||||
animation.Duration(timeSpan(duration));
|
||||
animation.DelayTime(timeSpan(delay));
|
||||
|
||||
// Use a scoped batch to detect when the fade animation completes, then remove
|
||||
// the fully-transparent shape from the collection so it does not leak.
|
||||
auto batch = m_compositor.CreateScopedBatch(winrt::CompositionBatchTypes::Animation);
|
||||
circleShape.FillBrush().StartAnimation(L"Color", animation);
|
||||
batch.End();
|
||||
auto shapes = m_shape.Shapes();
|
||||
batch.Completed([shape = circleShape, shapes](winrt::IInspectable const&, winrt::CompositionBatchCompletedEventArgs const&) {
|
||||
uint32_t index;
|
||||
if (shapes.IndexOf(shape, index))
|
||||
{
|
||||
shapes.RemoveAt(index);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Highlighter::ClearDrawingPoint()
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace PowerLauncher.Helper
|
||||
// Many bug reports because users see the "Report problem UI" after "the" crash with System.Runtime.InteropServices.COMException 0xD0000701 or 0x80263001.
|
||||
// However, displaying this "Report problem UI" during WPF crashes, especially when DWM composition is changing, is not ideal; some users reported it hangs for up to a minute before the "Report problem UI" appears.
|
||||
// This change modifies the behavior to log the exception instead of showing the "Report problem UI".
|
||||
if (ExceptionHelper.IsRecoverableDwmCompositionException(e as System.Runtime.InteropServices.COMException))
|
||||
if (ExceptionHelper.IsRecoverableDwmCompositionException(e))
|
||||
{
|
||||
var logger = LogManager.GetLogger(LoggerName);
|
||||
logger.Error($"From {(isNotUIThread ? "non" : string.Empty)} UI thread's exception: {ExceptionFormatter.FormatException(e)}");
|
||||
|
||||
@@ -22,6 +22,13 @@ namespace PowerLauncher.Helper
|
||||
/// </summary>
|
||||
internal static bool IsRecoverableDwmCompositionException(Exception exception)
|
||||
{
|
||||
// Unwrap TargetInvocationException: WPF raises theme-change events via reflection,
|
||||
// so DWM COMExceptions (0x80263001) surface as TargetInvocationException.InnerException.
|
||||
if (exception is System.Reflection.TargetInvocationException tie && tie.InnerException != null)
|
||||
{
|
||||
return IsRecoverableDwmCompositionException(tie.InnerException);
|
||||
}
|
||||
|
||||
if (exception is not COMException comException)
|
||||
{
|
||||
return false;
|
||||
|
||||
@@ -160,12 +160,14 @@ namespace PowerLauncher.Helper
|
||||
|
||||
return;
|
||||
}
|
||||
catch (COMException ex) when (ExceptionHelper.IsRecoverableDwmCompositionException(ex))
|
||||
catch (Exception ex) when (ExceptionHelper.IsRecoverableDwmCompositionException(ex))
|
||||
{
|
||||
// Unwrap TargetInvocationException to extract the DWM HRESULT for logging.
|
||||
var dwmEx = ex is System.Reflection.TargetInvocationException { InnerException: COMException inner } ? inner : ex as COMException;
|
||||
switch (attempt)
|
||||
{
|
||||
case 1:
|
||||
Log.Warn($"Desktop composition is disabled (HRESULT: 0x{ex.HResult:X}). Scheduling retries for theme update.", typeof(ThemeManager));
|
||||
Log.Warn($"Desktop composition is disabled (HRESULT: 0x{dwmEx?.HResult ?? ex.HResult:X}). Scheduling retries for theme update.", typeof(ThemeManager));
|
||||
delayMs = InitialDelayMs;
|
||||
break;
|
||||
case < maxAttempts:
|
||||
|
||||
128
src/modules/launcher/Wox.Test/ExceptionHelperTest.cs
Normal file
128
src/modules/launcher/Wox.Test/ExceptionHelperTest.cs
Normal file
@@ -0,0 +1,128 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using PowerLauncher.Helper;
|
||||
|
||||
namespace Wox.Test;
|
||||
|
||||
[TestClass]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2201:Do not raise reserved exception types", Justification = "Test code intentionally constructs COMException instances to verify exception-handling logic")]
|
||||
public class ExceptionHelperTest
|
||||
{
|
||||
private const int DwmCompositionDisabledHResult = unchecked((int)0x80263001);
|
||||
private const int StatusMessageLostHResult = unchecked((int)0xD0000701);
|
||||
private const int UnrelatedHResult = unchecked((int)0x80004005); // E_FAIL
|
||||
|
||||
/// <summary>
|
||||
/// A direct <see cref="COMException"/> with HRESULT 0x80263001 (DWM_E_COMPOSITIONDISABLED)
|
||||
/// must be identified as recoverable.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void IsRecoverableDwmCompositionException_DirectCOMException_ReturnsTrue()
|
||||
{
|
||||
var ex = new COMException("Desktop composition is disabled", DwmCompositionDisabledHResult);
|
||||
Assert.IsTrue(ExceptionHelper.IsRecoverableDwmCompositionException(ex));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="TargetInvocationException"/> wrapping the DWM <see cref="COMException"/> must
|
||||
/// be identified as recoverable. WPF raises theme-change events via reflection, so the
|
||||
/// 0x80263001 <see cref="COMException"/> surfaces wrapped in a
|
||||
/// <see cref="TargetInvocationException"/>.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void IsRecoverableDwmCompositionException_TargetInvocationWrappingDwmCOMException_ReturnsTrue()
|
||||
{
|
||||
var inner = new COMException("Desktop composition is disabled", DwmCompositionDisabledHResult);
|
||||
var ex = new TargetInvocationException(inner);
|
||||
Assert.IsTrue(ExceptionHelper.IsRecoverableDwmCompositionException(ex));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="TargetInvocationException"/> wrapping an unrelated exception must NOT be
|
||||
/// identified as recoverable.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void IsRecoverableDwmCompositionException_TargetInvocationWrappingUnrelatedException_ReturnsFalse()
|
||||
{
|
||||
var inner = new COMException("Some other COM error", UnrelatedHResult);
|
||||
var ex = new TargetInvocationException(inner);
|
||||
Assert.IsFalse(ExceptionHelper.IsRecoverableDwmCompositionException(ex));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="TargetInvocationException"/> with a null inner exception must NOT be
|
||||
/// identified as recoverable.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void IsRecoverableDwmCompositionException_TargetInvocationWithNullInner_ReturnsFalse()
|
||||
{
|
||||
var ex = new TargetInvocationException(null);
|
||||
Assert.IsFalse(ExceptionHelper.IsRecoverableDwmCompositionException(ex));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="COMException"/> with HRESULT 0xD0000701 (STATUS_MESSAGE_LOST) and Source
|
||||
/// "PresentationFramework" must be identified as recoverable.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void IsRecoverableDwmCompositionException_StatusMessageLostFromPresentationFramework_ReturnsTrue()
|
||||
{
|
||||
var ex = new COMException("Status message lost", StatusMessageLostHResult)
|
||||
{
|
||||
Source = "PresentationFramework",
|
||||
};
|
||||
|
||||
Assert.IsTrue(ExceptionHelper.IsRecoverableDwmCompositionException(ex));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="COMException"/> with HRESULT 0xD0000701 (STATUS_MESSAGE_LOST) but a
|
||||
/// non-PresentationFramework source must NOT be identified as recoverable.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void IsRecoverableDwmCompositionException_StatusMessageLostFromOtherSource_ReturnsFalse()
|
||||
{
|
||||
var ex = new COMException("Status message lost", StatusMessageLostHResult)
|
||||
{
|
||||
Source = "SomeOtherAssembly",
|
||||
};
|
||||
|
||||
Assert.IsFalse(ExceptionHelper.IsRecoverableDwmCompositionException(ex));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="COMException"/> with an unrelated HRESULT must NOT be identified as
|
||||
/// recoverable.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void IsRecoverableDwmCompositionException_UnrelatedCOMException_ReturnsFalse()
|
||||
{
|
||||
var ex = new COMException("Some other COM error", UnrelatedHResult);
|
||||
Assert.IsFalse(ExceptionHelper.IsRecoverableDwmCompositionException(ex));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A non-COM exception must NOT be identified as recoverable.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void IsRecoverableDwmCompositionException_NonCOMException_ReturnsFalse()
|
||||
{
|
||||
var ex = new InvalidOperationException("Not a COM exception");
|
||||
Assert.IsFalse(ExceptionHelper.IsRecoverableDwmCompositionException(ex));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Null input must NOT be identified as recoverable.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void IsRecoverableDwmCompositionException_NullException_ReturnsFalse()
|
||||
{
|
||||
Assert.IsFalse(ExceptionHelper.IsRecoverableDwmCompositionException(null));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user