diff --git a/DataClasses/RowData.cs b/DataClasses/RowData.cs
index 274346c..dfadd0b 100644
--- a/DataClasses/RowData.cs
+++ b/DataClasses/RowData.cs
@@ -113,7 +113,10 @@ namespace SystemTrayMenu.DataClasses
}
else if (isDirectory)
{
- icon = IconReader.GetFolderIconSTA(TargetFilePath);
+ icon = IconReader.GetFolderIconSTA(
+ TargetFilePath,
+ IconReader.FolderType.Closed,
+ false);
}
else
{
@@ -139,8 +142,20 @@ namespace SystemTrayMenu.DataClasses
{
try
{
- icon = IconReader.GetFileIconWithCache(TargetFilePath);
+ icon = IconReader.GetFileIconWithCache(TargetFilePath, false);
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;
+
+ // IShellItemImageFactory GetImage works, but missing link overlay there #149
}
catch (Exception ex)
{
@@ -265,7 +280,7 @@ namespace SystemTrayMenu.DataClasses
resolvedLnkPath = FileLnk.GetResolvedFileName(TargetFilePath);
if (FileLnk.IsDirectory(resolvedLnkPath))
{
- icon = IconReader.GetFolderIconSTA(TargetFilePath);
+ icon = IconReader.GetFolderIconSTA(TargetFilePath, IconReader.FolderType.Open, true);
handled = true;
isLnkDirectory = true;
}
@@ -328,7 +343,7 @@ namespace SystemTrayMenu.DataClasses
}
else
{
- icon = IconReader.GetFileIconWithCache(browserPath);
+ icon = IconReader.GetFileIconWithCache(browserPath, false);
diposeIcon = false;
handled = true;
}
diff --git a/NativeDllImport/DeleteObject.cs b/NativeDllImport/DeleteObject.cs
deleted file mode 100644
index 2caa746..0000000
--- a/NativeDllImport/DeleteObject.cs
+++ /dev/null
@@ -1,23 +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
- {
- internal static bool Gdi32DeleteObject(IntPtr hObject)
- {
- return DeleteObject(hObject);
- }
-
- [DllImport("gdi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
- private static extern bool DeleteObject(IntPtr hObject);
- }
-}
diff --git a/NativeDllImport/GetIcon.cs b/NativeDllImport/GetIcon.cs
new file mode 100644
index 0000000..b59ec01
--- /dev/null
+++ b/NativeDllImport/GetIcon.cs
@@ -0,0 +1,50 @@
+//
+// 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/GetMenuDefaultItem.cs b/NativeDllImport/GetMenuDefaultItem.cs
new file mode 100644
index 0000000..a3ed1a4
--- /dev/null
+++ b/NativeDllImport/GetMenuDefaultItem.cs
@@ -0,0 +1,25 @@
+//
+// 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
+ {
+ public static int User32GetMenuDefaultItem(IntPtr hMenu, bool fByPos, uint gmdiFlags)
+ {
+ return GetMenuDefaultItem(hMenu, fByPos, gmdiFlags);
+ }
+
+ // Determines the default menu item on the specified menu
+ [DllImport("user32", SetLastError = true, CharSet = CharSet.Unicode)]
+ [DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
+ private static extern int GetMenuDefaultItem(IntPtr hMenu, bool fByPos, uint gmdiFlags);
+ }
+}
diff --git a/NativeDllImport/IShellItem.cs b/NativeDllImport/IShellItem.cs
deleted file mode 100644
index 08decaa..0000000
--- a/NativeDllImport/IShellItem.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-//
-// 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
deleted file mode 100644
index 72cbf88..0000000
--- a/NativeDllImport/IShellItemImageFactory.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-//
-// 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
deleted file mode 100644
index bd016a1..0000000
--- a/NativeDllImport/SHCreateItemFromParsingName.cs
+++ /dev/null
@@ -1,22 +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
- {
- [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/SHGetFileInfo.cs b/NativeDllImport/SHGetFileInfo.cs
new file mode 100644
index 0000000..ea7fdbb
--- /dev/null
+++ b/NativeDllImport/SHGetFileInfo.cs
@@ -0,0 +1,55 @@
+//
+// 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
+ {
+ private const int maxPath = 256;
+
+ internal static IntPtr Shell32SHGetFileInfo(
+ string pszPath,
+ uint dwFileAttributes,
+ ref SHFILEINFO psfi,
+ uint cbFileInfo,
+ uint uFlags)
+ {
+ return SHGetFileInfo(
+ pszPath,
+ dwFileAttributes,
+ ref psfi,
+ cbFileInfo,
+ uFlags);
+ }
+
+ [DllImport("Shell32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
+ [DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
+ private static extern IntPtr SHGetFileInfo(
+ string pszPath,
+ uint dwFileAttributes,
+ ref SHFILEINFO psfi,
+ uint cbFileInfo,
+ uint uFlags);
+
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
+
+ internal struct SHFILEINFO
+ {
+ public const int NAMESIZE = 80;
+ public IntPtr hIcon;
+ public int iIcon;
+ public uint dwAttributes;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = maxPath)]
+ public string szDisplayName;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = NAMESIZE)]
+ public string szTypeName;
+ }
+ }
+}
diff --git a/NativeDllImport/SIGDN.cs b/NativeDllImport/SIGDN.cs
deleted file mode 100644
index cd30b96..0000000
--- a/NativeDllImport/SIGDN.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-//
-// 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
deleted file mode 100644
index c95ad6c..0000000
--- a/NativeDllImport/SIIGBF.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-//
-// 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
deleted file mode 100644
index 8348745..0000000
--- a/NativeDllImport/SIZE.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-//
-// Copyright (c) PlaceholderCompany. All rights reserved.
-//
-
-namespace SystemTrayMenu.DllImports
-{
- using System.Runtime.InteropServices;
-
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
- public struct SIZE
- {
- public int Cx;
- public int Cy;
-
- public SIZE(int cx, int cy)
- {
- Cx = cx;
- Cy = cy;
- }
- }
-}
diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs
index ae7b512..7b5df1b 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.18")]
-[assembly: AssemblyFileVersion("1.0.17.18")]
+[assembly: AssemblyVersion("1.0.17.19")]
+[assembly: AssemblyFileVersion("1.0.17.19")]
diff --git a/Utilities/FileIni.cs b/Utilities/File/FileIni.cs
similarity index 93%
rename from Utilities/FileIni.cs
rename to Utilities/File/FileIni.cs
index 03172cb..a8167de 100644
--- a/Utilities/FileIni.cs
+++ b/Utilities/File/FileIni.cs
@@ -5,6 +5,7 @@
namespace SystemTrayMenu.Utilities
{
using System.Collections.Generic;
+ using System.IO;
using System.Linq;
public class FileIni
@@ -13,7 +14,7 @@ namespace SystemTrayMenu.Utilities
public FileIni(string path)
{
- values = System.IO.File.ReadLines(path)
+ values = 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/FileLnk.cs b/Utilities/File/FileLnk.cs
similarity index 98%
rename from Utilities/FileLnk.cs
rename to Utilities/File/FileLnk.cs
index 71db8e1..4ab292f 100644
--- a/Utilities/FileLnk.cs
+++ b/Utilities/File/FileLnk.cs
@@ -52,7 +52,7 @@ namespace SystemTrayMenu.Utilities
public static bool IsNetworkRoot(string path)
{
- return !File.Exists(path) &&
+ return !System.IO.File.Exists(path) &&
path.StartsWith(@"\\", StringComparison.InvariantCulture) &&
!path.Substring(2).Contains(@"\", StringComparison.InvariantCulture);
}
diff --git a/Utilities/FileUrl.cs b/Utilities/File/FileUrl.cs
similarity index 100%
rename from Utilities/FileUrl.cs
rename to Utilities/File/FileUrl.cs
diff --git a/Utilities/File/IconReader.cs b/Utilities/File/IconReader.cs
new file mode 100644
index 0000000..9417e7f
--- /dev/null
+++ b/Utilities/File/IconReader.cs
@@ -0,0 +1,257 @@
+//
+// 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/IconReader.cs b/Utilities/IconReader.cs
deleted file mode 100644
index 425c0c3..0000000
--- a/Utilities/IconReader.cs
+++ /dev/null
@@ -1,100 +0,0 @@
-//
-// Copyright (c) PlaceholderCompany. All rights reserved.
-//
-
-namespace SystemTrayMenu.Utilities
-{
- using System.Collections.Concurrent;
- using System.Collections.Generic;
- using System.Drawing;
- using System.Drawing.Imaging;
- using System.IO;
- 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();
-
- 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
deleted file mode 100644
index cf81264..0000000
--- a/Utilities/IconsFromSystemCache.cs
+++ /dev/null
@@ -1,127 +0,0 @@
-//
-// 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;
- }
- }
-}