Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[release/3.1] Prevent NULL HWND's from being parented under SystemResources listener windows #2101

Merged
4 commits merged into from
Oct 29, 2019

Conversation

vatsan-madhavan
Copy link
Member

Addresses #2089
.NET 5 PR: #2100

Description (Summary)

When HwndHost hosted HWND's need to be "parked" while the HwndHost is not being actively shown, WPF will reparent such HWND's under temporary message-only windows maintained inside the SystemResources class.

There is a bug that causes null HWND's to be parented under such message-only windows. When this happens in High-DPI applications that use mixed-mode DPI capabilities introduced by WPF in .NET 4.8 (parent and child windows with different DPI_AWARENESS_CONTEXT values), a crash ensues.

Customer Impact

This is a fix for a crash affecting several applications including Visual Studio, and Azure Information Protection Add-in for Office

This was fixed recently in .NET 4.8, and is being forwarded ported to .NET Core for consistency.

Regresssion

Not a regression in .NET Core, but this was a regression introduced by .NET 4.8.

Risk

The fix is small and well understood, and has been tested well. The .NET Framework version of this fix has been validated by Visual Studio (the codebases are identical in this area and .NET Framework testing is a reliable proxy for this change in .NET Core).

Details

When an HwndHost receives SourceChanged event, it goes through BuildOrReparentWindow. When the hosted window is invisible, it is usually reparented under a temporary windows maintained by WPF in the SystemResources class, until later on the window can be rebuilt and parented back to a valid parent.

There is a latent bug in this logic where in NULL HWND's are attempted to be parented to SystemResources managed temporary windows. This bug goes back quite a while (.NET 4.5 likely). WPF seems to ignore the return value from kernel32!SetParent and not deal with this failure. This has not been a crashing failure until now.

Starting .NET 4.8, there have been some changes to this codepath that has resulted in the current bug becoming a crash. In addition to calling kernel32!SetParent on a NULL HWND, WPF attempts to obtain a DPI-specific parking-window. This process of querying a DPI-specific parking window fails because WPF is unable to use the DPI_AWARENESS_CONTEXT value returned by the system for (HWND)nullptr.

The only necessary part of this fix is in HwndHost: WPF should not attempt to reparent the hosted window under a parking-window if the hosted window is (HWND)nullptr. This only requires a simple check : else if (_hwnd.Handle != IntPtr.Zero)). All other changes in SystemResources and HwndHost are defensive improvements.

SystemResources.EnsureResourceChangeListener(HwndDpiInfo) can attempt to create a parking-window corresponding to DPI_AWARENESS_CONTEXT_VALUE that is invalid/meaningless. This should not be allowed. A few additional checks are added to ensure this. Further, GetDpiAwarenessCompatibleNotificationWindow is augmented to be more defensive.

Also, variant of EnsureResourceChangeListener is dead code - it is being removed.

If for some unknown reason SystemResources.GetDpiAwarenessCompatibleNotificationWindow fails and returns null to HwndHost.BuildOrReparentWindow, WPF will fail to reparent the hosted window, and it will be 'lost'. This seems very unlikely - I have added a Trace to ensure that we can debug this situation if it does occur.

…uildOrReparentWindow`. When the hosted window is invisible, it is usually reparented under a temporary windows maintained by WPF in the `SystemResources `class, until later on the window can be rebuilt and parented back to a valid parent.

There is a latent bug in this logic where in `NULL ` `HWND's `are attempted to be parented to `SystemResources `managed temporary windows. This bug goes back quite a while (.NET 4.5 likely). WPF seems to ignore the return value from `kernel32!SetParent` and not deal with this failure. This has not been a crashing failure until now.

Starting .NET 4.8, there have been some changes to this codepath that has resulted in the current bug becoming a crash. In addition to calling `kernel32!SetParent` on a `NULL` `HWND`, WPF attempts to obtain a DPI-specific parking-window. This process of querying a DPI-specific parking window fails because WPF is unable to use the `DPI_AWARENESS_CONTEXT` value returned by the system for `(HWND)nullptr`.

The only necessary part of this fix is in `HwndHost`: WPF should not attempt to reparent the hosted window under a parking-window if the hosted window is `(HWND)nullptr`. This only requires a simple check : `else if (_hwnd.Handle != IntPtr.Zero)`). All other changes in `SystemResources` and `HwndHost` are defensive improvements.

`SystemResources.EnsureResourceChangeListener(HwndDpiInfo)` can attempt to create a parking-window corresponding to `DPI_AWARENESS_CONTEXT_VALUE` that is invalid/meaningless. This should not be allowed. A few additional checks are added to ensure this. Further, `GetDpiAwarenessCompatibleNotificationWindow` is augmented to be more defensive.

Also, variant of `EnsureResourceChangeListener`  is dead code - it is being removed.

If for some unknown reason `SystemResources.GetDpiAwarenessCompatibleNotificationWindow`  fails and returns `null` to `HwndHost.BuildOrReparentWindow`, WPF will fail to reparent the hosted window, and it will be 'lost'. This seems very unlikely - I have added a Trace to ensure that we can debug this situation if it does occur.
@ghost ghost requested a review from rladuca October 23, 2019 22:54
@ghost ghost added the PR metadata: Label to tag PRs, to facilitate with triage label Oct 23, 2019
@ghost ghost requested a review from SamBent October 23, 2019 22:54
@vatsan-madhavan vatsan-madhavan added this to the 3.1 milestone Oct 23, 2019
@vatsan-madhavan vatsan-madhavan self-assigned this Oct 23, 2019
@vatsan-madhavan vatsan-madhavan added Bug Product bug (most likely) issue-type-netfx-port Ports from .NET Framework labels Oct 23, 2019
@vatsan-madhavan vatsan-madhavan added ask-mode * NO MERGE * metadata: The PR is not ready for merge yet (see discussion for detailed reasons) labels Oct 25, 2019
@vatsan-madhavan vatsan-madhavan added auto_merge bot-command and removed * NO MERGE * metadata: The PR is not ready for merge yet (see discussion for detailed reasons) labels Oct 29, 2019
@ghost
Copy link

ghost commented Oct 29, 2019

Hello @vatsan-madhavan!

Because this pull request has the auto_merge label, I will be glad to assist with helping to merge this pull request once all check-in policies pass.

p.s. you can customize the way I help with merging this pull request, such as holding this pull request until a specific person approves. Simply @mention me (@msftbot) and give me an instruction to get started! Learn more here.

@ghost ghost merged commit 37ada65 into dotnet:release/3.1 Oct 29, 2019
@ghost ghost locked as resolved and limited conversation to collaborators Apr 14, 2022
This pull request was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
auto_merge bot-command Bug Product bug (most likely) issue-type-netfx-port Ports from .NET Framework PR metadata: Label to tag PRs, to facilitate with triage Servicing-approved
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants