[path_provider] Support unicode encoded version info values (#4986)

diff --git a/packages/path_provider/path_provider_windows/CHANGELOG.md b/packages/path_provider/path_provider_windows/CHANGELOG.md
index d933b0d..f48093b 100644
--- a/packages/path_provider/path_provider_windows/CHANGELOG.md
+++ b/packages/path_provider/path_provider_windows/CHANGELOG.md
@@ -1,5 +1,6 @@
-## NEXT
+## 2.0.7
 
+* Added support for unicode encoded VERSIONINFO
 * Minor fixes for new analysis options.
 
 ## 2.0.6
diff --git a/packages/path_provider/path_provider_windows/lib/src/path_provider_windows_real.dart b/packages/path_provider/path_provider_windows/lib/src/path_provider_windows_real.dart
index bfffa84..9a8b0cf 100644
--- a/packages/path_provider/path_provider_windows/lib/src/path_provider_windows_real.dart
+++ b/packages/path_provider/path_provider_windows/lib/src/path_provider_windows_real.dart
@@ -13,21 +13,43 @@
 
 import 'folders.dart';
 
+/// Constant for en-US language used in VersionInfo keys.
+@visibleForTesting
+const String languageEn = '0409';
+
+/// Constant for CP1252 encoding used in VersionInfo keys
+@visibleForTesting
+const String encodingCP1252 = '04e4';
+
+/// Constant for Unicode encoding used in VersionInfo keys
+@visibleForTesting
+const String encodingUnicode = '04b0';
+
 /// Wraps the Win32 VerQueryValue API call.
 ///
 /// This class exists to allow injecting alternate metadata in tests without
 /// building multiple custom test binaries.
 @visibleForTesting
 class VersionInfoQuerier {
-  /// Returns the value for [key] in [versionInfo]s English strings section, or
-  /// null if there is no such entry, or if versionInfo is null.
-  String? getStringValue(Pointer<Uint8>? versionInfo, String key) {
+  /// Returns the value for [key] in [versionInfo]s in section with given
+  /// language and encoding, or null if there is no such entry,
+  /// or if versionInfo is null.
+  ///
+  /// See https://docs.microsoft.com/en-us/windows/win32/menurc/versioninfo-resource
+  /// for list of possible language and encoding values.
+  String? getStringValue(
+    Pointer<Uint8>? versionInfo,
+    String key, {
+    required String language,
+    required String encoding,
+  }) {
+    assert(language.isNotEmpty);
+    assert(encoding.isNotEmpty);
     if (versionInfo == null) {
       return null;
     }
-    const String kEnUsLanguageCode = '040904e4';
     final Pointer<Utf16> keyPath =
-        TEXT('\\StringFileInfo\\$kEnUsLanguageCode\\$key');
+        TEXT('\\StringFileInfo\\$language$encoding\\$key');
     final Pointer<Uint32> length = calloc<Uint32>();
     final Pointer<Pointer<Utf16>> valueAddress = calloc<Pointer<Utf16>>();
     try {
@@ -150,6 +172,12 @@
     }
   }
 
+  String? _getStringValue(Pointer<Uint8>? infoBuffer, String key) =>
+      versionInfoQuerier.getStringValue(infoBuffer, key,
+          language: languageEn, encoding: encodingCP1252) ??
+      versionInfoQuerier.getStringValue(infoBuffer, key,
+          language: languageEn, encoding: encodingUnicode);
+
   /// Returns the relative path string to append to the root directory returned
   /// by Win32 APIs for application storage (such as RoamingAppDir) to get a
   /// directory that is unique to the application.
@@ -187,10 +215,10 @@
           infoBuffer = null;
         }
       }
-      companyName = _sanitizedDirectoryName(
-          versionInfoQuerier.getStringValue(infoBuffer, 'CompanyName'));
-      productName = _sanitizedDirectoryName(
-          versionInfoQuerier.getStringValue(infoBuffer, 'ProductName'));
+      companyName =
+          _sanitizedDirectoryName(_getStringValue(infoBuffer, 'CompanyName'));
+      productName =
+          _sanitizedDirectoryName(_getStringValue(infoBuffer, 'ProductName'));
 
       // If there was no product name, use the executable name.
       productName ??=
diff --git a/packages/path_provider/path_provider_windows/pubspec.yaml b/packages/path_provider/path_provider_windows/pubspec.yaml
index f75dd05..a995b9f 100644
--- a/packages/path_provider/path_provider_windows/pubspec.yaml
+++ b/packages/path_provider/path_provider_windows/pubspec.yaml
@@ -2,7 +2,7 @@
 description: Windows implementation of the path_provider plugin
 repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_windows
 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22
-version: 2.0.6
+version: 2.0.7
 
 environment:
   sdk: ">=2.12.0 <3.0.0"
diff --git a/packages/path_provider/path_provider_windows/test/path_provider_windows_test.dart b/packages/path_provider/path_provider_windows/test/path_provider_windows_test.dart
index 7e4118c..571c314 100644
--- a/packages/path_provider/path_provider_windows/test/path_provider_windows_test.dart
+++ b/packages/path_provider/path_provider_windows/test/path_provider_windows_test.dart
@@ -7,15 +7,33 @@
 import 'package:flutter_test/flutter_test.dart';
 import 'package:path_provider_platform_interface/path_provider_platform_interface.dart';
 import 'package:path_provider_windows/path_provider_windows.dart';
+import 'package:path_provider_windows/src/path_provider_windows_real.dart'
+    show languageEn, encodingCP1252, encodingUnicode;
 
 // A fake VersionInfoQuerier that just returns preset responses.
 class FakeVersionInfoQuerier implements VersionInfoQuerier {
-  FakeVersionInfoQuerier(this.responses);
+  FakeVersionInfoQuerier(
+    this.responses, {
+    this.language = languageEn,
+    this.encoding = encodingUnicode,
+  });
 
+  final String language;
+  final String encoding;
   final Map<String, String> responses;
 
-  String? getStringValue(Pointer<Uint8>? versionInfo, String key) =>
-      responses[key];
+  String? getStringValue(
+    Pointer<Uint8>? versionInfo,
+    String key, {
+    required String language,
+    required String encoding,
+  }) {
+    if (language == this.language && encoding == this.encoding) {
+      return responses[key];
+    } else {
+      return null;
+    }
+  }
 }
 
 void main() {
@@ -40,12 +58,12 @@
     expect(path, endsWith(r'flutter_tester'));
   }, skip: !Platform.isWindows);
 
