File: System\Windows\Forms\Application.ParkingWindowTests.cs
Web Access
Project: src\src\System.Windows.Forms\tests\UnitTests\System.Windows.Forms.Tests.csproj (System.Windows.Forms.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using Microsoft.DotNet.RemoteExecutor;
using static System.Windows.Forms.Application;
 
namespace System.Windows.Forms.Tests;
 
public class ParkingWindowTests
{
    [WinFormsFact(Skip = "Crash with AbandonedMutexException. See: https://github.com/dotnet/arcade/issues/5325")]
    public void ParkingWindow_DoesNotThrowOnGarbageCollecting()
    {
        using RemoteInvokeHandle invokerHandle = RemoteExecutor.Invoke(() =>
        {
            Control.CheckForIllegalCrossThreadCalls = true;
 
            Form form = InitFormWithControlToGarbageCollect();
 
            try
            {
                // Force garbage collecting to access ComboBox from another (GC) thread.
                GC.Collect();
 
                GC.WaitForPendingFinalizers();
            }
            catch (Exception ex)
            {
                Assert.True(ex is null, $"Expected no exception, but got: {ex.Message}"); // Actually need to check whether GC.Collect() does not throw exception.
            }
        });
 
        // verify the remote process succeeded
        Assert.Equal(RemoteExecutor.SuccessExitCode, invokerHandle.ExitCode);
    }
 
    private Form InitFormWithControlToGarbageCollect()
    {
        Form form = new();
        ComboBox comboBox = new()
        {
            DropDownStyle = ComboBoxStyle.DropDown
        };
 
        form.Controls.Add(comboBox);
        form.Show();
 
        // Park ComboBox handle in ParkingWindow.
        comboBox.Parent = null;
 
        // Recreate ComboBox handle to set parent to ParkingWindow.
        comboBox.DropDownStyle = ComboBoxStyle.DropDownList;
 
        // Lose the reference to ComboBox to allow Garbage collecting ComboBox.
        comboBox = null;
 
        return form;
    }
 
    [WinFormsFact]
    public void ParkingWindow_Unaware()
    {
        // Run tests only on Windows 10 versions that support thread dpi awareness.
        if (!PlatformDetection.IsWindows10Version1803OrGreater)
        {
            return;
        }
 
        // set thread awareness context to PermonitorV2(PMv2).
        // if process/thread is not in PMv2, calling 'EnterDpiAwarenessScope' is a no-op and that is by design.
        // In this case, we will be setting thread to PMv2 mode and then scope to UNAWARE
        DPI_AWARENESS_CONTEXT originalAwarenessContext = PInvoke.SetThreadDpiAwarenessContextInternal(DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
 
        try
        {
            using (ScaleHelper.EnterDpiAwarenessScope(DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_UNAWARE))
            {
                using Control control = new();
                ThreadContext ctx = GetContextForHandle(control);
                Assert.NotNull(ctx);
                ParkingWindow parkingWindow = ctx.TestAccessor().Dynamic.GetParkingWindowForContext(DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_UNAWARE);
                Assert.NotNull(parkingWindow);
 
                DPI_AWARENESS_CONTEXT dpiContext = PInvoke.GetWindowDpiAwarenessContext(parkingWindow.HWND);
                Assert.True(dpiContext.IsEquivalent(DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_UNAWARE));
            }
        }
        finally
        {
            // reset back to original awareness context.
            PInvoke.SetThreadDpiAwarenessContextInternal(originalAwarenessContext);
        }
    }
 
    [WinFormsFact]
    public void ParkingWindow_SystemAware()
    {
        // run tests only on Windows 10 versions that support thread dpi awareness.
        if (!PlatformDetection.IsWindows10Version1803OrGreater)
        {
            return;
        }
 
        // set thread awareness context to PermonitorV2(PMv2).
        // if process/thread is not in PMv2, calling 'EnterDpiAwarenessScope' is a no-op and that is by design.
        // In this case, we will be setting thread to PMv2 mode and then scope to UNAWARE
        DPI_AWARENESS_CONTEXT originalAwarenessContext = PInvoke.SetThreadDpiAwarenessContextInternal(DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
 
        try
        {
            using (ScaleHelper.EnterDpiAwarenessScope(DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_SYSTEM_AWARE))
            {
                using Control control = new();
                ThreadContext ctx = GetContextForHandle(control);
                Assert.NotNull(ctx);
                ParkingWindow parkingWindow = ctx.TestAccessor().Dynamic.GetParkingWindowForContext(DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_SYSTEM_AWARE);
                Assert.NotNull(parkingWindow);
 
                DPI_AWARENESS_CONTEXT dpiContext = PInvoke.GetWindowDpiAwarenessContext(parkingWindow.HWND);
                Assert.True(dpiContext.IsEquivalent(DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_SYSTEM_AWARE));
            }
        }
        finally
        {
            // reset back to original awareness context.
            PInvoke.SetThreadDpiAwarenessContextInternal(originalAwarenessContext);
        }
    }
 
    [WinFormsFact]
    public void ParkingWindow_PermonitorV2()
    {
        // run tests only on Windows 10 versions that support thread dpi awareness.
        if (!PlatformDetection.IsWindows10Version1803OrGreater)
        {
            return;
        }
 
        // set thread awareness context to PermonitorV2(PMv2).
        // if process/thread is not in PMv2, calling 'EnterDpiAwarenessScope' is a no-op and that is by design.
        // In this case, we will be setting thread to PMv2 mode and then scope to UNAWARE
        DPI_AWARENESS_CONTEXT originalAwarenessContext = PInvoke.SetThreadDpiAwarenessContextInternal(DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
 
        try
        {
            using Control control = new();
            ThreadContext ctx = GetContextForHandle(control);
            Assert.NotNull(ctx);
 
            ParkingWindow parkingWindow = ctx.TestAccessor().Dynamic.GetParkingWindowForContext(DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
            Assert.NotNull(parkingWindow);
 
            DPI_AWARENESS_CONTEXT dpiContext = PInvoke.GetWindowDpiAwarenessContext(parkingWindow.HWND);
            Assert.True(dpiContext.IsEquivalent(DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2));
        }
        finally
        {
            // reset back to original awareness context.
            PInvoke.SetThreadDpiAwarenessContextInternal(originalAwarenessContext);
        }
    }
 
    [WinFormsFact]
    public void ParkingWindow_Multiple()
    {
        // run tests only on Windows 10 versions that support thread dpi awareness.
        if (!PlatformDetection.IsWindows10Version1803OrGreater)
        {
            return;
        }
 
        // set thread awareness context to PermonitorV2(PMv2).
        // if process/thread is not in PMv2, calling 'EnterDpiAwarenessScope' is a no-op and that is by design.
        // In this case, we will be setting thread to PMv2 mode and then scope to UNAWARE
        DPI_AWARENESS_CONTEXT originalAwarenessContext = PInvoke.SetThreadDpiAwarenessContextInternal(DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
 
        try
        {
            using Control control = new();
            ThreadContext ctx = GetContextForHandle(control);
            Assert.NotNull(ctx);
            ParkingWindow parkingWindow = ctx.TestAccessor().Dynamic.GetParkingWindowForContext(DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
            Assert.NotNull(parkingWindow);
 
            DPI_AWARENESS_CONTEXT dpiContext = PInvoke.GetWindowDpiAwarenessContext(parkingWindow.HWND);
            Assert.True(dpiContext.IsEquivalent(DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2));
 
            using (ScaleHelper.EnterDpiAwarenessScope(DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_SYSTEM_AWARE))
            {
                using Control systemControl = new();
                ctx = GetContextForHandle(systemControl);
                Assert.NotNull(ctx);
                parkingWindow = ctx.TestAccessor().Dynamic.GetParkingWindowForContext(DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_SYSTEM_AWARE);
                Assert.NotNull(parkingWindow);
 
                dpiContext = PInvoke.GetWindowDpiAwarenessContext(parkingWindow.HWND);
                Assert.True(dpiContext.IsEquivalent(DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_SYSTEM_AWARE));
 
                // check PMv2 parking window still available.
                parkingWindow = ctx.TestAccessor().Dynamic.GetParkingWindowForContext(DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
                Assert.NotNull(parkingWindow);
 
                dpiContext = PInvoke.GetWindowDpiAwarenessContext(parkingWindow.HWND);
                Assert.True(dpiContext.IsEquivalent(DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2));
            }
        }
        finally
        {
            // reset back to original awareness context.
            PInvoke.SetThreadDpiAwarenessContextInternal(originalAwarenessContext);
        }
    }
}