File: System\Windows\Interop\HwndHost.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\PresentationFramework\PresentationFramework.csproj (PresentationFramework)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Windows.Automation.Peers;
using System.Windows.Automation.Provider;
using System.Windows.Input;
using System.Collections;
using MS.Win32;
using MS.Internal;
using MS.Internal.Interop;
using System.Windows.Media;
using System.Runtime.InteropServices;
using System.Windows.Threading;
 
// Disable pragma warnings to enable PREsharp pragmas
#pragma warning disable 1634, 1691
 
namespace System.Windows.Interop
{
    /// <summary>
    ///     The HwndHost class hosts an HWND inside of an Avalon tree.
    /// </summary>
    ///<remarks> Subclassing requires unmanaged code permission</remarks>
 
    public abstract class HwndHost : FrameworkElement, IDisposable, IWin32Window, IKeyboardInputSink
    {
        static HwndHost()
        {
            FocusableProperty.OverrideMetadata(typeof(HwndHost), new FrameworkPropertyMetadata(true));
            HwndHost.DpiChangedEvent = Window.DpiChangedEvent.AddOwner(typeof(HwndHost));
        }
 
        /// <summary>
        ///     Constructs an instance of the HwndHost class.
        /// </summary>
        ///<remarks> Not available in Internet zone</remarks>
        protected HwndHost()
        {
            Initialize( false ) ;
        }
 
        internal HwndHost(bool fTrusted )
        {
            Initialize( fTrusted ) ;
        }
 
        /// <summary>
        ///    Because we own an HWND, we implement a finalizer to make sure that we destroy it.
        /// </summary>
        ~HwndHost()
        {
            Dispose(false);
        }
 
        /// <summary>
        ///     Disposes this object.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
 
        /// <summary>
        ///     The Win32 handle of the hosted window.
        /// </summary>
        public IntPtr Handle
        {
            get
            {
                if (_hwnd.Handle != IntPtr.Zero)
                {
                    if (!UnsafeNativeMethods.IsWindow(_hwnd))
                    {
                        _hwnd = new HandleRef(null, IntPtr.Zero);
                    }
                }
 
                return _hwnd.Handle;
            }
        }
 
        /// <summary>
        ///     An event that is notified of all unhandled messages received
        ///     by the hosted window.
        /// </summary>
        public event HwndSourceHook MessageHook
        {
            add
            {
 
                if(_hooks == null)
                {
                    _hooks = new ArrayList(8);
                }
 
                _hooks.Add(value);
            }
 
            remove
            {
 
                if(_hooks != null)
                {
                    _hooks.Remove(value);
 
                    if(_hooks.Count == 0)
                    {
                        _hooks = null;
                    }
                }
            }
        }
 
        /// <summary>
        ///     This event is raised after the DPI of the screen on which the HwndHost is displayed, changes.
        /// </summary>
        public event DpiChangedEventHandler DpiChanged
        {
            add { AddHandler(HwndHost.DpiChangedEvent, value); }
            remove { RemoveHandler(HwndHost.DpiChangedEvent, value); }
        }
 
        /// <summary>
        /// RoutedEvent for when DPI of the screen the HwndHost is on, changes.
        /// </summary>
        public static readonly RoutedEvent DpiChangedEvent;
 
        /// <summary>
        /// </summary>
        /// <param name="e"></param>
        protected override void OnKeyUp(KeyEventArgs e)
        {
            MSG msg;
            if (_fTrusted)
            {
                msg = ComponentDispatcher.UnsecureCurrentKeyboardMessage;
            }
            else
            {
                msg = ComponentDispatcher.CurrentKeyboardMessage;
            }
 
            ModifierKeys modifiers = HwndKeyboardInputProvider.GetSystemModifierKeys();
 
            bool handled = ((IKeyboardInputSink)this).TranslateAccelerator(ref msg, modifiers);
 
            if(handled)
                e.Handled = handled;
 
            base.OnKeyUp(e);
        }
 
        /// <summary>
        /// OnDpiChanged is called when the DPI at which this HwndHost is rendered, changes.
        /// </summary>
        protected override void OnDpiChanged(DpiScale oldDpi, DpiScale newDpi)
        {
            RaiseEvent(new DpiChangedEventArgs(oldDpi, newDpi, HwndHost.DpiChangedEvent, this));
            UpdateWindowPos();
        }
 
        /// <summary>
        /// </summary>
        /// <param name="e"></param>
        protected override void OnKeyDown(KeyEventArgs e)
        {
            MSG msg;
            if (_fTrusted)
            {
                msg = ComponentDispatcher.UnsecureCurrentKeyboardMessage;
            }
            else
            {
                msg = ComponentDispatcher.CurrentKeyboardMessage;
            }
 
 
            ModifierKeys modifiers = HwndKeyboardInputProvider.GetSystemModifierKeys();
 
            bool handled = ((IKeyboardInputSink)this).TranslateAccelerator(ref msg, modifiers);
 
            if(handled)
                e.Handled = handled;
 
            base.OnKeyDown(e);
        }
 
 
#region IKeyboardInputSink
 
