Icon
public sealed class Icon : MarshalByRefObject, ICloneable, IDisposable, ISerializable, IIcon, IHandle<HICON>
Represents a Windows icon, which is a small bitmap image that is used to represent an object. Icons can be thought of as transparent bitmaps, although their size is determined by the system.
using System.Buffers;
using System.ComponentModel;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.Graphics.Gdi;
using Windows.Win32.System.Com;
using Windows.Win32.System.Ole;
using Windows.Win32.UI.WindowsAndMessaging;
namespace System.Drawing
{
[Serializable]
[NullableContext(1)]
[Nullable(0)]
[Editor("System.Drawing.Design.IconEditor, System.Drawing.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
[TypeConverter(typeof(IconConverter))]
[TypeForwardedFrom("System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
public sealed class Icon : MarshalByRefObject, ICloneable, IDisposable, ISerializable, IIcon, IHandle<HICON>
{
private static int s_bitDepth;
private const int PNGSignature1 = 1196314761;
private const int PNGSignature2 = 169478669;
[Nullable(2)]
private readonly byte[] _iconData;
private uint _bestImageOffset;
private uint _bestBitDepth;
private uint _bestBytesInRes;
private bool? _isBestImagePng;
private Size _iconSize = Size.Empty;
[NonSerialized]
private HICON _handle;
private readonly bool _ownHandle = true;
[Browsable(false)]
public IntPtr Handle {
get {
if (!_handle.IsNull)
return _handle;
throw new ObjectDisposedException(GetType().Name);
}
}
HICON IHandle<HICON>.Handle {
get {
return (HICON)Handle;
}
}
[Browsable(false)]
public int Height {
get {
return Size.Height;
}
}
public unsafe Size Size {
get {
if (!_iconSize.IsEmpty)
return _iconSize;
ICONINFO iconInfo = PInvokeCore.GetIconInfo(this);
BITMAP bITMAP = default(BITMAP);
if (!iconInfo.hbmColor.IsNull) {
PInvokeCore.GetObject(iconInfo.hbmColor, sizeof(BITMAP), &bITMAP);
PInvokeCore.DeleteObject(iconInfo.hbmColor);
_iconSize = new Size(bITMAP.bmWidth, bITMAP.bmHeight);
} else if (!iconInfo.hbmMask.IsNull) {
PInvokeCore.GetObject(iconInfo.hbmMask, sizeof(BITMAP), &bITMAP);
_iconSize = new Size(bITMAP.bmWidth, bITMAP.bmHeight / 2);
}
if (!iconInfo.hbmMask.IsNull)
PInvokeCore.DeleteObject(iconInfo.hbmMask);
return _iconSize;
}
}
[Browsable(false)]
public int Width {
get {
return Size.Width;
}
}
private Icon()
{
}
internal Icon(HICON handle)
: this(handle, false)
{
}
internal Icon(HICON handle, bool takeOwnership)
{
if (handle.IsNull)
throw new ArgumentException(System.SR.Format(System.SR.InvalidGDIHandle, "Icon"));
_handle = handle;
_ownHandle = takeOwnership;
}
public Icon(string fileName)
: this(fileName, 0, 0)
{
}
public Icon(string fileName, Size size)
: this(fileName, size.Width, size.Height)
{
}
public Icon(string fileName, int width, int height)
: this()
{
using (FileStream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) {
_iconData = new byte[(int)fileStream.Length];
fileStream.ReadExactly(_iconData);
}
Initialize(width, height);
}
public Icon(Icon original, Size size)
: this(original, size.Width, size.Height)
{
}
public Icon(Icon original, int width, int height)
: this()
{
ArgumentNullException.ThrowIfNull(original, "original");
_iconData = original._iconData;
if (_iconData == null) {
_iconSize = original.Size;
_handle = PInvokeCore.CopyIcon(original, _iconSize.Width, _iconSize.Height, IMAGE_FLAGS.LR_DEFAULTCOLOR);
} else
Initialize(width, height);
}
public Icon(Type type, string resource)
: this()
{
ArgumentNullException.ThrowIfNull(resource, "resource");
Stream manifestResourceStream = type.Module.Assembly.GetManifestResourceStream(type, resource);
if (manifestResourceStream == null)
throw new ArgumentException(System.SR.Format(System.SR.ResourceNotFound, type, resource));
Stream stream = manifestResourceStream;
_iconData = new byte[(int)stream.Length];
stream.ReadExactly(_iconData);
Initialize(0, 0);
}
public Icon(Stream stream)
: this(stream, 0, 0)
{
}
public Icon(Stream stream, Size size)
: this(stream, size.Width, size.Height)
{
}
public Icon(Stream stream, int width, int height)
: this()
{
ArgumentNullException.ThrowIfNull(stream, "stream");
_iconData = new byte[(int)stream.Length];
stream.ReadExactly(_iconData);
Initialize(width, height);
}
private Icon(SerializationInfo info, StreamingContext context)
{
_iconData = (byte[])info.GetValue("IconData", typeof(byte[]));
_iconSize = (Size)info.GetValue("IconSize", typeof(Size));
Initialize(_iconSize.Width, _iconSize.Height);
}
void ISerializable.GetObjectData(SerializationInfo si, StreamingContext context)
{
if (_iconData != null)
si.AddValue("IconData", _iconData, typeof(byte[]));
else {
MemoryStream memoryStream = new MemoryStream();
Save(memoryStream);
si.AddValue("IconData", memoryStream.ToArray(), typeof(byte[]));
}
si.AddValue("IconSize", _iconSize, typeof(Size));
}
[return: Nullable(2)]
public static Icon ExtractAssociatedIcon(string filePath)
{
return ExtractAssociatedIcon(filePath, 0);
}
[return: Nullable(2)]
private unsafe static Icon ExtractAssociatedIcon(string filePath, int index)
{
ArgumentNullException.ThrowIfNull(filePath, "filePath");
if (string.IsNullOrEmpty(filePath))
throw new ArgumentException(System.SR.NullOrEmptyPath, "filePath");
filePath = Path.GetFullPath(filePath);
if (!File.Exists(filePath))
throw new FileNotFoundException(null, filePath);
char[] array = ArrayPool<char>.Shared.Rent(Math.Max(260, filePath.Length));
filePath.CopyTo(0, array, 0, filePath.Length);
array[filePath.Length] = ' ';
HICON handle;
fixed (char* value = array) {
ushort num = (ushort)index;
handle = PInvoke.ExtractAssociatedIcon(HINSTANCE.Null, value, &num);
}
ArrayPool<char>.Shared.Return(array, false);
if (handle.IsNull)
return null;
return new Icon(handle, true);
}
public object Clone()
{
Size size = Size;
int width = size.Width;
size = Size;
return new Icon(this, width, size.Height);
}
internal void DestroyHandle()
{
if (_ownHandle) {
PInvokeCore.DestroyIcon(_handle);
_handle = HICON.Null;
GC.KeepAlive(this);
}
}
public void Dispose()
{
if (!_handle.IsNull)
DestroyHandle();
GC.SuppressFinalize(this);
}
private void DrawIcon(HDC hdc, Rectangle imageRect, Rectangle targetRect, bool stretch)
{
int num = 0;
int num2 = 0;
int num3 = 0;
int num4 = 0;
Size size = Size;
int width;
int height;
if (!imageRect.IsEmpty) {
num = imageRect.X;
num2 = imageRect.Y;
width = imageRect.Width;
height = imageRect.Height;
} else {
width = size.Width;
height = size.Height;
}
int width2;
int height2;
if (!targetRect.IsEmpty) {
num3 = targetRect.X;
num4 = targetRect.Y;
width2 = targetRect.Width;
height2 = targetRect.Height;
} else {
width2 = size.Width;
height2 = size.Height;
}
int cxWidth;
int cyWidth;
int num5;
int num6;
if (stretch) {
cxWidth = size.Width * width2 / width;
cyWidth = size.Height * height2 / height;
num5 = width2;
num6 = height2;
} else {
cxWidth = size.Width;
cyWidth = size.Height;
num5 = ((width2 < width) ? width2 : width);
num6 = ((height2 < height) ? height2 : height);
}
RegionScope regionScope = new RegionScope(hdc);
try {
try {
PInvokeCore.IntersectClipRect(hdc, num3, num4, num3 + num5, num4 + num6);
PInvokeCore.DrawIconEx(hdc, num3 - num, num4 - num2, this, cxWidth, cyWidth, DI_FLAGS.DI_NORMAL);
} finally {
PInvokeCore.SelectClipRgn(hdc, regionScope);
}
} finally {
regionScope.Dispose();
}
}
internal void Draw(Graphics graphics, int x, int y)
{
Size size = Size;
Draw(graphics, new Rectangle(x, y, size.Width, size.Height));
}
internal void Draw(Graphics graphics, Rectangle targetRect)
{
Rectangle targetRect2 = targetRect;
using (Matrix matrix = graphics.Transform) {
PointF offset = matrix.Offset;
targetRect2.X += (int)offset.X;
targetRect2.Y += (int)offset.Y;
DeviceContextHdcScope scope = new DeviceContextHdcScope(graphics, ApplyGraphicsProperties.Clipping, false);
try {
DrawIcon((HDC)ref scope, Rectangle.Empty, targetRect2, true);
} finally {
scope.Dispose();
}
}
}
internal void DrawUnstretched(Graphics graphics, Rectangle targetRect)
{
Rectangle targetRect2 = targetRect;
using (Matrix matrix = graphics.Transform) {
PointF offset = matrix.Offset;
targetRect2.X += (int)offset.X;
targetRect2.Y += (int)offset.Y;
DeviceContextHdcScope scope = new DeviceContextHdcScope(graphics, ApplyGraphicsProperties.Clipping, false);
try {
DrawIcon((HDC)ref scope, Rectangle.Empty, targetRect2, false);
} finally {
scope.Dispose();
}
}
}
~Icon()
{
Dispose();
}
public static Icon FromHandle(IntPtr handle)
{
if (handle == IntPtr.Zero)
throw new ArgumentException(null, "handle");
return new Icon((HICON)handle);
}
private unsafe void Initialize(int width, int height)
{
if (_iconData == null || !_handle.IsNull)
throw new InvalidOperationException(System.SR.Format(System.SR.IllegalState, GetType().Name));
SpanReader<byte> spanReader = new SpanReader<byte>(_iconData);
ICONDIR value;
if (!spanReader.TryRead(out value) || value.idReserved != 0 || value.idType != 1 || value.idCount == 0)
throw new ArgumentException(System.SR.Format(System.SR.InvalidPictureType, "picture", "Icon"));
if (width == 0)
width = PInvokeCore.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CXICON);
if (height == 0)
height = PInvokeCore.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CYICON);
if (s_bitDepth == 0) {
GetDcScope scope = GetDcScope.ScreenDC;
try {
s_bitDepth = PInvokeCore.GetDeviceCaps((HDC)ref scope, GET_DEVICE_CAPS_INDEX.BITSPIXEL);
s_bitDepth *= PInvokeCore.GetDeviceCaps((HDC)ref scope, GET_DEVICE_CAPS_INDEX.PLANES);
if (s_bitDepth == 8)
s_bitDepth = 4;
} finally {
scope.Dispose();
}
}
byte b = 0;
byte b2 = 0;
if (!spanReader.TryRead(value.idCount, out ReadOnlySpan<ICONDIRENTRY> value2))
throw new ArgumentException(System.SR.Format(System.SR.InvalidPictureType, "picture", "Icon"));
ReadOnlySpan<ICONDIRENTRY> readOnlySpan = value2;
foreach (ICONDIRENTRY iCONDIRENTRY in readOnlySpan) {
bool flag = false;
uint num;
if (iCONDIRENTRY.bColorCount != 0) {
num = 4;
if (iCONDIRENTRY.bColorCount < 16)
num = 1;
} else
num = iCONDIRENTRY.wBitCount;
if (num == 0)
num = 8;
if (_bestBytesInRes == 0)
flag = true;
else {
int num2 = Math.Abs(b - width) + Math.Abs(b2 - height);
int num3 = Math.Abs(iCONDIRENTRY.bWidth - width) + Math.Abs(iCONDIRENTRY.bHeight - height);
if (num3 < num2 || (num3 == num2 && ((num <= s_bitDepth && num > _bestBitDepth) || (_bestBitDepth > s_bitDepth && num < _bestBitDepth))))
flag = true;
}
if (flag) {
b = iCONDIRENTRY.bWidth;
b2 = iCONDIRENTRY.bHeight;
_bestImageOffset = iCONDIRENTRY.dwImageOffset;
_bestBytesInRes = iCONDIRENTRY.dwBytesInRes;
_bestBitDepth = num;
}
}
if (_bestImageOffset > 2147483647)
throw new ArgumentException(System.SR.Format(System.SR.InvalidPictureType, "picture", "Icon"));
if (_bestBytesInRes > 2147483647)
throw new Win32Exception(87);
uint num4;
try {
num4 = checked(_bestImageOffset + _bestBytesInRes);
} catch (OverflowException) {
throw new Win32Exception(87);
}
if (num4 > _iconData.Length)
throw new ArgumentException(System.SR.Format(System.SR.InvalidPictureType, "picture", "Icon"));
ReadOnlySpan<byte> readOnlySpan2 = spanReader.Span.Slice((int)_bestImageOffset, (int)_bestBytesInRes);
if ((long)_bestImageOffset % (long)sizeof(IntPtr) == 0) {
fixed (byte* presbits = &readOnlySpan2.GetPinnableReference()) {
_handle = PInvoke.CreateIconFromResourceEx(presbits, (uint)readOnlySpan2.Length, true, 196608, 0, 0, IMAGE_FLAGS.LR_DEFAULTCOLOR);
}
} else {
BufferScope<byte> bufferScope = new BufferScope<byte>((int)_bestBytesInRes);
try {
readOnlySpan2.CopyTo(bufferScope.AsSpan());
fixed (byte* presbits2 = &bufferScope.GetPinnableReference()) {
_handle = PInvoke.CreateIconFromResourceEx(presbits2, (uint)readOnlySpan2.Length, true, 196608, 0, 0, IMAGE_FLAGS.LR_DEFAULTCOLOR);
}
} finally {
bufferScope.Dispose();
}
}
if (_handle.IsNull)
throw new Win32Exception();
}
private unsafe void CopyBitmapData(BitmapData sourceData, BitmapData targetData)
{
byte* ptr = (byte*)(long)sourceData.Scan0;
byte* ptr2 = (byte*)(long)targetData.Scan0;
int num = Math.Min(sourceData.Height, targetData.Height);
long num2 = Math.Abs(targetData.Stride);
for (int i = 0; i < num; i++) {
Buffer.MemoryCopy(ptr, ptr2, num2, num2);
ptr += sourceData.Stride;
ptr2 += targetData.Stride;
}
GC.KeepAlive(this);
}
private unsafe static bool BitmapHasAlpha(BitmapData bmpData)
{
for (int i = 0; i < bmpData.Height; i++) {
for (int j = 3; j < Math.Abs(bmpData.Stride); j += 4) {
if (((byte*)bmpData.Scan0.ToPointer() + i * bmpData.Stride)[j] != 0)
return true;
}
}
return false;
}
public Bitmap ToBitmap()
{
if (HasPngSignature() && !System.LocalAppContextSwitches.DontSupportPngFramesInIcons) {
using (MemoryStream memoryStream = new MemoryStream()) {
memoryStream.Write(_iconData, (int)_bestImageOffset, (int)_bestBytesInRes);
return new Bitmap(memoryStream);
}
}
return BmpFrame();
}
private unsafe Bitmap BmpFrame()
{
Bitmap bitmap = null;
if (_iconData != null && _bestBitDepth == 32) {
Size size = Size;
int width = size.Width;
size = Size;
bitmap = new Bitmap(width, size.Height, PixelFormat.Format32bppArgb);
Bitmap bitmap2 = bitmap;
size = Size;
int width2 = size.Width;
size = Size;
BitmapData bitmapData = bitmap2.LockBits(new Rectangle(0, 0, width2, size.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
try {
uint* ptr = (uint*)bitmapData.Scan0.ToPointer();
int num = (int)(_bestImageOffset + sizeof(BITMAPINFOHEADER));
size = Size;
int length = size.Width * 4;
size = Size;
int width3 = size.Width;
size = Size;
for (int num2 = (size.Height - 1) * 4; num2 >= 0; num2 -= 4) {
Marshal.Copy(_iconData, num + num2 * width3, (IntPtr)ptr, length);
ptr += width3;
}
} finally {
bitmap.UnlockBits(bitmapData);
}
} else {
uint bestBitDepth = _bestBitDepth;
if ((bestBitDepth == 0 || bestBitDepth == 32) ? true : false) {
ICONINFO iconInfo = PInvokeCore.GetIconInfo(this);
BITMAP bITMAP = default(BITMAP);
try {
if (!iconInfo.hbmColor.IsNull) {
PInvokeCore.GetObject(iconInfo.hbmColor, sizeof(BITMAP), &bITMAP);
if (bITMAP.bmBitsPixel == 32) {
Bitmap bitmap3 = null;
BitmapData bitmapData2 = null;
BitmapData bitmapData3 = null;
try {
bitmap3 = Image.FromHbitmap(iconInfo.hbmColor);
bitmapData2 = bitmap3.LockBits(new Rectangle(0, 0, bitmap3.Width, bitmap3.Height), ImageLockMode.ReadOnly, bitmap3.PixelFormat);
if (BitmapHasAlpha(bitmapData2)) {
bitmap = new Bitmap(bitmapData2.Width, bitmapData2.Height, PixelFormat.Format32bppArgb);
bitmapData3 = bitmap.LockBits(new Rectangle(0, 0, bitmapData2.Width, bitmapData2.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
CopyBitmapData(bitmapData2, bitmapData3);
}
} finally {
if (bitmap3 != null && bitmapData2 != null)
bitmap3.UnlockBits(bitmapData2);
if (bitmap != null && bitmapData3 != null)
bitmap.UnlockBits(bitmapData3);
}
bitmap3.Dispose();
}
}
} finally {
if (!iconInfo.hbmColor.IsNull)
PInvokeCore.DeleteObject(iconInfo.hbmColor);
if (!iconInfo.hbmMask.IsNull)
PInvokeCore.DeleteObject(iconInfo.hbmMask);
}
}
}
if (bitmap == null) {
Size size2 = Size;
bitmap = new Bitmap(size2.Width, size2.Height);
Graphics graphics;
using (graphics = Graphics.FromImage(bitmap))
try {
using (Bitmap image = Bitmap.FromHicon(Handle))
graphics.DrawImage(image, new Rectangle(0, 0, size2.Width, size2.Height));
} catch (ArgumentException) {
Draw(graphics, new Rectangle(0, 0, size2.Width, size2.Height));
}
Color transparentColor = Color.FromArgb(13, 11, 12);
bitmap.MakeTransparent(transparentColor);
}
return bitmap;
}
private bool HasPngSignature()
{
if (!_isBestImagePng.HasValue) {
if (_iconData != null && _iconData.Length >= _bestImageOffset + 8) {
int num = BitConverter.ToInt32(_iconData, (int)_bestImageOffset);
int num2 = BitConverter.ToInt32(_iconData, (int)(_bestImageOffset + 4));
_isBestImagePng = (num == 1196314761 && num2 == 169478669);
} else
_isBestImagePng = false;
}
return _isBestImagePng.Value;
}
public override string ToString()
{
return System.SR.toStringIcon;
}
public unsafe void Save(Stream outputStream)
{
ArgumentNullException.ThrowIfNull(outputStream, "outputStream");
if (_iconData != null)
outputStream.Write(_iconData, 0, _iconData.Length);
else {
ComScope<IPicture> comScope = this.CreateIPicture(false);
try {
ComScope<IStream> scope = outputStream.ToIStream(true);
try {
comScope.Value->SaveAsFile((IStream*)ref scope, (BOOL)(-1), null).ThrowOnFailure((IntPtr)0);
} finally {
scope.Dispose();
}
} finally {
comScope.Dispose();
}
}
}
[return: Nullable(2)]
public static Icon ExtractIcon(string filePath, int id, int size)
{
if ((size > 0 && size <= 65535) || 1 == 0)
return ExtractIcon(filePath, id, size, false);
throw new ArgumentOutOfRangeException("size");
}
[return: Nullable(2)]
public static Icon ExtractIcon(string filePath, int id, bool smallIcon = false)
{
return ExtractIcon(filePath, id, 0, smallIcon);
}
[return: Nullable(2)]
private unsafe static Icon ExtractIcon(string filePath, int id, int size, bool smallIcon = false)
{
ArgumentNullException.ThrowIfNull(filePath, "filePath");
HICON null = HICON.Null;
IntPtr value;
if (filePath == null)
value = (IntPtr)(void*)null;
else {
ref reference = ref filePath.GetPinnableReference();
value = (IntPtr)(&reference);
}
HRESULT hRESULT = PInvoke.SHDefExtractIcon((char*)(long)value, id, 0, (HICON*)(long)(smallIcon ? ((IntPtr)(void*)null) : ((IntPtr)(&null))), (HICON*)(long)(smallIcon ? ((IntPtr)(&null)) : ((IntPtr)(void*)null)), (uint)(((ushort)size << 16) | (ushort)size));
ref reference = ref *(char*)null;
if (hRESULT == HRESULT.S_FALSE)
return null;
try {
Marshal.ThrowExceptionForHR(hRESULT);
} catch (COMException innerException) {
throw new IOException(System.SR.IconCouldNotBeExtracted, innerException);
}
return new Icon(null, true);
}
}
}