using Microsoft.Win32; using Pilz.Win32.Mapped; using Pilz.Win32.Native; using System.Reflection; using System.Runtime.InteropServices; using static Pilz.Win32.Native.Shell32; namespace Pilz.Win32.Internals; public class IconFactory { #region Custom exceptions class public class IconNotFoundException : Exception { public IconNotFoundException(string fileName, int index) : base(string.Format("Icon with Id = {0} wasn't found in file {1}", index, fileName)) { } } public class UnableToExtractIconsException : Exception { public UnableToExtractIconsException(string fileName, int firstIconIndex, int iconCount) : base(string.Format("Tryed to extract {2} icons starting from the one with id {1} from the \"{0}\" file but failed", fileName, firstIconIndex, iconCount)) { } } #endregion /// /// Get the number of icons in the specified file. /// /// Full path of the file to look for. /// private static int GetIconsCountInFile(string fileName) { return ExtractIconEx(fileName, -1, null, null, 0); } #region ExtractIcon-like functions public static void ExtractEx(string fileName, List largeIcons, List smallIcons, int firstIconIndex, int iconCount) { // // Memory allocations // var smallIconsPtrs = (nint[])null; var largeIconsPtrs = (nint[])null; if (smallIcons is not null) smallIconsPtrs = new nint[iconCount]; if (largeIcons is not null) largeIconsPtrs = new nint[iconCount]; // // Call to native Win32 API // var apiResult = ExtractIconEx(fileName, firstIconIndex, largeIconsPtrs, smallIconsPtrs, iconCount); if (apiResult != iconCount) throw new UnableToExtractIconsException(fileName, firstIconIndex, iconCount); // // Fill lists // if (smallIcons is not null) { smallIcons.Clear(); foreach (var actualIconPtr in smallIconsPtrs) smallIcons.Add(Icon.FromHandle(actualIconPtr)); } if (largeIcons is not null) { largeIcons.Clear(); foreach (var actualIconPtr in largeIconsPtrs) largeIcons.Add(Icon.FromHandle(actualIconPtr)); } } public static List ExtractEx(string fileName, SystemIconSize size, int firstIconIndex, int iconCount) { var iconList = new List(); switch (size) { case SystemIconSize.Large: { ExtractEx(fileName, iconList, null, firstIconIndex, iconCount); break; } case SystemIconSize.Small: { ExtractEx(fileName, null, iconList, firstIconIndex, iconCount); break; } default: { throw new ArgumentOutOfRangeException("size"); } } return iconList; } public static void Extract(string fileName, List largeIcons, List smallIcons) { var iconCount = GetIconsCountInFile(fileName); ExtractEx(fileName, largeIcons, smallIcons, 0, iconCount); } public static List Extract(string fileName, SystemIconSize size) { var iconCount = GetIconsCountInFile(fileName); return ExtractEx(fileName, size, 0, iconCount); } public static Icon ExtractOne(string fileName, int index, SystemIconSize size) { try { var iconList = ExtractEx(fileName, size, index, 1); return iconList[0]; } catch (UnableToExtractIconsException __unusedUnableToExtractIconsException1__) { throw new IconNotFoundException(fileName, index); } } public static void ExtractOne(string fileName, int index, out Icon largeIcon, out Icon smallIcon) { var smallIconList = new List(); var largeIconList = new List(); try { ExtractEx(fileName, largeIconList, smallIconList, index, 1); largeIcon = largeIconList[0]; smallIcon = smallIconList[0]; } catch (UnableToExtractIconsException __unusedUnableToExtractIconsException1__) { throw new IconNotFoundException(fileName, index); } } #endregion // this will look throw the registry // to find if the Extension have an icon. public static Icon IconFromExtension(string extension, SystemIconSize size) { // Add the '.' to the extension if needed if (extension[0] != '.') extension = '.' + extension; // opens the registry for the wanted key. var Root = Registry.ClassesRoot; var ExtensionKey = Root.OpenSubKey(extension); ExtensionKey.GetValueNames(); var ApplicationKey = Root.OpenSubKey(ExtensionKey.GetValue("").ToString()); // gets the name of the file that have the icon. var IconLocation = ApplicationKey.OpenSubKey("DefaultIcon").GetValue("").ToString(); var IconPath = IconLocation.Split(','); if (Equals(IconPath[1], null)) IconPath[1] = "0"; var Large = new nint[1]; var Small = new nint[1]; // extracts the icon from the file. ExtractIconEx(IconPath[0], Convert.ToInt16(IconPath[1]), Large, Small, 1); return size == SystemIconSize.Large ? Icon.FromHandle(Large[0]) : Icon.FromHandle(Small[0]); } public static Icon IconFromExtensionShell(string extension, SystemIconSize size) { // add '.' if nessesry if (extension[0] != '.') extension = '.' + extension; // temp struct for getting file shell info var fileInfo = new SHFILEINFO(); Shell32.SHGetFileInfo(extension, 0U, ref fileInfo, (uint)Marshal.SizeOf(fileInfo), (uint)(FileInfoFlags.SHGFI_ICON | FileInfoFlags.SHGFI_USEFILEATTRIBUTES | (FileInfoFlags)size)); return Icon.FromHandle(fileInfo.hIcon); } public static Icon IconFromResource(string resourceName) { var @assembly = Assembly.GetCallingAssembly(); return new Icon(assembly.GetManifestResourceStream(resourceName)); } /// /// Parse strings in registry who contains the name of the icon and /// the index of the icon an return both parts. /// /// The full string in the form "path,index" as found in registry. /// The "path" part of the string. /// The "index" part of the string. public static void ExtractInformationsFromRegistryString(string regString, out string fileName, out int index) { if (Equals(regString, null)) throw new ArgumentNullException("regString"); if (regString.Length == 0) throw new ArgumentException("The string should not be empty.", "regString"); index = 0; var strArr = regString.Replace("\"", "").Split(','); fileName = strArr[0].Trim(); if (strArr.Length > 1) int.TryParse(strArr[1].Trim(), out index); } public static Icon ExtractFromRegistryString(string regString, SystemIconSize size) { string fileName; int index; ExtractInformationsFromRegistryString(regString, out fileName, out index); return ExtractOne(fileName, index, size); } }