        // General security note on the implementation pattern of this interface. In Dev10 it was chosen
        // to expose the interface implementation for overriding to customers. We did so by keeping the
        // explicit interface implementations (that do have the property of being hidden from the public
        // contract, which limits IntelliSense on derived types like WebBrowser) while sticking protected
        // virtuals next to them. Those virtuals contain our base implementation, while the explicit
        // interface implementation methods do call trivially into the virtuals.
        //
        // This comment outlines the security rationale applied to those methods.
        //
        // <SecurityNote Name="IKeyboardInputSink_Implementation">
        //     The security attributes on the virtual methods within this region mirror the corresponding
        //     IKeyboardInputSink methods; customers can override those methods, so we insert a LinkDemand
        //     to encourage them to have a LinkDemand too (via FxCop).
 
        /// <summary>
        ///     Registers a IKeyboardInputSink with the HwndSource in order
        ///     to retreive a unique IKeyboardInputSite for it.
        /// </summary>
        protected virtual IKeyboardInputSite RegisterKeyboardInputSinkCore(IKeyboardInputSink sink)
        {
            throw new InvalidOperationException(SR.HwndHostDoesNotSupportChildKeyboardSinks);
        }
 
        IKeyboardInputSite IKeyboardInputSink.RegisterKeyboardInputSink(IKeyboardInputSink sink)
        {
            return RegisterKeyboardInputSinkCore(sink);
        }
 
        /// <summary>
        ///     Gives the component a chance to process keyboard input.
        ///     Return value is true if handled, false if not.  Components
        ///     will generally call a child component's TranslateAccelerator
        ///     if they can't handle the input themselves.  The message must
        ///     either be WM_KEYDOWN or WM_SYSKEYDOWN.  It is illegal to
        ///     modify the MSG structure, it's passed by reference only as
        ///     a performance optimization.
        /// </summary>
        protected virtual bool TranslateAcceleratorCore(ref MSG msg, ModifierKeys modifiers)
        {
            return false;
        }
 
        bool IKeyboardInputSink.TranslateAccelerator(ref MSG msg, ModifierKeys modifiers)
        {
            return TranslateAcceleratorCore(ref msg, modifiers);
        }
 
        /// <summary>
        ///     Set focus to the first or last tab stop (according to the
        ///     TraversalRequest).  If it can't, because it has no tab stops,
        ///     the return value is false.
        /// </summary>
        protected virtual bool TabIntoCore(TraversalRequest request)
        {
            return false;
        }
 
        bool IKeyboardInputSink.TabInto(TraversalRequest request)
        {
            return TabIntoCore(request);
        }
 
        /// <summary>
        ///     The property should start with a null value.  The component's
        ///     container will set this property to a non-null value before
        ///     any other methods are called.  It may be set multiple times,
        ///     and should be set to null before disposal.
        /// </summary>
        IKeyboardInputSite IKeyboardInputSink.KeyboardInputSite { get;  set; }
 
        /// <summary>
        ///     This method is called whenever one of the component's
        ///     mnemonics is invoked.  The message must either be WM_KEYDOWN
        ///     or WM_SYSKEYDOWN.  It's illegal to modify the MSG structrure,
        ///     it's passed by reference only as a performance optimization.
        ///     If this component contains child components, the container
        ///     OnMnemonic will need to call the child's OnMnemonic method.
        /// </summary>
        protected virtual bool OnMnemonicCore(ref MSG msg, ModifierKeys modifiers)
        {
            return false;
        }
 
        bool IKeyboardInputSink.OnMnemonic(ref MSG msg, ModifierKeys modifiers)
        {
            return OnMnemonicCore(ref msg, modifiers);
        }
 
        /// <summary>
        ///     Gives the component a chance to process keyboard input messages
        ///     WM_CHAR, WM_SYSCHAR, WM_DEADCHAR or WM_SYSDEADCHAR before calling OnMnemonic.
        ///     Will return true if "handled" meaning don't pass it to OnMnemonic.
        ///     The message must be WM_CHAR, WM_SYSCHAR, WM_DEADCHAR or WM_SYSDEADCHAR.
        ///     It is illegal to modify the MSG structure, it's passed by reference
        ///     only as a performance optimization.
        /// </summary>
        protected virtual bool TranslateCharCore(ref MSG msg, ModifierKeys modifiers)
        {
            return false;
        }
 
        bool IKeyboardInputSink.TranslateChar(ref MSG msg, ModifierKeys modifiers)
        {
            return TranslateCharCore(ref msg, modifiers);
        }
 
        /// <summary>
        ///     This returns true if the sink, or a child of it, has focus. And false otherwise.
        /// </summary>
        protected virtual bool HasFocusWithinCore()
        {
            HandleRef hwndFocus = new HandleRef(this, UnsafeNativeMethods.GetFocus());
            if (Handle != IntPtr.Zero && (hwndFocus.Handle == _hwnd.Handle || UnsafeNativeMethods.IsChild(_hwnd, hwndFocus)))
            {
                return true;
            }
            return false;
        }
 
        bool IKeyboardInputSink.HasFocusWithin()
        {
            return HasFocusWithinCore();
        }
 
#endregion IKeyboardInputSink
 