-  test('getApplicationSupportPath with full version info', () async {
+  test('getApplicationSupportPath with full version info in CP1252', () async {
     final PathProviderWindows pathProvider = PathProviderWindows();
     pathProvider.versionInfoQuerier = FakeVersionInfoQuerier(<String, String>{
       'CompanyName': 'A Company',
       'ProductName': 'Amazing App',
-    });
+    }, language: languageEn, encoding: encodingCP1252);
     final String? path = await pathProvider.getApplicationSupportPath();
     expect(path, isNotNull);
     if (path != null) {
@@ -54,6 +72,35 @@
     }
   }, skip: !Platform.isWindows);
 
+  test('getApplicationSupportPath with full version info in Unicode', () async {
+    final PathProviderWindows pathProvider = PathProviderWindows();
+    pathProvider.versionInfoQuerier = FakeVersionInfoQuerier(<String, String>{
+      'CompanyName': 'A Company',
+      'ProductName': 'Amazing App',
+    }, language: languageEn, encoding: encodingUnicode);
+    final String? path = await pathProvider.getApplicationSupportPath();
+    expect(path, isNotNull);
+    if (path != null) {
+      expect(path, endsWith(r'AppData\Roaming\A Company\Amazing App'));
+      expect(Directory(path).existsSync(), isTrue);
+    }
+  }, skip: !Platform.isWindows);
+
+  test(
+      'getApplicationSupportPath with full version info in Unsupported Encoding',
+      () async {
+    final PathProviderWindows pathProvider = PathProviderWindows();
+    pathProvider.versionInfoQuerier = FakeVersionInfoQuerier(<String, String>{
+      'CompanyName': 'A Company',
+      'ProductName': 'Amazing App',
+    }, language: '0000', encoding: '0000');
+    final String? path = await pathProvider.getApplicationSupportPath();
+    expect(path, contains(r'C:\'));
+    expect(path, contains(r'AppData'));
+    // The last path component should be the executable name.
+    expect(path, endsWith(r'flutter_tester'));
+  }, skip: !Platform.isWindows);
+
   test('getApplicationSupportPath with missing company', () async {
     final PathProviderWindows pathProvider = PathProviderWindows();
     pathProvider.versionInfoQuerier = FakeVersionInfoQuerier(<String, String>{