-
Notifications
You must be signed in to change notification settings - Fork 28
/
PE.Resources.Windows.pas
307 lines (273 loc) · 9.49 KB
/
PE.Resources.Windows.pas
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
{
Unit to manipulate resources in Windows oriented way.
It means 3 resource levels:
- Type (RT_...)
- Name
- Language
}
unit PE.Resources.Windows;
interface
uses
System.Classes,
PE.Image,
PE.Resources,
PE.Resources.VersionInfo;
{ Values for Windows PE. }
type
RSRCID = UInt32;
const
// The following are the predefined resource types.
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms648009(v=vs.85).aspx
RT_CURSOR = RSRCID(1); // Hardware-dependent cursor resource.
RT_BITMAP = RSRCID(2); // Bitmap resource.
RT_ICON = RSRCID(3); // Hardware-dependent icon resource.
RT_MENU = RSRCID(4); // Menu resource.
RT_DIALOG = RSRCID(5); // Dialog box.
RT_STRING = RSRCID(6); // String-table entry.
RT_FONTDIR = RSRCID(7); // Font directory resource.
RT_FONT = RSRCID(8); // Font resource.
RT_ACCELERATOR = RSRCID(9); // Accelerator table.
RT_RCDATA = RSRCID(10); // Application-defined resource (raw data).
RT_MESSAGETABLE = RSRCID(11); // Message-table entry.
RT_GROUP_CURSOR = RSRCID(UInt32(RT_CURSOR) + 11); // Hardware-independent cursor resource.
RT_GROUP_ICON = RSRCID(UInt32(RT_ICON) + 11); // Hardware-independent icon resource.
RT_VERSION = RSRCID(16); // Version resource.
RT_DLGINCLUDE = RSRCID(17); // Allows a resource editing tool to associate a string with an .rc file.
RT_PLUGPLAY = RSRCID(19); // Plug and Play resource.
RT_VXD = RSRCID(20); // VXD.
RT_ANICURSOR = RSRCID(21); // Animated cursor.
RT_ANIICON = RSRCID(22); // Animated icon.
RT_HTML = RSRCID(23); // HTML resource.
RT_MANIFEST = RSRCID(24); // Side-by-Side Assembly Manifest.
RT_NAMES: array [0 .. 24] of string = (
'#0', // 0
'RT_CURSOR', // 1
'RT_BITMAP', // 2
'RT_ICON', // 3
'RT_MENU', // 4
'RT_DIALOG', // 5
'RT_STRING', // 6
'RT_FONTDIR', // 7
'RT_FONT', // 8
'RT_ACCELERATOR', // 9
'RT_RCDATA', // 10
'RT_MESSAGETABLE', // 11
'RT_GROUP_CURSOR', // 12
'#13', // 13
'RT_GROUP_ICON', // 14
'#15', // 15
'RT_VERSION', // 16
'RT_DLGINCLUDE', // 17
'#18', // 18
'RT_PLUGPLAY', // 19
'RT_VXD', // 20
'RT_ANICURSOR', // 21
'RT_ANIICON', // 22
'RT_HTML', // 23
'RT_MANIFEST' // 24
);
type
TWindowsResourceTree = class
private
function FindResourceInternal(lpType, lpName: PChar; Language: word; Depth: cardinal): TResourceTreeNode;
protected
FResourceTree: TResourceTree;
public
constructor Create(ResourceTree: TResourceTree);
// Find Type-Name-Language leaf.
function FindResource(lpType, lpName: PChar; Language: word): TResourceTreeLeafNode; overload;
// Find Type-Name branch.
function FindResource(lpType, lpName: PChar): TResourceTreeBranchNode; overload;
// Find Type branch.
function FindResource(lpType: PChar): TResourceTreeBranchNode; overload;
// Update resource by full path.
// lpType, lpName: if <= $FFFF it's ID otherwise it point to Name string.
// If (lpData=nil) and (cbData=0) then resource is deleted.
procedure UpdateResource(lpType, lpName: PChar; Language: word;
lpData: PByte; cbData: UInt32);
function RemoveResource(lpType, lpName: PChar; Language: word): boolean;
end;
function IsIntResource(lpszType: PChar): boolean; inline;
function GetIntResource(lpszType: PChar): word; inline;
function MakeIntResource(wInteger: uint16): PChar; inline;
// See Windows GetFileVersionInfo function.
// Find RT_VERSION raw data or nil if failed.
// Don't Free returned stream because it's part of ResourceTree.
function PeGetFileVersionInfo(img: TPEImage): TMemoryStream;
function PeVerQueryValueFixed(stream: TStream; out value: VS_FIXEDFILEINFO): boolean;
implementation
uses
System.SysUtils;
function IsIntResource(lpszType: PChar): boolean; inline;
begin
Result := NativeUInt(lpszType) shr 16 = 0;
end;
function GetIntResource(lpszType: PChar): word; // inline;
begin
Result := NativeUInt(lpszType) and $FFFF;
end;
function MakeIntResource(wInteger: uint16): PChar;
begin
Result := PChar(wInteger);
end;
constructor TWindowsResourceTree.Create(ResourceTree: TResourceTree);
begin
FResourceTree := ResourceTree;
end;
// Find or create node.
// IsBranch: Is fetched node must be branch (or leaf).
// lpName: Name or ID (intresource)
function FetchNode(
Parent: TResourceTreeBranchNode;
IsBranch: boolean;
CreateIfNotExists: boolean;
lpName: PChar): TResourceTreeNode;
var
bIntRsrc: boolean;
begin
bIntRsrc := IsIntResource(lpName);
// Check if node with such Name/Id already exists.
if bIntRsrc then
Result := Parent.FindByID(GetIntResource(lpName))
else
Result := Parent.FindByName(lpName);
// If not exists, create it.
if Result = nil then
begin
if CreateIfNotExists then
begin
// Create sub-node.
if IsBranch then
Result := Parent.AddNewBranch
else
Result := Parent.AddNewLeaf;
// Set Id/Name
if bIntRsrc then
Result.Id := GetIntResource(lpName)
else
Result.Name := lpName;
end;
end
else
// If exists, check if branch/leaf.
begin
if not(Result.IsBranch = IsBranch) then
raise Exception.Create('Node type mismatch.');
end;
end;
function TWindowsResourceTree.RemoveResource(lpType, lpName: PChar; Language: word): boolean;
var
n: TResourceTreeNode;
begin
n := FindResourceInternal(lpType, lpName, Language, 2);
if not assigned(n) then
exit(false);
n.Parent.Remove(n);
exit(true);
end;
function TWindowsResourceTree.FindResource(lpType, lpName: PChar; Language: word): TResourceTreeLeafNode;
begin
Result := TResourceTreeLeafNode(FindResourceInternal(lpType, lpName, Language, 2));
end;
function TWindowsResourceTree.FindResource(lpType, lpName: PChar):
TResourceTreeBranchNode;
begin
Result := TResourceTreeBranchNode(FindResourceInternal(lpType, lpName, 0, 1));
end;
function TWindowsResourceTree.FindResource(lpType: PChar): TResourceTreeBranchNode;
begin
Result := TResourceTreeBranchNode(FindResourceInternal(lpType, nil, 0, 0));
end;
function TWindowsResourceTree.FindResourceInternal(lpType, lpName: PChar;
Language: word; Depth: cardinal): TResourceTreeNode;
const
IsBranches: array [0 .. 2] of boolean = (true, true, false);
var
i: integer;
n: TResourceTreeNode;
val: PChar;
begin
if Depth > 2 then
Depth := 2;
n := FResourceTree.Root;
val := nil; // compiler friendly
for i := 0 to Depth do
begin
case i of
0:
val := lpType;
1:
val := lpName;
2:
val := PChar(Language);
end;
n := FetchNode(TResourceTreeBranchNode(n), IsBranches[i], false, val);
if n = nil then
exit(nil);
end;
Result := n;
end;
procedure TWindowsResourceTree.UpdateResource(lpType, lpName: PChar;
Language: word; lpData: PByte; cbData: UInt32);
var
nRoot, nType, nName: TResourceTreeBranchNode;
nLang: TResourceTreeLeafNode;
begin
if (lpData = nil) and (cbData = 0) then
begin
RemoveResource(lpType, lpName, Language);
exit;
end;
nRoot := FResourceTree.Root;
nType := FetchNode(nRoot, true, true, lpType) as TResourceTreeBranchNode;
nName := FetchNode(nType, true, true, lpName) as TResourceTreeBranchNode;
nLang := FetchNode(nName, false, true, PChar(Language)) as TResourceTreeLeafNode;
nLang.UpdateData(lpData, cbData);
end;
function PeGetFileVersionInfo(img: TPEImage): TMemoryStream;
var
rt: TWindowsResourceTree;
versionBranch: TResourceTreeBranchNode;
versionLeaf: TResourceTreeLeafNode;
begin
rt := TWindowsResourceTree.Create(img.ResourceTree);
try
versionBranch := rt.FindResource(MakeIntResource(RT_VERSION));
if assigned(versionBranch) and versionBranch.IsBranch and (versionBranch.Children.Count > 0) then
begin
versionBranch := TResourceTreeBranchNode(versionBranch.Children.First);
if assigned(versionBranch) and versionBranch.IsBranch and (versionBranch.Children.Count > 0) then
begin
versionLeaf := TResourceTreeLeafNode(versionBranch.Children.First);
if assigned(versionLeaf) and versionLeaf.IsLeaf then
begin
exit(versionLeaf.Data);
end;
end;
end;
exit(nil);
finally
rt.Free;
end;
end;
function PeVerQueryValueFixed(stream: TStream; out value: VS_FIXEDFILEINFO): boolean;
var
verInfo: TPEVersionInfo;
block: TBlock;
begin
verInfo := TPEVersionInfo.Create;
try
verInfo.LoadFromStream(stream);
if assigned(verInfo.Root) then
for block in verInfo.Root.Children do
if block.ClassType = TBlockVersionInfo then
begin
value := TBlockVersionInfo(block).FixedInfo;
exit(true);
end;
exit(false);
finally
verInfo.Free;
end;
end;
end.