        /// <summary>
        ///     Updates the child window to reflect the state of this element.
        /// </summary>
        /// <remarks>
        ///     This includes the size of the window, the position of the
        ///     window, and the visibility of the window.
        /// </remarks>
        ///<remarks> Not available in Internet zone</remarks>
        public void UpdateWindowPos()
        {
            // Verify the thread has access to the context.
            // VerifyAccess();
 
            if (_isDisposed)
            {
                return;
            }
 
            // Position the child HWND where layout put it.  To do this we
            // have to get coordinates relative to the parent window.
 
            PresentationSource source = null;
            CompositionTarget vt = null;
            if (( Handle != IntPtr.Zero) && IsVisible)
            {
                source = PresentationSource.CriticalFromVisual(this, false /* enable2DTo3DTransition */);
                if(source != null)
                {
                    vt = source.CompositionTarget;
                }
            }
 
            if(vt != null && vt.RootVisual != null)
            {
                // Translate the layout information assigned to us from the co-ordinate
                // space of this element, through the root visual, to the Win32 client
                // co-ordinate space
                NativeMethods.RECT rcClientRTLAdjusted = CalculateAssignedRC(source);
 
                // Set the Win32 position for the child window.
                //
                // Note, we can't check the existing position because we use
                // SWP_ASYNCWINDOWPOS, which means we could have pending position
                // change requests that haven't been applied yet.  If we need
                // this functionality (to avoid the extra SetWindowPos calls),
                // we'll have to track the last RECT we sent Win32 ourselves.
                //
                Rect rectClientRTLAdjusted = PointUtil.ToRect(rcClientRTLAdjusted);
                OnWindowPositionChanged(rectClientRTLAdjusted);
 
                // Show the window
                // Based on Dwayne, the reason we also show/hide window in UpdateWindowPos is for the 
                // following kind of scenario: When applying RenderTransform to HwndHost, the hwnd
                // will be left behind. Developer can workaround by hide the hwnd first using pinvoke. 
                // After the RenderTransform is applied to the HwndHost, call UpdateWindowPos to sync up
                // the hwnd's location, size and visibility with WPF.
                UnsafeNativeMethods.ShowWindowAsync(_hwnd, NativeMethods.SW_SHOW);
            }
            else
            {
                // For some reason we shouldn't be displayed: either we don't
                // have a parent, or the parent no longer has a root visual,
                // or we are marked as not being visible.
                //
                // Just hide the window to get it out of the way.
                UnsafeNativeMethods.ShowWindowAsync(_hwnd, NativeMethods.SW_HIDE);
            }
        }
 
        // Translate the layout information assigned to us from the co-ordinate
        // space of this element, through the root visual, to the Win32 client
        // co-ordinate space
        private NativeMethods.RECT CalculateAssignedRC(PresentationSource source)
        {
            Rect rectElement = new Rect(RenderSize);
            Rect rectRoot = PointUtil.ElementToRoot(rectElement, this, source);
            Rect rectClient = PointUtil.RootToClient(rectRoot, source);
 
            // Adjust for Right-To-Left oriented windows
            IntPtr hwndParent = UnsafeNativeMethods.GetParent(_hwnd);
            NativeMethods.RECT rcClient = PointUtil.FromRect(rectClient);
            NativeMethods.RECT rcClientRTLAdjusted = PointUtil.AdjustForRightToLeft(rcClient, new HandleRef(null, hwndParent));
 
            if (!CoreAppContextSwitches.DoNotUsePresentationDpiCapabilityTier2OrGreater)
            {
                //Adjust for differences in DPI between _hwnd and hwndParent
                rcClientRTLAdjusted = AdjustRectForDpi(rcClientRTLAdjusted);
            }
 
            return rcClientRTLAdjusted;
        }
 
        /// <summary>
        /// Gets the ratio of the DPI between the parent of <see cref="_hwnd"/>
        /// and <see cref="_hwnd"/>. Normally, this ratio is 1. 
        /// </summary>
        private double DpiParentToChildRatio
        {
            get
            {
                if (!_hasDpiAwarenessContextTransition) return 1;
                DpiScale2 dpi = DpiUtil.GetWindowDpi(Handle, fallbackToNearestMonitorHeuristic: false);
                DpiScale2 dpiParent = DpiUtil.GetWindowDpi(UnsafeNativeMethods.GetParent(_hwnd), fallbackToNearestMonitorHeuristic: false);
 
                if (dpi == null || dpiParent == null)
                {
                    // if DPI of the window can not be queried directly, then the platform
                    // is too old to support mixed mode DPI. The DPI ratio is 1.0
                    return 1.0d;
                }
 
                return dpiParent.DpiScaleX / dpi.DpiScaleX;
            }
        }
 
        /// <summary>
        /// Adjusts a rectangle to factor in the differences in DPI between 
        /// the parent of <see cref="_hwnd"/> and <see cref="_hwnd"/>
        /// </summary>
        /// <param name="rcRect">The rectangle to adjust</param>
        /// <returns>The adjusted rectangle</returns>
        private NativeMethods.RECT AdjustRectForDpi(NativeMethods.RECT rcRect)
        {
            if (_hasDpiAwarenessContextTransition)
            {
                double dpiRatio = DpiParentToChildRatio;
                rcRect.left = (int)(rcRect.left / dpiRatio);
                rcRect.top = (int)(rcRect.top / dpiRatio);
                rcRect.right = (int)(rcRect.right / dpiRatio);
                rcRect.bottom = (int)(rcRect.bottom / dpiRatio);
            }
 
            return rcRect;
        }
        
