File: FormOwnerTestForm.cs
Web Access
Project: src\src\System.Windows.Forms\tests\IntegrationTests\WinformsControlsTest\WinformsControlsTest.csproj (WinFormsControlsTest)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Drawing;
 
namespace WinFormsControlsTest;
 
[DesignerCategory("Default")]
internal class FormOwnerTestForm : Form
{
    public FormOwnerTestForm()
    {
        Shown += (object sender, EventArgs e) => RunFormOwnerMemoryLeakTest();
    }
 
    /// <summary>
    /// This method runs a loop to open and close a parent form.
    /// The parent form opens a child form.show(IWin32Window) overload.
    /// The parent form is then closed, which also closes the child.
    /// The output shows how much memory was used in the process and
    /// can be used to check if a memory leak is present.
    /// This is used to test the bug: https://github.com/dotnet/winforms/issues/530
    /// </summary>
    private void RunFormOwnerMemoryLeakTest()
    {
        long memoryStart = GC.GetTotalMemory(false);
 
        List<Form> childForms = [];
        for (int i = 0; i < 500; i++)
        {
            MemoryTestParentForm parent = new();
            parent.Show();
            childForms.Add(parent.CreateChildFormWithMemory());
            parent.Close();
        }
 
        childForms.ForEach(childForm => childForm.Close());
        childForms.Clear();
 
        long memoryBeforeGC = GC.GetTotalMemory(false);
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();
        long memoryAfterGC = GC.GetTotalMemory(true);
 
        Controls.Add(new Label()
        {
            Text = $"Memory Usage:\nBefore: {memoryStart:N0} bytes\nDuring: {memoryBeforeGC:N0} bytes\nAfter: {memoryAfterGC:N0} bytes",
            Dock = DockStyle.Fill,
            TextAlign = ContentAlignment.MiddleCenter
        });
    }
 
    public class MemoryTestParentForm : Form
    {
        private byte[] _array;
 
        public Form CreateChildFormWithMemory()
        {
            // Create a byte array to consume memory on the parent so the leak is more obvious.
            _array = new byte[1024 * 1024];
            Array.Clear(_array, 0, _array.Length);
            Form child = new();
 
            // Show the child and pass the parent as the owner, this is where the leak can happen.
            child.Show(this);
            return child;
        }
    }
}