1

I'm trying to create a Window by creating HwndSource directly. At the moment I have an alternative solution - inheriting from Window class, but I'm just curious what's wrong with my HwndSource implementation. Since Window uses HwndSource in its core, I feel like there should be a way.

Here is the simplified version of my code:

private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
    HwndSource wndPopup = new HwndSource(0, 0x12cf0000/*WS_VISIBLE|WS_OVERLAPPEDWINDOW*/, 0, 10, 10, 500, 500, "Test", IntPtr.Zero);
    wndPopup.RootVisual = new Rectangle() { Fill = Brushes.Red, Width = 100, Height = 100 };
}

Window is created as expected, but after I close it (Alt+F4 or Close icon) and hover mouse over my main window, lots of messages are spitted into debugger:

Exception thrown: 'System.ComponentModel.Win32Exception' in WindowsBase.dll

Exception details are:

Exception thrown: 'System.ComponentModel.Win32Exception' in WindowsBase.dll
Additional information: Invalid window handle

Stack trace:

 WindowsBase.dll!MS.Win32.UnsafeNativeMethods.GetWindowText(System.Runtime.InteropServices.HandleRef, System.Text.StringBuilder, int)
 PresentationCore.dll!System.Windows.Automation.Peers.GenericRootAutomationPeer.GetNameCore()
 PresentationCore.dll!System.Windows.Automation.Peers.AutomationPeer.UpdateSubtree()
 PresentationCore.dll!System.Windows.ContextLayoutManager.fireAutomationEvents()
 PresentationCore.dll!System.Windows.ContextLayoutManager.UpdateLayout()
 PresentationCore.dll!System.Windows.ContextLayoutManager.UpdateLayoutCallback(object)
 PresentationCore.dll!System.Windows.Media.MediaContext.InvokeOnRenderCallback.DoWork()
 PresentationCore.dll!System.Windows.Media.MediaContext.FireInvokeOnRenderCallbacks()
 PresentationCore.dll!System.Windows.Media.MediaContext.RenderMessageHandlerCore(object)
 PresentationCore.dll!System.Windows.Media.MediaContext.RenderMessageHandler(object)
 WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate, object, int)
 WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(object, System.Delegate, object, int, System.Delegate)
 WindowsBase.dll!System.Windows.Threading.DispatcherOperation.InvokeImpl()
 WindowsBase.dll!System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(object)
 mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, object, bool)
 mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, object, bool)
 mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, object)
 WindowsBase.dll!System.Windows.Threading.DispatcherOperation.Invoke()
 WindowsBase.dll!System.Windows.Threading.Dispatcher.ProcessQueue()
 WindowsBase.dll!System.Windows.Threading.Dispatcher.WndProcHook(System.IntPtr, int, System.IntPtr, System.IntPtr, ref bool)
 WindowsBase.dll!MS.Win32.HwndWrapper.WndProc(System.IntPtr, int, System.IntPtr, System.IntPtr, ref bool)
 WindowsBase.dll!MS.Win32.HwndSubclass.DispatcherCallbackOperation(object)
 WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate, object, int)
 WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(object, System.Delegate, object, int, System.Delegate)
 WindowsBase.dll!System.Windows.Threading.Dispatcher.LegacyInvokeImpl(System.Windows.Threading.DispatcherPriority, System.TimeSpan, System.Delegate, object, int)
 WindowsBase.dll!MS.Win32.HwndSubclass.SubclassWndProc(System.IntPtr, int, System.IntPtr, System.IntPtr)
 [Native to Managed Transition]    
 [Managed to Native Transition]    
 WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame)
 WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrame(System.Windows.Threading.DispatcherFrame)
 PresentationFramework.dll!System.Windows.Application.RunDispatcher(object)
 PresentationFramework.dll!System.Windows.Application.RunInternal(System.Windows.Window)
 PresentationFramework.dll!System.Windows.Application.Run(System.Windows.Window)
 PresentationFramework.dll!System.Windows.Application.Run()
 WpfCombox.exe!WpfCombox.App.Main()
 [Native to Managed Transition]    
 [Managed to Native Transition]    
 mscorlib.dll!System.AppDomain.ExecuteAssembly(string, System.Security.Policy.Evidence, string[])
 Microsoft.VisualStudio.HostingProcess.Utilities.dll!Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
 mscorlib.dll!System.Threading.ThreadHelper.ThreadStart_Context(object)
 mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, object, bool)
 mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, object, bool)
 mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, object)
 mscorlib.dll!System.Threading.ThreadHelper.ThreadStart()

So, errors happens somewhere deep in framework code.

I tried filling other properties or adding cleanup to the Disposed handler, but that has no effect on the outcome. If I don't set RootVisual, there are no errors (but, of course, that's not an option).

Any ideas what I'm missing?

nevermind
  • 2,300
  • 1
  • 20
  • 36
  • Can't duplicate. My window seems to behave fine. After closing I do not see any exceptions. – AQuirky May 11 '17 at 17:04
  • @AQuirky Just to clarify: that's not an unhandled exception. Message are written to the Output window, and program continues. But 1) it indicates that something is wrong 2) since lots of such messages are added to the output, debugging becomes terribly slow – nevermind May 11 '17 at 17:18
  • Yes, I understand. I have nothing in my debugger output window. – AQuirky May 11 '17 at 17:27
  • A lot of cleanup needs to happen when the window is destroyed. Done by the HwndSource.Dispose() method, looks like it wasn't called. – Hans Passant May 11 '17 at 18:24
  • @HansPassant I've checked, `Dispose()` is called by framework when user closes the window, and it also fires `Disposed` event. I tried setting `RootVisual = null` in the event handler, but that doesn't help.Calling `Dispose()` from WndProc-hook in response to `WM_CLOSE` doesn't help either. – nevermind May 12 '17 at 07:46

1 Answers1

1

One solution I've found is to create an automation peer for the root visual. Otherwise, GenericRootAutomationPeer will be created by framework, which calls GetWindowText Windows API.

public class PopupRootAutomationPeer : UIElementAutomationPeer
{
    public PopupRootAutomationPeer(FrameworkElement owner)
        : base(owner) { }

    protected override string GetClassNameCore()
    {
        return "Pane";
    }

    protected override AutomationControlType GetAutomationControlTypeCore()
    {
        return AutomationControlType.Pane;
    }

    protected override string GetNameCore()
    {
        return "PopupRootAutomationPeer";
    }
}

// Wrap content of the window with this class
class PopupRoot : Canvas
{
    protected override AutomationPeer OnCreateAutomationPeer()
    {
        return new PopupRootAutomationPeer(this);
    }
}

Probably there is a way to deinitialize the HwndSource when it closes, so that the Automation is not called on non-existing window, but I haven't found it yet.

nevermind
  • 2,300
  • 1
  • 20
  • 36