diff --git a/src/System.Windows.Forms.Primitives/src/System/Windows/Forms/Internals/ScaleHelper.cs b/src/System.Windows.Forms.Primitives/src/System/Windows/Forms/Internals/ScaleHelper.cs index 788671cfc6b..764a35ca3a1 100644 --- a/src/System.Windows.Forms.Primitives/src/System/Windows/Forms/Internals/ScaleHelper.cs +++ b/src/System.Windows.Forms.Primitives/src/System/Windows/Forms/Internals/ScaleHelper.cs @@ -424,12 +424,12 @@ internal static HighDpiMode GetThreadHighDpiMode() /// /// Get X, Y metrics at DPI, IF icon is not already that size, create and return a new one. /// - internal static Icon ScaleSmallIconToDpi(Icon icon, int dpi) + internal static Icon ScaleSmallIconToDpi(Icon icon, int dpi, bool alwaysCreateNew = false) { int width = PInvoke.GetCurrentSystemMetrics(SYSTEM_METRICS_INDEX.SM_CXSMICON, (uint)dpi); int height = PInvoke.GetCurrentSystemMetrics(SYSTEM_METRICS_INDEX.SM_CYSMICON, (uint)dpi); - return (icon.Width == width && icon.Height == height) ? icon : new(icon, width, height); + return (icon.Width == width && icon.Height == height && !alwaysCreateNew) ? icon : new(icon, width, height); } /// diff --git a/src/System.Windows.Forms/System/Windows/Forms/Form.cs b/src/System.Windows.Forms/System/Windows/Forms/Form.cs index a977b9de151..c3900d09c6b 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Form.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Form.cs @@ -6453,11 +6453,14 @@ private unsafe void UpdateWindowIcon(bool redrawFrame, int dpi = 0) if (icon is not null) { - Icon? oldSmallIcon = _smallIcon; - try { - _smallIcon = ScaleHelper.ScaleSmallIconToDpi(icon, dpi); + // As we dispose the _smallIcon in multiple places, we'd need to track when we've + // actually created a scaled icon to know when we should free it selectively (so we + // don't free user provided Icons. For now, just always create a new scaled icon. + _smallIcon?.Dispose(); + _smallIcon = null; + _smallIcon = ScaleHelper.ScaleSmallIconToDpi(icon, dpi, alwaysCreateNew: true); } catch { @@ -6466,10 +6469,6 @@ private unsafe void UpdateWindowIcon(bool redrawFrame, int dpi = 0) if (_smallIcon is not null) { PInvokeCore.SendMessage(this, PInvokeCore.WM_SETICON, (WPARAM)PInvoke.ICON_SMALL, (LPARAM)_smallIcon.Handle); - if (oldSmallIcon is not null && oldSmallIcon.Handle != _smallIcon.Handle) - { - oldSmallIcon.Dispose(); - } } PInvokeCore.SendMessage(this, PInvokeCore.WM_SETICON, (WPARAM)PInvoke.ICON_BIG, (LPARAM)icon.Handle); diff --git a/src/test/unit/System.Windows.Forms/System/Windows/Forms/FormTests.cs b/src/test/unit/System.Windows.Forms/System/Windows/Forms/FormTests.cs index 365b9bc3370..26574491052 100644 --- a/src/test/unit/System.Windows.Forms/System/Windows/Forms/FormTests.cs +++ b/src/test/unit/System.Windows.Forms/System/Windows/Forms/FormTests.cs @@ -2748,6 +2748,21 @@ public void Form_DoesNot_PreventShutDown() Assert.True((BOOL)message.ResultInternal); } + [WinFormsFact] + public void Form_DoesNot_DisposeUserIcon() + { + // https://github.com/dotnet/winforms/issues/13963 + using Form form = new(); + int dpi = form.DeviceDpi; + using Icon icon = new(typeof(Form), "wfc"); + int smallDpi = PInvoke.GetCurrentSystemMetrics(SYSTEM_METRICS_INDEX.SM_CXSMICON, (uint)dpi); + using Icon smallIcon = new(icon, new Size(smallDpi, smallDpi)); + form.Icon = smallIcon; + form.Show(); + form.Close(); + smallIcon.Handle.Should().NotBe(0); + } + public partial class ParentedForm : Form { private ParentingForm _parentForm;