/* * IconExtractor/IconUtil for .NET * Copyright (C) 2014 Tsuda Kageyu. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.IO; using System.Runtime.InteropServices; using System.Windows.Forms; namespace TurnoffMonitorOnLock.IconExtract { internal class IconExtractor { //////////////////////////////////////////////////////////////////////// // Constants // Flags for LoadLibraryEx(). private const uint LOAD_LIBRARY_AS_DATAFILE = 0x00000002; // Resource types for EnumResourceNames(). private static readonly IntPtr RtIcon = (IntPtr)3; private static readonly IntPtr RtGroupIcon = (IntPtr)14; //////////////////////////////////////////////////////////////////////// // Fields private byte[][] _iconData = null; // Binary data of each icon. //////////////////////////////////////////////////////////////////////// // Public properties /// /// Gets the count of the icons in the associated file. /// public int Count => _iconData.Length; /// /// Initializes a new instance of the IconExtractor class from the specified file name. /// /// The file to extract icons from. public IconExtractor(string fileName) => Initialize(fileName); /// /// Extracts an icon from the file. /// /// Zero based index of the icon to be extracted. /// A System.Drawing.Icon object. /// Always returns new copy of the Icon. It should be disposed by the user. public Icon GetIcon(int index) { if (index < 0 || Count <= index) throw new ArgumentOutOfRangeException(nameof(index)); // Create an Icon based on a .ico file in memory. using (var ms = new MemoryStream(_iconData[index])) return new Icon(ms); } private void Initialize(string fileName) { if (fileName == null) throw new ArgumentNullException(nameof(fileName)); var hModule = IntPtr.Zero; try { hModule = NativeMethods.LoadLibraryEx(fileName, IntPtr.Zero, LOAD_LIBRARY_AS_DATAFILE); if (hModule == IntPtr.Zero) throw new Win32Exception(); // Enumerate the icon resource and build .ico files in memory. var tmpData = new List(); bool Callback(IntPtr h, IntPtr t, IntPtr name, IntPtr l) { // Refer the following URL for the data structures used here: // http://msdn.microsoft.com/en-us/library/ms997538.aspx // RT_GROUP_ICON resource consists of a GRPICONDIR and GRPICONDIRENTRY's. var dir = GetDataFromResource(hModule, RtGroupIcon, name); // Calculate the size of an entire .icon file. int count = BitConverter.ToUInt16(dir, 4); // GRPICONDIR.idCount var len = 6 + 16 * count; // sizeof(ICONDIR) + sizeof(ICONDIRENTRY) * count for (var i = 0; i < count; ++i) len += BitConverter.ToInt32(dir, 6 + 14 * i + 8); // GRPICONDIRENTRY.dwBytesInRes using (var dst = new BinaryWriter(new MemoryStream(len))) { // Copy GRPICONDIR to ICONDIR. dst.Write(dir, 0, 6); var picOffset = 6 + 16 * count; // sizeof(ICONDIR) + sizeof(ICONDIRENTRY) * count for (var i = 0; i < count; ++i) { // Load the picture. var id = BitConverter.ToUInt16(dir, 6 + 14 * i + 12); // GRPICONDIRENTRY.nID var pic = GetDataFromResource(hModule, RtIcon, (IntPtr)id); // Copy GRPICONDIRENTRY to ICONDIRENTRY. dst.Seek(6 + 16 * i, SeekOrigin.Begin); dst.Write(dir, 6 + 14 * i, 8); // First 8bytes are identical. dst.Write(pic.Length); // ICONDIRENTRY.dwBytesInRes dst.Write(picOffset); // ICONDIRENTRY.dwImageOffset // Copy a picture. dst.Seek(picOffset, SeekOrigin.Begin); dst.Write(pic, 0, pic.Length); picOffset += pic.Length; } tmpData.Add(((MemoryStream)dst.BaseStream).ToArray()); } return true; } NativeMethods.EnumResourceNames(hModule, RtGroupIcon, Callback, IntPtr.Zero); _iconData = tmpData.ToArray(); } finally { if (hModule != IntPtr.Zero) NativeMethods.FreeLibrary(hModule); } } private static byte[] GetDataFromResource(IntPtr hModule, IntPtr type, IntPtr name) { // Load the binary data from the specified resource. var hResInfo = NativeMethods.FindResource(hModule, name, type); if (hResInfo == IntPtr.Zero) throw new Win32Exception(); var hResData = NativeMethods.LoadResource(hModule, hResInfo); if (hResData == IntPtr.Zero) throw new Win32Exception(); var pResData = NativeMethods.LockResource(hResData); if (pResData == IntPtr.Zero) throw new Win32Exception(); var size = NativeMethods.SizeofResource(hModule, hResInfo); if (size == 0) throw new Win32Exception(); var buf = new byte[size]; Marshal.Copy(pResData, buf, 0, buf.Length); return buf; } public static Icon GetMainIcon() { var iex = new IconExtractor(Application.ExecutablePath); return iex.Count > 0 ? iex.GetIcon(0) : null; } } }