        /// <summary>
        ///     Disposes this object.
        /// </summary>
        /// <param name="disposing">
        ///     true if called from explisit Dispose; and we free all objects managed and un-managed.
        ///     false if called from the finalizer; and we free only un-managed objects.
        /// </param>
        /// <remarks>
        ///     Derived classes should override this if they have additional
        ///     cleanup to do.  The base class implementation should be called.
        ///     Note that the calling thread must be the dispatcher thread.
        ///     If a window is being hosted, that window is destroyed.
        /// </remarks>
        protected virtual void Dispose(bool disposing)
        {
            if (_isDisposed == true)
            {
                return;
            }
 
 
            if(disposing)
            {
                // Verify the thread has access to the context.
#pragma warning suppress 6519
                 VerifyAccess();
 
 
                // Remove our subclass.  Even if this fails, it will be forcably removed
                // when the window is destroyed.
                if (_hwndSubclass != null)
                {
                    // Check if it is trusted (WebOC and AddInHost), call CriticalDetach to avoid the Demand.
                    if (_fTrusted == true)
                    {
                        _hwndSubclass.CriticalDetach(false);
                    }
                    else
                    {
                        _hwndSubclass.RequestDetach(false);
                    }
 
                    _hwndSubclass = null;
                }
 
                // Drop the hooks so that they can be garbage-collected.
                _hooks = null;
 
                // We no longer need to know about the source changing.
                PresentationSource.RemoveSourceChangedHandler(this, new SourceChangedEventHandler(OnSourceChanged));
            }
 
            if (_weakEventDispatcherShutdown != null) // Can be null if the static ctor failed ... see WebBrowser.
            {
                _weakEventDispatcherShutdown.Dispose();
                _weakEventDispatcherShutdown = null;
            }
 
            DestroyWindow();
 
            _isDisposed = true;
        }
 
        private void OnDispatcherShutdown(object sender, EventArgs e)
        {
            Dispose();
        }
 
        /// <summary>
        ///     Derived classes override this method to actually build the
        ///     window being hosted.
        /// </summary>
        /// <param name="hwndParent">
        ///     The parent HWND for the child window.
        /// </param>
        /// <returns>
        ///     The HWND handle to the child window that was created.
        /// </returns>
        /// <remarks>
        ///     The window that is returned must be a child window of the
        ///     specified parent window.
        ///     <para/>
        ///     In addition, the child window will only be subclassed if
        ///     the window is owned by the calling thread.
        /// </remarks>
        protected abstract HandleRef BuildWindowCore(HandleRef hwndParent);
 
        /// <summary>
        ///     Derived classes override this method to destroy the
        ///     window being hosted.
        /// </summary>
        protected abstract void DestroyWindowCore(HandleRef hwnd);
 
        /// <summary>
        ///     A protected override for accessing the window proc of the
        ///     hosted child window.
        /// </summary>
        ///<remarks> Not available in Internet zone</remarks>
        protected virtual IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            DemandIfUntrusted();
 
            switch ((WindowMessage)msg)
            {
                case WindowMessage.WM_NCDESTROY:
                    _hwnd = new HandleRef(null, IntPtr.Zero);
                    break;
 
                // When layout happens, we first calculate the right size/location then call SetWindowPos.
                // We only allow the changes that are coming from Avalon layout. The hwnd is not allowed to change by itself.
                // So the size of the hwnd should always be RenderSize and the position be where layout puts it.
                case WindowMessage.WM_WINDOWPOSCHANGING:
                    PresentationSource source = PresentationSource.CriticalFromVisual(this, false /* enable2DTo3DTransition */);
 
                    if (source != null)
                    {
                        // Get the rect assigned by layout to us.
                        NativeMethods.RECT assignedRC = CalculateAssignedRC(source);
                        
                        // The lParam is a pointer to a WINDOWPOS structure
                        // that contains information about the size and
                        // position that the window is changing to.  Note that
                        // modifying this structure during WM_WINDOWPOSCHANGING
                        // will change what happens to the window.
                        unsafe
                        {
                            NativeMethods.WINDOWPOS * windowPos = (NativeMethods.WINDOWPOS *)lParam;
 
                            // Always force the size of the window to be the
                            // size of our assigned rectangle.  Note that we
                            // have to always clear the SWP_NOSIZE flag.
                            windowPos->cx = assignedRC.right - assignedRC.left;
                            windowPos->cy = assignedRC.bottom - assignedRC.top;
                            windowPos->flags &= ~NativeMethods.SWP_NOSIZE;
 
                            // Always force the position of the window to be
                            // the upper-left corner of our assigned rectangle.
                            // Note that we have to always clear the
                            // SWP_NOMOVE flag.
                            windowPos->x = assignedRC.left;
                            windowPos->y = assignedRC.top;
                            windowPos->flags &= ~NativeMethods.SWP_NOMOVE;
 
                            // Windows has an optimization to copy pixels
                            // around to reduce the amount of repainting
                            // needed when moving or resizing a window.
                            // Unfortunately, this is not compatible with WPF
                            // in many cases due to our use of DirectX for
                            // rendering from our rendering thread.
                            // To be safe, we disable this optimization and
                            // pay the cost of repainting.
                            windowPos->flags |= NativeMethods.SWP_NOCOPYBITS;
                        }
                    }
 
                    break;
 
 
                case WindowMessage.WM_GETOBJECT:
                    handled = true;
                    return OnWmGetObject(wParam, lParam);
            }
 
            return IntPtr.Zero ;
        }
 
        #region Automation
 
