Browse Source

Add finalizer to Win32Icon which frees its GDI handle (#19813)

Improve error messages when we run out of GDI handles
Removed SetLastError from GDI methods; they don't use that system
release/11.3.8
Tom Edwards 4 months ago
committed by Julien Lebosquain
parent
commit
c989818851
  1. 8
      src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
  2. 35
      src/Windows/Avalonia.Win32/Interop/Win32Icon.cs

8
src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs

@ -1370,10 +1370,10 @@ namespace Avalonia.Win32.Interop
[DllImport("user32.dll", EntryPoint = "LoadCursorW", ExactSpelling = true)]
public static extern IntPtr LoadCursor(IntPtr hInstance, IntPtr lpCursorName);
[DllImport("user32.dll")]
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr CreateIconIndirect([In] ref ICONINFO iconInfo);
[DllImport("user32.dll")]
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr CreateIconFromResourceEx(byte* pbIconBits, uint cbIconBits,
int fIcon, int dwVersion, int csDesired, int cyDesired, int flags);
@ -1683,9 +1683,9 @@ namespace Avalonia.Win32.Interop
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(IntPtr hObject);
[DllImport("gdi32.dll", SetLastError = true)]
[DllImport("gdi32.dll")]
public static extern IntPtr CreateDIBSection(IntPtr hDC, ref BITMAPINFOHEADER pBitmapInfo, int un, out IntPtr lplpVoid, IntPtr handle, int dw);
[DllImport("gdi32.dll", SetLastError = true)]
[DllImport("gdi32.dll")]
public static extern IntPtr CreateBitmap(int width, int height, int planes, int bitCount, IntPtr data);
[DllImport("gdi32.dll")]
public static extern int DeleteObject(IntPtr hObject);

35
src/Windows/Avalonia.Win32/Interop/Win32Icon.cs

@ -49,23 +49,30 @@ internal class Win32Icon : IDisposable
}
}
~Win32Icon()
{
UnmanagedMethods.DestroyIcon(Handle);
}
public IntPtr Handle { get; private set; }
public PixelSize Size { get; }
private readonly byte[]? _bytes;
private const string GdiError = "A GDI+ error occurred.";
IntPtr CreateIcon(Bitmap bitmap, PixelPoint hotSpot = default)
{
var mainBitmap = CreateHBitmap(bitmap);
if (mainBitmap == IntPtr.Zero)
throw new Win32Exception();
throw new Win32Exception(GdiError);
var alphaBitmap = AlphaToMask(bitmap);
try
{
if (alphaBitmap == IntPtr.Zero)
throw new Win32Exception();
throw new Win32Exception(GdiError);
var info = new UnmanagedMethods.ICONINFO
{
IsIcon = false,
@ -77,7 +84,7 @@ internal class Win32Icon : IDisposable
var hIcon = UnmanagedMethods.CreateIconIndirect(ref info);
if (hIcon == IntPtr.Zero)
throw new Win32Exception();
throw new Win32Exception(Marshal.GetLastWin32Error());
return hIcon;
}
finally
@ -303,6 +310,7 @@ internal class Win32Icon : IDisposable
var bestSize = new PixelSize(bestWidth, bestHeight);
IntPtr handle;
// Copy the bytes into an aligned buffer if needed.
if ((_bestImageOffset % IntPtr.Size) != 0)
{
@ -314,8 +322,8 @@ internal class Win32Icon : IDisposable
{
fixed (byte* pbAlignedBuffer = alignedBuffer)
{
return (UnmanagedMethods.CreateIconFromResourceEx(pbAlignedBuffer, _bestBytesInRes, 1,
0x00030000, 0, 0, 0), bestSize);
handle = UnmanagedMethods.CreateIconFromResourceEx(pbAlignedBuffer, _bestBytesInRes, 1,
0x00030000, 0, 0, 0);
}
}
finally
@ -328,14 +336,26 @@ internal class Win32Icon : IDisposable
{
try
{
return (UnmanagedMethods.CreateIconFromResourceEx(checked(b + _bestImageOffset), _bestBytesInRes,
1, 0x00030000, 0, 0, 0), bestSize);
handle = UnmanagedMethods.CreateIconFromResourceEx(checked(b + _bestImageOffset), _bestBytesInRes,
1, 0x00030000, 0, 0, 0);
}
catch (OverflowException)
{
return default;
}
}
if (handle == default)
{
var error = Marshal.GetLastWin32Error();
// If there we are out of GDI handles then our fallback won't work either, so throw now.
if (error == (int)Windows.Win32.Foundation.WIN32_ERROR.ERROR_NOT_ENOUGH_MEMORY)
{
throw new Win32Exception(error);
}
}
return (handle, bestSize);
}
}
@ -348,5 +368,6 @@ internal class Win32Icon : IDisposable
{
UnmanagedMethods.DestroyIcon(Handle);
Handle = IntPtr.Zero;
GC.SuppressFinalize(this);
}
}

Loading…
Cancel
Save