diff --git a/DataClasses/RowData.cs b/DataClasses/RowData.cs
index 5e5e2b5..274346c 100644
--- a/DataClasses/RowData.cs
+++ b/DataClasses/RowData.cs
@@ -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;
}
diff --git a/NativeDllImport/BringWindowToTop.cs b/NativeDllImport/BringWindowToTop.cs
index 1247e48..4f3bf76 100644
--- a/NativeDllImport/BringWindowToTop.cs
+++ b/NativeDllImport/BringWindowToTop.cs
@@ -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)]
diff --git a/NativeDllImport/DeleteObject.cs b/NativeDllImport/DeleteObject.cs
new file mode 100644
index 0000000..38c196c
--- /dev/null
+++ b/NativeDllImport/DeleteObject.cs
@@ -0,0 +1,23 @@
+//
+// Copyright (c) PlaceholderCompany. All rights reserved.
+//
+
+namespace SystemTrayMenu.DllImports
+{
+ using System;
+ using System.Runtime.InteropServices;
+
+ ///
+ /// wraps the methodcalls to native windows dll's.
+ ///
+ public static partial class NativeMethods
+ {
+ internal static bool Gdi32DeleteObject(IntPtr hObject)
+ {
+ return DeleteObject(hObject);
+ }
+
+ [DllImport("gdi32.dll")]
+ private static extern bool DeleteObject(IntPtr hObject);
+ }
+}
diff --git a/NativeDllImport/GetIcon.cs b/NativeDllImport/GetIcon.cs
deleted file mode 100644
index b59ec01..0000000
--- a/NativeDllImport/GetIcon.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-//
-// Copyright (c) PlaceholderCompany. All rights reserved.
-//
-
-namespace SystemTrayMenu.DllImports
-{
- using System;
- using System.Runtime.InteropServices;
-
- ///
- /// wraps the methodcalls to native windows dll's.
- ///
- 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
-
- ///
- /// comctl32 ImageList_GetIcon(IntPtr hIcon).
- ///
- /// hIcon.
- public static void Comctl32ImageListGetIcon(IntPtr hIcon)
- {
- _ = DestroyIcon(hIcon);
- }
-
- ///
- /// comctl32 ImageList_GetIcon(IntPtr himl, int i, int flags).
- ///
- /// himl.
- /// i.
- /// flags.
- /// IntPtr.
- [DllImport("comctl32", SetLastError = true, CharSet = CharSet.Unicode)]
- [DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
- internal static extern IntPtr ImageList_GetIcon(
- IntPtr himl,
- int i,
- int flags);
- }
-}
diff --git a/NativeDllImport/IShellItem.cs b/NativeDllImport/IShellItem.cs
new file mode 100644
index 0000000..08decaa
--- /dev/null
+++ b/NativeDllImport/IShellItem.cs
@@ -0,0 +1,29 @@
+//
+// Copyright (c) PlaceholderCompany. All rights reserved.
+//
+
+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);
+ }
+}
diff --git a/NativeDllImport/IShellItemImageFactory.cs b/NativeDllImport/IShellItemImageFactory.cs
new file mode 100644
index 0000000..72cbf88
--- /dev/null
+++ b/NativeDllImport/IShellItemImageFactory.cs
@@ -0,0 +1,20 @@
+//
+// Copyright (c) PlaceholderCompany. All rights reserved.
+//
+
+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);
+ }
+}
diff --git a/NativeDllImport/SHCreateItemFromParsingName.cs b/NativeDllImport/SHCreateItemFromParsingName.cs
new file mode 100644
index 0000000..439597d
--- /dev/null
+++ b/NativeDllImport/SHCreateItemFromParsingName.cs
@@ -0,0 +1,23 @@
+//
+// Copyright (c) PlaceholderCompany. All rights reserved.
+//
+
+namespace SystemTrayMenu.DllImports
+{
+ using System;
+ using System.Drawing;
+ using System.Runtime.InteropServices;
+
+ ///
+ /// wraps the methodcalls to native windows dll's.
+ ///
+ 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);
+ }
+}
diff --git a/NativeDllImport/SIGDN.cs b/NativeDllImport/SIGDN.cs
new file mode 100644
index 0000000..cd30b96
--- /dev/null
+++ b/NativeDllImport/SIGDN.cs
@@ -0,0 +1,18 @@
+//
+// Copyright (c) PlaceholderCompany. All rights reserved.
+//
+
+namespace SystemTrayMenu.DllImports
+{
+ public enum SIGDN : uint
+ {
+ NORMALDISPLAY = 0,
+ PARENTRELATIVEPARSING = 0x80018001,
+ PARENTRELATIVEFORADDRESSBAR = 0x8001c001,
+ DESKTOPABSOLUTEPARSING = 0x80028000,
+ PARENTRELATIVEEDITING = 0x80031001,
+ DESKTOPABSOLUTEEDITING = 0x8004c000,
+ FILESYSPATH = 0x80058000,
+ URL = 0x80068000,
+ }
+}
diff --git a/NativeDllImport/SIIGBF.cs b/NativeDllImport/SIIGBF.cs
new file mode 100644
index 0000000..c95ad6c
--- /dev/null
+++ b/NativeDllImport/SIIGBF.cs
@@ -0,0 +1,19 @@
+//
+// Copyright (c) PlaceholderCompany. All rights reserved.
+//
+
+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,
+ }
+}
diff --git a/NativeDllImport/SIZE.cs b/NativeDllImport/SIZE.cs
new file mode 100644
index 0000000..305542a
--- /dev/null
+++ b/NativeDllImport/SIZE.cs
@@ -0,0 +1,21 @@
+//
+// Copyright (c) PlaceholderCompany. All rights reserved.
+//
+
+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;
+ }
+ }
+}
diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs
index 3ac235c..e9d9db2 100644
--- a/Properties/AssemblyInfo.cs
+++ b/Properties/AssemblyInfo.cs
@@ -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")]
diff --git a/UserInterface/SettingsForm.cs b/UserInterface/SettingsForm.cs
index 1e5e265..1538ac2 100644
--- a/UserInterface/SettingsForm.cs
+++ b/UserInterface/SettingsForm.cs
@@ -127,11 +127,11 @@ namespace SystemTrayMenu.UserInterface
/// bool success.
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;
}
diff --git a/Utilities/File/IconReader.cs b/Utilities/File/IconReader.cs
deleted file mode 100644
index 9417e7f..0000000
--- a/Utilities/File/IconReader.cs
+++ /dev/null
@@ -1,257 +0,0 @@
-//
-// Copyright (c) PlaceholderCompany. All rights reserved.
-//
-
-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
-
- ///
- /// Provides static methods to read system icons for both folders and files.
- ///
- ///
- /// IconReader.GetFileIcon("c:\\general.xls");
- ///
- public static class IconReader
- {
- private static readonly ConcurrentDictionary DictIconCache = new ConcurrentDictionary();
-
- // 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 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 extensionsWithDiffIcons = new List
- { 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 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;
- }
- }
-}
\ No newline at end of file
diff --git a/Utilities/File/FileIni.cs b/Utilities/FileIni.cs
similarity index 93%
rename from Utilities/File/FileIni.cs
rename to Utilities/FileIni.cs
index a8167de..03172cb 100644
--- a/Utilities/File/FileIni.cs
+++ b/Utilities/FileIni.cs
@@ -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))
diff --git a/Utilities/File/FileLnk.cs b/Utilities/FileLnk.cs
similarity index 98%
rename from Utilities/File/FileLnk.cs
rename to Utilities/FileLnk.cs
index 4ab292f..71db8e1 100644
--- a/Utilities/File/FileLnk.cs
+++ b/Utilities/FileLnk.cs
@@ -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);
}
diff --git a/Utilities/File/FileUrl.cs b/Utilities/FileUrl.cs
similarity index 100%
rename from Utilities/File/FileUrl.cs
rename to Utilities/FileUrl.cs
diff --git a/Utilities/IconReader.cs b/Utilities/IconReader.cs
new file mode 100644
index 0000000..fedc539
--- /dev/null
+++ b/Utilities/IconReader.cs
@@ -0,0 +1,117 @@
+//
+// Copyright (c) PlaceholderCompany. All rights reserved.
+//
+
+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
+
+ ///
+ /// Provides static methods to read system icons for both folders and files.
+ ///
+ ///
+ /// IconReader.GetFileIcon("c:\\general.xls");
+ ///
+ public static class IconReader
+ {
+ private static readonly ConcurrentDictionary DictIconCache = new ConcurrentDictionary();
+
+ // 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 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 extensionsWithDiffIcons = new List
+ { string.Empty, ".EXE", ".LNK", ".ICO", ".URL" };
+ if (extensionsWithDiffIcons.Contains(fileExtension.ToUpperInvariant()))
+ {
+ isExtensionWitSameIcon = false;
+ }
+
+ return isExtensionWitSameIcon;
+ }
+
+ private static Icon GetFileIconSTA(string filePath)
+ {
+ Task task = Task.Factory.StartNew(() => IconsFromSystemCache.GetIcon(filePath));
+ return task.Result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Utilities/IconsFromSystemCache.cs b/Utilities/IconsFromSystemCache.cs
new file mode 100644
index 0000000..cf81264
--- /dev/null
+++ b/Utilities/IconsFromSystemCache.cs
@@ -0,0 +1,127 @@
+//
+// Copyright (c) PlaceholderCompany. All rights reserved.
+//
+
+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
+ {
+ ///
+ /// Get Icon from system cache.
+ /// http://pinvoke.net/default.aspx/Interfaces/IShellItem.html?diff=y.
+ /// https://github.com/Hofknecht/SystemTrayMenu/issues/149.
+ ///
+ /// filename.
+ /// Icon.
+ 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;
+ }
+
+ ///
+ /// 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).
+ ///
+ /// nativeHBitmap.
+ /// Bitmap.
+ 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;
+ }
+ }
+}