        /// <summary>
        /// Creates AutomationPeer (<see cref="UIElement.OnCreateAutomationPeer"/>)
        /// </summary>
        protected override AutomationPeer OnCreateAutomationPeer()
        {
            return new HwndHostAutomationPeer(this);
        }
 
        private IntPtr OnWmGetObject(IntPtr wparam, IntPtr lparam)
        {
            IntPtr result = IntPtr.Zero;
 
            AutomationPeer containerPeer = UIElementAutomationPeer.CreatePeerForElement(this);
            if (containerPeer != null)
            {
                // get the element proxy
                IRawElementProviderSimple el = containerPeer.GetInteropChild();
                result = AutomationInteropProvider.ReturnRawElementProvider(Handle, wparam, lparam, el);
            }
            return result;
        }
 
        #endregion Automation
 
        // Make this protected virtual when enabling the WebOC code.
        //NEEDS final signoff from the owning team.
        /// <summary>
        /// Called when the window rect changes. Subclasses can override this to
        /// update child window's Rect using these new coordinates.
        /// </summary>
        /// <param name="rcBoundingBox"></param>
        protected virtual void OnWindowPositionChanged(Rect rcBoundingBox)
        {
            if (_isDisposed)
            {
                return;
            }
 
            UnsafeNativeMethods.SetWindowPos(_hwnd,
                                           new HandleRef(null, IntPtr.Zero),
                                           (int)rcBoundingBox.X,
                                           (int)rcBoundingBox.Y,
                                           (int)rcBoundingBox.Width,
                                           (int)rcBoundingBox.Height,
                                           NativeMethods.SWP_ASYNCWINDOWPOS
                                           | NativeMethods.SWP_NOZORDER
                                           | NativeMethods.SWP_NOCOPYBITS
                                           | NativeMethods.SWP_NOACTIVATE);
        }
 
        /// <summary>
        ///     Return the desired size of the HWND.
        /// </summary>
        /// <remarks>
        ///     HWNDs usually expect a very simplisitic layout model where
        ///     a window gets to be whatever size it wants to be.  To respect
        ///     this we request the initial size that the window was created
        ///     at.  A window created with a 0 dimension will adopt whatever
        ///     size the containing layout wants it to be.  Layouts are free
        ///     to actually size the window to whatever they want, and the
        ///     child window will always be sized accordingly.
        ///     <para/>
        ///     Derived classes should only override this method if they
        ///     have special knowlege about the size the window wants to be.
        ///     Examples of such may be special HWND types like combo boxes.
        ///     In such cases, the base class must still be called, but the
        ///     return value can be changed appropriately.
        /// </remarks>
        ///<remarks> Not available in Internet zone</remarks>
        protected override Size MeasureOverride(Size constraint)
        {
            DemandIfUntrusted();
 
            Size desiredSize = new Size(0,0);
 
            // Measure to our desired size.  If we have a 0-length dimension,
            // the system will assume we don't care about that dimension.
            if(Handle != IntPtr.Zero)
            {
                desiredSize.Width = Math.Min(_desiredSize.Width, constraint.Width);
                desiredSize.Height = Math.Min(_desiredSize.Height, constraint.Height);
            }
 
            return desiredSize;
        }
 
        /// <summary>
        ///     GetDrawing - Returns the drawing content of this Visual.
        /// </summary>
        /// <remarks>
        ///     This returns a bitmap obtained by calling the PrintWindow Win32 API.
        /// </remarks>
        internal override DrawingGroup GetDrawing()
        {
            return GetDrawingHelper();
        }
 
        /// <summary>
        /// Returns the bounding box of the content.
        /// </summary>
        internal override Rect GetContentBounds()
        {
            return new Rect(RenderSize);
        }
 
