Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

[path_provider] Migrate path_provider_windows to nullsafety #3410

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/path_provider/path_provider_windows/CHANGELOG.md
@@ -1,3 +1,7 @@
## 0.1.0-nullsafety

* Migrate to null safety

## 0.0.4+4

* Update Flutter SDK constraint.
Expand Down
Expand Up @@ -22,20 +22,19 @@ import 'folders.dart';
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.
getStringValue(Pointer<Uint8> versionInfo, key) {
getStringValue(Pointer<Uint8>? versionInfo, key) {
if (versionInfo == null) {
return null;
}
const kEnUsLanguageCode = '040904e4';
final keyPath = TEXT('\\StringFileInfo\\$kEnUsLanguageCode\\$key');
final length = allocate<Uint32>();
final valueAddress = allocate<IntPtr>();
final valueAddress = allocate<Pointer<Utf16>>();
try {
if (VerQueryValue(versionInfo, keyPath, valueAddress, length) == 0) {
return null;
}
return Pointer<Utf16>.fromAddress(valueAddress.value)
.unpackString(length.value);
return valueAddress.value.unpackString(length.value);
} finally {
free(keyPath);
free(length);
Expand All @@ -54,7 +53,7 @@ class PathProviderWindows extends PathProviderPlatform {

/// This is typically the same as the TMP environment variable.
@override
Future<String> getTemporaryPath() async {
Future<String?> getTemporaryPath() async {
final buffer = allocate<Uint16>(count: MAX_PATH + 1).cast<Utf16>();
String path;

Expand Down Expand Up @@ -88,7 +87,7 @@ class PathProviderWindows extends PathProviderPlatform {
}

@override
Future<String> getApplicationSupportPath() async {
Future<String?> getApplicationSupportPath() async {
final appDataRoot = await getPath(WindowsKnownFolder.RoamingAppData);
final directory = Directory(
path.join(appDataRoot, _getApplicationSpecificSubdirectory()));
Expand All @@ -105,25 +104,23 @@ class PathProviderWindows extends PathProviderPlatform {
}

@override
Future<String> getApplicationDocumentsPath() =>
Future<String?> getApplicationDocumentsPath() =>
getPath(WindowsKnownFolder.Documents);

@override
Future<String> getDownloadsPath() => getPath(WindowsKnownFolder.Downloads);
Future<String?> getDownloadsPath() => getPath(WindowsKnownFolder.Downloads);

/// Retrieve any known folder from Windows.
///
/// folderID is a GUID that represents a specific known folder ID, drawn from
/// [WindowsKnownFolder].
Future<String> getPath(String folderID) {
final pathPtrPtr = allocate<IntPtr>();
Pointer<Utf16> pathPtr;
final pathPtrPtr = allocate<Pointer<Utf16>>();
final Pointer<GUID> knownFolderID = calloc<GUID>()..setGUID(folderID);

try {
GUID knownFolderID = GUID.fromString(folderID);

final hr = SHGetKnownFolderPath(
knownFolderID.addressOf, // ignore: deprecated_member_use
knownFolderID,
KF_FLAG_DEFAULT,
NULL,
pathPtrPtr,
Expand All @@ -135,12 +132,11 @@ class PathProviderWindows extends PathProviderPlatform {
}
}

pathPtr = Pointer<Utf16>.fromAddress(pathPtrPtr.value);
final path = pathPtr.unpackString(MAX_PATH);
final path = pathPtrPtr.value.unpackString(MAX_PATH);
return Future.value(path);
} finally {
CoTaskMemFree(pathPtr.cast());
free(pathPtrPtr);
free(knownFolderID);
}
}

Expand All @@ -155,13 +151,13 @@ class PathProviderWindows extends PathProviderPlatform {
/// - If the product name isn't there, it will use the exe's filename (without
/// extension).
String _getApplicationSpecificSubdirectory() {
String companyName;
String productName;
String? companyName;
String? productName;

final Pointer<Utf16> moduleNameBuffer =
allocate<Uint16>(count: MAX_PATH + 1).cast<Utf16>();
final Pointer<Uint32> unused = allocate<Uint32>();
Pointer<Uint8> infoBuffer;
Pointer<Uint8>? infoBuffer;
try {
// Get the module name.
final moduleNameLength = GetModuleFileName(0, moduleNameBuffer, MAX_PATH);
Expand Down Expand Up @@ -207,7 +203,7 @@ class PathProviderWindows extends PathProviderPlatform {
/// https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#naming-conventions
///
/// If after sanitizing the string is empty, returns null.
String _sanitizedDirectoryName(String rawString) {
String? _sanitizedDirectoryName(String? rawString) {
if (rawString == null) {
return null;
}
Expand Down
Expand Up @@ -19,7 +19,7 @@ class PathProviderWindows extends PathProviderPlatform {
}

/// Stub; see comment on VersionInfoQuerier.
VersionInfoQuerier versionInfoQuerier;
VersionInfoQuerier versionInfoQuerier = VersionInfoQuerier();

/// Match PathProviderWindows so that the analyzer won't report invalid
/// overrides if tests provide fake PathProviderWindows implementations.
Expand Down
16 changes: 8 additions & 8 deletions packages/path_provider/path_provider_windows/pubspec.yaml
@@ -1,7 +1,7 @@
name: path_provider_windows
description: Windows implementation of the path_provider plugin
homepage: https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider_windows
version: 0.0.4+4
version: 0.1.0-nullsafety

flutter:
plugin:
Expand All @@ -11,19 +11,19 @@ flutter:
pluginClass: none

dependencies:
path_provider_platform_interface: ^1.0.3
meta: ^1.0.5
path: ^1.6.4
path_provider_platform_interface: ^2.0.0-nullsafety
meta: ^1.3.0-nullsafety.6
path: ^1.8.0-nullsafety.3
flutter:
sdk: flutter
ffi: ^0.1.3
win32: ^1.7.1
ffi: ^0.2.0-nullsafety.1
win32: ^2.0.0-nullsafety.8

dev_dependencies:
flutter_test:
sdk: flutter
pedantic: ^1.8.0
pedantic: ^1.10.0-nullsafety.3

environment:
sdk: ">=2.1.0 <3.0.0"
sdk: '>=2.12.0-0 <3.0.0'
flutter: ">=1.12.13+hotfix.4"
Expand Up @@ -13,7 +13,7 @@ class FakeVersionInfoQuerier implements VersionInfoQuerier {

final Map<String, String> responses;

getStringValue(Pointer<Uint8> versionInfo, key) => responses[key];
getStringValue(Pointer<Uint8>? versionInfo, key) => responses[key];
}

void main() {
Expand All @@ -40,8 +40,11 @@ void main() {
'ProductName': 'Amazing App',
});
final path = await pathProvider.getApplicationSupportPath();
expect(path, endsWith(r'AppData\Roaming\A Company\Amazing App'));
expect(Directory(path).existsSync(), isTrue);
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 missing company', () async {
Expand All @@ -50,8 +53,11 @@ void main() {
'ProductName': 'Amazing App',
});
final path = await pathProvider.getApplicationSupportPath();
expect(path, endsWith(r'AppData\Roaming\Amazing App'));
expect(Directory(path).existsSync(), isTrue);
expect(path, isNotNull);
if (path != null) {
expect(path, endsWith(r'AppData\Roaming\Amazing App'));
expect(Directory(path).existsSync(), isTrue);
}
}, skip: !Platform.isWindows);

test('getApplicationSupportPath with problematic values', () async {
Expand All @@ -61,12 +67,15 @@ void main() {
'ProductName': r'A"/Terrible\|App?*Name',
});
final path = await pathProvider.getApplicationSupportPath();
expect(
path,
endsWith(r'AppData\Roaming\'
r'A _Bad_ Company_ Name\'
r'A__Terrible__App__Name'));
expect(Directory(path).existsSync(), isTrue);
expect(path, isNotNull);
if (path != null) {
expect(
path,
endsWith(r'AppData\Roaming\'
r'A _Bad_ Company_ Name\'
r'A__Terrible__App__Name'));
expect(Directory(path).existsSync(), isTrue);
}
}, skip: !Platform.isWindows);

test('getApplicationSupportPath with a completely invalid company', () async {
Expand All @@ -76,8 +85,11 @@ void main() {
'ProductName': r'Amazing App',
});
final path = await pathProvider.getApplicationSupportPath();
expect(path, endsWith(r'AppData\Roaming\Amazing App'));
expect(Directory(path).existsSync(), isTrue);
expect(path, isNotNull);
if (path != null) {
expect(path, endsWith(r'AppData\Roaming\Amazing App'));
expect(Directory(path).existsSync(), isTrue);
}
}, skip: !Platform.isWindows);

test('getApplicationSupportPath with very long app name', () async {
Expand Down