File: System\Windows\Forms\Application.ParkingWindow.cs
Web Access
Project: src\src\System.Windows.Forms\src\System.Windows.Forms.csproj (System.Windows.Forms)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Windows.Forms.Layout;
 
namespace System.Windows.Forms;
 
public sealed partial class Application
{
    /// <summary>
    ///  This class embodies our parking window, which we create when the
    ///  first message loop is pushed onto the thread.
    /// </summary>
    internal sealed class ParkingWindow : ContainerControl, IArrangedElement
    {
        // In .NET 2.0 we now aggressively tear down the parking window
        //   when the last control has been removed off of it.
 
        private const int WM_CHECKDESTROY = (int)PInvokeCore.WM_USER + 0x01;
 
        private int _childCount;
 
        public ParkingWindow()
        {
            SetExtendedState(ExtendedStates.InterestedInUserPreferenceChanged, false);
            SetState(States.TopLevel, true);
            Text = "WindowsFormsParkingWindow";
            Visible = false;
        }
 
        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams cp = base.CreateParams;
 
                // Message only windows are cheaper and have fewer issues than full blown invisible windows.
                cp.Parent = HWND.HWND_MESSAGE;
                return cp;
            }
        }
 
        internal override void AddReflectChild()
        {
            if (_childCount < 0)
            {
                Debug.Fail("How did parkingwindow childcount go negative???");
                _childCount = 0;
            }
 
            _childCount++;
        }
 
        internal override void RemoveReflectChild()
        {
            if (--_childCount < 0)
            {
                Debug.Fail("How did parkingwindow childcount go negative???");
                _childCount = 0;
            }
 
            if (_childCount != 0 || !IsHandleCreated)
            {
                return;
            }
 
            // Check to see if we are running on the thread that owns the parkingwindow.
            // If so, we can destroy immediately.
            //
            // This is important for scenarios where apps leak controls until after the
            // messagepump is gone and then decide to clean them up. We should clean
            // up the parkingwindow in this case and a postmessage won't do it.
            uint id = PInvoke.GetWindowThreadProcessId(HWNDInternal, out _);
            ThreadContext? context = ThreadContext.FromId(id);
 
            // We only do this if the ThreadContext tells us that we are currently handling a window message.
            if (context is null || !ReferenceEquals(context, ThreadContext.FromCurrent()))
            {
                PInvokeCore.PostMessage(HWNDInternal, WM_CHECKDESTROY);
            }
            else
            {
                CheckDestroy();
            }
        }
 
        private void CheckDestroy()
        {
            if (_childCount != 0)
                return;
 
            HWND hwndChild = PInvoke.GetWindow(this, GET_WINDOW_CMD.GW_CHILD);
            if (hwndChild.IsNull)
            {
                DestroyHandle();
            }
        }
 
        public void Destroy()
        {
            DestroyHandle();
        }
 
        /// <summary>
        ///  "Parks" the given HWND to a temporary HWND. This allows WS_CHILD windows to be parked.
        /// </summary>
        internal void ParkHandle<T>(T handle) where T : IHandle<HWND>
        {
            if (!IsHandleCreated)
            {
                CreateHandle();
            }
 
            PInvoke.SetParent(handle, (IHandle<HWND>)this);
        }
 
        /// <summary>
        ///  "Unparks" the given HWND to a temporary HWND. This allows WS_CHILD windows to be parked.
        /// </summary>
        internal void UnparkHandle<T>(T handle) where T : IHandle<HWND>
        {
            if (!IsHandleCreated)
            {
                return;
            }
 
            Debug.Assert(
                PInvoke.GetParent(handle) != HWND,
                "Always set the handle's parent to someone else before calling UnparkHandle");
 
            // If there are no child windows in this handle any longer, destroy the parking window.
            CheckDestroy();
        }
 
        // Do nothing on layout to reduce the calls into the LayoutEngine while debugging.
        protected override void OnLayout(LayoutEventArgs levent) { }
        void IArrangedElement.PerformLayout(IArrangedElement affectedElement, string? affectedProperty) { }
 
        protected override void WndProc(ref Message m)
        {
            if (m.MsgInternal == PInvokeCore.WM_SHOWWINDOW)
                return;
 
            base.WndProc(ref m);
            switch (m.MsgInternal)
            {
                case PInvokeCore.WM_PARENTNOTIFY:
                    if (m.WParamInternal.LOWORD == PInvokeCore.WM_DESTROY)
                    {
                        PInvokeCore.PostMessage(this, WM_CHECKDESTROY);
                    }
 
                    break;
                case WM_CHECKDESTROY:
                    CheckDestroy();
                    break;
            }
        }
    }
}