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

# User Rev Content
1 dmarschall 113 /*
2     This file is part of "Filter Foundry", a filter plugin for Adobe Photoshop
3 daniel-marschall 192 Copyright (C) 2003-2009 Toby Thain, toby@telegraphics.com.au
4 daniel-marschall 206 Copyright (C) 2018-2021 Daniel Marschall, ViaThinkSoft
5 dmarschall 113
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 dmarschall 185 NodeSetValue(p, newValue, (ULONG)((wcslen(newValue)+1)*sizeof(wchar_t)));
354 dmarschall 113 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 dmarschall 162 if (_UpdateResource(Ctx->hUpdate, lpszType, lpszName, wIDLanguage, pv, size))
395 dmarschall 113 {
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 dmarschall 162 if (ctx.hUpdate = _BeginUpdateResource(FileName, FALSE))
423 dmarschall 113 {
424     ctx.fDiscard = TRUE;
425    
426 daniel-marschall 356 // 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 dmarschall 113 {
430     if (!EnumResourceLanguages(hmod, RT_VERSION,
431     MAKEINTRESOURCE(VS_VERSION_INFO),
432 daniel-marschall 194 (ENUMRESLANGPROC)(void*)EnumResLangProc, (LONG_PTR)&ctx))
433 dmarschall 113 {
434     dwError = GetLastError();
435     }
436    
437     FreeLibrary(hmod);
438     }
439     else
440     {
441     dwError = GetLastError();
442     }
443    
444 dmarschall 162 if (!dwError && !_EndUpdateResource(ctx.hUpdate, ctx.fDiscard))
445 dmarschall 113 {
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 daniel-marschall 356 // 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 dmarschall 113 {
473     if (!EnumResourceLanguages(hmod, RT_VERSION,
474     MAKEINTRESOURCE(VS_VERSION_INFO),
475 daniel-marschall 194 (ENUMRESLANGPROC)(void*)EnumResLangProc, (LONG_PTR)&ctx))
476 dmarschall 113 {
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 daniel-marschall 192 // 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 dmarschall 113
499 daniel-marschall 192 UpdateVersionInfoByFilename("C:\\Test.dll", changes);
500 dmarschall 113 return 0;
501     }
502     */