[Feature] Read icons from system cache (#149), version 1.0.17.17

This commit is contained in:
Markus Hofknecht 2021-04-22 19:18:03 +02:00
parent a5e1ff4c25
commit 010af66b42
18 changed files with 412 additions and 336 deletions

View file

@ -113,10 +113,7 @@ namespace SystemTrayMenu.DataClasses
}
else if (isDirectory)
{
icon = IconReader.GetFolderIconSTA(
TargetFilePath,
IconReader.FolderType.Closed,
false);
icon = IconReader.GetFolderIconSTA(TargetFilePath);
}
else
{
@ -142,18 +139,8 @@ namespace SystemTrayMenu.DataClasses
{
try
{
icon = IconReader.GetFileIconWithCache(TargetFilePath, false);
icon = IconReader.GetFileIconWithCache(TargetFilePath);
diposeIcon = false;
// other project -> fails sometimes
// icon = IconHelper.ExtractIcon(TargetFilePath, 0);
// standard way -> fails sometimes
// icon = Icon.ExtractAssociatedIcon(filePath);
// API Code Pack -> fails sometimes
// ShellFile shellFile = ShellFile.FromFilePath(filePath);
// Bitmap shellThumb = shellFile.Thumbnail.ExtraLargeBitmap;
}
catch (Exception ex)
{
@ -278,7 +265,7 @@ namespace SystemTrayMenu.DataClasses
resolvedLnkPath = FileLnk.GetResolvedFileName(TargetFilePath);
if (FileLnk.IsDirectory(resolvedLnkPath))
{
icon = IconReader.GetFolderIconSTA(TargetFilePath, IconReader.FolderType.Open, true);
icon = IconReader.GetFolderIconSTA(TargetFilePath);
handled = true;
isLnkDirectory = true;
}
@ -341,7 +328,7 @@ namespace SystemTrayMenu.DataClasses
}
else
{
icon = IconReader.GetFileIconWithCache(browserPath, false);
icon = IconReader.GetFileIconWithCache(browserPath);
diposeIcon = false;
handled = true;
}

View file

@ -51,7 +51,7 @@ namespace SystemTrayMenu.DllImports
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr processId);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]

View file

@ -0,0 +1,23 @@
// <copyright file="DeleteObject.cs" company="PlaceholderCompany">
// Copyright (c) PlaceholderCompany. All rights reserved.
// </copyright>
namespace SystemTrayMenu.DllImports
{
using System;
using System.Runtime.InteropServices;
/// <summary>
/// wraps the methodcalls to native windows dll's.
/// </summary>
public static partial class NativeMethods
{
internal static bool Gdi32DeleteObject(IntPtr hObject)
{
return DeleteObject(hObject);
}
[DllImport("gdi32.dll")]
private static extern bool DeleteObject(IntPtr hObject);
}
}

View file

