Creating HwndSource directly

631 Views Asked by At

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?

1

There are 1 best solutions below

0
On

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.