ohmu
·
2026-10-15
1#include "common/debug.h"
2#include "common/mallocHelper.h"
3#include "common/windows/version.h"
4#include "common/windows/unicode.h"
5
6#include <windows.h>
7
8bool ffGetFileVersion(const wchar_t* filePath, const wchar_t* stringName, FFstrbuf* version) {
9 FF_DEBUG("ffGetFileVersion: enter filePath=%ls stringName=%ls", filePath, stringName);
10
11 DWORD handle;
12 DWORD size = GetFileVersionInfoSizeW(filePath, &handle);
13 if (size == 0) {
14 FF_DEBUG("GetFileVersionInfoSizeW failed: %s", ffDebugWin32Error(GetLastError()));
15 return false;
16 }
17
18 FF_DEBUG("GetFileVersionInfoSizeW ok: size=%lu handle=%lu",
19 (unsigned long) size,
20 (unsigned long) handle);
21
22 FF_AUTO_FREE void* versionData = malloc(size);
23 if (!versionData) {
24 FF_DEBUG("malloc failed: size=%lu", (unsigned long) size);
25 return false;
26 }
27
28 if (!GetFileVersionInfoW(filePath, handle, size, versionData)) {
29 FF_DEBUG("GetFileVersionInfoW failed: %s", ffDebugWin32Error(GetLastError()));
30 return false;
31 }
32
33 FF_DEBUG("GetFileVersionInfoW ok");
34
35 if (!stringName) {
36 VS_FIXEDFILEINFO* verInfo;
37 UINT len;
38 if (VerQueryValueW(versionData, L"\\", (void**) &verInfo, &len) &&
39 len &&
40 verInfo->dwSignature == 0xFEEF04BD) {
41 ffStrbufSetF(version, "%u.%u.%u.%u", (unsigned) ((verInfo->dwProductVersionMS >> 16) & 0xffff), (unsigned) ((verInfo->dwProductVersionMS >> 0) & 0xffff), (unsigned) ((verInfo->dwProductVersionLS >> 16) & 0xffff), (unsigned) ((verInfo->dwProductVersionLS >> 0) & 0xffff));
42 FF_DEBUG("fixed version resolved: %s", version->chars);
43 return true;
44 }
45
46 FF_DEBUG("fixed version query failed or invalid signature");
47 return false;
48 }
49
50 struct {
51 WORD language;
52 WORD codePage;
53 }* translations;
54 UINT translationsLen;
55
56 if (VerQueryValueW(versionData, L"\\VarFileInfo\\Translation", (void**) &translations, &translationsLen) &&
57 translationsLen >= sizeof(*translations)) {
58 wchar_t subBlock[128];
59 snwprintf(subBlock, ARRAY_SIZE(subBlock), L"\\StringFileInfo\\%04x%04x\\%ls", translations[0].language, translations[0].codePage, stringName);
60 FF_DEBUG("query version string with translation: %ls", subBlock);
61
62 wchar_t* value;
63 UINT valueLen; // Number of characters, including null terminator
64 if (VerQueryValueW(versionData, subBlock, (void**) &value, &valueLen) && valueLen > 0) {
65 ffStrbufSetNWS(version, valueLen - 1, value);
66 FF_DEBUG("version string resolved (translation fallback): %s", version->chars);
67 return true;
68 }
69
70 FF_DEBUG("translation fallback query failed");
71 } else {
72 FF_DEBUG("no translation table found in version resource");
73 }
74
75 FF_DEBUG("ffGetFileVersion failed");
76 return false;
77}