Skip to content

Commit 793cb7f

Browse files
committed
Package Info: add Windows support
1 parent 64b5964 commit 793cb7f

10 files changed

Lines changed: 312 additions & 2 deletions

File tree

packages/package_info_plus/lib/package_info_plus.dart

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33
// found in the LICENSE file.
44

55
import 'dart:async';
6+
import 'dart:io' show Platform;
67

8+
import 'package:flutter/foundation.dart' show visibleForTesting;
79
import 'package:package_info_plus_platform_interface/package_info_platform_interface.dart';
10+
import 'package:package_info_plus_windows/package_info_plus_windows.dart';
811

912
/// Application metadata. Provides application bundle information on iOS and
1013
/// application package information on Android.
@@ -22,6 +25,27 @@ class PackageInfo {
2225
this.buildNumber,
2326
});
2427

28+
/// Disables the platform override in order to use a manually registered
29+
/// [PackageInfoPlatform] for testing purposes.
30+
/// See https://github.com/flutter/flutter/issues/52267 for more details.
31+
@visibleForTesting
32+
static set disablePackageInfoPlatformOverride(bool override) {
33+
_disablePlatformOverride = override;
34+
}
35+
36+
static bool _disablePlatformOverride = false;
37+
static PackageInfoPlatform __platform;
38+
39+
// This is to manually endorse the Windows plugin until automatic registration
40+
// of dart plugins is implemented.
41+
// See https://github.com/flutter/flutter/issues/52267 for more details.
42+
static PackageInfoPlatform get _platform {
43+
__platform ??= Platform.isWindows && !_disablePlatformOverride
44+
? PackageInfoWindows()
45+
: PackageInfoPlatform.instance;
46+
return __platform;
47+
}
48+
2549
static PackageInfo _fromPlatform;
2650

2751
/// Retrieves package information from the platform.
@@ -31,7 +55,7 @@ class PackageInfo {
3155
return _fromPlatform;
3256
}
3357

34-
final platformData = await PackageInfoPlatform.instance.getAll();
58+
final platformData = await _platform.getAll();
3559
_fromPlatform = PackageInfo(
3660
appName: platformData.appName,
3761
packageName: platformData.packageName,

packages/package_info_plus/pubspec.yaml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,16 @@ flutter:
1515
default_package: package_info_plus_macos
1616
web:
1717
default_package: package_info_plus_web
18-
18+
windows:
19+
default_package: package_info_plus_windows
20+
1921
dependencies:
2022
flutter:
2123
sdk: flutter
2224
package_info_plus_platform_interface: ^0.1.0
2325
package_info_plus_web: ^0.1.0
2426
package_info_plus_macos: ^0.1.0
27+
package_info_plus_windows: ^0.1.0
2528

2629
dev_dependencies:
2730
flutter_test:
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Miscellaneous
2+
*.class
3+
*.log
4+
*.pyc
5+
*.swp
6+
.DS_Store
7+
.atom/
8+
.buildlog/
9+
.history
10+
.svn/
11+
12+
# IntelliJ related
13+
*.iml
14+
*.ipr
15+
*.iws
16+
.idea/
17+
18+
# The .vscode folder contains launch configuration and tasks you configure in
19+
# VS Code which you may wish to be included in version control, so this line
20+
# is commented out by default.
21+
#.vscode/
22+
23+
# Flutter/Dart/Pub related
24+
**/doc/api/
25+
.dart_tool/
26+
.flutter-plugins
27+
.flutter-plugins-dependencies
28+
.packages
29+
.pub-cache/
30+
.pub/
31+
build/
32+
33+
# Android related
34+
**/android/**/gradle-wrapper.jar
35+
**/android/.gradle
36+
**/android/captures/
37+
**/android/gradlew
38+
**/android/gradlew.bat
39+
**/android/local.properties
40+
**/android/**/GeneratedPluginRegistrant.java
41+
42+
# iOS/XCode related
43+
**/ios/**/*.mode1v3
44+
**/ios/**/*.mode2v3
45+
**/ios/**/*.moved-aside
46+
**/ios/**/*.pbxuser
47+
**/ios/**/*.perspectivev3
48+
**/ios/**/*sync/
49+
**/ios/**/.sconsign.dblite
50+
**/ios/**/.tags*
51+
**/ios/**/.vagrant/
52+
**/ios/**/DerivedData/
53+
**/ios/**/Icon?
54+
**/ios/**/Pods/
55+
**/ios/**/.symlinks/
56+
**/ios/**/profile
57+
**/ios/**/xcuserdata
58+
**/ios/.generated/
59+
**/ios/Flutter/App.framework
60+
**/ios/Flutter/Flutter.framework
61+
**/ios/Flutter/Flutter.podspec
62+
**/ios/Flutter/Generated.xcconfig
63+
**/ios/Flutter/app.flx
64+
**/ios/Flutter/app.zip
65+
**/ios/Flutter/flutter_assets/
66+
**/ios/Flutter/flutter_export_environment.sh
67+
**/ios/ServiceDefinitions.json
68+
**/ios/Runner/GeneratedPluginRegistrant.*
69+
70+
# Exceptions to above rules.
71+
!**/ios/**/default.mode1v3
72+
!**/ios/**/default.mode2v3
73+
!**/ios/**/default.pbxuser
74+
!**/ios/**/default.perspectivev3
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# This file tracks properties of this Flutter project.
2+
# Used by Flutter tool to assess capabilities and perform upgrades etc.
3+
#
4+
# This file should be version controlled and should not be manually edited.
5+
6+
version:
7+
revision: ec40df9576d6020dd56820b9b5aff8f828706e49
8+
channel: master
9+
10+
project_type: package
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## 0.1.0
2+
3+
- Initial Windows support.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
Copyright 2017 The Chromium Authors. All rights reserved.
2+
3+
Redistribution and use in source and binary forms, with or without
4+
modification, are permitted provided that the following conditions are
5+
met:
6+
7+
* Redistributions of source code must retain the above copyright
8+
notice, this list of conditions and the following disclaimer.
9+
* Redistributions in binary form must reproduce the above
10+
copyright notice, this list of conditions and the following disclaimer
11+
in the documentation and/or other materials provided with the
12+
distribution.
13+
* Neither the name of Google Inc. nor the names of its
14+
contributors may be used to endorse or promote products derived from
15+
this software without specific prior written permission.
16+
17+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21+
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Package Info Plus Windows
2+
3+
[![Flutter Community: package_info_plus_windows](https://fluttercommunity.dev/_github/header/package_info_plus_windows)](https://github.com/fluttercommunity/community)
4+
5+
[![pub package](https://img.shields.io/pub/v/package_info_plus_windows.svg)](https://pub.dev/packages/package_info_plus_windows)
6+
7+
The Windows implementation of [`package_info_plus`](https://pub.dev/packages/package_info_plus).
8+
9+
## Usage
10+
11+
This package is already included as part of the `package_info_plus` package dependency, and will
12+
be included when using `package_info_plus` as normal.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/// The Windows implementation of `package_info_plus`.
2+
library package_info_plus_windows;
3+
4+
import 'dart:ffi';
5+
import 'dart:io';
6+
7+
import 'package:ffi/ffi.dart';
8+
import 'package:package_info_plus_platform_interface/package_info_data.dart';
9+
import 'package:package_info_plus_platform_interface/package_info_platform_interface.dart';
10+
import 'package:win32/win32.dart';
11+
12+
part 'src/file_version_info.dart';
13+
14+
/// The Windows implementation of [PackageInfoPlatform].
15+
class PackageInfoWindows extends PackageInfoPlatform {
16+
/// Returns a map with the following keys:
17+
/// appName, packageName, version, buildNumber
18+
@override
19+
Future<PackageInfoData> getAll() {
20+
final info = _FileVersionInfo(Platform.resolvedExecutable);
21+
final data = PackageInfoData(
22+
appName: info.productName,
23+
packageName: info.internalName,
24+
version: info.productVersion,
25+
buildNumber: info.fileVersion,
26+
);
27+
info.dispose();
28+
return Future.value(data);
29+
}
30+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
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+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
name: package_info_plus_windows
2+
description: Windows implementation of the package_info_plus plugin
3+
homepage: https://github.com/fluttercommunity/plus_plugins/tree/main/packages/package_info_plus_windows
4+
version: 0.1.0
5+
6+
environment:
7+
sdk: ">=2.7.0 <3.0.0"
8+
flutter: ">=1.17.0 <2.0.0"
9+
10+
dependencies:
11+
package_info_plus_platform_interface: ^1.1.0
12+
ffi: ^0.1.3
13+
flutter:
14+
sdk: flutter
15+
win32: ^1.7.3
16+
17+
dev_dependencies:
18+
flutter_test:
19+
sdk: flutter

0 commit comments

Comments
 (0)