-
Notifications
You must be signed in to change notification settings - Fork 77
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
25 changed files
with
888 additions
and
196 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,237 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.ComponentModel; | ||
using System.Drawing; | ||
using System.IO; | ||
using System.Runtime.InteropServices; | ||
using System.Text; | ||
|
||
namespace ProcessInjector | ||
{ | ||
public class IconExtractor | ||
{ | ||
//////////////////////////////////////////////////////////////////////// | ||
// Constants | ||
|
||
// Flags for LoadLibraryEx(). | ||
|
||
private const uint LOAD_LIBRARY_AS_DATAFILE = 0x00000002; | ||
|
||
// Resource types for EnumResourceNames(). | ||
|
||
private readonly static IntPtr RT_ICON = (IntPtr)3; | ||
private readonly static IntPtr RT_GROUP_ICON = (IntPtr)14; | ||
|
||
private const int MAX_PATH = 260; | ||
|
||
//////////////////////////////////////////////////////////////////////// | ||
// Fields | ||
|
||
private byte[][] iconData = null; // Binary data of each icon. | ||
|
||
//////////////////////////////////////////////////////////////////////// | ||
// Public properties | ||
|
||
/// <summary> | ||
/// Gets the full path of the associated file. | ||
/// </summary> | ||
public string FileName | ||
{ | ||
get; | ||
private set; | ||
} | ||
|
||
/// <summary> | ||
/// Gets the count of the icons in the associated file. | ||
/// </summary> | ||
public int Count | ||
{ | ||
get { return iconData.Length; } | ||
} | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the IconExtractor class from the specified file name. | ||
/// </summary> | ||
/// <param name="fileName">The file to extract icons from.</param> | ||
public IconExtractor(string fileName) | ||
{ | ||
Initialize(fileName); | ||
} | ||
|
||
/// <summary> | ||
/// Extracts an icon from the file. | ||
/// </summary> | ||
/// <param name="index">Zero based index of the icon to be extracted.</param> | ||
/// <returns>A System.Drawing.Icon object.</returns> | ||
/// <remarks>Always returns new copy of the Icon. It should be disposed by the user.</remarks> | ||
public Icon GetIcon(int index) | ||
{ | ||
if (index < 0 || Count <= index) | ||
throw new ArgumentOutOfRangeException("index"); | ||
|
||
// Create an Icon based on a .ico file in memory. | ||
|
||
using (var ms = new MemoryStream(iconData[index])) | ||
{ | ||
return new Icon(ms); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Extracts all the icons from the file. | ||
/// </summary> | ||
/// <returns>An array of System.Drawing.Icon objects.</returns> | ||
/// <remarks>Always returns new copies of the Icons. They should be disposed by the user.</remarks> | ||
public Icon[] GetAllIcons() | ||
{ | ||
var icons = new List<Icon>(); | ||
for (int i = 0; i < Count; ++i) | ||
icons.Add(GetIcon(i)); | ||
|
||
return icons.ToArray(); | ||
} | ||
|
||
private void Initialize(string fileName) | ||
{ | ||
if (fileName == null) | ||
throw new ArgumentNullException("fileName"); | ||
|
||
IntPtr hModule = IntPtr.Zero; | ||
try | ||
{ | ||
hModule = NativeMethods.LoadLibraryEx(fileName, IntPtr.Zero, LOAD_LIBRARY_AS_DATAFILE); | ||
if (hModule == IntPtr.Zero) | ||
throw new Win32Exception(); | ||
|
||
FileName = GetFileName(hModule); | ||
|
||
// Enumerate the icon resource and build .ico files in memory. | ||
|
||
var tmpData = new List<byte[]>(); | ||
|
||
ENUMRESNAMEPROC callback = (h, t, name, 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, RT_GROUP_ICON, name); | ||
|
||
// Calculate the size of an entire .icon file. | ||
|
||
int count = BitConverter.ToUInt16(dir, 4); // GRPICONDIR.idCount | ||
int len = 6 + 16 * count; // sizeof(ICONDIR) + sizeof(ICONDIRENTRY) * count | ||
for (int 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); | ||
|
||
int picOffset = 6 + 16 * count; // sizeof(ICONDIR) + sizeof(ICONDIRENTRY) * count | ||
|
||
for (int i = 0; i < count; ++i) | ||
{ | ||
// Copy GRPICONDIRENTRY to ICONDIRENTRY. | ||
|
||
dst.Seek(6 + 16 * i, SeekOrigin.Begin); | ||
|
||
dst.Write(dir, 6 + 14 * i, 12); // First 12bytes are identical. | ||
dst.Write(picOffset); // Write offset instead of ID. | ||
|
||
// Copy a picture. | ||
|
||
dst.Seek(picOffset, SeekOrigin.Begin); | ||
|
||
ushort id = BitConverter.ToUInt16(dir, 6 + 14 * i + 12); // GRPICONDIRENTRY.nID | ||
var pic = GetDataFromResource(hModule, RT_ICON, (IntPtr)id); | ||
|
||
dst.Write(pic, 0, pic.Length); | ||
|
||
picOffset += pic.Length; | ||
} | ||
|
||
tmpData.Add(((MemoryStream)dst.BaseStream).ToArray()); | ||
} | ||
|
||
return true; | ||
}; | ||
NativeMethods.EnumResourceNames(hModule, RT_GROUP_ICON, callback, IntPtr.Zero); | ||
|
||
iconData = tmpData.ToArray(); | ||
} | ||
finally | ||
{ | ||
if (hModule != IntPtr.Zero) | ||
NativeMethods.FreeLibrary(hModule); | ||
} | ||
} | ||
|
||
private byte[] GetDataFromResource(IntPtr hModule, IntPtr type, IntPtr name) | ||
{ | ||
// Load the binary data from the specified resource. | ||
|
||
IntPtr hResInfo = NativeMethods.FindResource(hModule, name, type); | ||
if (hResInfo == IntPtr.Zero) | ||
throw new Win32Exception(); | ||
|
||
IntPtr hResData = NativeMethods.LoadResource(hModule, hResInfo); | ||
if (hResData == IntPtr.Zero) | ||
throw new Win32Exception(); | ||
|
||
IntPtr pResData = NativeMethods.LockResource(hResData); | ||
if (pResData == IntPtr.Zero) | ||
throw new Win32Exception(); | ||
|
||
uint size = NativeMethods.SizeofResource(hModule, hResInfo); | ||
if (size == 0) | ||
throw new Win32Exception(); | ||
|
||
byte[] buf = new byte[size]; | ||
Marshal.Copy(pResData, buf, 0, buf.Length); | ||
|
||
return buf; | ||
} | ||
|
||
private string GetFileName(IntPtr hModule) | ||
{ | ||
// Alternative to GetModuleFileName() for the module loaded with | ||
// LOAD_LIBRARY_AS_DATAFILE option. | ||
|
||
// Get the file name in the format like: | ||
// "\\Device\\HarddiskVolume2\\Windows\\System32\\shell32.dll" | ||
|
||
string fileName; | ||
{ | ||
var buf = new StringBuilder(MAX_PATH); | ||
int len = NativeMethods.GetMappedFileName( | ||
NativeMethods.GetCurrentProcess(), hModule, buf, buf.Capacity); | ||
if (len == 0) | ||
throw new Win32Exception(); | ||
|
||
fileName = buf.ToString(); | ||
} | ||
|
||
// Convert the device name to drive name like: | ||
// "C:\\Windows\\System32\\shell32.dll" | ||
|
||
for (char c = 'A'; c <= 'Z'; ++c) | ||
{ | ||
var drive = c + ":"; | ||
var buf = new StringBuilder(MAX_PATH); | ||
int len = NativeMethods.QueryDosDevice(drive, buf, buf.Capacity); | ||
if (len == 0) | ||
continue; | ||
|
||
var devPath = buf.ToString(); | ||
if (fileName.StartsWith(devPath)) | ||
return (drive + fileName.Substring(devPath.Length)); | ||
} | ||
|
||
return fileName; | ||
} | ||
} | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Drawing; | ||
using System.IO; | ||
using System.Reflection; | ||
using System.Reflection.Emit; | ||
|
||
namespace ProcessInjector | ||
{ | ||
public static class IconUtil | ||
{ | ||
private delegate byte[] GetIconDataDelegate(Icon icon); | ||
|
||
static GetIconDataDelegate getIconData; | ||
|
||
static IconUtil() | ||
{ | ||
// Create a dynamic method to access Icon.iconData private field. | ||
|
||
var dm = new DynamicMethod( | ||
"GetIconData", typeof(byte[]), new Type[] { typeof(Icon) }, typeof(Icon)); | ||
var fi = typeof(Icon).GetField( | ||
"iconData", BindingFlags.Instance | BindingFlags.NonPublic); | ||
var gen = dm.GetILGenerator(); | ||
gen.Emit(OpCodes.Ldarg_0); | ||
gen.Emit(OpCodes.Ldfld, fi); | ||
gen.Emit(OpCodes.Ret); | ||
|
||
getIconData = (GetIconDataDelegate)dm.CreateDelegate(typeof(GetIconDataDelegate)); | ||
} | ||
|
||
/// <summary> | ||
/// Split an Icon consists of multiple icons into an array of Icon each | ||
/// consists of single icons. | ||
/// </summary> | ||
/// <param name="icon">A System.Drawing.Icon to be split.</param> | ||
/// <returns>An array of System.Drawing.Icon.</returns> | ||
public static Icon[] Split(Icon icon) | ||
{ | ||
if (icon == null) | ||
throw new ArgumentNullException("icon"); | ||
|
||
// Create an .ico file in memory, then split it into separate icons. | ||
|
||
var src = GetIconData(icon); | ||
|
||
var splitIcons = new List<Icon>(); | ||
{ | ||
int count = BitConverter.ToUInt16(src, 4); | ||
|
||
for (int i = 0; i < count; i++) | ||
{ | ||
int length = BitConverter.ToInt32(src, 6 + 16 * i + 8); // ICONDIRENTRY.dwBytesInRes | ||
int offset = BitConverter.ToInt32(src, 6 + 16 * i + 12); // ICONDIRENTRY.dwImageOffset | ||
|
||
using (var dst = new BinaryWriter(new MemoryStream(6 + 16 + length))) | ||
{ | ||
// Copy ICONDIR and set idCount to 1. | ||
|
||
dst.Write(src, 0, 4); | ||
dst.Write((short)1); | ||
|
||
// Copy ICONDIRENTRY and set dwImageOffset to 22. | ||
|
||
dst.Write(src, 6 + 16 * i, 12); // ICONDIRENTRY except dwImageOffset | ||
dst.Write(22); // ICONDIRENTRY.dwImageOffset | ||
|
||
// Copy a picture. | ||
|
||
dst.Write(src, offset, length); | ||
|
||
// Create an icon from the in-memory file. | ||
|
||
dst.BaseStream.Seek(0, SeekOrigin.Begin); | ||
splitIcons.Add(new Icon(dst.BaseStream)); | ||
} | ||
} | ||
} | ||
|
||
return splitIcons.ToArray(); | ||
} | ||
|
||
/// <summary> | ||
/// Converts an Icon to a GDI+ Bitmap preserving the transparent area. | ||
/// </summary> | ||
/// <param name="icon">An System.Drawing.Icon to be converted.</param> | ||
/// <returns>A System.Drawing.Bitmap Object.</returns> | ||
public static Bitmap ToBitmap(Icon icon) | ||
{ | ||
if (icon == null) | ||
throw new ArgumentNullException("icon"); | ||
|
||
// Quick workaround: Create an .ico file in memory, then load it as a Bitmap. | ||
|
||
using (var ms = new MemoryStream()) | ||
{ | ||
icon.Save(ms); | ||
return (Bitmap)Image.FromStream(ms); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Gets the bit depth of an Icon. | ||
/// </summary> | ||
/// <param name="icon">An System.Drawing.Icon object.</param> | ||
/// <returns>The biggest bit depth of the icons.</returns> | ||
public static int GetBitCount(Icon icon) | ||
{ | ||
if (icon == null) | ||
throw new ArgumentNullException("icon"); | ||
|
||
// Create an .ico file in memory, then read the header. | ||
|
||
var data = GetIconData(icon); | ||
|
||
int count = BitConverter.ToInt16(data, 4); | ||
int bitDepth = 0; | ||
for (int i = 0; i < count; ++i) | ||
{ | ||
int depth = BitConverter.ToUInt16(data, 6 + 16 * i + 6); // ICONDIRENTRY.wBitCount | ||
if (depth > bitDepth) | ||
bitDepth = depth; | ||
} | ||
|
||
return bitDepth; | ||
} | ||
|
||
private static byte[] GetIconData(Icon icon) | ||
{ | ||
var data = getIconData(icon); | ||
if (data != null) | ||
{ | ||
return data; | ||
} | ||
else | ||
{ | ||
using (var ms = new MemoryStream()) | ||
{ | ||
icon.Save(ms); | ||
return ms.ToArray(); | ||
} | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.