|
| 1 | +// Copyright (c) 2020 The Chromium Authors. All rights reserved. |
| 2 | +// Use of this source code is governed by a BSD-style license that can be |
| 3 | +// found in the LICENSE file. |
| 4 | + |
| 5 | +// Inspired by: |
| 6 | +// - github.com/chromium/chromium/src/base/file_version_info_win.cc |
| 7 | +// - github.com/timsneath/win32/example/filever.dart |
| 8 | + |
| 9 | +part of package_info_plus_windows; |
| 10 | + |
| 11 | +class _LANGANDCODEPAGE extends Struct { |
| 12 | + @Uint16() |
| 13 | + int wLanguage; |
| 14 | + |
| 15 | + @Uint16() |
| 16 | + int wCodePage; |
| 17 | +} |
| 18 | + |
| 19 | +final _kernel32 = DynamicLibrary.open('kernel32.dll'); |
| 20 | +final _GetUserDefaultLangID = _kernel32 |
| 21 | + .lookupFunction<Uint16 Function(), int Function()>('GetUserDefaultLangID'); |
| 22 | + |
| 23 | +class _FileVersionInfoData { |
| 24 | + _FileVersionInfoData({this.lpBlock, this.lpLang}); |
| 25 | + final Pointer<Uint8> lpBlock; |
| 26 | + final Pointer<_LANGANDCODEPAGE> lpLang; |
| 27 | +} |
| 28 | + |
| 29 | +class _FileVersionInfo { |
| 30 | + final String filePath; |
| 31 | + final _FileVersionInfoData _data; |
| 32 | + |
| 33 | + _FileVersionInfo(this.filePath) : _data = _getData(filePath); |
| 34 | + |
| 35 | + void dispose() => free(_data.lpBlock); |
| 36 | + |
| 37 | + String get companyName => getValue('CompanyName'); |
| 38 | + String get companyShortName => getValue('CompanyShortName'); |
| 39 | + String get productName => getValue('ProductName'); |
| 40 | + String get productShortName => getValue('ProductShortName'); |
| 41 | + String get internalName => getValue('InternalName'); |
| 42 | + String get productVersion => getValue('ProductVersion'); |
| 43 | + String get specialBuild => getValue('SpecialBuild'); |
| 44 | + String get originalFilename => getValue('OriginalFilename'); |
| 45 | + String get fileDescription => getValue('FileDescription'); |
| 46 | + String get fileVersion => getValue('FileVersion'); |
| 47 | + |
| 48 | + String getValue(String name) { |
| 49 | + final langCodepages = [ |
| 50 | + // try the language and codepage from the EXE |
| 51 | + [_data.lpLang.ref.wLanguage, _data.lpLang.ref.wCodePage], |
| 52 | + // try the default language and codepage from the EXE |
| 53 | + [_GetUserDefaultLangID(), _data.lpLang.ref.wCodePage], |
| 54 | + // try the language from the EXE and Latin codepage (most common) |
| 55 | + [_data.lpLang.ref.wLanguage, 1252], |
| 56 | + // try the default language and Latin codepage (most common) |
| 57 | + [_GetUserDefaultLangID(), 1252], |
| 58 | + ]; |
| 59 | + |
| 60 | + var value; |
| 61 | + final lplpBuffer = allocate<IntPtr>(); |
| 62 | + final puLen = allocate<Uint32>(); |
| 63 | + |
| 64 | + String toHex4(int val) => val.toRadixString(16).padLeft(4, '0'); |
| 65 | + |
| 66 | + for (final langCodepage in langCodepages) { |
| 67 | + final lang = toHex4(langCodepage[0]); |
| 68 | + final codepage = toHex4(langCodepage[1]); |
| 69 | + final lpSubBlock = TEXT('\\StringFileInfo\\$lang$codepage\\$name'); |
| 70 | + final res = VerQueryValue(_data.lpBlock, lpSubBlock, lplpBuffer, puLen); |
| 71 | + free(lpSubBlock); |
| 72 | + |
| 73 | + if (res != 0 && lplpBuffer.value != 0 && puLen.value > 0) { |
| 74 | + final ptr = Pointer<Utf16>.fromAddress(lplpBuffer.value); |
| 75 | + value = ptr.unpackString(puLen.value); |
| 76 | + break; |
| 77 | + } |
| 78 | + } |
| 79 | + |
| 80 | + free(lplpBuffer); |
| 81 | + free(puLen); |
| 82 | + return value; |
| 83 | + } |
| 84 | + |
| 85 | + static _FileVersionInfoData _getData(String filePath) { |
| 86 | + final lptstrFilename = TEXT(filePath); |
| 87 | + final lpdwDummy = allocate<Uint32>(); |
| 88 | + final dwBlockSize = GetFileVersionInfoSize(lptstrFilename, lpdwDummy); |
| 89 | + final lpBlock = allocate<Uint8>(count: dwBlockSize); |
| 90 | + if (GetFileVersionInfo(lptstrFilename, 0, dwBlockSize, lpBlock) == 0) { |
| 91 | + throw WindowsException(HRESULT_FROM_WIN32(GetLastError())); |
| 92 | + } |
| 93 | + final lpSubBlock = TEXT(r'\VarFileInfo\Translation'); |
| 94 | + final lpTranslate = allocate<IntPtr>(); |
| 95 | + if (VerQueryValue(lpBlock, lpSubBlock, lpTranslate, lpdwDummy) == 0) { |
| 96 | + throw WindowsException(HRESULT_FROM_WIN32(GetLastError())); |
| 97 | + } |
| 98 | + final data = _FileVersionInfoData( |
| 99 | + lpBlock: lpBlock, |
| 100 | + lpLang: Pointer<_LANGANDCODEPAGE>.fromAddress(lpTranslate.value), |
| 101 | + ); |
| 102 | + free(lptstrFilename); |
| 103 | + free(lpTranslate); |
| 104 | + free(lpSubBlock); |
| 105 | + free(lpdwDummy); |
| 106 | + return data; |
| 107 | + } |
| 108 | +} |
0 commit comments