Skip to content

Conversation

@harshit7962
Copy link
Member

@harshit7962 harshit7962 commented Nov 25, 2025

Description

The following changes aim to fix certain repetitive crashes due to win32 DWM API. The idea here is to re-try extending content into client area which might result in a crash for about 2 times and if that still results in an exception, we log the same.

cc:/ @himgoyalmicro

Customer Impact

Possibly lesser crashes in fluent and might help in fluent adoption

Regression

Yes

Testing

Local Build Pass
Sample Application Testing
Tested with a runtime application

Risk

Low, things are very much in parity with previous impl.

Microsoft Reviewers: Open in CodeFlow

Copilot AI review requested due to automatic review settings November 25, 2025 09:31
@harshit7962 harshit7962 requested a review from a team as a code owner November 25, 2025 09:31
@dotnet-policy-service dotnet-policy-service bot added the PR metadata: Label to tag PRs, to facilitate with triage label Nov 25, 2025
Copilot finished reviewing on behalf of harshit7962 November 25, 2025 09:34

public static int DwmExtendFrameIntoClientArea(IntPtr hwnd, ref MARGINS pMarInset)
{
int retryCount = 2;

Choose a reason for hiding this comment

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

Is there no flag which we can add to enable/disable this functionality ? (VelocityKey, Containment ? )

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah it makes sense. We can use AppContextSwitch for containment. Will make the changes.

Copy link
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 implements a retry mechanism for the DwmExtendFrameIntoClientArea Win32 API call to address repetitive crashes in WPF's Fluent theme implementation. The change wraps the previously direct P/Invoke declaration with error handling that attempts the operation up to 2 times with a 100ms delay between attempts.

Key changes:

  • Converted direct P/Invoke method to private _DwmExtendFrameIntoClientArea and added public wrapper with retry logic
  • Catches COMException from DWM API failures and returns error HRESULT instead of crashing
  • Added System.Threading namespace import for Thread.Sleep functionality

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

// DWM composition may not be available or the operation failed
// Return the error code rather than crashing the application
retryCount--;
if(retryCount == 0)
Copy link

Copilot AI Nov 25, 2025

Choose a reason for hiding this comment

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

Missing space after 'if' keyword. Should be if (retryCount == 0) for consistency with C# formatting conventions.

Suggested change
if(retryCount == 0)
if (retryCount == 0)

Copilot uses AI. Check for mistakes.

public static int DwmExtendFrameIntoClientArea(IntPtr hwnd, ref MARGINS pMarInset)
{
int retryCount = 2;
Copy link

Copilot AI Nov 25, 2025

Choose a reason for hiding this comment

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

[nitpick] The retry logic may be confusing. With retryCount = 2, the code makes at most 2 total attempts (1 initial + 1 retry), not 2 retries as the PR description suggests. If the intention is to retry twice after the initial failure (3 total attempts), retryCount should be initialized to 3. Consider clarifying the intended behavior or adding a comment explaining that retryCount represents total attempts.

Copilot uses AI. Check for mistakes.
return ex.HResult;
}
System.Diagnostics.Trace.WriteLine($"DwmExtendFrameIntoClientArea: retrying after COMException (HResult={ex.HResult})");
Thread.Sleep(100);
Copy link

Copilot AI Nov 25, 2025

Choose a reason for hiding this comment

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

[nitpick] Using Thread.Sleep(100) blocks the calling thread. If this method is called from a UI thread (which is likely given it's dealing with window composition), it will freeze the UI for 100ms on retry. Consider using a smaller delay (e.g., 10-50ms) or document that callers should invoke this on a background thread if the blocking behavior is acceptable.

Suggested change
Thread.Sleep(100);
// WARNING: This will block the calling thread. If called from the UI thread, it may cause UI freezing.
Thread.Sleep(20); // Reduced from 100ms to 20ms to minimize UI impact.

Copilot uses AI. Check for mistakes.

[DllImport("dwmapi.dll", EntryPoint = "DwmGetColorizationColor", PreserveSig = true)]
private static extern HRESULT _DwmGetColorizationColor(out uint pcrColorization, [Out, MarshalAs(UnmanagedType.Bool)] out bool pfOpaqueBlend);

Copy link

Copilot AI Nov 25, 2025

Choose a reason for hiding this comment

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

[nitpick] This public method lacks documentation. Consider adding XML documentation comments to explain:

  1. The retry behavior (number of attempts and delay)
  2. Return value semantics (HRESULT - 0 for success, error code on failure)
  3. When/why retries might be necessary (transient DWM composition issues)

Similar wrapper methods like DwmGetColorizationColor include explanatory comments about error handling.

Suggested change
/// <summary>
/// Extends the window frame into the client area for the specified window using Desktop Window Manager (DWM).
/// </summary>
/// <param name="hwnd">A handle to the window to which the frame will be extended.</param>
/// <param name="pMarInset">A reference to a <see cref="MARGINS"/> structure that describes the margins to use when extending the frame.</param>
/// <returns>
/// Returns 0 (S_OK) if the operation succeeds; otherwise, returns an HRESULT error code.
/// </returns>
/// <remarks>
/// This method wraps the native DwmExtendFrameIntoClientArea API and implements a retry mechanism to handle transient DWM composition issues.
/// If a <see cref="System.Runtime.InteropServices.COMException"/> is thrown (e.g., if DWM composition is temporarily unavailable),
/// the method will retry the operation once after a 100ms delay (for a total of 2 attempts).
/// If both attempts fail, the HRESULT from the exception is returned.
/// </remarks>

Copilot uses AI. Check for mistakes.
}
}

return 0;
Copy link

Copilot AI Nov 25, 2025

Choose a reason for hiding this comment

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

This return statement is unreachable. The while loop can only exit via return statements inside the loop (line 2522 or 2531), making this line dead code. Consider removing it or restructuring the logic to make the control flow clearer.

Suggested change
return 0;

Copilot uses AI. Check for mistakes.
public static int DwmExtendFrameIntoClientArea(IntPtr hwnd, ref MARGINS pMarInset)
{
int retryCount = 2;
while(retryCount > 0)
Copy link

Copilot AI Nov 25, 2025

Choose a reason for hiding this comment

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

Missing space after 'while' keyword. Should be while (retryCount > 0) for consistency with C# formatting conventions.

Suggested change
while(retryCount > 0)
while (retryCount > 0)

Copilot uses AI. Check for mistakes.
Copy link
Contributor

@JanviMahajan14 JanviMahajan14 left a comment

Choose a reason for hiding this comment

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

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

PR metadata: Label to tag PRs, to facilitate with triage Win 11 Theming

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants