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;