Creating HwndSource directly

647 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
nevermind 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.