-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Description
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 have resulted in this bug becoming a crashing bug. In addition to calling kernel32!SetParent on a NULL HWND, WPF attempts to perform several other operations on the HWND, including querying its DPI characteristics (which returns invalid information). Once of the subsidiary steps returns an invalid value that in turn results in a crash with the following stack:
System.Collections.Generic.KeyNotFoundException: 'The given key was not present in the dictionary.'
mscorlib.dll!System.Collections.Generic.Dictionary<System.__Canon, System.__Canon>.this[System.__Canon].get(System.__Canon key) Line 183 C#
PresentationFramework.dll!System.Windows.SystemResources.GetDpiAwarenessCompatibleNotificationWindow(System.Runtime.InteropServices.HandleRef hwnd) Line 1731 C#
PresentationFramework.dll!System.Windows.Interop.HwndHost.BuildOrReparentWindow() Line 1196 C#
PresentationFramework.dll!System.Windows.Interop.HwndHost.OnSourceChanged(object sender, System.Windows.SourceChangedEventArgs e) Line 1077 C#
PresentationCore.dll!System.Windows.SourceChangedEventArgs.InvokeEventHandler(System.Delegate genericHandler, object genericTarget) Line 142 C#
PresentationCore.dll!System.Windows.RoutedEventArgs.InvokeHandler(System.Delegate handler, object target) Line 341 C#
PresentationCore.dll!System.Windows.RoutedEventHandlerInfo.InvokeHandler(object target, System.Windows.RoutedEventArgs routedEventArgs) Line 81 C#
PresentationCore.dll!System.Windows.EventRoute.InvokeHandlersImpl(object source, System.Windows.RoutedEventArgs args, bool reRaised) Line 203 C#
PresentationCore.dll!System.Windows.UIElement.RaiseEventImpl(System.Windows.DependencyObject sender, System.Windows.RoutedEventArgs args) Line 2354 C#
PresentationCore.dll!System.Windows.UIElement.RaiseEvent(System.Windows.RoutedEventArgs e) Line 432 C#
PresentationCore.dll!System.Windows.PresentationSource.UpdateSourceOfElement(System.Windows.DependencyObject doTarget, System.Windows.DependencyObject doAncestor, System.Windows.DependencyObject doOldParent) Line 861 C#
PresentationCore.dll!System.Windows.PresentationSource.RootChanged(System.Windows.Media.Visual oldRoot, System.Windows.Media.Visual newRoot) Line 566 C#
PresentationCore.dll!System.Windows.Interop.HwndSource.RootVisualInternal.set(System.Windows.Media.Visual value) Line 699 C#
PresentationCore.dll!System.Windows.Interop.HwndSource.Dispose(bool disposing) Line 2895 C#
PresentationCore.dll!System.Windows.Interop.HwndSource.OnHwndDisposed(object sender, System.EventArgs args) Line 2819 C#
WindowsBase.dll!MS.Win32.HwndWrapper.Dispose(bool disposing, bool isHwndBeingDestroyed) Line 230 C#
WindowsBase.dll!MS.Win32.HwndWrapper.WndProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled) Line 360 C#
WindowsBase.dll!MS.Win32.HwndSubclass.DispatcherCallbackOperation(object o) Line 494 C#
WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs) Line 104 C#
WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(object source, System.Delegate callback, object args, int numArgs, System.Delegate catchHandler) Line 37 C#
WindowsBase.dll!System.Windows.Threading.Dispatcher.LegacyInvokeImpl(System.Windows.Threading.DispatcherPriority priority, System.TimeSpan timeout, System.Delegate method, object args, int numArgs) Line 1445 C#
WindowsBase.dll!MS.Win32.HwndSubclass.SubclassWndProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam) Line 398 C#
This issue is related to, but not a duplicate of, #1198/#1953. That said, it affects the same sorts of scenarios described in #1198.
// ****BUG***
// unconditional else block
// hwnd.Handle could be IntPtr.Zero, bad things can happen here
// ****BUG*** wpf/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Interop/HwndHost.cs
Lines 979 to 997 in ac9d1b7
| else | |
| { | |
| // Reparent the window to notification-only window provided by SystemResources | |
| // This keeps the child window around, but it is not visible. We can reparent the | |
| // window later when a new parent is available | |
| var hwnd = SystemResources.GetDpiAwarenessCompatibleNotificationWindow(_hwnd); | |
| UnsafeNativeMethods.SetParent(_hwnd, new HandleRef(null, hwnd.Handle)); | |
| // ...But we have a potential problem: If the SystemResources listener window gets | |
| // destroyed ahead of the call to HwndHost.OnDispatcherShutdown(), the HwndHost's window | |
| // will be destroyed too, before the "logical" Dispose has had a chance to do proper | |
| // shutdown. This turns out to be very significant for WebBrowser/ActiveXHost, which shuts | |
| // down the hosted control through the COM interfaces, and the control destroys its | |
| // window internally. Evidently, the WebOC fails to do full, proper cleanup if its | |
| // window is destroyed unexpectedly. | |
| // To avoid this situation, we make sure SystemResources responds to the Dispatcher | |
| // shutdown event after this HwndHost. | |
| SystemResources.DelayHwndShutdown(); | |
| } | |
| } |