        private DrawingGroup GetDrawingHelper()
        {
            DrawingGroup drawingGroup = null;
 
            if(Handle != IntPtr.Zero)
            {
                NativeMethods.RECT rc = new NativeMethods.RECT();
                SafeNativeMethods.GetWindowRect(_hwnd, ref rc);
                int width = rc.right - rc.left;
                int height = rc.bottom - rc.top;
 
                HandleRef hdcScreen = new HandleRef(this, UnsafeNativeMethods.GetDC(new HandleRef(this, IntPtr.Zero)));
                if(hdcScreen.Handle != IntPtr.Zero)
                {
                    HandleRef hdcBitmap = new HandleRef(this, IntPtr.Zero);
                    HandleRef hBitmap = new HandleRef(this, IntPtr.Zero);
 
                    try
                    {
                        hdcBitmap = new HandleRef(this, UnsafeNativeMethods.CriticalCreateCompatibleDC(hdcScreen));
                        if(hdcBitmap.Handle != IntPtr.Zero)
                        {
                            hBitmap = new HandleRef(this, UnsafeNativeMethods.CriticalCreateCompatibleBitmap(hdcScreen, width, height));
 
                            if(hBitmap.Handle != IntPtr.Zero)
                            {
                                // Select the bitmap into the DC so that we draw to it.
                                IntPtr hOldBitmap = UnsafeNativeMethods.CriticalSelectObject(hdcBitmap, hBitmap.Handle);
                                try
                                {
                                    // Clear the bitmap to white (so we don't waste toner printing a black bitmap something fails).
                                    NativeMethods.RECT rcPaint = new NativeMethods.RECT(0,0,width, height);
                                    IntPtr hbrWhite = UnsafeNativeMethods.CriticalGetStockObject(NativeMethods.WHITE_BRUSH);
                                    UnsafeNativeMethods.CriticalFillRect(hdcBitmap.Handle, ref rcPaint, hbrWhite);
 
                                    // First try to use the PrintWindow API.
                                    bool result = UnsafeNativeMethods.CriticalPrintWindow(_hwnd, hdcBitmap, 0);
                                    if(result == false)
                                    {
                                        // Fall back to sending a WM_PRINT message to the window.
                                        //
                                        // Note: there are known cases where WM_PRINT is not implemented
                                        // to provide visual parity with WM_PAINT.  However, since the
                                        // GetDrawing method is virtual, the derived class can override
                                        // this default implementation and provide a better implementation.
                                        UnsafeNativeMethods.SendMessage(_hwnd.Handle, WindowMessage.WM_PRINT, hdcBitmap.Handle, (IntPtr) (NativeMethods.PRF_CHILDREN | NativeMethods.PRF_CLIENT | NativeMethods.PRF_ERASEBKGND | NativeMethods.PRF_NONCLIENT));
                                    }
                                    else
                                    {
                                        // There is a know issue where calling PrintWindow on a window will
                                        // clear all dirty regions (but since it is redirected, the screen
                                        // won't be updated).  As a result we can leave unpainted pixels on
                                        // the screen if PrintWindow is called when the window was dirty.
                                        //
                                        // To fix this, we just force the child window to repaint.
                                        //
                                        UnsafeNativeMethods.CriticalRedrawWindow(_hwnd, IntPtr.Zero, IntPtr.Zero, NativeMethods.RDW_INVALIDATE | NativeMethods.RDW_ALLCHILDREN);
                                    }
 
                                    // Create a DrawingGroup that only contains an ImageDrawing that wraps the bitmap.
                                    drawingGroup = new DrawingGroup();
                                    System.Windows.Media.Imaging.BitmapSource bitmapSource = Imaging.CriticalCreateBitmapSourceFromHBitmap(hBitmap.Handle, IntPtr.Zero, Int32Rect.Empty, null, WICBitmapAlphaChannelOption.WICBitmapIgnoreAlpha);
                                    Rect rectElement    = new Rect(RenderSize);
                                    drawingGroup.Children.Add(new ImageDrawing(bitmapSource, rectElement));
                                    drawingGroup.Freeze();
                                }
                                finally
                                {
                                    // Put the old bitmap back into the DC.
                                    UnsafeNativeMethods.CriticalSelectObject(hdcBitmap, hOldBitmap);
                                }
                            }
                        }
                    }
                    finally
                    {
                        UnsafeNativeMethods.ReleaseDC(new HandleRef(this, IntPtr.Zero), hdcScreen);
                        hdcScreen = new HandleRef(null, IntPtr.Zero);
 
                        if(hBitmap.Handle != IntPtr.Zero)
                        {
                            UnsafeNativeMethods.DeleteObject(hBitmap);
                            hBitmap = new HandleRef(this, IntPtr.Zero);
                        }
 
                        if(hdcBitmap.Handle != IntPtr.Zero)
                        {
                            UnsafeNativeMethods.CriticalDeleteDC(hdcBitmap);
                            hdcBitmap = new HandleRef(this, IntPtr.Zero);
                        }
                    }
                }
            }
 
            return drawingGroup;
        }
 
        private void Initialize( bool fTrusted )
        {
            _fTrusted = fTrusted;
 
            _hwndSubclassHook = new HwndWrapperHook(SubclassWndProc);
            _handlerLayoutUpdated = new EventHandler(OnLayoutUpdated);
            _handlerEnabledChanged = new DependencyPropertyChangedEventHandler(OnEnabledChanged);
            _handlerVisibleChanged = new DependencyPropertyChangedEventHandler(OnVisibleChanged);
            PresentationSource.AddSourceChangedHandler(this, new SourceChangedEventHandler(OnSourceChanged));
 
            _weakEventDispatcherShutdown = new WeakEventDispatcherShutdown(this, this.Dispatcher);
        }
 
        ///<summary>
        ///     Use this method as a defense-in-depth measure only.
        ///</summary>
        private void DemandIfUntrusted()
        {
            if ( ! _fTrusted )
            {
            }
        }
 
        private void OnSourceChanged(object sender, SourceChangedEventArgs e)
        {
            // Remove ourselves as an IKeyboardInputSinks child of our previous
            // containing window.
            IKeyboardInputSite keyboardInputSite = ((IKeyboardInputSink)this).KeyboardInputSite;
            if (keyboardInputSite != null)
            {
                // Derived classes that implement IKeyboardInputSink should support setting it to null.
                ((IKeyboardInputSink)this).KeyboardInputSite = null;
 
                keyboardInputSite.Unregister();
            }
 
            // Add ourselves as an IKeyboardInputSinks child of our containing window.
            IKeyboardInputSink source = PresentationSource.CriticalFromVisual(this, false /* enable2DTo3DTransition */) as IKeyboardInputSink;
            if(source != null)
            {
                ((IKeyboardInputSink)this).KeyboardInputSite = source.RegisterKeyboardInputSink(this);
            }
 
            BuildOrReparentWindow();
        }
 
        private void OnLayoutUpdated(object sender, EventArgs e)
        {
            UpdateWindowPos();
        }
 
        private void OnEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (_isDisposed)
            {
                return;
            }
 
