[path_provider] Remove win32 (#7073)

Per https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#dependencies, we generally do not want third-party dependencies. `path_provider` in particular is a key part of the ecosystem (well over 1,000 packages depend directly on it, and transitively it's probably several times that at least), and thus the maintenance and security considerations are particularly acute.

This eliminates the dependency on `win32`, a large third-party dependency, in favor of direct FFI code written from scratch using the official Win32 reference documentation from Microsoft as the source. The only behavioral change that should result here is that the exceptions thrown in failure cases have changed, but they were never documented, and were entirely platform-specific, so it's relatively unlikely that people will be broken by that. (As noted in a TODO, the longer term solution is to provide real exceptions for this package, and use those across platforms.)

Fixes https://github.com/flutter/flutter/issues/130940
This commit is contained in:
stuartmorgan
2024-07-09 06:03:23 -04:00
committed by GitHub
parent 9627de9141
commit 47a92dbdea
8 changed files with 314 additions and 67 deletions

View File

@ -1,5 +1,6 @@
## NEXT ## 2.3.0
* Replaces `win32` dependency with direct FFI usage.
* Updates minimum supported SDK version to Flutter 3.16/Dart 3.2. * Updates minimum supported SDK version to Flutter 3.16/Dart 3.2.
## 2.2.1 ## 2.2.1

View File

@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:win32/win32.dart';
// ignore_for_file: non_constant_identifier_names // ignore_for_file: non_constant_identifier_names
// ignore: avoid_classes_with_only_static_members // ignore: avoid_classes_with_only_static_members
@ -11,122 +9,127 @@ import 'package:win32/win32.dart';
/// known folders. A property of this class may be passed to the `getPath` /// known folders. A property of this class may be passed to the `getPath`
/// method in the [PathProvidersWindows] class to retrieve a known folder from /// method in the [PathProvidersWindows] class to retrieve a known folder from
/// Windows. /// Windows.
// These constants come from
// https://learn.microsoft.com/windows/win32/shell/knownfolderid
class WindowsKnownFolder { class WindowsKnownFolder {
/// The file system directory that is used to store administrative tools for /// The file system directory that is used to store administrative tools for
/// an individual user. The MMC will save customized consoles to this /// an individual user. The MMC will save customized consoles to this
/// directory, and it will roam with the user. /// directory, and it will roam with the user.
static String get AdminTools => FOLDERID_AdminTools; static String get AdminTools => '{724EF170-A42D-4FEF-9F26-B60E846FBA4F}';
/// The file system directory that acts as a staging area for files waiting to /// The file system directory that acts as a staging area for files waiting to
/// be written to a CD. A typical path is C:\Documents and /// be written to a CD. A typical path is C:\Documents and
/// Settings\username\Local Settings\Application Data\Microsoft\CD Burning. /// Settings\username\Local Settings\Application Data\Microsoft\CD Burning.
static String get CDBurning => FOLDERID_CDBurning; static String get CDBurning => '{9E52AB10-F80D-49DF-ACB8-4330F5687855}';
/// The file system directory that contains administrative tools for all users /// The file system directory that contains administrative tools for all users
/// of the computer. /// of the computer.
static String get CommonAdminTools => FOLDERID_CommonAdminTools; static String get CommonAdminTools =>
'{D0384E7D-BAC3-4797-8F14-CBA229B392B5}';
/// The file system directory that contains the directories for the common /// The file system directory that contains the directories for the common
/// program groups that appear on the Start menu for all users. A typical path /// program groups that appear on the Start menu for all users. A typical path
/// is C:\Documents and Settings\All Users\Start Menu\Programs. /// is C:\Documents and Settings\All Users\Start Menu\Programs.
static String get CommonPrograms => FOLDERID_CommonPrograms; static String get CommonPrograms => '{0139D44E-6AFE-49F2-8690-3DAFCAE6FFB8}';
/// The file system directory that contains the programs and folders that /// The file system directory that contains the programs and folders that
/// appear on the Start menu for all users. A typical path is C:\Documents and /// appear on the Start menu for all users. A typical path is C:\Documents and
/// Settings\All Users\Start Menu. /// Settings\All Users\Start Menu.
static String get CommonStartMenu => FOLDERID_CommonStartMenu; static String get CommonStartMenu => '{A4115719-D62E-491D-AA7C-E74B8BE3B067}';
/// The file system directory that contains the programs that appear in the /// The file system directory that contains the programs that appear in the
/// Startup folder for all users. A typical path is C:\Documents and /// Startup folder for all users. A typical path is C:\Documents and
/// Settings\All Users\Start Menu\Programs\Startup. /// Settings\All Users\Start Menu\Programs\Startup.
static String get CommonStartup => FOLDERID_CommonStartup; static String get CommonStartup => '{82A5EA35-D9CD-47C5-9629-E15D2F714E6E}';
/// The file system directory that contains the templates that are available /// The file system directory that contains the templates that are available
/// to all users. A typical path is C:\Documents and Settings\All /// to all users. A typical path is C:\Documents and Settings\All
/// Users\Templates. /// Users\Templates.
static String get CommonTemplates => FOLDERID_CommonTemplates; static String get CommonTemplates => '{B94237E7-57AC-4347-9151-B08C6C32D1F7}';
/// The virtual folder that represents My Computer, containing everything on /// The virtual folder that represents My Computer, containing everything on
/// the local computer: storage devices, printers, and Control Panel. The /// the local computer: storage devices, printers, and Control Panel. The
/// folder can also contain mapped network drives. /// folder can also contain mapped network drives.
static String get ComputerFolder => FOLDERID_ComputerFolder; static String get ComputerFolder => '{0AC0837C-BBF8-452A-850D-79D08E667CA7}';
/// The virtual folder that represents Network Connections, that contains /// The virtual folder that represents Network Connections, that contains
/// network and dial-up connections. /// network and dial-up connections.
static String get ConnectionsFolder => FOLDERID_ConnectionsFolder; static String get ConnectionsFolder =>
'{6F0CD92B-2E97-45D1-88FF-B0D186B8DEDD}';
/// The virtual folder that contains icons for the Control Panel applications. /// The virtual folder that contains icons for the Control Panel applications.
static String get ControlPanelFolder => FOLDERID_ControlPanelFolder; static String get ControlPanelFolder =>
'{82A74AEB-AEB4-465C-A014-D097EE346D63}';
/// The file system directory that serves as a common repository for Internet /// The file system directory that serves as a common repository for Internet
/// cookies. A typical path is C:\Documents and Settings\username\Cookies. /// cookies. A typical path is C:\Documents and Settings\username\Cookies.
static String get Cookies => FOLDERID_Cookies; static String get Cookies => '{2B0F765D-C0E9-4171-908E-08A611B84FF6}';
/// The virtual folder that represents the Windows desktop, the root of the /// The virtual folder that represents the Windows desktop, the root of the
/// namespace. /// namespace.
static String get Desktop => FOLDERID_Desktop; static String get Desktop => '{B4BFCC3A-DB2C-424C-B029-7FE99A87C641}';
/// The virtual folder that represents the My Documents desktop item. /// The virtual folder that represents the My Documents desktop item.
static String get Documents => FOLDERID_Documents; static String get Documents => '{FDD39AD0-238F-46AF-ADB4-6C85480369C7}';
/// The file system directory that serves as a repository for Internet /// The file system directory that serves as a repository for Internet
/// downloads. /// downloads.
static String get Downloads => FOLDERID_Downloads; static String get Downloads => '{374DE290-123F-4565-9164-39C4925E467B}';
/// The file system directory that serves as a common repository for the /// The file system directory that serves as a common repository for the
/// user's favorite items. A typical path is C:\Documents and /// user's favorite items. A typical path is C:\Documents and
/// Settings\username\Favorites. /// Settings\username\Favorites.
static String get Favorites => FOLDERID_Favorites; static String get Favorites => '{1777F761-68AD-4D8A-87BD-30B759FA33DD}';
/// A virtual folder that contains fonts. A typical path is C:\Windows\Fonts. /// A virtual folder that contains fonts. A typical path is C:\Windows\Fonts.
static String get Fonts => FOLDERID_Fonts; static String get Fonts => '{FD228CB7-AE11-4AE3-864C-16F3910AB8FE}';
/// The file system directory that serves as a common repository for Internet /// The file system directory that serves as a common repository for Internet
/// history items. /// history items.
static String get History => FOLDERID_History; static String get History => '{D9DC8A3B-B784-432E-A781-5A1130A75963}';
/// The file system directory that serves as a common repository for temporary /// The file system directory that serves as a common repository for temporary
/// Internet files. A typical path is C:\Documents and Settings\username\Local /// Internet files. A typical path is C:\Documents and Settings\username\Local
/// Settings\Temporary Internet Files. /// Settings\Temporary Internet Files.
static String get InternetCache => FOLDERID_InternetCache; static String get InternetCache => '{352481E8-33BE-4251-BA85-6007CAEDCF9D}';
/// A virtual folder for Internet Explorer. /// A virtual folder for Internet Explorer.
static String get InternetFolder => FOLDERID_InternetFolder; static String get InternetFolder => '{4D9F7874-4E0C-4904-967B-40B0D20C3E4B}';
/// The file system directory that serves as a data repository for local /// The file system directory that serves as a data repository for local
/// (nonroaming) applications. A typical path is C:\Documents and /// (nonroaming) applications. A typical path is C:\Documents and
/// Settings\username\Local Settings\Application Data. /// Settings\username\Local Settings\Application Data.
static String get LocalAppData => FOLDERID_LocalAppData; static String get LocalAppData => '{F1B32785-6FBA-4FCF-9D55-7B8E7F157091}';
/// The file system directory that serves as a common repository for music /// The file system directory that serves as a common repository for music
/// files. A typical path is C:\Documents and Settings\User\My Documents\My /// files. A typical path is C:\Documents and Settings\User\My Documents\My
/// Music. /// Music.
static String get Music => FOLDERID_Music; static String get Music => '{4BD8D571-6D19-48D3-BE97-422220080E43}';
/// A file system directory that contains the link objects that may exist in /// A file system directory that contains the link objects that may exist in
/// the My Network Places virtual folder. A typical path is C:\Documents and /// the My Network Places virtual folder. A typical path is C:\Documents and
/// Settings\username\NetHood. /// Settings\username\NetHood.
static String get NetHood => FOLDERID_NetHood; static String get NetHood => '{C5ABBF53-E17F-4121-8900-86626FC2C973}';
/// The folder that represents other computers in your workgroup. /// The folder that represents other computers in your workgroup.
static String get NetworkFolder => FOLDERID_NetworkFolder; static String get NetworkFolder => '{D20BEEC4-5CA8-4905-AE3B-BF251EA09B53}';
/// The file system directory that serves as a common repository for image /// The file system directory that serves as a common repository for image
/// files. A typical path is C:\Documents and Settings\username\My /// files. A typical path is C:\Documents and Settings\username\My
/// Documents\My Pictures. /// Documents\My Pictures.
static String get Pictures => FOLDERID_Pictures; static String get Pictures => '{33E28130-4E1E-4676-835A-98395C3BC3BB}';
/// The file system directory that contains the link objects that can exist in /// The file system directory that contains the link objects that can exist in
/// the Printers virtual folder. A typical path is C:\Documents and /// the Printers virtual folder. A typical path is C:\Documents and
/// Settings\username\PrintHood. /// Settings\username\PrintHood.
static String get PrintHood => FOLDERID_PrintHood; static String get PrintHood => '{9274BD8D-CFD1-41C3-B35E-B13F55A758F4}';
/// The virtual folder that contains installed printers. /// The virtual folder that contains installed printers.
static String get PrintersFolder => FOLDERID_PrintersFolder; static String get PrintersFolder => '{76FC4E2D-D6AD-4519-A663-37BD56068185}';
/// The user's profile folder. A typical path is C:\Users\username. /// The user's profile folder. A typical path is C:\Users\username.
/// Applications should not create files or folders at this level. /// Applications should not create files or folders at this level.
static String get Profile => FOLDERID_Profile; static String get Profile => '{5E6C858F-0E22-4760-9AFE-EA3317B67173}';
/// The file system directory that contains application data for all users. A /// The file system directory that contains application data for all users. A
/// typical path is C:\Documents and Settings\All Users\Application Data. This /// typical path is C:\Documents and Settings\All Users\Application Data. This
@ -134,110 +137,114 @@ class WindowsKnownFolder {
/// example, an application can store a spell-check dictionary, a database of /// example, an application can store a spell-check dictionary, a database of
/// clip art, or a log file in the CSIDL_COMMON_APPDATA folder. This /// clip art, or a log file in the CSIDL_COMMON_APPDATA folder. This
/// information will not roam and is available to anyone using the computer. /// information will not roam and is available to anyone using the computer.
static String get ProgramData => FOLDERID_ProgramData; static String get ProgramData => '{62AB5D82-FDC1-4DC3-A9DD-070D1D495D97}';
/// The Program Files folder. A typical path is C:\Program Files. /// The Program Files folder. A typical path is C:\Program Files.
static String get ProgramFiles => FOLDERID_ProgramFiles; static String get ProgramFiles => '{905e63b6-c1bf-494e-b29c-65b732d3d21a}';
/// The common Program Files folder. A typical path is C:\Program /// The common Program Files folder. A typical path is C:\Program
/// Files\Common. /// Files\Common.
static String get ProgramFilesCommon => FOLDERID_ProgramFilesCommon; static String get ProgramFilesCommon =>
'{F7F1ED05-9F6D-47A2-AAAE-29D317C6F066}';
/// On 64-bit systems, a link to the common Program Files folder. A typical path is /// On 64-bit systems, a link to the common Program Files folder. A typical path is
/// C:\Program Files\Common Files. /// C:\Program Files\Common Files.
static String get ProgramFilesCommonX64 => FOLDERID_ProgramFilesCommonX64; static String get ProgramFilesCommonX64 =>
'{6365D5A7-0F0D-45e5-87F6-0DA56B6A4F7D}';
/// On 64-bit systems, a link to the 32-bit common Program Files folder. A /// On 64-bit systems, a link to the 32-bit common Program Files folder. A
/// typical path is C:\Program Files (x86)\Common Files. On 32-bit systems, a /// typical path is C:\Program Files (x86)\Common Files. On 32-bit systems, a
/// link to the Common Program Files folder. /// link to the Common Program Files folder.
static String get ProgramFilesCommonX86 => FOLDERID_ProgramFilesCommonX86; static String get ProgramFilesCommonX86 =>
'{DE974D24-D9C6-4D3E-BF91-F4455120B917}';
/// On 64-bit systems, a link to the Program Files folder. A typical path is /// On 64-bit systems, a link to the Program Files folder. A typical path is
/// C:\Program Files. /// C:\Program Files.
static String get ProgramFilesX64 => FOLDERID_ProgramFilesX64; static String get ProgramFilesX64 => '{6D809377-6AF0-444b-8957-A3773F02200E}';
/// On 64-bit systems, a link to the 32-bit Program Files folder. A typical /// On 64-bit systems, a link to the 32-bit Program Files folder. A typical
/// path is C:\Program Files (x86). On 32-bit systems, a link to the Common /// path is C:\Program Files (x86). On 32-bit systems, a link to the Common
/// Program Files folder. /// Program Files folder.
static String get ProgramFilesX86 => FOLDERID_ProgramFilesX86; static String get ProgramFilesX86 => '{7C5A40EF-A0FB-4BFC-874A-C0F2E0B9FA8E}';
/// The file system directory that contains the user's program groups (which /// The file system directory that contains the user's program groups (which
/// are themselves file system directories). /// are themselves file system directories).
static String get Programs => FOLDERID_Programs; static String get Programs => '{A77F5D77-2E2B-44C3-A6A2-ABA601054A51}';
/// The file system directory that contains files and folders that appear on /// The file system directory that contains files and folders that appear on
/// the desktop for all users. A typical path is C:\Documents and Settings\All /// the desktop for all users. A typical path is C:\Documents and Settings\All
/// Users\Desktop. /// Users\Desktop.
static String get PublicDesktop => FOLDERID_PublicDesktop; static String get PublicDesktop => '{C4AA340D-F20F-4863-AFEF-F87EF2E6BA25}';
/// The file system directory that contains documents that are common to all /// The file system directory that contains documents that are common to all
/// users. A typical path is C:\Documents and Settings\All Users\Documents. /// users. A typical path is C:\Documents and Settings\All Users\Documents.
static String get PublicDocuments => FOLDERID_PublicDocuments; static String get PublicDocuments => '{ED4824AF-DCE4-45A8-81E2-FC7965083634}';
/// The file system directory that serves as a repository for music files /// The file system directory that serves as a repository for music files
/// common to all users. A typical path is C:\Documents and Settings\All /// common to all users. A typical path is C:\Documents and Settings\All
/// Users\Documents\My Music. /// Users\Documents\My Music.
static String get PublicMusic => FOLDERID_PublicMusic; static String get PublicMusic => '{3214FAB5-9757-4298-BB61-92A9DEAA44FF}';
/// The file system directory that serves as a repository for image files /// The file system directory that serves as a repository for image files
/// common to all users. A typical path is C:\Documents and Settings\All /// common to all users. A typical path is C:\Documents and Settings\All
/// Users\Documents\My Pictures. /// Users\Documents\My Pictures.
static String get PublicPictures => FOLDERID_PublicPictures; static String get PublicPictures => '{B6EBFB86-6907-413C-9AF7-4FC2ABF07CC5}';
/// The file system directory that serves as a repository for video files /// The file system directory that serves as a repository for video files
/// common to all users. A typical path is C:\Documents and Settings\All /// common to all users. A typical path is C:\Documents and Settings\All
/// Users\Documents\My Videos. /// Users\Documents\My Videos.
static String get PublicVideos => FOLDERID_PublicVideos; static String get PublicVideos => '{2400183A-6185-49FB-A2D8-4A392A602BA3}';
/// The file system directory that contains shortcuts to the user's most /// The file system directory that contains shortcuts to the user's most
/// recently used documents. A typical path is C:\Documents and /// recently used documents. A typical path is C:\Documents and
/// Settings\username\My Recent Documents. /// Settings\username\My Recent Documents.
static String get Recent => FOLDERID_Recent; static String get Recent => '{AE50C081-EBD2-438A-8655-8A092E34987A}';
/// The virtual folder that contains the objects in the user's Recycle Bin. /// The virtual folder that contains the objects in the user's Recycle Bin.
static String get RecycleBinFolder => FOLDERID_RecycleBinFolder; static String get RecycleBinFolder =>
'{B7534046-3ECB-4C18-BE4E-64CD4CB7D6AC}';
/// The file system directory that contains resource data. A typical path is /// The file system directory that contains resource data. A typical path is
/// C:\Windows\Resources. /// C:\Windows\Resources.
static String get ResourceDir => FOLDERID_ResourceDir; static String get ResourceDir => '{8AD10C31-2ADB-4296-A8F7-E4701232C972}';
/// The file system directory that serves as a common repository for /// The file system directory that serves as a common repository for
/// application-specific data. A typical path is C:\Documents and /// application-specific data. A typical path is C:\Documents and
/// Settings\username\Application Data. /// Settings\username\Application Data.
static String get RoamingAppData => FOLDERID_RoamingAppData; static String get RoamingAppData => '{3EB685DB-65F9-4CF6-A03A-E3EF65729F3D}';
/// The file system directory that contains Send To menu items. A typical path /// The file system directory that contains Send To menu items. A typical path
/// is C:\Documents and Settings\username\SendTo. /// is C:\Documents and Settings\username\SendTo.
static String get SendTo => FOLDERID_SendTo; static String get SendTo => '{8983036C-27C0-404B-8F08-102D10DCFD74}';
/// The file system directory that contains Start menu items. A typical path /// The file system directory that contains Start menu items. A typical path
/// is C:\Documents and Settings\username\Start Menu. /// is C:\Documents and Settings\username\Start Menu.
static String get StartMenu => FOLDERID_StartMenu; static String get StartMenu => '{625B53C3-AB48-4EC1-BA1F-A1EF4146FC19}';
/// The file system directory that corresponds to the user's Startup program /// The file system directory that corresponds to the user's Startup program
/// group. The system starts these programs whenever the associated user logs /// group. The system starts these programs whenever the associated user logs
/// on. A typical path is C:\Documents and Settings\username\Start /// on. A typical path is C:\Documents and Settings\username\Start
/// Menu\Programs\Startup. /// Menu\Programs\Startup.
static String get Startup => FOLDERID_Startup; static String get Startup => '{B97D20BB-F46A-4C97-BA10-5E3608430854}';
/// The Windows System folder. A typical path is C:\Windows\System32. /// The Windows System folder. A typical path is C:\Windows\System32.
static String get System => FOLDERID_System; static String get System => '{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}';
/// The 32-bit Windows System folder. On 32-bit systems, this is typically /// The 32-bit Windows System folder. On 32-bit systems, this is typically
/// C:\Windows\system32. On 64-bit systems, this is typically /// C:\Windows\system32. On 64-bit systems, this is typically
/// C:\Windows\syswow64. /// C:\Windows\syswow64.
static String get SystemX86 => FOLDERID_SystemX86; static String get SystemX86 => '{D65231B0-B2F1-4857-A4CE-A8E7C6EA7D27}';
/// The file system directory that serves as a common repository for document /// The file system directory that serves as a common repository for document
/// templates. A typical path is C:\Documents and Settings\username\Templates. /// templates. A typical path is C:\Documents and Settings\username\Templates.
static String get Templates => FOLDERID_Templates; static String get Templates => '{A63293E8-664E-48DB-A079-DF759E0509F7}';
/// The file system directory that serves as a common repository for video /// The file system directory that serves as a common repository for video
/// files. A typical path is C:\Documents and Settings\username\My /// files. A typical path is C:\Documents and Settings\username\My
/// Documents\My Videos. /// Documents\My Videos.
static String get Videos => FOLDERID_Videos; static String get Videos => '{18989B1D-99B5-455B-841C-AB7C74E4DDFC}';
/// The Windows directory or SYSROOT. This corresponds to the %windir% or /// The Windows directory or SYSROOT. This corresponds to the %windir% or
/// %SYSTEMROOT% environment variables. A typical path is C:\Windows. /// %SYSTEMROOT% environment variables. A typical path is C:\Windows.
static String get Windows => FOLDERID_Windows; static String get Windows => '{F38BF404-1D43-42F2-9305-67DE0B28FC23}';
} }

View File

@ -0,0 +1,51 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ffi';
import 'dart:typed_data';
/// Representation of the Win32 GUID struct.
// For the layout of this struct, see
// https://learn.microsoft.com/windows/win32/api/guiddef/ns-guiddef-guid
@Packed(4)
base class GUID extends Struct {
/// Native Data1 field.
@Uint32()
external int data1;
/// Native Data2 field.
@Uint16()
external int data2;
/// Native Data3 field.
@Uint16()
external int data3;
/// Native Data4 field.
// This should be an eight-element byte array, but there's no such annotation.
@Uint64()
external int data4;
/// Parses a GUID string, with optional enclosing "{}"s and optional "-"s,
/// into data.
void parse(String guid) {
final String hexOnly = guid.replaceAll(RegExp(r'[{}-]'), '');
if (hexOnly.length != 32) {
throw ArgumentError.value(guid, 'guid', 'Invalid GUID string');
}
final ByteData bytes = ByteData(16);
for (int i = 0; i < 16; ++i) {
bytes.setUint8(
i, int.parse(hexOnly.substring(i * 2, i * 2 + 2), radix: 16));
}
data1 = bytes.getInt32(0);
data2 = bytes.getInt16(4);
data3 = bytes.getInt16(6);
// [bytes] is big endian, but the host is little endian, so a default
// big-endian read would reverse the bytes. Since data4 is supposed to be
// a byte array, the order should be preserved, so do a little-endian read.
// https://en.wikipedia.org/wiki/Universally_unique_identifier#Encoding
data4 = bytes.getInt64(8, Endian.little);
}
}

View File

@ -7,11 +7,13 @@ import 'dart:io';
import 'package:ffi/ffi.dart'; import 'package:ffi/ffi.dart';
import 'package:flutter/foundation.dart' show visibleForTesting; import 'package:flutter/foundation.dart' show visibleForTesting;
import 'package:flutter/services.dart';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart';
import 'package:win32/win32.dart';
import 'folders.dart'; import 'folders.dart';
import 'guid.dart';
import 'win32_wrappers.dart';
/// Constant for en-US language used in VersionInfo keys. /// Constant for en-US language used in VersionInfo keys.
@visibleForTesting @visibleForTesting
@ -35,7 +37,7 @@ class VersionInfoQuerier {
/// language and encoding, or null if there is no such entry, /// language and encoding, or null if there is no such entry,
/// or if versionInfo is null. /// or if versionInfo is null.
/// ///
/// See https://docs.microsoft.com/en-us/windows/win32/menurc/versioninfo-resource /// See https://docs.microsoft.com/windows/win32/menurc/versioninfo-resource
/// for list of possible language and encoding values. /// for list of possible language and encoding values.
String? getStringValue( String? getStringValue(
Pointer<Uint8>? versionInfo, Pointer<Uint8>? versionInfo,
@ -49,7 +51,7 @@ class VersionInfoQuerier {
return null; return null;
} }
final Pointer<Utf16> keyPath = final Pointer<Utf16> keyPath =
TEXT('\\StringFileInfo\\$language$encoding\\$key'); '\\StringFileInfo\\$language$encoding\\$key'.toNativeUtf16();
final Pointer<UINT> length = calloc<UINT>(); final Pointer<UINT> length = calloc<UINT>();
final Pointer<Pointer<Utf16>> valueAddress = calloc<Pointer<Utf16>>(); final Pointer<Pointer<Utf16>> valueAddress = calloc<Pointer<Utf16>>();
try { try {
@ -89,7 +91,7 @@ class PathProviderWindows extends PathProviderPlatform {
if (length == 0) { if (length == 0) {
final int error = GetLastError(); final int error = GetLastError();
throw WindowsException(error); throw _createWin32Exception(error);
} else { } else {
path = buffer.toDartString(); path = buffer.toDartString();
@ -134,7 +136,7 @@ class PathProviderWindows extends PathProviderPlatform {
/// [WindowsKnownFolder]. /// [WindowsKnownFolder].
Future<String?> getPath(String folderID) { Future<String?> getPath(String folderID) {
final Pointer<Pointer<Utf16>> pathPtrPtr = calloc<Pointer<Utf16>>(); final Pointer<Pointer<Utf16>> pathPtrPtr = calloc<Pointer<Utf16>>();
final Pointer<GUID> knownFolderID = calloc<GUID>()..ref.setGUID(folderID); final Pointer<GUID> knownFolderID = calloc<GUID>()..ref.parse(folderID);
try { try {
final int hr = SHGetKnownFolderPath( final int hr = SHGetKnownFolderPath(
@ -146,7 +148,7 @@ class PathProviderWindows extends PathProviderPlatform {
if (FAILED(hr)) { if (FAILED(hr)) {
if (hr == E_INVALIDARG || hr == E_FAIL) { if (hr == E_INVALIDARG || hr == E_FAIL) {
throw WindowsException(hr); throw _createWin32Exception(hr);
} }
return Future<String?>.value(); return Future<String?>.value();
} }
@ -179,7 +181,8 @@ class PathProviderWindows extends PathProviderPlatform {
String? companyName; String? companyName;
String? productName; String? productName;
final Pointer<Utf16> moduleNameBuffer = wsalloc(MAX_PATH + 1); final Pointer<Utf16> moduleNameBuffer =
calloc<WCHAR>(MAX_PATH + 1).cast<Utf16>();
final Pointer<DWORD> unused = calloc<DWORD>(); final Pointer<DWORD> unused = calloc<DWORD>();
Pointer<BYTE>? infoBuffer; Pointer<BYTE>? infoBuffer;
try { try {
@ -188,7 +191,7 @@ class PathProviderWindows extends PathProviderPlatform {
GetModuleFileName(0, moduleNameBuffer, MAX_PATH); GetModuleFileName(0, moduleNameBuffer, MAX_PATH);
if (moduleNameLength == 0) { if (moduleNameLength == 0) {
final int error = GetLastError(); final int error = GetLastError();
throw WindowsException(error); throw _createWin32Exception(error);
} }
// From that, load the VERSIONINFO resource // From that, load the VERSIONINFO resource
@ -223,7 +226,7 @@ class PathProviderWindows extends PathProviderPlatform {
} }
/// Makes [rawString] safe as a directory component. See /// Makes [rawString] safe as a directory component. See
/// https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#naming-conventions /// https://docs.microsoft.com/windows/win32/fileio/naming-a-file#naming-conventions
/// ///
/// If after sanitizing the string is empty, returns null. /// If after sanitizing the string is empty, returns null.
String? _sanitizedDirectoryName(String? rawString) { String? _sanitizedDirectoryName(String? rawString) {
@ -263,3 +266,15 @@ class PathProviderWindows extends PathProviderPlatform {
return directory.path; return directory.path;
} }
} }
Exception _createWin32Exception(int errorCode) {
return PlatformException(
code: 'Win32 Error',
// TODO(stuartmorgan): Consider getting the system error message via
// FormatMessage if it turns out to be necessary for debugging issues.
// Plugin-client-level usability isn't a major consideration since per
// https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#platform-exception-handling
// any case that comes up in practice should be handled and returned
// via a plugin-specific exception, not this fallback.
message: 'Error code 0x${errorCode.toRadixString(16)}');
}

View File

@ -0,0 +1,112 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// The types and functions here correspond directly to corresponding Windows
// types and functions, so the Windows docs are the definitive source of
// documentation.
// ignore_for_file: public_member_api_docs
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'guid.dart';
typedef BOOL = Int32;
typedef BYTE = Uint8;
typedef DWORD = Uint32;
typedef UINT = Uint32;
typedef HANDLE = IntPtr;
typedef HMODULE = HANDLE;
typedef HRESULT = Int32;
typedef LPCVOID = Pointer<NativeType>;
typedef LPCWSTR = Pointer<Utf16>;
typedef LPDWORD = Pointer<DWORD>;
typedef LPWSTR = Pointer<Utf16>;
typedef LPVOID = Pointer<NativeType>;
typedef PUINT = Pointer<UINT>;
typedef PWSTR = Pointer<Pointer<Utf16>>;
typedef WCHAR = Uint16;
const int NULL = 0;
// https://learn.microsoft.com/windows/win32/fileio/maximum-file-path-limitation?tabs=registry
const int MAX_PATH = 260;
// https://learn.microsoft.com/windows/win32/seccrypto/common-hresult-values
// ignore: non_constant_identifier_names
final int E_FAIL = 0x80004005.toSigned(32);
// ignore: non_constant_identifier_names
final int E_INVALIDARG = 0x80070057.toSigned(32);
// https://learn.microsoft.com/windows/win32/api/winerror/nf-winerror-failed#remarks
// ignore: non_constant_identifier_names
bool FAILED(int hr) => hr < 0;
// https://learn.microsoft.com/windows/win32/api/shlobj_core/ne-shlobj_core-known_folder_flag
const int KF_FLAG_DEFAULT = 0x00000000;
final DynamicLibrary _dllKernel32 = DynamicLibrary.open('kernel32.dll');
final DynamicLibrary _dllVersion = DynamicLibrary.open('version.dll');
final DynamicLibrary _dllShell32 = DynamicLibrary.open('shell32.dll');
// https://learn.microsoft.com/windows/win32/api/shlobj_core/nf-shlobj_core-shgetknownfolderpath
typedef _FFITypeSHGetKnownFolderPath = HRESULT Function(
Pointer<GUID>, DWORD, HANDLE, PWSTR);
typedef FFITypeSHGetKnownFolderPathDart = int Function(
Pointer<GUID>, int, int, Pointer<Pointer<Utf16>>);
// ignore: non_constant_identifier_names
final FFITypeSHGetKnownFolderPathDart SHGetKnownFolderPath =
_dllShell32.lookupFunction<_FFITypeSHGetKnownFolderPath,
FFITypeSHGetKnownFolderPathDart>('SHGetKnownFolderPath');
// https://learn.microsoft.com/windows/win32/api/winver/nf-winver-getfileversioninfow
typedef _FFITypeGetFileVersionInfoW = BOOL Function(
LPCWSTR, DWORD, DWORD, LPVOID);
typedef FFITypeGetFileVersionInfoW = int Function(
Pointer<Utf16>, int, int, Pointer<NativeType>);
// ignore: non_constant_identifier_names
final FFITypeGetFileVersionInfoW GetFileVersionInfo = _dllVersion
.lookupFunction<_FFITypeGetFileVersionInfoW, FFITypeGetFileVersionInfoW>(
'GetFileVersionInfoW');
// https://learn.microsoft.com/windows/win32/api/winver/nf-winver-getfileversioninfosizew
typedef _FFITypeGetFileVersionInfoSizeW = DWORD Function(LPCWSTR, LPDWORD);
typedef FFITypeGetFileVersionInfoSizeW = int Function(
Pointer<Utf16>, Pointer<Uint32>);
// ignore: non_constant_identifier_names
final FFITypeGetFileVersionInfoSizeW GetFileVersionInfoSize =
_dllVersion.lookupFunction<_FFITypeGetFileVersionInfoSizeW,
FFITypeGetFileVersionInfoSizeW>('GetFileVersionInfoSizeW');
// https://learn.microsoft.com/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror
typedef _FFITypeGetLastError = DWORD Function();
typedef FFITypeGetLastError = int Function();
// ignore: non_constant_identifier_names
final FFITypeGetLastError GetLastError = _dllKernel32
.lookupFunction<_FFITypeGetLastError, FFITypeGetLastError>('GetLastError');
// https://learn.microsoft.com/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulefilenamew
typedef _FFITypeGetModuleFileNameW = DWORD Function(HMODULE, LPWSTR, DWORD);
typedef FFITypeGetModuleFileNameW = int Function(int, Pointer<Utf16>, int);
// ignore: non_constant_identifier_names
final FFITypeGetModuleFileNameW GetModuleFileName = _dllKernel32.lookupFunction<
_FFITypeGetModuleFileNameW,
FFITypeGetModuleFileNameW>('GetModuleFileNameW');
// https://learn.microsoft.com/windows/win32/api/winver/nf-winver-verqueryvaluew
typedef _FFITypeVerQueryValueW = BOOL Function(LPCVOID, LPCWSTR, LPVOID, PUINT);
typedef FFITypeVerQueryValueW = int Function(
Pointer<NativeType>, Pointer<Utf16>, Pointer<NativeType>, Pointer<Uint32>);
// ignore: non_constant_identifier_names
final FFITypeVerQueryValueW VerQueryValue =
_dllVersion.lookupFunction<_FFITypeVerQueryValueW, FFITypeVerQueryValueW>(
'VerQueryValueW');
// https://learn.microsoft.com/windows/win32/api/fileapi/nf-fileapi-gettemppathw
typedef _FFITypeGetTempPathW = DWORD Function(DWORD, LPWSTR);
typedef FFITypeGetTempPathW = int Function(int, Pointer<Utf16>);
// ignore: non_constant_identifier_names
final FFITypeGetTempPathW GetTempPath = _dllKernel32
.lookupFunction<_FFITypeGetTempPathW, FFITypeGetTempPathW>('GetTempPathW');

View File

@ -2,7 +2,7 @@ name: path_provider_windows
description: Windows implementation of the path_provider plugin description: Windows implementation of the path_provider plugin
repository: https://github.com/flutter/packages/tree/main/packages/path_provider/path_provider_windows repository: https://github.com/flutter/packages/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 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22
version: 2.2.1 version: 2.3.0
environment: environment:
sdk: ^3.2.0 sdk: ^3.2.0
@ -21,7 +21,6 @@ dependencies:
sdk: flutter sdk: flutter
path: ^1.8.0 path: ^1.8.0
path_provider_platform_interface: ^2.1.0 path_provider_platform_interface: ^2.1.0
win32: ">=2.1.0 <6.0.0"
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

View File

@ -0,0 +1,63 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ffi';
import 'dart:typed_data';
import 'package:ffi/ffi.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:path_provider_windows/src/guid.dart';
void main() {
test('has correct byte representation', () async {
final Pointer<GUID> guid = calloc<GUID>()
..ref.parse('{00112233-4455-6677-8899-aabbccddeeff}');
final ByteData data = ByteData(16)
..setInt32(0, guid.ref.data1, Endian.little)
..setInt16(4, guid.ref.data2, Endian.little)
..setInt16(6, guid.ref.data3, Endian.little)
..setInt64(8, guid.ref.data4, Endian.little);
expect(data.getUint8(0), 0x33);
expect(data.getUint8(1), 0x22);
expect(data.getUint8(2), 0x11);
expect(data.getUint8(3), 0x00);
expect(data.getUint8(4), 0x55);
expect(data.getUint8(5), 0x44);
expect(data.getUint8(6), 0x77);
expect(data.getUint8(7), 0x66);
expect(data.getUint8(8), 0x88);
expect(data.getUint8(9), 0x99);
expect(data.getUint8(10), 0xAA);
expect(data.getUint8(11), 0xBB);
expect(data.getUint8(12), 0xCC);
expect(data.getUint8(13), 0xDD);
expect(data.getUint8(14), 0xEE);
expect(data.getUint8(15), 0xFF);
calloc.free(guid);
});
test('handles alternate forms', () async {
final Pointer<GUID> guid1 = calloc<GUID>()
..ref.parse('{00112233-4455-6677-8899-aabbccddeeff}');
final Pointer<GUID> guid2 = calloc<GUID>()
..ref.parse('00112233445566778899AABBCCDDEEFF');
expect(guid1.ref.data1, guid2.ref.data1);
expect(guid1.ref.data2, guid2.ref.data2);
expect(guid1.ref.data3, guid2.ref.data3);
expect(guid1.ref.data4, guid2.ref.data4);
calloc.free(guid1);
calloc.free(guid2);
});
test('throws for bad data', () async {
final Pointer<GUID> guid = calloc<GUID>();
expect(() => guid.ref.parse('{00112233-4455-6677-88'), throwsArgumentError);
calloc.free(guid);
});
}

View File

@ -13,7 +13,6 @@
# cautious about adding to this list. # cautious about adding to this list.
- build_verify - build_verify
- google_maps - google_maps
- win32
## Allowed by default ## Allowed by default