@ -1,50 +0,0 @@
// <copyright file="GetIcon.cs" company="PlaceholderCompany">
// Copyright (c) PlaceholderCompany. All rights reserved.
// </copyright>
namespace SystemTrayMenu.DllImports
{
using System;
using System.Runtime.InteropServices;
/// <summary>
/// wraps the methodcalls to native windows dll's.
/// </summary>
public static partial class NativeMethods
{
#pragma warning disable SA1600 // Elements should be documented
public const uint ShgfiIcon = 0x000000100; // get icon
public const uint ShgfiSYSICONINDEX = 0x000004000; // get system icon index
public const uint ShgfiLINKOVERLAY = 0x000008000; // put a link overlay on icon
public const uint ShgfiLARGEICON = 0x000000000; // get large icon
public const uint ShgfiSMALLICON = 0x000000001; // get small icon
public const uint ShgfiOPENICON = 0x000000002; // get open icon
public const uint FileAttributeDirectory = 0x00000010;
public const uint FileAttributeNormal = 0x00000080;
public const int IldTransparent = 0x00000001;
#pragma warning restore SA1600 // Elements should be documented
/// <summary>
/// comctl32 ImageList_GetIcon(IntPtr hIcon).
/// </summary>
/// <param name="hIcon">hIcon.</param>
public static void Comctl32ImageListGetIcon(IntPtr hIcon)
{
_ = DestroyIcon(hIcon);
}
/// <summary>
/// comctl32 ImageList_GetIcon(IntPtr himl, int i, int flags).
/// </summary>
/// <param name="himl">himl.</param>
/// <param name="i">i.</param>
/// <param name="flags">flags.</param>
/// <returns>IntPtr.</returns>
[DllImport("comctl32", SetLastError = true, CharSet = CharSet.Unicode)]
[DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
internal static extern IntPtr ImageList_GetIcon(
IntPtr himl,
int i,
int flags);
}
}

View file

@ -0,0 +1,29 @@
// <copyright file="IShellItem.cs" company="PlaceholderCompany">
// Copyright (c) PlaceholderCompany. All rights reserved.
// </copyright>
namespace SystemTrayMenu.DllImports
{
using System;
using System.Runtime.InteropServices;
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")]
public interface IShellItem
{
void BindToHandler(
IntPtr pbc,
[MarshalAs(UnmanagedType.LPStruct)] Guid bhid,
[MarshalAs(UnmanagedType.LPStruct)] Guid riid,
out IntPtr ppv);
void GetParent(out IShellItem ppsi);
void GetDisplayName(SIGDN sigdnName, out IntPtr ppszName);
void GetAttributes(uint sfgaoMask, out uint psfgaoAttribs);
void Compare(IShellItem psi, uint hint, out int piOrder);
}
}

View file

@ -0,0 +1,20 @@
// <copyright file="IShellItemImageFactory.cs" company="PlaceholderCompany">
// Copyright (c) PlaceholderCompany. All rights reserved.
// </copyright>
namespace SystemTrayMenu.DllImports
{
using System;
using System.Runtime.InteropServices;
[ComImport]
[Guid("bcc18b79-ba16-442f-80c4-8a59c30c463b")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IShellItemImageFactory
{
void GetImage(
[In, MarshalAs(UnmanagedType.Struct)] SIZE size,
[In] SIIGBF flags,
[Out] out IntPtr phbm);
}
}

View file

@ -0,0 +1,23 @@
// <copyright file="SHCreateItemFromParsingName.cs" company="PlaceholderCompany">
// Copyright (c) PlaceholderCompany. All rights reserved.
// </copyright>
namespace SystemTrayMenu.DllImports
{
using System;
using System.Drawing;
using System.Runtime.InteropServices;
/// <summary>
/// wraps the methodcalls to native windows dll's.
/// </summary>
public static partial class NativeMethods
{
[DllImport("shell32.dll", CharSet = CharSet.Unicode, PreserveSig = false)]
internal static extern void SHCreateItemFromParsingName(
[In][MarshalAs(UnmanagedType.LPWStr)] string pszPath,
[In] IntPtr pbc,
[In][MarshalAs(UnmanagedType.LPStruct)] Guid riid,
[Out][MarshalAs(UnmanagedType.Interface, IidParameterIndex = 2)] out IShellItem ppv);
}
}

18
NativeDllImport/SIGDN.cs Normal file
View file

@ -0,0 +1,18 @@
// <copyright file="SIGDN.cs" company="PlaceholderCompany">
// Copyright (c) PlaceholderCompany. All rights reserved.
// </copyright>
namespace SystemTrayMenu.DllImports
{
public enum SIGDN : uint
{
NORMALDISPLAY = 0,
PARENTRELATIVEPARSING = 0x80018001,
PARENTRELATIVEFORADDRESSBAR = 0x8001c001,
DESKTOPABSOLUTEPARSING = 0x80028000,
PARENTRELATIVEEDITING = 0x80031001,
DESKTOPABSOLUTEEDITING = 0x8004c000,
FILESYSPATH = 0x80058000,
URL = 0x80068000,
}
}

19
NativeDllImport/SIIGBF.cs Normal file
View file

@ -0,0 +1,19 @@
// <copyright file="SIIGBF.cs" company="PlaceholderCompany">
// Copyright (c) PlaceholderCompany. All rights reserved.
// </copyright>
namespace SystemTrayMenu.DllImports
{
using System;
[Flags]
public enum SIIGBF
{
SIIGBF_RESIZETOFIT = 0x00,
SIIGBF_BIGGERSIZEOK = 0x01,
SIIGBF_MEMORYONLY = 0x02,
SIIGBF_ICONONLY = 0x04,
SIIGBF_THUMBNAILONLY = 0x08,
SIIGBF_INCACHEONLY = 0x10,
}
}

21
NativeDllImport/SIZE.cs Normal file
View file

@ -0,0 +1,21 @@
// <copyright file="SIZE.cs" company="PlaceholderCompany">
// Copyright (c) PlaceholderCompany. All rights reserved.
// </copyright>
namespace SystemTrayMenu.DllImports
{
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential)]
public struct SIZE
{
public int Cx;
public int Cy;
public SIZE(int cx, int cy)
{
this.Cx = cx;
this.Cy = cy;
}
}
}