            bool boolNewValue = (bool)e.NewValue;
            UnsafeNativeMethods.EnableWindow(_hwnd, boolNewValue);
        }
 
        private void OnVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (_isDisposed)
            {
                return;
            }
 
            bool vis = (bool)e.NewValue;
 
            // BUG 148548 HwndHost does not always repaint on restore from minimize.
            // We used to call ShowWindow here and ShowWindowAsync in other places (UpdateWindowPos). 
            // The inconsistent sync/async showing window causes the repainting bug. 
            // There was recollection from Dwayne that ShowWindow sync might cause rereentrancy issues.
            // So change here to show async to be consistent with everywhere else (instead of changing everywhere else
            // to show window sync).            
            if(vis)
                UnsafeNativeMethods.ShowWindowAsync(_hwnd, NativeMethods.SW_SHOWNA);
            else
                UnsafeNativeMethods.ShowWindowAsync(_hwnd, NativeMethods.SW_HIDE);
        }
 
        // This routine handles the following cases:
        // 1) a parent window is present, build the child window
        // 2) a parent is present, reparent the child window to it
        // 3) a parent window is not present, hide the child window by parenting it to SystemResources.Hwnd window.
        private void BuildOrReparentWindow()
        {
            DemandIfUntrusted();
 
            // Verify the thread has access to the context.
            // VerifyAccess();
 
            // Prevent reentry while building a child window,
            // also prevent the reconstruction of Disposed objects.
            if(_isBuildingWindow || _isDisposed)
            {
                return;
            }
 
            _isBuildingWindow = true;
 
            // Find the source window, this must be the parent window of
            // the child window.
            IntPtr hwndParent = IntPtr.Zero;
            PresentationSource source = PresentationSource.CriticalFromVisual(this, false /* enable2DTo3DTransition */);
            if(source != null)
            {
                HwndSource hwndSource = source as HwndSource ;
                if(hwndSource != null)
                {
                    hwndParent = hwndSource.Handle;
                }
            }
            else
            {
                // attempt to also walk through 3D - if we get a non-null result then we know we are inside of
                // a 3D scene which is not supported
                PresentationSource goingThrough3DSource = PresentationSource.CriticalFromVisual(this, true /* enable2DTo3DTransition */);
                if (goingThrough3DSource != null)
                {
                    if (TraceHwndHost.IsEnabled)
                    {
                        TraceHwndHost.Trace(TraceEventType.Warning, TraceHwndHost.HwndHostIn3D);
                    }
                }
            }
 
            try
            {
                if(hwndParent != IntPtr.Zero)
                {
                    if(_hwnd.Handle == IntPtr.Zero)
                    {
                        // We now have a parent window, so we can create the child
                        // window.
                        BuildWindow(new HandleRef(null, hwndParent));
                        this.LayoutUpdated += _handlerLayoutUpdated;
                        this.IsEnabledChanged += _handlerEnabledChanged;
                        this.IsVisibleChanged += _handlerVisibleChanged;
                    }
                    else if(hwndParent != UnsafeNativeMethods.GetParent(_hwnd))
                    {
                        // We have a different parent window.  Just reparent the
                        // child window under the new parent window.
                        UnsafeNativeMethods.SetParent(_hwnd, new HandleRef(null,hwndParent));
                    }
                }
                else if (Handle != IntPtr.Zero)
                {
                    // 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);
                    Debug.Assert(hwnd != null);
                    if (hwnd != null)
                    {
                        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();
                    }
                    else
                    {
                        Trace.WriteLineIf(hwnd == null, $"- Warning - Notification Window is null\n{new System.Diagnostics.StackTrace(true).ToString()}");
                    }
                }
            }
            finally
            {
                // Be careful to clear our guard bit.
                _isBuildingWindow = false;
            }
        }
 
 
        private void BuildWindow(HandleRef hwndParent)
        {
            // Demand unmanaged code to the caller. IT'S RISKY TO REMOVE THIS
            DemandIfUntrusted();
 
            // Allow the derived class to build our HWND.
            _hwnd = BuildWindowCore(hwndParent);
 
            if(_hwnd.Handle == IntPtr.Zero || !UnsafeNativeMethods.IsWindow(_hwnd))
            {
                throw new InvalidOperationException(SR.ChildWindowNotCreated);
            }
 
            // Make sure that the window that was created is indeed a child window.
            int windowStyle = UnsafeNativeMethods.GetWindowLong(new HandleRef(this,_hwnd.Handle), NativeMethods.GWL_STYLE);
            if((windowStyle & NativeMethods.WS_CHILD) == 0)
            {
                throw new InvalidOperationException(SR.HostedWindowMustBeAChildWindow);
            }
 
            // Make sure the child window is the child of the expected parent window.
            if(hwndParent.Handle != UnsafeNativeMethods.GetParent(_hwnd))
            {
                throw new InvalidOperationException(SR.ChildWindowMustHaveCorrectParent);
            }
 
            // Test to see if hwndParent and _hwnd have different DPI_AWARENESS_CONTEXT's
            if (DpiUtil.GetDpiAwarenessContext(_hwnd.Handle) != DpiUtil.GetDpiAwarenessContext(hwndParent.Handle))
            {
                _hasDpiAwarenessContextTransition = true;
            }
 
            // Only subclass the child HWND if it is owned by our thread.
            int idWindowProcess;
            int idWindowThread = UnsafeNativeMethods.GetWindowThreadProcessId(_hwnd, out idWindowProcess);
 
#if WCP_SERVER2003_OR_LATER_ENABLED
            IntPtr hCurrentThread = UnsafeNativeMethods.GetCurrentThread();
            if ((idWindowThread == SafeNativeMethods.GetThreadId(hCurrentThread)) &&
                (idWindowProcess == UnsafeNativeMethods.GetProcessIdOfThread(hCurrentThread)))
#else
            if ((idWindowThread == SafeNativeMethods.GetCurrentThreadId()) &&
                (idWindowProcess == Environment.ProcessId))
#endif
            {
                _hwndSubclass = new HwndSubclass(_hwndSubclassHook);
                _hwndSubclass.CriticalAttach(_hwnd.Handle);
            }
 
            // Initially make sure the window is hidden.  We will show it later during rendering.
            UnsafeNativeMethods.ShowWindowAsync(_hwnd, NativeMethods.SW_HIDE);
 
            // Assume the desired size is the initial size.  If the window was
            // created with a 0-length dimension, we assume this means we
            // should fill all available space.
            NativeMethods.RECT rc = new NativeMethods.RECT();
            SafeNativeMethods.GetWindowRect(_hwnd, ref rc);
 
            // Convert from pixels to measure units.
            // PresentationSource can't be null if we get here.
            PresentationSource source = PresentationSource.CriticalFromVisual(this, false /* enable2DTo3DTransition */);
            Point ptUpperLeft = new Point(rc.left, rc.top);
            Point ptLowerRight = new Point(rc.right, rc.bottom);
            ptUpperLeft = source.CompositionTarget.TransformFromDevice.Transform(ptUpperLeft);
            ptLowerRight = source.CompositionTarget.TransformFromDevice.Transform(ptLowerRight);
            _desiredSize = new Size(ptLowerRight.X - ptUpperLeft.X, ptLowerRight.Y - ptUpperLeft.Y);
 
            // We have a new desired size, so invalidate measure.
            InvalidateMeasure();
        }
 
        private void DestroyWindow()
        {
            // Destroy the window if we are hosting one.
            if( Handle == IntPtr.Zero)
                return;
 
            if(!CheckAccess())
            {
                // I understand we can get in here on the finalizer thread.  And
                // touching other GC'ed objects in the finalizer is typically bad.
                // But a Context object can be accessed after finalization.
                // We need to touch the Context to switch to the right thread.
                // If the Context has been finalized then we won't get switched
                // and that is OK.
                Dispatcher.BeginInvoke(DispatcherPriority.Normal, new DispatcherOperationCallback(AsyncDestroyWindow), null);
                return;
            }
 
            HandleRef hwnd = _hwnd;
            _hwnd = new HandleRef(null, IntPtr.Zero);
 
            DestroyWindowCore(hwnd);
        }
 
        private object AsyncDestroyWindow(object arg)
        {
            DestroyWindow();
            return null;
        }
 
        private IntPtr SubclassWndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            IntPtr result = IntPtr.Zero ;
 
            // Call the virtual first.
            result = WndProc(hwnd, msg, wParam, lParam, ref handled);
 
            // Call the handlers for the MessageHook event.
            if(!handled && _hooks != null)
            {
                for(int i = 0, nCount = _hooks.Count; i < nCount; i++)
                {
                    result = ((HwndSourceHook)_hooks[i])(hwnd, msg, wParam, lParam, ref handled);
                    if(handled)
                    {
                        break;
                    }
                }
            }
 
            return result;
        }
 
        private DependencyPropertyChangedEventHandler _handlerEnabledChanged;
        private DependencyPropertyChangedEventHandler _handlerVisibleChanged;
        private EventHandler _handlerLayoutUpdated;
 
        private HwndSubclass _hwndSubclass;
 
        private HwndWrapperHook _hwndSubclassHook;
 
        private HandleRef _hwnd;
 
        private ArrayList _hooks;
        private Size _desiredSize;
 
        /// <summary>
        /// True when the parent of <see cref="_hwnd"/> and <see cref="_hwnd"/>
        /// have different DPI_AWARENESS_CONTEXT values. This indicates that 
        /// DPI transitions are possible in content hosted by this <see cref="HwndHost"/>. 
        /// </summary>
        private bool _hasDpiAwarenessContextTransition = false;
 
        private bool _fTrusted;
 
        private bool _isBuildingWindow = false;
 
        private bool _isDisposed = false;
 
        private class WeakEventDispatcherShutdown: WeakReference
        {
            public WeakEventDispatcherShutdown(HwndHost hwndHost, Dispatcher that): base(hwndHost)
            {
                _that = that;
                _that.ShutdownFinished += new EventHandler(this.OnShutdownFinished);
            }
 
            public void OnShutdownFinished(object sender, EventArgs e)
            {
                HwndHost hwndHost = this.Target as HwndHost;
                if(null != hwndHost)
                {
                    hwndHost.OnDispatcherShutdown(sender, e);
                }
                else
                {
                    Dispose();
                }
            }
 
            public void Dispose()
            {
                if(null != _that)
                {
                    _that.ShutdownFinished-= new EventHandler(this.OnShutdownFinished);
                }
            }
 
            private Dispatcher _that;
        }
        WeakEventDispatcherShutdown _weakEventDispatcherShutdown;
    }
}