Skip to content

Commit 93e65c0

Browse files
authored
[release/10.0] Always create new small icons for forms (#13984)
Backport of #13983 to release/10.0 /cc @JeremyKuhne ## Customer Impact Users setting a Form Icon that matches the current screen small icon size will have their Icon disposed, causing an `ObjectDisposedException` when using the original Icon. There is no easy workaround. ## Testing Manual testing with user scenario. Regression test. ## Risk Low, now always creating a new Icon instance.
2 parents d5eebf7 + ac2f620 commit 93e65c0

File tree

3 files changed

+23
-9
lines changed
  • src
    • System.Windows.Forms.Primitives/src/System/Windows/Forms/Internals
    • System.Windows.Forms/System/Windows/Forms
    • test/unit/System.Windows.Forms/System/Windows/Forms

3 files changed

+23
-9
lines changed

src/System.Windows.Forms.Primitives/src/System/Windows/Forms/Internals/ScaleHelper.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -424,12 +424,12 @@ internal static HighDpiMode GetThreadHighDpiMode()
424424
/// <summary>
425425
/// Get X, Y metrics at DPI, IF icon is not already that size, create and return a new one.
426426
/// </summary>
427-
internal static Icon ScaleSmallIconToDpi(Icon icon, int dpi)
427+
internal static Icon ScaleSmallIconToDpi(Icon icon, int dpi, bool alwaysCreateNew = false)
428428
{
429429
int width = PInvoke.GetCurrentSystemMetrics(SYSTEM_METRICS_INDEX.SM_CXSMICON, (uint)dpi);
430430
int height = PInvoke.GetCurrentSystemMetrics(SYSTEM_METRICS_INDEX.SM_CYSMICON, (uint)dpi);
431431

432-
return (icon.Width == width && icon.Height == height) ? icon : new(icon, width, height);
432+
return (icon.Width == width && icon.Height == height && !alwaysCreateNew) ? icon : new(icon, width, height);
433433
}
434434

435435
/// <summary>

src/System.Windows.Forms/System/Windows/Forms/Form.cs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6453,11 +6453,14 @@ private unsafe void UpdateWindowIcon(bool redrawFrame, int dpi = 0)
64536453

64546454
if (icon is not null)
64556455
{
6456-
Icon? oldSmallIcon = _smallIcon;
6457-
64586456
try
64596457
{
6460-
_smallIcon = ScaleHelper.ScaleSmallIconToDpi(icon, dpi);
6458+
// As we dispose the _smallIcon in multiple places, we'd need to track when we've
6459+
// actually created a scaled icon to know when we should free it selectively (so we
6460+
// don't free user provided Icons. For now, just always create a new scaled icon.
6461+
_smallIcon?.Dispose();
6462+
_smallIcon = null;
6463+
_smallIcon = ScaleHelper.ScaleSmallIconToDpi(icon, dpi, alwaysCreateNew: true);
64616464
}
64626465
catch
64636466
{
@@ -6466,10 +6469,6 @@ private unsafe void UpdateWindowIcon(bool redrawFrame, int dpi = 0)
64666469
if (_smallIcon is not null)
64676470
{
64686471
PInvokeCore.SendMessage(this, PInvokeCore.WM_SETICON, (WPARAM)PInvoke.ICON_SMALL, (LPARAM)_smallIcon.Handle);
6469-
if (oldSmallIcon is not null && oldSmallIcon.Handle != _smallIcon.Handle)
6470-
{
6471-
oldSmallIcon.Dispose();
6472-
}
64736472
}
64746473

64756474
PInvokeCore.SendMessage(this, PInvokeCore.WM_SETICON, (WPARAM)PInvoke.ICON_BIG, (LPARAM)icon.Handle);

src/test/unit/System.Windows.Forms/System/Windows/Forms/FormTests.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2748,6 +2748,21 @@ public void Form_DoesNot_PreventShutDown()
27482748
Assert.True((BOOL)message.ResultInternal);
27492749
}
27502750

2751+
[WinFormsFact]
2752+
public void Form_DoesNot_DisposeUserIcon()
2753+
{
2754+
// https://github.com/dotnet/winforms/issues/13963
2755+
using Form form = new();
2756+
int dpi = form.DeviceDpi;
2757+
using Icon icon = new(typeof(Form), "wfc");
2758+
int smallDpi = PInvoke.GetCurrentSystemMetrics(SYSTEM_METRICS_INDEX.SM_CXSMICON, (uint)dpi);
2759+
using Icon smallIcon = new(icon, new Size(smallDpi, smallDpi));
2760+
form.Icon = smallIcon;
2761+
form.Show();
2762+
form.Close();
2763+
smallIcon.Handle.Should().NotBe(0);
2764+
}
2765+
27512766
public partial class ParentedForm : Form
27522767
{
27532768
private ParentingForm _parentForm;

0 commit comments

Comments
 (0)