View file

@ -39,5 +39,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.17.16")]
[assembly: AssemblyFileVersion("1.0.17.16")]
[assembly: AssemblyVersion("1.0.17.17")]
[assembly: AssemblyFileVersion("1.0.17.17")]

View file

@ -127,11 +127,11 @@ namespace SystemTrayMenu.UserInterface
/// <returns>bool success.</returns>
private static bool RegisterHotkey(StringBuilder failedKeys, string hotkeyString, HotKeyHandler handler)
{
Keys modifierKeyCode = HotkeyControl.HotkeyModifiersFromString(hotkeyString);
Keys virtualKeyCode = HotkeyControl.HotkeyFromString(hotkeyString);
Keys modifierKeyCode = HotkeyModifiersFromString(hotkeyString);
Keys virtualKeyCode = HotkeyFromString(hotkeyString);
if (!Keys.None.Equals(virtualKeyCode))
{
if (HotkeyControl.RegisterHotKey(modifierKeyCode, virtualKeyCode, handler) < 0)
if (RegisterHotKey(modifierKeyCode, virtualKeyCode, handler) < 0)
{
if (failedKeys.Length > 0)
{
@ -205,13 +205,13 @@ namespace SystemTrayMenu.UserInterface
if (dr == DialogResult.Retry)
{
// LOG.DebugFormat("Re-trying to register hotkeys");
HotkeyControl.UnregisterHotkeys();
UnregisterHotkeys();
success = RegisterHotkeys(false);
}
else if (dr == DialogResult.Ignore)
{
// LOG.DebugFormat("Ignoring failed hotkey registration");
HotkeyControl.UnregisterHotkeys();
UnregisterHotkeys();
success = RegisterHotkeys(true);
}
@ -290,7 +290,7 @@ namespace SystemTrayMenu.UserInterface
private void TextBoxHotkeyEnter(object sender, EventArgs e)
{
HotkeyControl.UnregisterHotkeys();
UnregisterHotkeys();
inHotkey = true;
}

View file

@ -1,257 +0,0 @@
// <copyright file="IconReader.cs" company="PlaceholderCompany">
// Copyright (c) PlaceholderCompany. All rights reserved.
// </copyright>
namespace SystemTrayMenu.Utilities
{
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
// from https://www.codeproject.com/Articles/2532/Obtaining-and-managing-file-and-folder-icons-using
// added ImageList_GetIcon, IconCache, AddIconOverlay
/// <summary>
/// Provides static methods to read system icons for both folders and files.
/// </summary>
/// <example>
/// <code>IconReader.GetFileIcon("c:\\general.xls");</code>
/// </example>
public static class IconReader
{
private static readonly ConcurrentDictionary<string, Icon> DictIconCache = new ConcurrentDictionary<string, Icon>();
// private static readonly object ReadIcon = new object();
public enum IconSize
{
Large = 0, // 32x32 pixels
Small = 1, // 16x16 pixels
}
public enum FolderType
{
Open = 0,
Closed = 1,
}
public static void Dispose()
{
foreach (Icon icon in DictIconCache.Values)
{
icon?.Dispose();
}
}
public static Icon GetFileIconWithCache(string filePath, bool linkOverlay, IconSize size = IconSize.Small)
{
Icon icon = null;
string extension = Path.GetExtension(filePath);
if (IsExtensionWitSameIcon(extension))
{
icon = DictIconCache.GetOrAdd(extension, GetIcon);
Icon GetIcon(string keyExtension)
{
return GetFileIconSTA(filePath, linkOverlay, size);
}
}
else
{
icon = GetFileIconSTA(filePath, linkOverlay, size);
}
return icon;
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2008:Do not create tasks without passing a TaskScheduler", Justification = "todo")]
public static Icon GetFolderIconSTA(
string directoryPath,
FolderType folderType,
bool linkOverlay,
IconSize size = IconSize.Small)
{
Icon icon = null;
Task<Icon> task = Task.Factory.StartNew(() => GetFolderIcon(
directoryPath,
folderType,
linkOverlay,
size));
icon = task.Result;
return icon;
}
public static Icon GetFolderIcon(
string directoryPath,
FolderType folderType,
bool linkOverlay,
IconSize size = IconSize.Small)
{
Icon icon = null;
// Need to add size check, although errors generated at present!
// uint flags = Shell32.SHGFI_ICON | Shell32.SHGFI_USEFILEATTRIBUTES;
// MH: Removed SHGFI_USEFILEATTRIBUTES, otherwise was wrong folder icon
uint flags = DllImports.NativeMethods.ShgfiIcon; // | Shell32.SHGFI_USEFILEATTRIBUTES;
if (linkOverlay)
{
flags += DllImports.NativeMethods.ShgfiLINKOVERLAY;
}
if (folderType == FolderType.Open)
{
flags += DllImports.NativeMethods.ShgfiOPENICON;
}
if (size == IconSize.Small)
{
flags += DllImports.NativeMethods.ShgfiSMALLICON;
}
else
{
flags += DllImports.NativeMethods.ShgfiLARGEICON;
}
// Get the folder icon
DllImports.NativeMethods.SHFILEINFO shfi = default;
IntPtr success = DllImports.NativeMethods.Shell32SHGetFileInfo(
directoryPath,
DllImports.NativeMethods.FileAttributeDirectory,
ref shfi,
(uint)Marshal.SizeOf(shfi),
flags);
if (success != IntPtr.Zero &&
shfi.hIcon != IntPtr.Zero)
{
try
{
icon = (Icon)Icon.FromHandle(shfi.hIcon).Clone();
DllImports.NativeMethods.User32DestroyIcon(shfi.hIcon);
}
#pragma warning disable CA1031 // Do not catch general exception types
catch (Exception ex)
#pragma warning restore CA1031 // Do not catch general exception types
{
Log.Error($"directoryPath:'{directoryPath}'", ex);
}
}
return icon;
}
public static Icon AddIconOverlay(Icon originalIcon, Icon overlay)
{
Icon icon = null;
if (originalIcon != null)
{
using Bitmap target = new Bitmap(
originalIcon.Width,
originalIcon.Height,
PixelFormat.Format32bppArgb);
Graphics graphics = Graphics.FromImage(target);
graphics.DrawIcon(originalIcon, 0, 0);
graphics.DrawIcon(overlay, 0, 0);
target.MakeTransparent(target.GetPixel(1, 1));
icon = Icon.FromHandle(target.GetHicon());
}
return icon;
}
private static bool IsExtensionWitSameIcon(string fileExtension)
{
bool isExtensionWitSameIcon = true;
List<string> extensionsWithDiffIcons = new List<string>
{ string.Empty, ".EXE", ".LNK", ".ICO", ".URL" };
if (extensionsWithDiffIcons.Contains(fileExtension.ToUpperInvariant()))
{
isExtensionWitSameIcon = false;
}
return isExtensionWitSameIcon;
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2008:Do not create tasks without passing a TaskScheduler", Justification = "todo")]
private static Icon GetFileIconSTA(string filePath, bool linkOverlay, IconSize size = IconSize.Small)
{
Icon icon = null;
Task<Icon> task = Task.Factory.StartNew(() => GetFileIcon(filePath, linkOverlay, size));
icon = task.Result;
return icon;
}
private static Icon GetFileIcon(string filePath, bool linkOverlay, IconSize size = IconSize.Small)
{
Icon icon = null;
DllImports.NativeMethods.SHFILEINFO shfi = default;
uint flags = DllImports.NativeMethods.ShgfiIcon | DllImports.NativeMethods.ShgfiSYSICONINDEX;
if (linkOverlay)
{
flags += DllImports.NativeMethods.ShgfiLINKOVERLAY;
}
/* Check the size specified for return. */
if (size == IconSize.Small)
{
flags += DllImports.NativeMethods.ShgfiSMALLICON;
}
else
{
flags += DllImports.NativeMethods.ShgfiLARGEICON;
}
IntPtr hImageList = DllImports.NativeMethods.Shell32SHGetFileInfo(
filePath,
DllImports.NativeMethods.FileAttributeNormal,
ref shfi,
(uint)Marshal.SizeOf(shfi),
flags);
if (hImageList != IntPtr.Zero)
{
IntPtr hIcon;
if (linkOverlay)
{
hIcon = shfi.hIcon; // Get icon directly
}
else
{
// Get icon from .ink without overlay
hIcon = DllImports.NativeMethods.ImageList_GetIcon(hImageList, shfi.iIcon, DllImports.NativeMethods.IldTransparent);
}
try
{
// Copy (clone) the returned icon to a new object, thus allowing us to clean-up properly
icon = (Icon)Icon.FromHandle(hIcon).Clone();
}
#pragma warning disable CA1031 // Do not catch general exception types
catch (Exception ex)
#pragma warning restore CA1031 // Do not catch general exception types
{
Log.Error($"filePath:'{filePath}'", ex);
}
// Cleanup
if (!linkOverlay)
{
DllImports.NativeMethods.User32DestroyIcon(hIcon);
}
DllImports.NativeMethods.User32DestroyIcon(shfi.hIcon);
}
return icon;
}
}
}

View file

@ -5,7 +5,6 @@
namespace SystemTrayMenu.Utilities
{
using System.Collections.Generic;
using System.IO;
using System.Linq;
public class FileIni
@ -14,7 +13,7 @@ namespace SystemTrayMenu.Utilities
public FileIni(string path)
{
values = File.ReadLines(path)
values = System.IO.File.ReadLines(path)
.Where(line => !string.IsNullOrWhiteSpace(line) &&
!line.StartsWith("#", System.StringComparison.InvariantCulture))
.Select(line => line.Split(new char[] { '=' }, 2, 0))

View file

@ -52,7 +52,7 @@ namespace SystemTrayMenu.Utilities
public static bool IsNetworkRoot(string path)
{
return !System.IO.File.Exists(path) &&
return !File.Exists(path) &&
path.StartsWith(@"\\", StringComparison.InvariantCulture) &&
!path.Substring(2).Contains(@"\", StringComparison.InvariantCulture);
}

117
Utilities/IconReader.cs Normal file
View file

@ -0,0 +1,117 @@
// <copyright file="IconReader.cs" company="PlaceholderCompany">
// Copyright (c) PlaceholderCompany. All rights reserved.
// </copyright>
namespace SystemTrayMenu.Utilities
{
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using SystemTrayMenu.DllImports;
using TAFactory.IconPack;
// from https://www.codeproject.com/Articles/2532/Obtaining-and-managing-file-and-folder-icons-using
// added ImageList_GetIcon, IconCache, AddIconOverlay
/// <summary>
/// Provides static methods to read system icons for both folders and files.
/// </summary>
/// <example>
/// <code>IconReader.GetFileIcon("c:\\general.xls");</code>
/// </example>
public static class IconReader
{
private static readonly ConcurrentDictionary<string, Icon> DictIconCache = new ConcurrentDictionary<string, Icon>();
// private static readonly object ReadIcon = new object();
public enum IconSize
{
Large = 0, // 32x32 pixels
Small = 1, // 16x16 pixels
}
public enum FolderType
{
Open = 0,
Closed = 1,
}
public static void Dispose()
{
foreach (Icon icon in DictIconCache.Values)
{
icon?.Dispose();
}
}
public static Icon GetFileIconWithCache(string filePath)
{
Icon icon = null;
string extension = Path.GetExtension(filePath);
if (IsExtensionWitSameIcon(extension))
{
icon = DictIconCache.GetOrAdd(extension, GetIcon);
Icon GetIcon(string keyExtension)
{
return GetFileIconSTA(filePath);
}
}
else
{
icon = GetFileIconSTA(filePath);
}
return icon;
}
public static Icon GetFolderIconSTA(string directoryPath)
{
Task<Icon> task = Task.Factory.StartNew(() => IconsFromSystemCache.GetIcon(directoryPath));
return task.Result;
}
public static Icon AddIconOverlay(Icon originalIcon, Icon overlay)
{
Icon icon = null;
if (originalIcon != null)
{
using Bitmap target = new Bitmap(
originalIcon.Width,
originalIcon.Height,
PixelFormat.Format32bppArgb);
Graphics graphics = Graphics.FromImage(target);
graphics.DrawIcon(originalIcon, 0, 0);
graphics.DrawIcon(overlay, 0, 0);
target.MakeTransparent(target.GetPixel(1, 1));
icon = Icon.FromHandle(target.GetHicon());
}
return icon;
}
private static bool IsExtensionWitSameIcon(string fileExtension)
{
bool isExtensionWitSameIcon = true;
List<string> extensionsWithDiffIcons = new List<string>
{ string.Empty, ".EXE", ".LNK", ".ICO", ".URL" };
if (extensionsWithDiffIcons.Contains(fileExtension.ToUpperInvariant()))
{
isExtensionWitSameIcon = false;
}
return isExtensionWitSameIcon;
}
private static Icon GetFileIconSTA(string filePath)
{
Task<Icon> task = Task.Factory.StartNew(() => IconsFromSystemCache.GetIcon(filePath));
return task.Result;
}
}
}

View file

@ -0,0 +1,127 @@
// <copyright file="IconsFromSystemCache.cs" company="PlaceholderCompany">
// Copyright (c) PlaceholderCompany. All rights reserved.
// </copyright>
namespace SystemTrayMenu.Utilities
{
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
using SystemTrayMenu.DllImports;
public static partial class IconsFromSystemCache
{
/// <summary>
/// Get Icon from system cache.
/// http://pinvoke.net/default.aspx/Interfaces/IShellItem.html?diff=y.
/// https://github.com/Hofknecht/SystemTrayMenu/issues/149.
/// </summary>
/// <param name="filename">filename.</param>
/// <returns>Icon.</returns>
public static Icon GetIcon(string filename)
{
Icon icon = null;
// GUID of IShellItem.
Guid uuid = new Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe");
try
{
NativeMethods.SHCreateItemFromParsingName(filename, IntPtr.Zero, uuid, out IShellItem ppsi);
IntPtr hbitmap = IntPtr.Zero;
((IShellItemImageFactory)ppsi).GetImage(new SIZE(16, 16), SIIGBF.SIIGBF_ICONONLY, out hbitmap);
Bitmap bitmap = GetBitmapFromHBitmap(hbitmap);
NativeMethods.Gdi32DeleteObject(hbitmap);
bitmap.MakeTransparent();
IntPtr icH = bitmap.GetHicon();
icon = (Icon)Icon.FromHandle(icH).Clone();
NativeMethods.User32DestroyIcon(icH);
}
#pragma warning disable CA1031 // Do not catch general exception types
catch (Exception ex)
#pragma warning restore CA1031 // Do not catch general exception types
{
if (ex is FileNotFoundException)
{
Log.Warn($"filename:'{filename}'", ex);
}
else
{
Log.Error($"filename:'{filename}'", ex);
// throw; // comment in to remove CA1031
}
}
return icon;
}
/// <summary>
/// Preserving Alpha channel
/// https://stackoverflow.com/questions/4627376/use-native-hbitmap-in-c-sharp-while-preserving-alpha-channel-transparency/9291151#9291151.
/// (when using "Bitmap image = Image.FromHbitmap(hbitmap);" alpha channel lost).
/// </summary>
/// <param name="nativeHBitmap">nativeHBitmap.</param>
/// <returns>Bitmap.</returns>
private static Bitmap GetBitmapFromHBitmap(IntPtr nativeHBitmap)
{
Bitmap bmp = Image.FromHbitmap(nativeHBitmap);
if (Image.GetPixelFormatSize(bmp.PixelFormat) < 32)
{
return bmp;
}
if (IsAlphaBitmap(bmp, out BitmapData bmpData))
{
return GetlAlphaBitmapFromBitmapData(bmpData);
}
return bmp;
}
private static Bitmap GetlAlphaBitmapFromBitmapData(BitmapData bmpData)
{
return new Bitmap(
bmpData.Width,
bmpData.Height,
bmpData.Stride,
PixelFormat.Format32bppArgb,
bmpData.Scan0);
}
private static bool IsAlphaBitmap(Bitmap bmp, out BitmapData bmpData)
{
Rectangle bmBounds = new Rectangle(0, 0, bmp.Width, bmp.Height);
bmpData = bmp.LockBits(bmBounds, ImageLockMode.ReadOnly, bmp.PixelFormat);
try
{
for (int y = 0; y <= bmpData.Height - 1; y++)
{
for (int x = 0; x <= bmpData.Width - 1; x++)
{
Color pixelColor = Color.FromArgb(
Marshal.ReadInt32(bmpData.Scan0, (bmpData.Stride * y) + (4 * x)));
if (pixelColor.A > 0 & pixelColor.A < 255)
{
return true;
}
}
}
}
finally
{
bmp.UnlockBits(bmpData);
}
return false;
}
}
}