Last active
August 26, 2026 06:02
-
-
Save scriptingstudio/0b15927ac5cf9016d0fdcd0dc6e101e6 to your computer and use it in GitHub Desktop.
Yet another icon extractor from DLL,EXE,ICO for C#, PowerShell, and Python
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| Synopsis : A utility class that extracts the list of all icons handles (all sizes, all color counts) for a binary file like DLL, EXE, or ICO | |
| Version : 3.1 | |
| Lastupdate : 2026-Aug-11 | |
| // get amount of icons stored in a dll | |
| var icons = IconReader.GetTotal(@"c:\windows\system32\imageres.dll"); | |
| // get all icons handles with index 0 from a dll | |
| var icons = IconReader.ImportIcons(@"c:\windows\system32\shell32.dll", 0); | |
| // get all icons handles from a dll | |
| var icons = IconReader.ImportIcons(@"c:\windows\system32\shell32.dll"); | |
| // get all icons handles from an ico | |
| var icons = IconReader.ImportIcons(@"<your_ico_filepath>"); | |
| // dispose imported icons | |
| icons.ForEach(i => i.Dispose()); | |
| */ | |
| using System; | |
| using System.IO; | |
| using System.Runtime.InteropServices; | |
| using System.Collections; | |
| using System.Collections.Generic; | |
| using System.Globalization; | |
| public sealed class IconReader : IDisposable | |
| { | |
| public IntPtr Handle { get; } // primary ID for any icon | |
| public int Index { get; } // global index, inc multi sizing | |
| public int GroupIndex { get; } // icon group (multi-size icon) index; for EXE,DLL | |
| public string Id { get; } // primarily for EXE,DLL | |
| public string GroupId { get; } // primarily for EXE,DLL | |
| public int Width { get; } | |
| public int Height { get; } | |
| public int Bpp { get; } | |
| public int Colors { get; } | |
| public int ImgLength { get; } | |
| public object? Icon { get; set; } // icon image storage | |
| private IconReader(int groupIndex, string groupId, int index, string id, int width, int height, int bpp, int colors, int len, IntPtr handle) | |
| { | |
| GroupIndex = groupIndex; | |
| GroupId = groupId; | |
| Index = index; | |
| Id = id; | |
| Width = width; | |
| Height = height; | |
| Bpp = bpp; | |
| Colors = colors; | |
| ImgLength = len; | |
| Handle = handle; | |
| Icon = null; // not initialized because .NET requires System.Drawing.Common NuGet package installed | |
| } | |
| public override string ToString() => Index.ToString(); | |
| public void Dispose() => DestroyIcon(Handle); | |
| // DLL,EXE: the total doesn't take into account multi sizing | |
| public static int GetTotal(string iconFilePath) | |
| { | |
| if (string.IsNullOrEmpty(iconFilePath)) return 0; // or ArgumentNullException? | |
| if (Path.GetExtension(iconFilePath).ToLower() == ".ico") | |
| { | |
| byte[] signature; | |
| FileStream stream; | |
| try { | |
| using (stream = File.Open(iconFilePath, FileMode.Open)) | |
| { | |
| using (var reader = new BinaryReader(stream, System.Text.Encoding.UTF8, false)) | |
| { // don't modify "using" syntax | |
| signature = reader.ReadBytes(6); // 4+2 | |
| if (signature.Length < 6) // corrupted | |
| throw new ArgumentException($"'{iconFilePath}' is not a valid ICO file."); | |
| } | |
| } | |
| } | |
| catch {throw new ArgumentException($"Unable to read '{iconFilePath}' file.");} | |
| if (BitConverter.ToInt32(signature[..4], 0) != 0x10000) // Basic validation | |
| throw new ArgumentException($"'{iconFilePath}' is not a valid ICO file."); | |
| return BitConverter.ToInt16(signature[4..6], 0); | |
| } | |
| else // dll or exe | |
| { | |
| IntPtr large; IntPtr small; | |
| var r = ExtractIconEx(iconFilePath, -1, out large, out small, 1); | |
| if ((uint)r == uint.MaxValue) {return 0;} else {return r;} | |
| } | |
| } // GetTotal | |
| public static List<IconReader> ImportIcons(string iconFilePath, int? byIndexOrResourceId = null) | |
| { | |
| if (string.IsNullOrEmpty(iconFilePath)) | |
| throw new ArgumentNullException(nameof(iconFilePath)); | |
| if (Path.GetExtension(iconFilePath).ToLower() == ".ico") | |
| return ImportIco(iconFilePath, byIndexOrResourceId); | |
| var icons = new List<IconReader>(); | |
| var handle = LoadLibraryEx(iconFilePath, IntPtr.Zero, LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE); | |
| if (handle != IntPtr.Zero) | |
| { | |
| try | |
| { | |
| icons = ImportIcons(handle, byIndexOrResourceId); | |
| } | |
| finally | |
| { | |
| FreeLibrary(handle); | |
| } | |
| } | |
| return icons; | |
| } // ImportIcons | |
| private static List<IconReader> ImportIcons(IntPtr handle, int? byIndexOrResourceId) | |
| { | |
| var icons = new List<IconReader>(); | |
| var entries = new Dictionary<ushort, GRPICONDIRENTRY>(); | |
| var groupIndices = new Dictionary<ushort, int>(); | |
| var groupIds = new Dictionary<ushort, string>(); | |
| var groupIndex = 0; | |
| if (EnumResourceNames(handle, new IntPtr(RT_GROUP_ICON), (m, t, n, lp) => | |
| { | |
| if (byIndexOrResourceId.HasValue && byIndexOrResourceId.Value >= 0 && byIndexOrResourceId.Value != groupIndex) | |
| { | |
| groupIndex++; | |
| return true; | |
| } | |
| string name; | |
| if (n.ToInt64() > ushort.MaxValue) | |
| { | |
| name = Marshal.PtrToStringAuto(n); | |
| } | |
| else | |
| { | |
| name = n.ToInt32().ToString(CultureInfo.InvariantCulture); | |
| } | |
| if (byIndexOrResourceId.HasValue && byIndexOrResourceId.Value < 0 && !string.Equals((-byIndexOrResourceId.Value).ToString(CultureInfo.InvariantCulture), name, StringComparison.Ordinal)) | |
| { | |
| groupIndex++; | |
| return true; | |
| } | |
| try | |
| { | |
| ExtractIconGroupEntries(handle, n, t, groupIndex, entries, groupIndices, groupIds); | |
| groupIndex++; | |
| } | |
| catch | |
| { | |
| // do nothing | |
| } | |
| return true; | |
| }, IntPtr.Zero)) | |
| { | |
| EnumResourceNames(handle, new IntPtr(RT_ICON), (m, t, n, lp) => | |
| { | |
| var iconHandle = ExtractIcon(handle, n, t, entries); | |
| if (iconHandle != IntPtr.Zero) | |
| { | |
| var id = (ushort)n.ToInt32(); | |
| //var ent = entries[id]; // ICONDIR | |
| var w = entries[id].bWidth == 0 ? 256 : entries[id].bWidth; | |
| var h = entries[id].bHeight == 0 ? 256 : entries[id].bHeight; | |
| var l = entries[id].dwBytesInRes; | |
| var bpp = entries[id].wBitCount; | |
| var colors = entries[id].bColorCount; | |
| var info = new IconReader(groupIndices[id], groupIds[id], n.ToInt32() - 1, n.ToString(), w,h,bpp,colors,l, iconHandle); | |
| icons.Add(info); | |
| } | |
| return true; | |
| }, IntPtr.Zero); | |
| } | |
| return icons; | |
| } // ImportIcons | |
| private static List<IconReader> ImportIco(string iconFilePath, int? byIndexOrSize) | |
| { | |
| var icons = new List<IconReader>(); | |
| byte[] bytes; | |
| try {bytes = File.ReadAllBytes(iconFilePath);} catch | |
| { | |
| throw new ArgumentException($"Unable to read '{iconFilePath}' file."); | |
| } | |
| // Basic validation | |
| if (bytes.Length < 6 || BitConverter.ToInt32(bytes[..4],0) != 0x10000) | |
| throw new ArgumentException($"'{iconFilePath}' is not a valid ICO file."); | |
| int dirend = 16 * BitConverter.ToInt16(bytes[4..6],0); // dirlen * amount | |
| for (int i = 6, k = 0; i < dirend; i += 16, k++) | |
| { | |
| var direntry = bytes[i..(i + 16)]; // raw icon resource | |
| /* No advantages over simple byte indexing | |
| GCHandle enthandle = GCHandle.Alloc(direntry, GCHandleType.Pinned); | |
| var entry = Marshal.PtrToStructure<ICONDIRENTRY>(enthandle.AddrOfPinnedObject()); | |
| enthandle.Free();*/ | |
| int w = direntry[0] == 0 ? 256 : direntry[0]; | |
| int h = direntry[1] == 0 ? 256 : direntry[1]; | |
| var l = (int)BitConverter.ToUInt32(direntry[8..12],0); | |
| var bpp = BitConverter.ToUInt16(direntry[6..8],0); | |
| var dataoffset = (int)BitConverter.ToUInt32(direntry[12..16],0); | |
| var imgbytes = bytes[dataoffset .. (dataoffset + l)]; | |
| var iconHandle = CreateIconFromResourceEx(imgbytes,l,true,0x00030000,w,h,0); | |
| //TODO if (!byIndexOrSize.HasValue || byIndexOrSize.Value < 0 || byIndexOrSize.Value == k || byIndexOrSize.Value == w) { | |
| var info = new IconReader(0,"", k,(k+1).ToString(), w,h,bpp,direntry[2],l, iconHandle); | |
| icons.Add(info); | |
| } | |
| return icons; | |
| } // ImportIco | |
| private static void ExtractIconGroupEntries(IntPtr module, IntPtr name, IntPtr type, int index, Dictionary<ushort, GRPICONDIRENTRY> entries, Dictionary<ushort, int> groupIndices, Dictionary<ushort, string> groupIds) | |
| { | |
| var handle = FindResource(module, name, type); | |
| if (handle == IntPtr.Zero) return; | |
| var size = SizeofResource(module, handle); | |
| if (size == 0) return; | |
| var resource = LoadResource(module, handle); | |
| if (resource == IntPtr.Zero) return; | |
| var ptr = LockResource(resource); | |
| if (ptr == IntPtr.Zero) return; | |
| // GRPICONDIR | |
| ptr += 2; // idReserved; | |
| var idtype = Marshal.ReadInt16(ptr); | |
| if (idtype != 1) return; // idType, 1 for ICO | |
| var elementSize = Marshal.SizeOf<GRPICONDIRENTRY>(); | |
| ptr += 2; | |
| var count = Marshal.ReadInt16(ptr); | |
| ptr += 2; | |
| for (var i = 0; i < count; i++) | |
| { | |
| var entry = Marshal.PtrToStructure<GRPICONDIRENTRY>(ptr); | |
| ptr += elementSize; | |
| entries[entry.nId] = entry; | |
| // is it a string or an id? | |
| groupIndices[entry.nId] = index; | |
| if (name.ToInt64() > ushort.MaxValue) | |
| { | |
| var id = Marshal.PtrToStringAuto(name); | |
| groupIds[entry.nId] = id; | |
| } | |
| else | |
| { | |
| groupIds[entry.nId] = "#" + name.ToInt32(); | |
| } | |
| } | |
| } // ExtractIconGroupEntries | |
| private static IntPtr ExtractIcon(IntPtr module, IntPtr name, IntPtr type, Dictionary<ushort, GRPICONDIRENTRY> entries) | |
| { | |
| if (!entries.TryGetValue((ushort)name.ToInt32(), out _)) return IntPtr.Zero; | |
| var hres = FindResource(module, name, type); | |
| if (hres == IntPtr.Zero) return IntPtr.Zero; | |
| var size = SizeofResource(module, hres); | |
| if (size == 0) return IntPtr.Zero; | |
| var res = LoadResource(module, hres); | |
| if (res == IntPtr.Zero) return IntPtr.Zero; | |
| var ptr = LockResource(res); | |
| if (ptr == IntPtr.Zero) return IntPtr.Zero; | |
| return CreateIconFromResourceEx(ptr, size, true, 0x00030000, 0, 0, 0); | |
| } // ExtractIcon | |
| private delegate bool EnumResNameProc(IntPtr hModule, IntPtr lpszType, IntPtr lpszName, IntPtr lParam); | |
| [DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)] | |
| private static extern bool EnumResourceNames(IntPtr hModule, IntPtr lpszType, EnumResNameProc lpEnumFunc, IntPtr lParam); | |
| [DllImport("kernel32", CharSet = CharSet.Unicode)] | |
| private static extern IntPtr FindResource(IntPtr hModule, IntPtr lpName, IntPtr lpType); | |
| [DllImport("kernel32")] | |
| private static extern int SizeofResource(IntPtr hModule, IntPtr hResInfo); | |
| [DllImport("kernel32")] | |
| private static extern IntPtr LoadResource(IntPtr hModule, IntPtr hResInfo); | |
| [DllImport("user32")] | |
| private static extern IntPtr CreateIconFromResourceEx(IntPtr presbits, int dwResSize, bool fIcon, int dwVer, int cxDesired, int cyDesired, int flags); | |
| [DllImport("user32")] | |
| private static extern IntPtr CreateIconFromResourceEx(byte[] presbits, int dwResSize, bool fIcon, int dwVer, int cxDesired, int cyDesired, int flags); | |
| [DllImport("user32")] | |
| private static extern bool DestroyIcon(IntPtr handle); | |
| [DllImport("Shell32", EntryPoint = "ExtractIconExW", CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] | |
| private static extern int ExtractIconEx(string sFile, int iIndex, out IntPtr piLargeVersion, out IntPtr piSmallVersion, int amountIcons); | |
| [DllImport("kernel32", CharSet = CharSet.Unicode)] | |
| private static extern IntPtr LockResource(IntPtr hResData); | |
| [DllImport("kernel32", CharSet = CharSet.Unicode)] | |
| private static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, int dwFlags); | |
| [DllImport("kernel32")] | |
| private static extern bool FreeLibrary(IntPtr hModule); | |
| private const int LOAD_LIBRARY_AS_DATAFILE = 0x2; | |
| private const int LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x20; | |
| private const int RT_ICON = 3; | |
| private const int RT_GROUP_ICON = RT_ICON + 11; | |
| [StructLayout(LayoutKind.Sequential, Pack = 1)] | |
| private struct GRPICONDIRENTRY | |
| { | |
| public byte bWidth; | |
| public byte bHeight; | |
| public byte bColorCount; | |
| public byte bReserved; | |
| public short wPlanes; | |
| public short wBitCount; | |
| public int dwBytesInRes; | |
| public ushort nId; | |
| } | |
| /*[StructLayout(LayoutKind.Sequential, Pack = 1)] | |
| private struct ICONDIRENTRY | |
| { | |
| public byte bWidth; | |
| public byte bHeight; | |
| public byte bColorCount; | |
| public byte bReserved; | |
| public short wPlanes; | |
| public short wBitCount; | |
| public int dwBytesInRes; | |
| public int dwImageOffset; | |
| }*/ | |
| } // IconReader |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Synopsis : A utility class that extracts the list of all icons handles (all sizes, all color counts) for a binary file like DLL, EXE, or ICO | |
| # Version : 3.1 | |
| # Lastupdate : 2026-Aug-11 | |
| $IconReader = @" | |
| using System; | |
| using System.IO; | |
| using System.Runtime.InteropServices; | |
| using System.Collections; | |
| using System.Collections.Generic; | |
| using System.Globalization; | |
| public sealed class IconReader : IDisposable | |
| { | |
| public IntPtr Handle { get; private set; } // primary ID for any icon | |
| public int Index { get; private set; } // global index, inc multi sizing | |
| public int GroupIndex { get; private set; } // icon group (multi-size icon) index; for EXE,DLL | |
| public string Id { get; private set; } // primarily for EXE,DLL | |
| public string GroupId { get; private set; } // primarily for EXE,DLL | |
| public int Width { get; private set; } | |
| public int Height { get; private set; } | |
| public int Bpp { get; private set; } | |
| public int Colors { get; private set; } | |
| public int ImgLength { get; private set; } | |
| public object Icon { get; set; } // icon image storage | |
| private IconReader(int groupIndex, string groupId, int index, string id, int width, int height, int bpp, int colors, int len, IntPtr handle) | |
| { | |
| GroupIndex = groupIndex; | |
| GroupId = groupId; | |
| Index = index; | |
| Id = id; | |
| Width = width; | |
| Height = height; | |
| Bpp = bpp; | |
| Colors = colors; | |
| ImgLength = len; | |
| Handle = handle; | |
| Icon = null; // initializes outside the class because System.Drawing namespace should be loaded before compilation | |
| } | |
| public void Dispose() {DestroyIcon(Handle);} | |
| // DLL,EXE: the total doesn't take into account multi sizing | |
| public static int GetTotal(string iconFilePath) | |
| { | |
| if (string.IsNullOrEmpty(iconFilePath)) return 0; // or ArgumentNullException? | |
| if (Path.GetExtension(iconFilePath).ToLower() == ".ico") | |
| { | |
| FileStream stream; | |
| try {stream = File.Open(iconFilePath, FileMode.Open);} catch {return 0;} | |
| var reader = new BinaryReader(stream, System.Text.Encoding.UTF8, false); | |
| byte[] bytes = reader.ReadBytes(6); // 4+2 | |
| stream.Close(); | |
| stream.Dispose(); | |
| reader.Close(); | |
| reader.Dispose(); | |
| if (bytes.Length < 6) | |
| throw new ArgumentException("'"+iconFilePath+"' is not a valid ICO file."); | |
| var signature = new byte[4]; | |
| var total = new byte[2]; | |
| Array.Copy(bytes,0, signature,0,4); | |
| Array.Copy(bytes,4, total,0,2); | |
| if (BitConverter.ToInt32(signature,0) != 0x10000) // Basic validation | |
| throw new ArgumentException("'"+iconFilePath+"' is not a valid ICO file."); | |
| return BitConverter.ToInt16(total,0); | |
| } | |
| else // dll or exe | |
| { | |
| IntPtr large; IntPtr small; | |
| var r = ExtractIconEx(iconFilePath, -1, out large, out small, 1); | |
| if ((uint)r == uint.MaxValue) {return 0;} else {return r;} | |
| } | |
| } // GetTotal | |
| public static List<IconReader> ImportIcons(string iconFilePath, int? byIndexOrResourceId = null) | |
| { | |
| if (string.IsNullOrEmpty(iconFilePath)) | |
| throw new ArgumentNullException("Filename is empty."); | |
| if (Path.GetExtension(iconFilePath).ToLower() == ".ico") | |
| return ImportIco(iconFilePath, byIndexOrResourceId); | |
| var icons = new List<IconReader>(); | |
| var handle = LoadLibraryEx(iconFilePath, IntPtr.Zero, LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE); | |
| if (handle != IntPtr.Zero) | |
| { | |
| try | |
| { | |
| icons = ImportIcons(handle, byIndexOrResourceId); | |
| } | |
| finally | |
| { | |
| FreeLibrary(handle); | |
| } | |
| } | |
| return icons; | |
| } // ImportIcons | |
| private static List<IconReader> ImportIcons(IntPtr handle, int? byIndexOrResourceId) | |
| { | |
| var icons = new List<IconReader>(); | |
| var entries = new Dictionary<ushort, GRPICONDIRENTRY>(); | |
| var groupIndices = new Dictionary<ushort, int>(); | |
| var groupIds = new Dictionary<ushort, string>(); | |
| var groupIndex = 0; | |
| if (EnumResourceNames(handle, new IntPtr(RT_GROUP_ICON), (m, t, n, lp) => | |
| { | |
| if (byIndexOrResourceId.HasValue && byIndexOrResourceId.Value >= 0 && byIndexOrResourceId.Value != groupIndex) | |
| { | |
| groupIndex++; | |
| return true; | |
| } | |
| string name; | |
| if (n.ToInt64() > ushort.MaxValue) | |
| { | |
| name = Marshal.PtrToStringAuto(n); | |
| } | |
| else | |
| { | |
| name = n.ToInt32().ToString(CultureInfo.InvariantCulture); | |
| } | |
| if (byIndexOrResourceId.HasValue && byIndexOrResourceId.Value < 0 && !string.Equals((-byIndexOrResourceId.Value).ToString(CultureInfo.InvariantCulture), name, StringComparison.Ordinal)) | |
| { | |
| groupIndex++; | |
| return true; | |
| } | |
| try | |
| { | |
| ExtractIconGroupEntries(handle, n, t, groupIndex, entries, groupIndices, groupIds); | |
| groupIndex++; | |
| } | |
| catch | |
| { | |
| // do nothing | |
| } | |
| return true; | |
| }, IntPtr.Zero)) | |
| { | |
| EnumResourceNames(handle, new IntPtr(RT_ICON), (m, t, n, lp) => | |
| { | |
| var iconHandle = ExtractIcon(handle, n, t, entries); | |
| if (iconHandle != IntPtr.Zero) | |
| { | |
| var id = (ushort)n.ToInt32(); | |
| //var ent = entries[id]; // ICONDIR | |
| var w = entries[id].bWidth == 0 ? 256 : entries[id].bWidth; | |
| var h = entries[id].bHeight == 0 ? 256 : entries[id].bHeight; | |
| var l = entries[id].dwBytesInRes; | |
| var bpp = entries[id].wBitCount; | |
| var colors = entries[id].bColorCount; | |
| var info = new IconReader(groupIndices[id], groupIds[id], n.ToInt32() - 1, n.ToString(), w,h,bpp,colors,l, iconHandle); | |
| icons.Add(info); | |
| } | |
| return true; | |
| }, IntPtr.Zero); | |
| } | |
| return icons; | |
| } // ImportIcons | |
| private static List<IconReader> ImportIco(string iconFilePath, int? byIndexOrSize) | |
| { | |
| var icons = new List<IconReader>(); | |
| byte[] bytes; | |
| try {bytes = File.ReadAllBytes(iconFilePath);} catch { | |
| throw new ArgumentException("Unable to read '"+iconFilePath+"' file."); | |
| } | |
| if (bytes.Length < 6) // corrupted | |
| throw new ArgumentException("'"+iconFilePath+"' is not a valid ICO file."); | |
| var signature = new byte[4]; | |
| Array.Copy(bytes,0, signature,0,4); | |
| if (BitConverter.ToInt32(signature,0) != 0x10000) // basic validation | |
| throw new ArgumentException("'"+iconFilePath+"' is not a valid ICO file."); | |
| var direntry = new byte[16]; | |
| var total = new byte[2]; | |
| Array.Copy(bytes,4, total,0,2); | |
| int dirend = 16 * BitConverter.ToInt16(total,0); // dirlen * amount | |
| ICONDIRENTRY entry; | |
| for (int i = 6, k = 0; i < dirend; i += 16, k++) | |
| { | |
| Array.Copy(bytes,i, direntry,0,16); | |
| GCHandle enthandle = GCHandle.Alloc(direntry, GCHandleType.Pinned); | |
| try { // get icon resource structure | |
| entry = Marshal.PtrToStructure<ICONDIRENTRY>(enthandle.AddrOfPinnedObject()); | |
| } catch {continue;} | |
| finally {enthandle.Free();} | |
| var w = entry.bWidth == 0 ? 256 : entry.bWidth; | |
| var h = entry.bHeight == 0 ? 256 : entry.bHeight; | |
| var l = entry.dwBytesInRes; | |
| var imgbytes = new byte[l]; | |
| Array.Copy(bytes,entry.dwImageOffset, imgbytes,0,l); | |
| var iconHandle = CreateIconFromResourceEx(imgbytes,l,true,0x00030000,w,h,0); | |
| //TODO if (!byIndexOrSize.HasValue || byIndexOrSize.Value < 0 || byIndexOrSize.Value == k || byIndexOrSize.Value == w) { | |
| var info = new IconReader(0,"",k,(k+1).ToString(), w,h,entry.wBitCount,entry.bColorCount,l, iconHandle); | |
| icons.Add(info); | |
| } | |
| return icons; | |
| } // ImportIco | |
| private static void ExtractIconGroupEntries(IntPtr module, IntPtr name, IntPtr type, int index, Dictionary<ushort, GRPICONDIRENTRY> entries, Dictionary<ushort, int> groupIndices, Dictionary<ushort, string> groupIds) | |
| { | |
| var handle = FindResource(module, name, type); | |
| if (handle == IntPtr.Zero) return; | |
| var size = SizeofResource(module, handle); | |
| if (size == 0) return; | |
| var resource = LoadResource(module, handle); | |
| if (resource == IntPtr.Zero) return; | |
| var ptr = LockResource(resource); | |
| if (ptr == IntPtr.Zero) return; | |
| // GRPICONDIR | |
| ptr += 2; // idReserved; | |
| var idtype = Marshal.ReadInt16(ptr); | |
| if (idtype != 1) return; // idType, 1 for ICO | |
| var elementSize = Marshal.SizeOf<GRPICONDIRENTRY>(); | |
| ptr += 2; | |
| var count = Marshal.ReadInt16(ptr); | |
| ptr += 2; | |
| for (var i = 0; i < count; i++) | |
| { | |
| var entry = Marshal.PtrToStructure<GRPICONDIRENTRY>(ptr); | |
| ptr += elementSize; | |
| entries[entry.nId] = entry; | |
| // is it a string or an id? | |
| groupIndices[entry.nId] = index; | |
| if (name.ToInt64() > ushort.MaxValue) | |
| { | |
| var id = Marshal.PtrToStringAuto(name); | |
| groupIds[entry.nId] = id; | |
| } | |
| else | |
| { | |
| groupIds[entry.nId] = "#" + name.ToInt32(); | |
| } | |
| } | |
| } // ExtractIconGroupEntries | |
| private static IntPtr ExtractIcon(IntPtr module, IntPtr name, IntPtr type, Dictionary<ushort, GRPICONDIRENTRY> entries) | |
| { | |
| GRPICONDIRENTRY x; | |
| if (!entries.TryGetValue((ushort)name.ToInt32(), out x)) return IntPtr.Zero; | |
| var hres = FindResource(module, name, type); | |
| if (hres == IntPtr.Zero) return IntPtr.Zero; | |
| var size = SizeofResource(module, hres); | |
| if (size == 0) return IntPtr.Zero; | |
| var res = LoadResource(module, hres); | |
| if (res == IntPtr.Zero) return IntPtr.Zero; | |
| var ptr = LockResource(res); | |
| if (ptr == IntPtr.Zero) return IntPtr.Zero; | |
| return CreateIconFromResourceEx(ptr, size, true, 0x00030000, 0, 0, 0); | |
| } // ExtractIcon | |
| private delegate bool EnumResNameProc(IntPtr hModule, IntPtr lpszType, IntPtr lpszName, IntPtr lParam); | |
| [DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)] | |
| private static extern bool EnumResourceNames(IntPtr hModule, IntPtr lpszType, EnumResNameProc lpEnumFunc, IntPtr lParam); | |
| [DllImport("kernel32", CharSet = CharSet.Unicode)] | |
| private static extern IntPtr FindResource(IntPtr hModule, IntPtr lpName, IntPtr lpType); | |
| [DllImport("kernel32")] | |
| private static extern int SizeofResource(IntPtr hModule, IntPtr hResInfo); | |
| [DllImport("kernel32")] | |
| private static extern IntPtr LoadResource(IntPtr hModule, IntPtr hResInfo); | |
| [DllImport("user32")] | |
| private static extern IntPtr CreateIconFromResourceEx(IntPtr presbits, int dwResSize, bool fIcon, int dwVer, int cxDesired, int cyDesired, int flags); | |
| [DllImport("user32")] | |
| private static extern IntPtr CreateIconFromResourceEx(byte[] presbits, int dwResSize, bool fIcon, int dwVer, int cxDesired, int cyDesired, int flags); | |
| [DllImport("user32")] | |
| private static extern bool DestroyIcon(IntPtr handle); | |
| [DllImport("kernel32", CharSet = CharSet.Unicode)] | |
| private static extern IntPtr LockResource(IntPtr hResData); | |
| [DllImport("kernel32", CharSet = CharSet.Unicode)] | |
| private static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, int dwFlags); | |
| [DllImport("kernel32")] | |
| private static extern bool FreeLibrary(IntPtr hModule); | |
| [DllImport("Shell32", EntryPoint = "ExtractIconExW", CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] | |
| private static extern int ExtractIconEx(string sFile, int iIndex, out IntPtr piLargeVersion, out IntPtr piSmallVersion, int amountIcons); | |
| private const int LOAD_LIBRARY_AS_DATAFILE = 0x2; | |
| private const int LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x20; | |
| private const int RT_ICON = 3; | |
| private const int RT_GROUP_ICON = RT_ICON + 11; | |
| [StructLayout(LayoutKind.Sequential, Pack = 1)] | |
| private struct GRPICONDIRENTRY | |
| { | |
| public byte bWidth; | |
| public byte bHeight; | |
| public byte bColorCount; | |
| public byte bReserved; | |
| public short wPlanes; | |
| public short wBitCount; | |
| public int dwBytesInRes; | |
| public ushort nId; | |
| } | |
| [StructLayout(LayoutKind.Sequential, Pack = 1)] | |
| private struct ICONDIRENTRY | |
| { | |
| public byte bWidth; | |
| public byte bHeight; | |
| public byte bColorCount; | |
| public byte bReserved; | |
| public short wPlanes; | |
| public short wBitCount; | |
| public int dwBytesInRes; | |
| public int dwImageOffset; | |
| } | |
| } | |
| "@ # IconReader | |
| Add-Type -TypeDefinition $IconReader | |
| Add-Type -AssemblyName System.Drawing |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ''' | |
| SYNOPSIS : Icon reader | |
| DESCRIPTION : A utility class that extracts the list of all icons handles (all sizes, all color counts) for a binary file like DLL, EXE, or ICO. | |
| FEATURES : Export: by size and index, multisize icons, split icon groups by size, various formats including ICO,PNG,JPG,WEBP,TIF; Win32 engine, PIL for none-ICO images | |
| VERSION : 1.4 | |
| LASTUPDATE : 2026-Aug-25 | |
| STATUS : Demo as Python learning track; tested with 3.14.7 | |
| LINK : https://en.wikipedia.org/wiki/ICO_(file_format) | |
| NOTES : Only builtin sizes can be exported, th's no native way to resize | |
| BACKLOG : Input filters | |
| ISSUES : PIL sucks: bmp, no transparency, no antialising; gif, no antialising | |
| ''' | |
| import pathlib,struct,os,re | |
| import ctypes | |
| from ctypes import wintypes | |
| try: | |
| from PIL import Image # external package :-( | |
| pilpkg = True | |
| except: pilpkg = False | |
| class IconReader: | |
| @property | |
| def Handle(self): return self.__handle # for .NET | |
| @property | |
| def Index(self): return self.__index | |
| @property | |
| def GroupIndex(self): return self.__groupIndex | |
| @property | |
| def Id(self): return self.__id | |
| @property | |
| def GroupId(self): return self.__groupId | |
| @property | |
| def Width(self): return self.__width | |
| @property | |
| def Height(self): return self.__height | |
| @property | |
| def Bpp(self): return self.__bpp | |
| @property | |
| def Colors(self): return self.__colors | |
| @property | |
| def ImgLength(self): return self.__imgLength | |
| def __init__(self,groupIndex,groupId,index,iid,width,height,bpp,colors,ilen,handle,imgdata=None): | |
| self.__handle = handle # pri dyn ID for icon in .NET; useless in Python | |
| self.__index = index # global index, inc multi sizing | |
| self.__groupIndex = groupIndex # EXE,DLL: icon group (multi-size icon) index; ICO: always 0 | |
| self.__id = iid # for EXE,DLL | |
| self.__groupId = groupId # for EXE,DLL | |
| self.__width = width # 256 if width == 0 else width | |
| self.__height = height # 256 if height == 0 else height | |
| self.__bpp = bpp | |
| self.__colors = colors | |
| self.__imgLength = ilen | |
| self.Icon = imgdata # icon image/object storage | |
| # Utilities | |
| @staticmethod | |
| def GetTotal(iconFilePath) -> int: | |
| if os.name != 'nt' : return 0 | |
| if iconFilePath == '' or iconFilePath is None : return 0 | |
| #if pathlib.Path(iconFilePath).exists() == False : return 0 | |
| # FileNotFoundError: No such file or directory | |
| CLS = IconReader # work around: surrogate "self" | |
| icount = 0 | |
| if pathlib.Path(iconFilePath).suffix.lower() == '.ico': | |
| try: | |
| with open(iconFilePath, 'rb') as f: | |
| data = f.read(6) | |
| if len(data) < 6 : return 0 | |
| isignature,icount = struct.unpack(CLS.__ICOHEADER, data[0:6]) | |
| if isignature != 0x10000 : return 0 | |
| except: | |
| print('Error reading file.') | |
| return None | |
| else: | |
| handle = CLS.__load_library(CLS,iconFilePath) | |
| if handle != 0 and handle is not None: | |
| # enumerator callback | |
| def lpEnumFunc(m,t,n,lp) : nonlocal icount; icount += 1; return True | |
| CLS.__enum_resource_names(CLS,handle,CLS.__RT_GROUP_ICON,lpEnumFunc) | |
| CLS.__kernel32.FreeLibrary(ctypes.c_ulong(handle)) | |
| return icount | |
| # END GetTotal | |
| @staticmethod | |
| def ImportIcons(iconFilePath, byIndexOrResourceId=None): | |
| if os.name != 'nt' : return None | |
| if iconFilePath == '' or iconFilePath is None : return None | |
| #if pathlib.Path(iconFilePath).exists() == False : return None | |
| CLS = IconReader # work around: surrogate "self" | |
| if pathlib.Path(iconFilePath).suffix.lower() == '.ico': | |
| return CLS.__import_ico(CLS, iconFilePath, byIndexOrResourceId) | |
| icons = [] # output | |
| handle = CLS.__load_library(CLS,iconFilePath) | |
| if handle != 0 and handle is not None: | |
| #icons = CLS.__extract_icons(CLS, handle, byIndexOrResourceId) | |
| #CLS.__kernel32.FreeLibrary(ctypes.c_ulong(handle)) | |
| try: icons = CLS.__extract_icons(CLS, handle, byIndexOrResourceId) | |
| finally: CLS.__kernel32.FreeLibrary(ctypes.c_ulong(handle)) | |
| return icons | |
| # END ImportIcons | |
| @staticmethod | |
| def ExportIcons(iconFilePath, iconInfo=None, outputPath=None, index=[], sizes=[], split=False, type='ico', background='#ffffff'): | |
| if os.name != 'nt' : return 0 | |
| if (outputPath == '' or outputPath is None) and (iconFilePath == '' or iconFilePath is None) : return False | |
| src = str(pathlib.Path(iconFilePath).resolve().suffix)[1:].lower() # exe,dll,ico | |
| if src == 'ico' and not split and (sizes is None or len(sizes) == 0) : split = True | |
| if iconInfo is None or len(iconInfo) == 0 : iconInfo = IconReader.ImportIcons(iconFilePath) | |
| if iconInfo is None or len(iconInfo) == 0 : return False | |
| # Resolve output file name and create a template | |
| if outputPath == '' or outputPath is None: | |
| sfx = r'-extract-%index%.ico' if src == 'ico' else r'-%index%.ico' | |
| outputPath = str(pathlib.Path(iconFilePath).resolve().with_suffix('')) + sfx | |
| else: | |
| fn = pathlib.Path(iconFilePath).resolve().stem | |
| outputPath = r'{0}\{1}-%index%.ico'.format(pathlib.Path(outputPath).resolve(), fn) | |
| if re.match(r'c:\\(windows.+|program.+|[^\\]+)$',outputPath,re.I) is not None: | |
| print('WARNING: System location specified as output. Export may fail. Make sure that you have write access rights.') | |
| # Validate input filters | |
| if index is not None or len(index) > 0: | |
| index = sorted( | |
| list(set([n for n in index if type(n).__name__ == 'int' and n > -1])), reverse=True | |
| ) | |
| if sizes is not None or len(sizes) > 0: | |
| sizes = sorted( | |
| list(set([n for n in sizes if type(n).__name__ == 'int' and n > 15 and n < 257])), reverse=True | |
| ) | |
| # experimental; png,jpg,... | |
| if type.lower() != 'ico': | |
| return IconReader.__export_image(IconReader, iconInfo, outputPath, type, index, sizes,background) | |
| # Group by GroupIndex first; extract indices | |
| gi_list = set([n.GroupIndex for n in iconInfo if n.Icon is not None]) | |
| for i in gi_list: # foreach index of icon resource as a group of sizes | |
| if len(index) != 0 and i not in index : continue # filter | |
| iconGroup = [n for n in iconInfo if i == n.GroupIndex] # select a group by index | |
| series = len(iconGroup) if split else 1 # mode selector | |
| for s in range(series): | |
| icoWork = [iconGroup[s]] if split else iconGroup # select a chunk to work with | |
| # resolve output file name | |
| # PROBLEM: size may be wrong because of filtering (below) | |
| icofile = outputPath.replace('%index%',f'{str(i)}%sz%%color%') | |
| if split: | |
| if icoWork[0].Colors == 0: | |
| icofile = icofile.replace('%sz%%color%',f'-{icoWork[0].Width}') | |
| else: | |
| icofile = icofile.replace('%sz%%color%',f'-{icoWork[0].Width}-{icoWork[0].Colors}') | |
| else: | |
| icofile = icofile.replace('%sz%%color%','') | |
| try: fhandle = open(icofile,'wb') | |
| except: | |
| print(f"ERROR: Failed to create file '{icofile}'. Check filepath, write access, file lock and try again.") | |
| #if pathlib.Path(icofile).exists() : os.remove(icofile) | |
| continue | |
| inew = False # no icons to export | |
| memstream = bytearray() # memory buffer | |
| memstream.extend(struct.pack('@BBHH',0,0,1,len(icoWork))) # ICO file header | |
| offset = 6 + (16 * len(icoWork)) | |
| # foreach size in icon (group of sizes) | |
| #natives = set([n.Width for n in icoWork]) # builtin sizes | |
| for icocount,icon in enumerate(icoWork): | |
| #if icon.Icon is None : continue # already filtered | |
| if len(sizes) != 0 and icon.Width not in sizes : continue # filter | |
| w = 0 if icon.Width == 256 else icon.Width | |
| h = 0 if icon.Height == 256 else icon.Height | |
| entry = struct.pack('@BBBBHHII',w,h,0,0,0,32,icon.ImgLength,offset) | |
| memstream.extend(entry) | |
| offset += icon.ImgLength | |
| inew = True | |
| for icon in icoWork: | |
| #if icon.Icon is not None: # already filtered | |
| if len(sizes) == 0 or icon.Width in sizes : memstream.extend(icon.Icon) | |
| icocount += 1 # because it starts with 0 | |
| if inew and icocount != len(icoWork): | |
| # if a filter happend update icon amount value with actual | |
| memstream[4],memstream[5] = struct.pack('@H',icocount) | |
| if inew : fhandle.write(memstream) # flush buffer to a file | |
| # Cleanup | |
| fhandle.close() | |
| memstream.clear() | |
| icoWork.clear() | |
| if not inew : os.remove(icofile) | |
| else : print(f"Icon '{icofile}' successfully exported.") | |
| iconGroup.clear() | |
| if len(gi_list) > 0: return True | |
| else: return False | |
| # END ExportIcons | |
| # Icon helpers | |
| def __extract_icons(self, handle, byIndexOrResourceId=None): | |
| icons = [] # output | |
| entries = {} # directory entry table | |
| groupIndices = {} # for DLL,EXE | |
| groupIds = {} # for DLL,EXE | |
| groupIndex = 0 # batch index | |
| def lpEnumFunc(m,t,n,lp): # enumerator callback | |
| nonlocal groupIndex, entries, groupIndices, groupIds | |
| # odd condition: test!!! | |
| if byIndexOrResourceId is not None and byIndexOrResourceId.Value >= 0 and byIndexOrResourceId != groupIndex: | |
| groupIndex += 1 | |
| return True | |
| if isinstance(n, str): | |
| name = n | |
| else: | |
| name = ctypes.cast(n, ctypes.c_wchar_p).value ##.decode() | |
| # odd condition: test!!! | |
| if byIndexOrResourceId is not None and byIndexOrResourceId < 0 and str(-byIndexOrResourceId).upper() != name.upper(): | |
| groupIndex += 1 | |
| return True | |
| try: | |
| self.__extract_icon_groups(self,handle, n, t, groupIndex, entries, groupIndices, groupIds) | |
| groupIndex += 1 | |
| except: pass # do nothing | |
| return True | |
| if self.__enum_resource_names(self,handle,self.__RT_GROUP_ICON,lpEnumFunc) != 0: | |
| def lpEnumFunc(m,t,n,lp): # enumerator callback | |
| nonlocal icons, groupIndices, groupIds | |
| if len(entries) == 0 : return False #TODO ??? True | |
| imgbytes = self.__get_imgbytes(self,handle,n,t) # th's no ImageOffset in RESDIR | |
| if len(imgbytes) > 0: | |
| iconHandle = self.__user32.CreateIconFromResourceEx(imgbytes, len(imgbytes), True, 0x00030000, 0, 0, 0) | |
| if iconHandle != 0 and iconHandle is not None: | |
| id = int(n) | |
| w,h = entries[id]['bWidth'],entries[id]['bHeight'] | |
| l = entries[id]['dwBytesInRes'] | |
| bpp = entries[id]['wBitCount'] | |
| colors = entries[id]['bColorCount'] | |
| if colors == 0 and bpp == 8 : colors = 256 # normalize | |
| if 0 == w : w = 256 # normalize | |
| if 0 == h : h = 256 # normalize | |
| # TODO if byIndexOrResourceId is None or byIndexOrResourceId >= 0 and (byIndexOrResourceId == w or byIndexOrResourceId == groupIndices[id]): | |
| info = IconReader(groupIndices[id], groupIds[id], int(n) - 1, str(n), w,h,bpp,colors,l, iconHandle,imgbytes) | |
| icons.append(info) | |
| return True | |
| self.__enum_resource_names(self,handle, self.__RT_ICON, lpEnumFunc) | |
| return icons | |
| # END __extract_icons | |
| # experimental; requires PIL package, sucks | |
| def __export_image(self, iconInfo, outputPath, type, index, sizes, background): | |
| if not pilpkg: | |
| print('ERROR: PIL package not installed. Installation command is "python -m pip install pillow".') | |
| return False | |
| import io | |
| # normalize type and background | |
| type = type.lower() # bmp,gif are unreliable | |
| if type not in ['png','jpg','jpeg','tif','tiff','webp','bmp','gif'] : return False | |
| if type == 'jpg' : type = 'jpeg' | |
| elif type == 'tif' : type = 'tiff' | |
| if background is None or background == '' : background = '#ffffff' | |
| bmphdr = struct.pack('@BBHH',0,0,1,1) # image header | |
| for icon in iconInfo: | |
| if icon.Icon == None or len(icon.Icon) == 0 : continue # test for image data | |
| ii = icon.GroupIndex | |
| if len(index) != 0 and ii not in index : continue # filter | |
| # for resizing; experimental | |
| #iconGroup = [n for n in iconInfo if ii == n['GroupIndex']] # select a group by index | |
| #natives = set([n['Width'] for n in iconGroup]) # builtin sizes | |
| if len(sizes) != 0 and icon.Width not in sizes : continue # filter | |
| # resolve file name | |
| icofile = outputPath.replace('%index%',f'{str(ii)}%sz%%color%') | |
| icofile = re.sub(r'\.ico$',f'.{type}',icofile) | |
| if icon.Colors == 0: | |
| icofile = icofile.replace('%sz%%color%',f'-{icon.Width}') | |
| else: | |
| icofile = icofile.replace('%sz%%color%',f'-{icon.Width}-{icon.Colors}') | |
| w = 0 if icon.Width == 256 else icon.Width | |
| h = 0 if icon.Height == 256 else icon.Height | |
| direntry = struct.pack('@BBBBHHII', w, h, 0, 0, 0, 32, icon.ImgLength, 22) | |
| inMemoryICO = io.BytesIO(bmphdr + direntry + icon.Icon) | |
| img = Image.open(inMemoryICO) | |
| if type in ['jpeg','bmp']: | |
| bg = Image.new('RGBA',(icon.Width,icon.Height),background) | |
| #bg.putalpha(255) # adds(255)/removes(0) antialiasing | |
| if type == 'jpeg' : img = Image.alpha_composite(bg,img).convert('RGB') | |
| else : | |
| #bg.putdata([(255,255,255,0) for n in bg.getdata()]) | |
| img = Image.alpha_composite(bg,img) # this adds antialiasing | |
| #elif type in ['gif']: # TODO remove boundary pixelization/add antialiasing | |
| params = {'quality':100} | |
| if type in ['jpeg','bmp'] : params['transparency'] = 0 | |
| img.save(icofile, **params) | |
| img.close() | |
| inMemoryICO.close() | |
| '''try: | |
| img = Image.open(inMemoryICO) | |
| img.save(icofile,type) | |
| print(f"Icon '{icofile}' successfully exported.") | |
| except PIL.UnidentifiedImageError: | |
| print(f"ERROR: Failed to open image.") | |
| except OSError: | |
| print(f"ERROR: Failed to create file '{icofile}'. Check filepath, write access, file lock and try again.") | |
| finally: | |
| if img != None : img.close() | |
| inMemoryICO.close()''' | |
| return True | |
| # END __export_image | |
| def __import_ico(self, iconFilePath, byIndexOrSize=None): | |
| try: | |
| with open(iconFilePath, 'rb') as f: | |
| data = f.read() | |
| if len(data) < 6 : return None | |
| isignature,icount = struct.unpack(self.__ICOHEADER, data[0:6]) | |
| if isignature != 0x10000 or icount == 0 : return None | |
| except: | |
| print('Error reading file.') | |
| return None | |
| if byIndexOrSize is not None and byIndexOrSize < 0 : byIndexOrSize = None | |
| ENTRYFORMAT = '@BBBBHHII' | |
| icons = [] # output icon storage | |
| #entrysize = struct.calcsize(ENTRYFORMAT) # must be 16 | |
| diroffset = range(6,16*icount,16) | |
| for k,i in enumerate(diroffset): | |
| w,h,colors,_,_,bpp,l,dataoffset = struct.unpack(ENTRYFORMAT, data[i:i+16]) | |
| if 0 == w : w = 256 # normalize | |
| if 0 == h : h = 256 # normalize | |
| # TODO if byIndexOrSize is None or byIndexOrSize >= 0 and (byIndexOrSize == w or byIndexOrSize == k)): | |
| imgbytes = data[dataoffset:(dataoffset+l)] | |
| iconHandle = self.__user32.CreateIconFromResourceEx(imgbytes, l, True, 0x0030000, w, h, 0) | |
| info = IconReader(0, "", k, k+1, w,h,bpp,colors,l, iconHandle,imgbytes) | |
| icons.append(info) | |
| return icons | |
| # END __import_ico | |
| def __extract_icon_groups(self, module, name, type, index, entries, groupIndices, groupIds): | |
| hres = self.__find_resource(self, module, name, type) | |
| if hres == 0 or hres is None : return | |
| #size = self.__sizeof_resource(self, module, hres) | |
| #if size == 0 : return | |
| resource = self.__load_resource(self, module, hres) | |
| if resource == 0 or resource is None : return | |
| ptr = self.__lock_resource(self, resource) | |
| if ptr == 0 or ptr is None : return | |
| # GRPICONDIR | |
| ptr += 2 # idReserved | |
| idtype = ctypes.cast(ptr, ctypes.POINTER(ctypes.c_uint16)).contents.value | |
| if idtype != 1 : return # idType: 1 - ICO, 2 - CUR | |
| ENTRYFORMAT = '@BBBBhhIH' | |
| entrySize = struct.calcsize(ENTRYFORMAT) # must be 14 | |
| ptr += 2 | |
| count = ctypes.cast(ptr, ctypes.POINTER(ctypes.c_uint16)).contents.value | |
| ptr += 2 | |
| for i in range(count): | |
| # restore ICONDIR | |
| ebytes = ctypes.cast(ptr, ctypes.POINTER((ctypes.c_byte * entrySize))).contents # raw entry | |
| ptr += entrySize | |
| values = struct.unpack(ENTRYFORMAT, ebytes) | |
| entry = dict(zip(self.__GRPICONDIRENTRY, values)) | |
| ##if entry['bWidth'] == 0: entry['bWidth'] = 256 # normalize later | |
| ##if entry['bHeight'] == 0: entry['bHeight'] = 256 | |
| entries[entry['nId']] = entry | |
| # is it a string or an id? | |
| groupIndices[entry['nId']] = index | |
| if isinstance(name, str): | |
| groupIds[entry['nId']] = f"#{name}" | |
| else: | |
| id = ctypes.cast(name, ctypes.c_wchar_p).value #.decode() | |
| groupIds[entry['nId']] = id | |
| # END __extract_icon_groups | |
| def __get_imgbytes(self, module, name, type): | |
| # https://devblogs.microsoft.com/oldnewthing/?p=7083 | |
| hres = self.__find_resource(self, module, name, type) | |
| if hres == 0 or hres is None : return 0 | |
| size = self.__sizeof_resource(self, module, hres) | |
| if size == 0 : return 0 | |
| resource = self.__load_resource(self, module, hres) | |
| if resource == 0 or resource is None : return 0 | |
| ptr = self.__lock_resource(self, resource) | |
| if ptr == 0 or ptr is None : return 0 | |
| return ctypes.cast(ptr, ctypes.POINTER((ctypes.c_byte * size))).contents | |
| # END __get_imgbytes | |
| # Generic helpers | |
| def __load_library(self, lpFileName): | |
| LoadLibraryExW = self.__kernel32.LoadLibraryExW | |
| LoadLibraryExW.argtypes = [wintypes.LPCWSTR, wintypes.HANDLE, wintypes.DWORD] | |
| LoadLibraryExW.restype = wintypes.HMODULE | |
| flags = self.__LOAD_LIBRARY_AS_DATAFILE | self.__LOAD_LIBRARY_AS_IMAGE_RESOURCE | |
| return LoadLibraryExW(lpFileName, 0, flags) | |
| def __enum_resource_names(self, handle, lpType, lpEnumFunc): | |
| ENUMFUNC = ctypes.WINFUNCTYPE(ctypes.c_bool, wintypes.HMODULE, wintypes.LPVOID, wintypes.LPVOID, wintypes.LPARAM) | |
| enum_func = ENUMFUNC(lpEnumFunc) | |
| enum_func.restype = ctypes.c_bool | |
| EnumResourceNames = self.__kernel32.EnumResourceNamesW | |
| EnumResourceNames.argtypes = [wintypes.HMODULE, wintypes.LPVOID, ENUMFUNC, wintypes.LPARAM] | |
| EnumResourceNames.restype = ctypes.c_bool | |
| return EnumResourceNames(handle, lpType, enum_func, 0) | |
| def __find_resource(self, hModule, lpName, lpType): | |
| FindResource = self.__kernel32.FindResourceW | |
| FindResource.argtypes = [wintypes.HMODULE, wintypes.LPVOID, wintypes.LPVOID] | |
| FindResource.restype = wintypes.HRSRC | |
| return FindResource(hModule, lpName, lpType) | |
| def __sizeof_resource(self, hModule, hResInfo): | |
| SizeofResource = self.__kernel32.SizeofResource | |
| SizeofResource.argtypes = [wintypes.HMODULE, wintypes.HRSRC] | |
| SizeofResource.restype = wintypes.DWORD | |
| return SizeofResource(hModule, hResInfo) | |
| def __load_resource(self, hModule, hResInfo): | |
| LoadResource = self.__kernel32.LoadResource | |
| LoadResource.argtypes = [wintypes.HMODULE, wintypes.HRSRC] | |
| LoadResource.restype = wintypes.HGLOBAL | |
| return LoadResource(hModule, hResInfo) | |
| def __lock_resource(self, hResData): | |
| LockResource = self.__kernel32.LockResource | |
| LockResource.argtypes = [wintypes.HGLOBAL] | |
| LockResource.restype = ctypes.c_void_p | |
| return LockResource(hResData) | |
| __user32 = ctypes.WinDLL(r'C:\Windows\System32\user32.dll') | |
| __kernel32 = ctypes.WinDLL(r'C:\Windows\System32\kernel32.dll') | |
| __LOAD_LIBRARY_AS_DATAFILE = 0x2 | |
| __LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x20 | |
| __RT_ICON = 3 | |
| __RT_GROUP_ICON = __RT_ICON + 11 | |
| __GRPICONDIRENTRY = ["bWidth","bHeight","bColorCount","bReserved","wPlanes","wBitCount","dwBytesInRes","nId"] | |
| #__ICONDIRENTRY = ["bWidth","bHeight","bColorCount","bReserved","wPlanes","wBitCount","dwBytesInRes","dwImageOffset"] | |
| __ICOHEADER = '@IH' | |
| # END class IconReader | |
| icons = IconReader.ImportIcons(r'C:\windows\system32\WindowsPowerShell\v1.0\PowerShell.exe') | |
| fmtR = '{0:>{1}}' | |
| fmtL = '{0:<{1}}' | |
| for i in icons: | |
| print( | |
| fmtR.format(i.Handle,11), | |
| fmtR.format(i.Index,3), | |
| fmtR.format(i.Width,4), | |
| fmtR.format(i.Bpp,3), | |
| fmtR.format(i.Colors,3), | |
| fmtR.format(i.ImgLength,8), | |
| fmtR.format(i.GroupIndex,3), | |
| fmtL.format(i.GroupId,13) | |
| ) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment