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; - } - } -}