| File: System\Windows\Forms\Application.ParkingWindow.cs | Web Access |
| Project: src\winforms\src\System.Windows.Forms\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 = PInvokeCore.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; } } } }