Login | ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/filter_foundry/trunk/versioninfo_modify_win.c
Revision: 356
Committed: Fri Oct 15 17:12:06 2021 UTC (11 months, 2 weeks ago) by daniel-marschall
Content type: text/x-csrc
File size: 10682 byte(s)
Log Message:
Fixed incompatibility with older versions of Wine (Windows emulator for Linux systems)

File Contents

# Content
1 /*
2 This file is part of "Filter Foundry", a filter plugin for Adobe Photoshop
3 Copyright (C) 2003-2009 Toby Thain, toby@telegraphics.com.au
4 Copyright (C) 2018-2021 Daniel Marschall, ViaThinkSoft
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 #include "ff.h"
22
23 #include <windows.h>
24 #include <stdbool.h>
25 #include <stdio.h>
26
27 // This unit modifies the VersionInfo resource structure of a PE file.
28 // Entries can be modified or deleted (not added).
29 // Reference code by "RbMm" at StackOverflow:
30 // https://stackoverflow.com/questions/53916682/programmatically-change-versioninfo-of-a-foreign-dll
31 // Translated from C++ to C by Daniel Marschall and extended/modified to fit Filter Foundry's requirements.
32
33 typedef struct RsrcHeader {
34 WORD wLength;
35 WORD wValueLength;
36 WORD wType;
37 WCHAR szKey[];
38 } RsrcHeader;
39
40 typedef struct RsrcNode {
41 struct RsrcNode *first;
42 struct RsrcNode *next;
43 PCWSTR name;
44 const void *pvValue;
45 ULONG cbValue;
46 WORD wValueLength;
47 WORD wType;
48 } RsrcNode;
49
50 typedef struct EnumVerData {
51 HANDLE hUpdate;
52 BOOL fDiscard;
53 PCWSTR changes;
54 } EnumVerData;
55
56 RsrcNode* NewNode() {
57 RsrcNode* node = (RsrcNode*)calloc(1,sizeof(RsrcNode));
58 return node;
59 }
60
61 bool NodeIsStringValue(RsrcNode* node) {
62 return node->wType;
63 }
64
65 bool NodeParseResourse(RsrcNode* node, PVOID buf, ULONG size, ULONG* pLength) {
66 WORD wType;
67 ULONG wValueLength;
68 ULONG wLength;
69 ULONG cbValue;
70
71 union {
72 PVOID pv;
73 RsrcHeader* ph;
74 ULONG_PTR up;
75 PCWSTR sz;
76 } x;
77
78 x.pv = buf;
79
80 if (size < sizeof(RsrcHeader) || (x.up & 3))
81 {
82 return false;
83 }
84
85 wType = x.ph->wType;
86 wValueLength = x.ph->wValueLength, wLength = x.ph->wLength;
87 cbValue = 0;
88
89 switch (wType)
90 {
91 case 1:
92 cbValue = wValueLength * sizeof(WCHAR);
93 break;
94 case 0:
95 cbValue = wValueLength;
96 break;
97 default:
98 return false;
99 }
100
101 *pLength = wLength;
102
103 if (wLength > size || wLength < sizeof(RsrcHeader) || cbValue >= (wLength -= sizeof(RsrcHeader)))
104 {
105 return false;
106 }
107
108 wLength -= cbValue;
109
110 x.sz = x.ph->szKey, node->name = x.sz;
111
112 do
113 {
114 if (wLength < sizeof(WCHAR))
115 {
116 return false;
117 }
118
119 wLength -= sizeof(WCHAR);
120 } while (*x.sz++);
121
122 if (x.up & 3)
123 {
124 if (wLength < 2)
125 {
126 return false;
127 }
128 x.up += 2, wLength -= 2;
129 }
130
131 node->wType = wType, node->wValueLength = (WORD)wValueLength, node->cbValue = cbValue, node->pvValue = x.pv;
132
133 if (wValueLength && wType)
134 {
135 if (x.sz[wValueLength - 1])
136 {
137 return false;
138 }
139 }
140
141 if (wLength)
142 {
143 x.up += wValueLength;
144
145 do
146 {
147 RsrcNode* lnode;
148
149 if (x.up & 3)
150 {
151 if (wLength < 2)
152 {
153 return false;
154 }
155
156 x.up += 2;
157
158 if (!(wLength -= 2))
159 {
160 break;
161 }
162 }
163
164 if (lnode = NewNode())
165 {
166 lnode->next = node->first, node->first = lnode;
167
168 if (NodeParseResourse(lnode, x.ph, wLength, &size))
169 {
170 continue;
171 }
172 }
173
174 return false;
175
176 } while (x.up += size, wLength -= size);
177 }
178
179 return true;
180 }
181
182 const void* NodeGetValue(RsrcNode* node, ULONG* cb) {
183 *cb = node->cbValue;
184 return node->pvValue;
185 }
186
187 bool NodeToBeDeleted(RsrcNode* node) {
188 return ((NodeIsStringValue(node)) && (!wcscmp((PCWSTR)node->pvValue, L"\b")));
189 }
190
191 void NodeSetValue(RsrcNode* node, const void* pv, ULONG cb) {
192 node->pvValue = pv, node->cbValue = cb;
193 node->wValueLength = (WORD)(node->wType ? cb / sizeof(WCHAR) : cb);
194 }
195
196 void FreeNode(RsrcNode* node) {
197 RsrcNode* next;
198
199 if (next = node->first)
200 {
201 do
202 {
203 RsrcNode* cur = next;
204 next = next->next;
205 FreeNode(cur);
206 } while (next);
207 }
208
209 free(node);
210 }
211
212 RsrcNode* NodeFind(RsrcNode* node, const PCWSTR strings[], ULONG n) {
213 PCWSTR str;
214 RsrcNode* next;
215
216 str = *strings++;
217
218 if (!str || !wcscmp(str, node->name))
219 {
220 if (!--n)
221 {
222 return node;
223 }
224
225 if (next = node->first)
226 {
227 do
228 {
229 RsrcNode* p;
230 if (p = NodeFind(next, strings, n))
231 {
232 return p;
233 }
234 } while (next = next->next);
235 }
236 }
237
238 return NULL;
239 }
240
241 ULONG NodeGetSize(RsrcNode* node) {
242 ULONG size;
243 RsrcNode* next;
244
245 size = sizeof(RsrcHeader) + (1 + (ULONG)wcslen(node->name)) * sizeof(WCHAR);
246
247 if (node->cbValue)
248 {
249 size = ((size + 3) & ~3) + node->cbValue;
250 }
251
252 if (next = node->first)
253 {
254 do
255 {
256 size = ((size + 3) & ~3) + NodeGetSize(next);
257 } while (next = next->next);
258 }
259
260 return size;
261 }
262
263 PVOID NodeStore(RsrcNode* node, PVOID buf, ULONG* pcb) {
264 ULONG size;
265 ULONG cb;
266 RsrcNode* next;
267
268 union {
269 RsrcHeader* ph;
270 ULONG_PTR up;
271 PVOID pv;
272 } x;
273
274 x.pv = buf;
275
276 x.ph->wType = node->wType;
277 x.ph->wValueLength = node->wValueLength;
278
279 size = (1 + (ULONG)wcslen(node->name)) * sizeof(WCHAR);
280
281 memcpy(x.ph->szKey, node->name, size);
282
283 x.up += (size += sizeof(RsrcHeader));
284
285 if (node->cbValue)
286 {
287 x.up = (x.up + 3) & ~3;
288 memcpy(x.pv, node->pvValue, node->cbValue);
289 x.up += node->cbValue;
290 size = ((size + 3) & ~3) + node->cbValue;
291 }
292
293 if (next = node->first)
294 {
295 do
296 {
297 if (!NodeToBeDeleted(next)) {
298 x.up = (x.up + 3) & ~3;
299 x.pv = NodeStore(next, x.pv, &cb);
300 size = ((size + 3) & ~3) + cb;
301 }
302 } while (next = next->next);
303 }
304
305 ((RsrcHeader*)buf)->wLength = (WORD)size;
306
307 *pcb = size;
308
309 return x.pv;
310 }
311
312 BOOL UpdateVersionRaw(PVOID pvVersion, ULONG cbVersion, PVOID* pvNewVersion, ULONG* cbNewVersion, PCWSTR changes) {
313 BOOL fOk = FALSE;
314 BOOL changesMade = FALSE;
315 RsrcNode* node;
316
317 if (node = NewNode())
318 {
319 // Parse VersionInfo (pvVersion) into a hierarchical structure with head "RsrcNode node"
320 if (NodeParseResourse(node, pvVersion, cbVersion, &cbVersion))
321 {
322 // Loop through all elements of "PCWSTR changes" and apply the changes to the hierarchical structure
323 while (1)
324 {
325 PCWSTR change;
326 PCWSTR newValue;
327 PCWSTR str[4];
328 RsrcNode *p;
329
330 change = changes;
331 if (wcslen(changes) == 0) break;
332 changes += (wcslen(changes)+1);
333
334 newValue = changes;
335 changes += (wcslen(changes)+1);
336
337 str[0] = L"VS_VERSION_INFO";
338 str[1] = L"StringFileInfo";
339 str[2] = NULL;
340 str[3] = change;
341
342 if (p = NodeFind(node, str, 4))
343 {
344 if (NodeIsStringValue(p))
345 {
346 ULONG cb;
347 PCWSTR prevValue = (PCWSTR)NodeGetValue(p, &cb);
348
349 //printf("Change %S: %S -> %S\n", change, prevValue, newValue);
350
351 if (cb != (wcslen(newValue)+1)*sizeof(wchar_t) || (wcscmp(prevValue, newValue)))
352 {
353 NodeSetValue(p, newValue, (ULONG)((wcslen(newValue)+1)*sizeof(wchar_t)));
354 changesMade = TRUE;
355 }
356 }
357 }
358 }
359
360 // Write back the hierarchical structure into the raw data pvVersion
361 if (changesMade) {
362 cbVersion = NodeGetSize(node);
363
364 if (pvVersion = LocalAlloc(0, cbVersion))
365 {
366 NodeStore(node, pvVersion, cbNewVersion);
367 *pvNewVersion = pvVersion;
368 fOk = TRUE;
369 }
370 }
371 }
372 FreeNode(node);
373 }
374
375 return fOk;
376 }
377
378 BOOL CALLBACK EnumResLangProc(HMODULE hModule, PCTSTR lpszType, PCTSTR lpszName, WORD wIDLanguage, EnumVerData* Ctx) {
379 HRSRC hResInfo;
380 HGLOBAL hg;
381 ULONG size;
382 PVOID pv;
383
384 if (hResInfo = FindResourceEx(hModule, lpszType, lpszName, wIDLanguage))
385 {
386 if (hg = LoadResource(hModule, hResInfo))
387 {
388 if (size = SizeofResource(hModule, hResInfo))
389 {
390 if (pv = LockResource(hg))
391 {
392 if (UpdateVersionRaw(pv, size, &pv, &size, Ctx->changes))
393 {
394 if (_UpdateResource(Ctx->hUpdate, lpszType, lpszName, wIDLanguage, pv, size))
395 {
396 Ctx->fDiscard = FALSE;
397 }
398
399 LocalFree(pv);
400 }
401 }
402 }
403 }
404 }
405
406 return TRUE;
407 }
408
409 // Format of argument "PCWSTR changes" is "<name>\0<value>\0<name>\0<value>\0....."
410 // You can CHANGE values for any given name
411 // You can DELETE entries by setting the value to "\b" (0x08 backspace character)
412 // You cannot (yet) ADD entries.
413 ULONG UpdateVersionInfo(PCTSTR FileName, PCWSTR changes) {
414 HMODULE hmod;
415 ULONG dwError;
416 EnumVerData ctx;
417
418 dwError = NOERROR;
419
420 ctx.changes = changes;
421
422 if (ctx.hUpdate = _BeginUpdateResource(FileName, FALSE))
423 {
424 ctx.fDiscard = TRUE;
425
426 // LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE requires at least Windows Vista, so we use
427 // LOAD_LIBRARY_AS_DATAFILE
428 if (hmod = LoadLibraryEx(FileName, 0, LOAD_LIBRARY_AS_DATAFILE/*_EXCLUSIVE*/))
429 {
430 if (!EnumResourceLanguages(hmod, RT_VERSION,
431 MAKEINTRESOURCE(VS_VERSION_INFO),
432 (ENUMRESLANGPROC)(void*)EnumResLangProc, (LONG_PTR)&ctx))
433 {
434 dwError = GetLastError();
435 }
436
437 FreeLibrary(hmod);
438 }
439 else
440 {
441 dwError = GetLastError();
442 }
443
444 if (!dwError && !_EndUpdateResource(ctx.hUpdate, ctx.fDiscard))
445 {
446 dwError = GetLastError();
447 }
448 }
449 else
450 {
451 dwError = GetLastError();
452 }
453
454 return dwError;
455 }
456
457 ULONG UpdateVersionInfoWithHandle(PCTSTR FileName, HANDLE hUpdate, PCWSTR changes) {
458 HMODULE hmod;
459 ULONG dwError;
460 EnumVerData ctx;
461
462 dwError = NOERROR;
463
464 ctx.changes = changes;
465 ctx.hUpdate = hUpdate;
466
467 ctx.fDiscard = TRUE;
468
469 // LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE requires at least Windows Vista, so we use
470 // LOAD_LIBRARY_AS_DATAFILE
471 if (hmod = LoadLibraryEx(FileName, 0, LOAD_LIBRARY_AS_DATAFILE/*_EXCLUSIVE*/))
472 {
473 if (!EnumResourceLanguages(hmod, RT_VERSION,
474 MAKEINTRESOURCE(VS_VERSION_INFO),
475 (ENUMRESLANGPROC)(void*)EnumResLangProc, (LONG_PTR)&ctx))
476 {
477 dwError = GetLastError();
478 }
479
480 FreeLibrary(hmod);
481 }
482 else
483 {
484 dwError = GetLastError();
485 }
486
487 return dwError;
488 }
489
490 /*
491 Usage example:
492
493 int main(int argc, char** argv) {
494 // Set CompanyName to "Contoso Ltd."
495 // Delete LegalCopyright
496 // Set OriginalFilename to "Test.dll"
497 static const PCWSTR changes = L"Foo\0Bar\0CompanyName\0Contoso Ltd.\0LegalCopyright\0\b\0OriginalFilename\0Test.dll\0";
498
499 UpdateVersionInfoByFilename("C:\\Test.dll", changes);
500 return 0;
501 }
502 */