-
Notifications
You must be signed in to change notification settings - Fork 20
Expand file tree
/
Copy pathResourceFileInfo.cs
More file actions
332 lines (289 loc) · 10.1 KB
/
Copy pathResourceFileInfo.cs
File metadata and controls
332 lines (289 loc) · 10.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
using BytecodeApi.Extensions;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Security;
namespace BytecodeApi.PEResources;
/// <summary>
/// Provides properties and instance methods for native resources in portable executables, typically EXE and DLL files.
/// </summary>
public class ResourceFileInfo
{
/// <summary>
/// Gets the path of the file.
/// </summary>
public string Path { get; }
/// <summary>
/// Initializes a new instance of the <see cref="ResourceFileInfo" /> class using the specified filename.
/// </summary>
/// <param name="path">A <see cref="string" /> specifying the path of a PE file.</param>
public ResourceFileInfo(string path)
{
Check.ArgumentNull(path);
Check.ArgumentEx.StringNotEmpty(path);
Path = path;
}
/// <summary>
/// Extracts a resource from the file, specified by a name.
/// </summary>
/// <param name="type">A <see cref="ResourceType" /> specifying the type of the resource.</param>
/// <param name="name">A <see cref="int" /> value specifying the name of the resource.</param>
/// <returns>
/// The extracted <see cref="byte" />[] resource.
/// </returns>
public byte[] GetResource(ResourceType type, int name)
{
Check.FileNotFound(Path);
Check.ArgumentOutOfRangeEx.GreaterEqual0(name);
nint module = 0;
try
{
module = Native.LoadLibraryEx(Path, 0, 2);
if (module == 0) throw Throw.Win32();
return GetData(module, type, name);
}
finally
{
if (module != 0) Native.FreeLibrary(module);
}
}
/// <summary>
/// Extracts all group icon resources from the file and returns their names.
/// </summary>
/// <returns>
/// A new <see cref="int" />[] with all group icon resource names.
/// </returns>
public int[] GetGroupIconResourceNames()
{
Check.FileNotFound(Path);
List<int> icons = [];
nint module = 0;
try
{
module = Native.LoadLibraryEx(Path, 0, 2);
if (module == 0) throw Throw.Win32();
Native.EnumResourceNames(module, (nint)ResourceType.GroupIcon, Callback, 0);
}
finally
{
if (module != 0) Native.FreeLibrary(module);
}
return icons.ToArray();
bool Callback(nint hModuleCallback, nint type, nint name, nint lParam)
{
icons.Add((int)name);
return true;
}
}
/// <summary>
/// Extracts a group icon resource from the file, specified by a name.
/// </summary>
/// <param name="name">A <see cref="int" /> value specifying the name of the resource.</param>
/// <returns>
/// The extracted <see cref="Icon" /> resource.
/// </returns>
public Icon GetGroupIconResource(int name)
{
Check.FileNotFound(Path);
Check.ArgumentOutOfRangeEx.GreaterEqual0(name);
nint module = 0;
try
{
module = Native.LoadLibraryEx(Path, 0, 2);
if (module == 0) throw Throw.Win32();
return GetGroupIconData(module, name);
}
finally
{
if (module != 0) Native.FreeLibrary(module);
}
}
/// <summary>
/// Changes the icon of the file to the specified icon file that is a valid ICO file.
/// </summary>
/// <param name="iconPath">A <see cref="string" /> specifying the path of a valid ICO file.</param>
public void ChangeIcon(string iconPath)
{
Check.FileNotFound(Path);
Check.ArgumentNull(iconPath);
Check.FileNotFound(iconPath);
ChangeIcon(new Icon(iconPath));
}
/// <summary>
/// Changes the icon of the file to the specified icon.
/// </summary>
/// <param name="icon">The <see cref="Icon" /> to be applied to the file.</param>
public unsafe void ChangeIcon(Icon icon)
{
Check.FileNotFound(Path);
Check.ArgumentNull(icon);
Native.IconDir iconDir = new();
List<Native.IconDirEntry> iconEntry = [];
List<byte[]> iconData = [];
using (BinaryReader reader = new(new MemoryStream(icon.ToArray())))
{
Marshal.Copy(reader.ReadBytes(sizeof(Native.IconDir)), 0, (nint)(&iconDir), sizeof(Native.IconDir));
for (int i = 0; i < iconDir.Count; i++)
{
Native.IconDirEntry entry = new();
Marshal.Copy(reader.ReadBytes(sizeof(Native.IconDirEntry)), 0, (nint)(&entry), sizeof(Native.IconDirEntry));
iconEntry.Add(entry);
}
for (int i = 0; i < iconDir.Count; i++)
{
iconData.Add(reader.ReadBytes((int)iconEntry[i].BytesInRes));
}
}
nint update = Native.BeginUpdateResource(Path, false);
byte[] data = new byte[sizeof(Native.IconDir) + sizeof(Native.GroupIconDirEntry) * iconDir.Count];
Marshal.Copy((nint)(&iconDir), data, 0, sizeof(Native.IconDir));
for (int i = 0, offset = sizeof(Native.IconDir); i < iconDir.Count; i++, offset += sizeof(Native.GroupIconDirEntry))
{
Native.BitmapInfoHeader header = new();
Marshal.Copy(iconData[i], 0, (nint)(&header), sizeof(Native.BitmapInfoHeader));
Native.GroupIconDirEntry groupEntry = new()
{
Width = iconEntry[i].Width,
Height = iconEntry[i].Height,
ColorCount = iconEntry[i].ColorCount,
Reserved = iconEntry[i].Reserved,
Planes = header.Planes,
BitCount = header.BitCount,
BytesInRes = iconEntry[i].BytesInRes,
Id = (ushort)(i + 10)
};
Marshal.Copy((nint)(&groupEntry), data, offset, Marshal.SizeOf(groupEntry));
}
Native.UpdateResource(update, (uint)ResourceType.GroupIcon, 1, 0, data, (uint)data.Length);
for (int i = 0; i < iconDir.Count; i++)
{
Native.UpdateResource(update, (uint)ResourceType.Icon, (uint)(10 + i), 0, iconData[i], (uint)iconData[i].Length);
}
Native.EndUpdateResource(update, false);
}
/// <summary>
/// Strips all resources from the file.
/// </summary>
public void DeleteResources()
{
Check.FileNotFound(Path);
nint handle = Native.BeginUpdateResource(Path, true);
if (handle == 0) throw Throw.Win32();
Native.EndUpdateResource(handle, false);
}
private static Icon GetGroupIconData(nint module, int name)
{
byte[] data = GetData(module, ResourceType.GroupIcon, name);
int count = BitConverter.ToUInt16(data, 4);
int size = 6 + 16 * count + Enumerable.Range(0, count).Sum(i => BitConverter.ToInt32(data, 14 + 14 * i));
using MemoryStream memoryStream = new(size);
using (BinaryWriter writer = new(memoryStream))
{
writer.Write(data, 0, 6);
for (int i = 0, offset = 6 + 16 * count; i < count; i++)
{
byte[] icon = GetData(module, ResourceType.Icon, BitConverter.ToUInt16(data, 18 + 14 * i));
memoryStream.Seek(6 + 16 * i, SeekOrigin.Begin);
writer.Write(data, 6 + 14 * i, 8);
writer.Write(icon.Length);
writer.Write(offset);
memoryStream.Seek(offset, SeekOrigin.Begin);
writer.Write(icon);
offset += icon.Length;
}
}
return Convert.ToIcon(memoryStream.ToArray());
}
private static byte[] GetData(nint module, ResourceType type, int name)
{
nint resource = Native.FindResource(module, name, (nint)type);
if (resource == 0) throw Throw.Win32();
nint data = Native.LoadResource(module, resource);
if (data == 0) throw Throw.Win32();
nint dataPtr = Native.LockResource(data);
if (dataPtr == 0) throw Throw.Win32();
uint size = Native.SizeofResource(module, resource);
if (size == 0) throw Throw.Win32();
byte[] buffer = new byte[size];
Marshal.Copy(dataPtr, buffer, 0, buffer.Length);
return buffer;
}
}
file static class Native
{
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[SuppressUnmanagedCodeSecurity]
public static extern nint LoadLibraryEx(string fileName, nint file, uint flags);
[DllImport("kernel32.dll", SetLastError = true)]
[SuppressUnmanagedCodeSecurity]
public static extern bool FreeLibrary(nint module);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[SuppressUnmanagedCodeSecurity]
public static extern bool EnumResourceNames(nint module, nint type, EnumerateResourceNames enumFunc, nint param);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[SuppressUnmanagedCodeSecurity]
public static extern nint FindResource(nint module, nint name, nint type);
[DllImport("kernel32.dll", SetLastError = true)]
[SuppressUnmanagedCodeSecurity]
public static extern nint LoadResource(nint module, nint resInfo);
[DllImport("kernel32.dll", SetLastError = true)]
[SuppressUnmanagedCodeSecurity]
public static extern nint LockResource(nint resData);
[DllImport("kernel32.dll", SetLastError = true)]
[SuppressUnmanagedCodeSecurity]
public static extern uint SizeofResource(nint module, nint resInfo);
[DllImport("kernel32.dll")]
public static extern nint BeginUpdateResource(string fileName, [MarshalAs(UnmanagedType.Bool)] bool deleteExistingResources);
[DllImport("kernel32.dll")]
public static extern bool EndUpdateResource(nint update, bool discard);
[DllImport("kernel32.dll")]
public static extern int UpdateResource(nint update, uint type, uint name, ushort language, byte[] data, uint dateLength);
[StructLayout(LayoutKind.Sequential)]
public struct IconDir
{
public ushort Reserved;
public ushort Type;
public ushort Count;
}
[StructLayout(LayoutKind.Sequential)]
public struct IconDirEntry
{
public byte Width;
public byte Height;
public byte ColorCount;
public byte Reserved;
public ushort Planes;
public ushort BitCount;
public uint BytesInRes;
public uint ImageOffset;
}
[StructLayout(LayoutKind.Sequential)]
public struct BitmapInfoHeader
{
public uint Size;
public int Width;
public int Height;
public ushort Planes;
public ushort BitCount;
public uint Compression;
public uint SizeImage;
public int PixelsPerMeterX;
public int PixelsPerMeterY;
public uint ClrUsed;
public uint ClrImportant;
}
[StructLayout(LayoutKind.Sequential, Pack = 2)]
public struct GroupIconDirEntry
{
public byte Width;
public byte Height;
public byte ColorCount;
public byte Reserved;
public ushort Planes;
public ushort BitCount;
public uint BytesInRes;
public ushort Id;
}
[UnmanagedFunctionPointer(CallingConvention.Winapi, SetLastError = true, CharSet = CharSet.Unicode)]
[SuppressUnmanagedCodeSecurity]
public delegate bool EnumerateResourceNames(nint module, nint type, nint name, nint lParam);
}