fix: Récupérer l'opération active depuis la table operations

- Corrige l'erreur SQL 'Unknown column fk_operation in users'
- L'opération active est récupérée depuis operations.chk_active = 1
- Jointure avec users pour filtrer par entité de l'admin créateur
- Query: SELECT o.id FROM operations o INNER JOIN users u ON u.fk_entite = o.fk_entite WHERE u.id = ? AND o.chk_active = 1
This commit is contained in:
2026-01-26 16:57:08 +01:00
parent c24a3afe6a
commit 0687900564
3040 changed files with 77204 additions and 1578 deletions

View File

@@ -1 +0,0 @@
/home/pierre/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0/

View File

@@ -0,0 +1,66 @@
# Below is a list of people and organizations that have contributed
# to the Flutter project. Names should be added to the list like so:
#
# Name/Organization <email address>
Google Inc.
The Chromium Authors
German Saprykin <saprykin.h@gmail.com>
Benjamin Sauer <sauer.benjamin@gmail.com>
larsenthomasj@gmail.com
Ali Bitek <alibitek@protonmail.ch>
Pol Batlló <pol.batllo@gmail.com>
Anatoly Pulyaevskiy
Hayden Flinner <haydenflinner@gmail.com>
Stefano Rodriguez <hlsroddy@gmail.com>
Salvatore Giordano <salvatoregiordanoo@gmail.com>
Brian Armstrong <brian@flutter.institute>
Paul DeMarco <paulmdemarco@gmail.com>
Fabricio Nogueira <feufeu@gmail.com>
Simon Lightfoot <simon@devangels.london>
Ashton Thomas <ashton@acrinta.com>
Thomas Danner <thmsdnnr@gmail.com>
Diego Velásquez <diego.velasquez.lopez@gmail.com>
Hajime Nakamura <nkmrhj@gmail.com>
Tuyển Vũ Xuân <netsoft1985@gmail.com>
Miguel Ruivo <miguel@miguelruivo.com>
Sarthak Verma <sarthak@artiosys.com>
Mike Diarmid <mike@invertase.io>
Invertase <oss@invertase.io>
Elliot Hesp <elliot@invertase.io>
Vince Varga <vince.varga@smaho.com>
Aawaz Gyawali <awazgyawali@gmail.com>
EUI Limited <ian.evans3@admiralgroup.co.uk>
Katarina Sheremet <katarina@sheremet.ch>
Thomas Stockx <thomas@stockxit.com>
Sarbagya Dhaubanjar <sarbagyastha@gmail.com>
Ozkan Eksi <ozeksi@gmail.com>
Rishab Nayak <rishab@bu.edu>
ko2ic <ko2ic.dev@gmail.com>
Jonathan Younger <jonathan@daikini.com>
Jose Sanchez <josesm82@gmail.com>
Debkanchan Samadder <debu.samadder@gmail.com>
Audrius Karosevicius <audrius.karosevicius@gmail.com>
Lukasz Piliszczuk <lukasz@intheloup.io>
SoundReply Solutions GmbH <ch@soundreply.com>
Rafal Wachol <rwachol@gmail.com>
Pau Picas <pau.picas@gmail.com>
Christian Weder <chrstian.weder@yapeal.ch>
Alexandru Tuca <salexandru.tuca@outlook.com>
Christian Weder <chrstian.weder@yapeal.ch>
Rhodes Davis Jr. <rody.davis.jr@gmail.com>
Luigi Agosti <luigi@tengio.com>
Quentin Le Guennec <quentin@tengio.com>
Koushik Ravikumar <koushik@tengio.com>
Nissim Dsilva <nissim@tengio.com>
Giancarlo Rocha <giancarloiff@gmail.com>
Ryo Miyake <ryo@miyake.id>
Théo Champion <contact.theochampion@gmail.com>
Kazuki Yamaguchi <y.kazuki0614n@gmail.com>
Eitan Schwartz <eshvartz@gmail.com>
Chris Rutkowski <chrisrutkowski89@gmail.com>
Juan Alvarez <juan.alvarez@resideo.com>
Aleksandr Yurkovskiy <sanekyy@gmail.com>
Anton Borries <mail@antonborri.es>
Alex Li <google@alexv525.com>
Rahul Raj <64.rahulraj@gmail.com>

View File

@@ -0,0 +1,123 @@
## 2.3.0
* Replaces `win32` dependency with direct FFI usage.
* Updates minimum supported SDK version to Flutter 3.16/Dart 3.2.
## 2.2.1
* Adds pub topics to package metadata.
* Updates minimum supported SDK version to Flutter 3.7/Dart 2.19.
## 2.2.0
* Adds getApplicationCachePath() for storing app-specific cache files.
## 2.1.7
* Adds compatibility with `win32` 5.x.
* Updates minimum supported SDK version to Flutter 3.3/Dart 2.18.
## 2.1.6
* Adds compatibility with `win32` 4.x.
## 2.1.5
* Clarifies explanation of endorsement in README.
* Aligns Dart and Flutter SDK constraints.
## 2.1.4
* Updates links for the merge of flutter/plugins into flutter/packages.
* Updates minimum Flutter version to 3.0.
## 2.1.3
* Updates minimum Flutter version to 2.10.
* Adds compatibility with `package:win32` 3.x.
## 2.1.2
* Fixes avoid_redundant_argument_values lint warnings and minor typos.
## 2.1.1
* Updates dependency version of `package:win32` to 2.1.0.
## 2.1.0
* Upgrades `package:ffi` dependency to 2.0.0.
* Added support for unicode encoded VERSIONINFO.
* Minor fixes for new analysis options.
## 2.0.6
* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors
lint warnings.
## 2.0.5
* Removes dependency on `meta`.
## 2.0.4
* Removed obsolete `pluginClass: none` from pubpsec.
## 2.0.3
* Updated installation instructions in README.
## 2.0.2
* Add `implements` to pubspec.yaml.
* Add `registerWith()` to the Dart main class.
## 2.0.1
* Fix a crash when a known folder can't be located.
## 2.0.0
* Migrate to null safety
## 0.0.4+4
* Update Flutter SDK constraint.
## 0.0.4+3
* Remove unused `test` dependency.
* Update Dart SDK constraint in example.
## 0.0.4+2
* Check in windows/ directory for example/
## 0.0.4+1
* Add getPath to the stub, so that the analyzer won't complain about
fakes that override it.
* export 'folders.dart' rather than importing it, since it's intended to be
public.
## 0.0.4
* Move the actual implementation behind a conditional import, exporting
a stub for platforms that don't support FFI. Fixes web builds in
projects with transitive dependencies on path_provider.
## 0.0.3
* Add missing `pluginClass: none` for compatibilty with stable channel.
## 0.0.2
* README update for endorsement.
* Changed getApplicationSupportPath location.
* Removed getLibraryPath.
## 0.0.1+2
* The initial implementation of path_provider for Windows
* Implements getTemporaryPath, getApplicationSupportPath, getLibraryPath,
getApplicationDocumentsPath and getDownloadsPath.

View File

@@ -0,0 +1,25 @@
Copyright 2013 The Flutter Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,15 @@
# path\_provider\_windows
The Windows implementation of [`path_provider`][1].
## Usage
This package is [endorsed][2], which means you can simply use `path_provider`
normally. This package will be automatically included in your app when you do,
so you do not need to add it to your `pubspec.yaml`.
However, if you `import` this package to use any of its APIs directly, you
should add it to your `pubspec.yaml` as usual.
[1]: https://pub.dev/packages/path_provider
[2]: https://flutter.dev/to/endorsed-federated-plugin

View File

@@ -0,0 +1,9 @@
# Platform Implementation Test App
This is a test app for manual testing and automated integration testing
of this platform implementation. It is not intended to demonstrate actual use of
this package, since the intent is that plugin clients use the app-facing
package.
Unless you are making changes to this implementation package, this example is
very unlikely to be relevant.

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:io';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:path_provider_windows/path_provider_windows.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets('getTemporaryDirectory', (WidgetTester tester) async {
final PathProviderWindows provider = PathProviderWindows();
final String? result = await provider.getTemporaryPath();
_verifySampleFile(result, 'temporaryDirectory');
});
testWidgets('getApplicationDocumentsDirectory', (WidgetTester tester) async {
final PathProviderWindows provider = PathProviderWindows();
final String? result = await provider.getApplicationDocumentsPath();
_verifySampleFile(result, 'applicationDocuments');
});
testWidgets('getApplicationSupportDirectory', (WidgetTester tester) async {
final PathProviderWindows provider = PathProviderWindows();
final String? result = await provider.getApplicationSupportPath();
_verifySampleFile(result, 'applicationSupport');
});
testWidgets('getApplicationCacheDirectory', (WidgetTester tester) async {
final PathProviderWindows provider = PathProviderWindows();
final String? result = await provider.getApplicationCachePath();
_verifySampleFile(result, 'applicationCache');
});
testWidgets('getDownloadsDirectory', (WidgetTester tester) async {
final PathProviderWindows provider = PathProviderWindows();
final String? result = await provider.getDownloadsPath();
_verifySampleFile(result, 'downloads');
});
}
/// Verify a file called [name] in [directoryPath] by recreating it with test
/// contents when necessary.
void _verifySampleFile(String? directoryPath, String name) {
expect(directoryPath, isNotNull);
if (directoryPath == null) {
return;
}
final Directory directory = Directory(directoryPath);
final File file = File('${directory.path}${Platform.pathSeparator}$name');
if (file.existsSync()) {
file.deleteSync();
expect(file.existsSync(), isFalse);
}
file.writeAsStringSync('Hello world!');
expect(file.readAsStringSync(), 'Hello world!');
expect(directory.listSync(), isNotEmpty);
file.deleteSync();
}

View File

@@ -0,0 +1,103 @@
// 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.
// ignore_for_file: public_member_api_docs
import 'package:flutter/material.dart';
import 'package:path_provider_windows/path_provider_windows.dart';
void main() {
runApp(const MyApp());
}
/// Sample app
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String? _tempDirectory = 'Unknown';
String? _downloadsDirectory = 'Unknown';
String? _appSupportDirectory = 'Unknown';
String? _documentsDirectory = 'Unknown';
String? _cacheDirectory = 'Unknown';
@override
void initState() {
super.initState();
initDirectories();
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initDirectories() async {
String? tempDirectory;
String? downloadsDirectory;
String? appSupportDirectory;
String? documentsDirectory;
String? cacheDirectory;
final PathProviderWindows provider = PathProviderWindows();
try {
tempDirectory = await provider.getTemporaryPath();
} catch (exception) {
tempDirectory = 'Failed to get temp directory: $exception';
}
try {
downloadsDirectory = await provider.getDownloadsPath();
} catch (exception) {
downloadsDirectory = 'Failed to get downloads directory: $exception';
}
try {
documentsDirectory = await provider.getApplicationDocumentsPath();
} catch (exception) {
documentsDirectory = 'Failed to get documents directory: $exception';
}
try {
appSupportDirectory = await provider.getApplicationSupportPath();
} catch (exception) {
appSupportDirectory = 'Failed to get app support directory: $exception';
}
try {
cacheDirectory = await provider.getApplicationCachePath();
} catch (exception) {
cacheDirectory = 'Failed to get cache directory: $exception';
}
setState(() {
_tempDirectory = tempDirectory;
_downloadsDirectory = downloadsDirectory;
_appSupportDirectory = appSupportDirectory;
_documentsDirectory = documentsDirectory;
_cacheDirectory = cacheDirectory;
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Path Provider example app'),
),
body: Center(
child: Column(
children: <Widget>[
Text('Temp Directory: $_tempDirectory\n'),
Text('Documents Directory: $_documentsDirectory\n'),
Text('Downloads Directory: $_downloadsDirectory\n'),
Text('Application Support Directory: $_appSupportDirectory\n'),
Text('Cache Directory: $_cacheDirectory\n'),
],
),
),
),
);
}
}

View File

@@ -0,0 +1,27 @@
name: path_provider_example
description: Demonstrates how to use the path_provider plugin.
publish_to: none
environment:
sdk: ^3.2.0
flutter: ">=3.16.0"
dependencies:
flutter:
sdk: flutter
path_provider_windows:
# When depending on this package from a real application you should use:
# path_provider_windows: ^x.y.z
# See https://dart.dev/tools/pub/dependencies#version-constraints
# The example app is bundled with the plugin so we use a path dependency on
# the parent directory to use the current plugin's version.
path: ../
dev_dependencies:
flutter_test:
sdk: flutter
integration_test:
sdk: flutter
flutter:
uses-material-design: true

View File

@@ -0,0 +1,7 @@
// 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 'package:integration_test/integration_test_driver.dart';
Future<void> main() => integrationDriver();

View File

@@ -0,0 +1,95 @@
cmake_minimum_required(VERSION 3.15)
project(example LANGUAGES CXX)
set(BINARY_NAME "example")
cmake_policy(SET CMP0063 NEW)
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
# Configure build options.
get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(IS_MULTICONFIG)
set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release"
CACHE STRING "" FORCE)
else()
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE "Debug" CACHE
STRING "Flutter build mode" FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
"Debug" "Profile" "Release")
endif()
endif()
set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}")
set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}")
set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}")
# Use Unicode for all projects.
add_definitions(-DUNICODE -D_UNICODE)
# Compilation settings that should be applied to most targets.
function(APPLY_STANDARD_SETTINGS TARGET)
target_compile_features(${TARGET} PUBLIC cxx_std_17)
target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100")
target_compile_options(${TARGET} PRIVATE /EHsc)
target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0")
target_compile_definitions(${TARGET} PRIVATE "$<$<CONFIG:Debug>:_DEBUG>")
endfunction()
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
# Flutter library and tool build rules.
add_subdirectory(${FLUTTER_MANAGED_DIR})
# Application build
add_subdirectory("runner")
# Generated plugin build rules, which manage building the plugins and adding
# them to the application.
include(flutter/generated_plugins.cmake)
# === Installation ===
# Support files are copied into place next to the executable, so that it can
# run in place. This is done instead of making a separate bundle (as on Linux)
# so that building and running from within Visual Studio will work.
set(BUILD_BUNDLE_DIR "$<TARGET_FILE_DIR:${BINARY_NAME}>")
# Make the "install" step default, as it's required to run.
set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
endif()
set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}")
install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
COMPONENT Runtime)
install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
COMPONENT Runtime)
install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
if(PLUGIN_BUNDLED_LIBRARIES)
install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endif()
# Fully re-copy the assets directory on each build to avoid having stale files
# from a previous install.
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
install(CODE "
file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
" COMPONENT Runtime)
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
# Install the AOT library on non-Debug builds only.
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
CONFIGURATIONS Profile;Release
COMPONENT Runtime)

View File

@@ -0,0 +1,107 @@
cmake_minimum_required(VERSION 3.15)
set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
# Configuration provided via flutter tool.
include(${EPHEMERAL_DIR}/generated_config.cmake)
# TODO: Move the rest of this into files in ephemeral. See
# https://github.com/flutter/flutter/issues/57146.
set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
# Set fallback configurations for older versions of the flutter tool.
if (NOT DEFINED FLUTTER_TARGET_PLATFORM)
set(FLUTTER_TARGET_PLATFORM "windows-x64")
endif()
# === Flutter Library ===
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")
# Published to parent scope for install step.
set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE)
list(APPEND FLUTTER_LIBRARY_HEADERS
"flutter_export.h"
"flutter_windows.h"
"flutter_messenger.h"
"flutter_plugin_registrar.h"
)
list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/")
add_library(flutter INTERFACE)
target_include_directories(flutter INTERFACE
"${EPHEMERAL_DIR}"
)
target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib")
add_dependencies(flutter flutter_assemble)
# === Wrapper ===
list(APPEND CPP_WRAPPER_SOURCES_CORE
"core_implementations.cc"
"standard_codec.cc"
)
list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/")
list(APPEND CPP_WRAPPER_SOURCES_PLUGIN
"plugin_registrar.cc"
)
list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/")
list(APPEND CPP_WRAPPER_SOURCES_APP
"flutter_engine.cc"
"flutter_view_controller.cc"
)
list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/")
# Wrapper sources needed for a plugin.
add_library(flutter_wrapper_plugin STATIC
${CPP_WRAPPER_SOURCES_CORE}
${CPP_WRAPPER_SOURCES_PLUGIN}
)
apply_standard_settings(flutter_wrapper_plugin)
set_target_properties(flutter_wrapper_plugin PROPERTIES
POSITION_INDEPENDENT_CODE ON)
set_target_properties(flutter_wrapper_plugin PROPERTIES
CXX_VISIBILITY_PRESET hidden)
target_link_libraries(flutter_wrapper_plugin PUBLIC flutter)
target_include_directories(flutter_wrapper_plugin PUBLIC
"${WRAPPER_ROOT}/include"
)
add_dependencies(flutter_wrapper_plugin flutter_assemble)
# Wrapper sources needed for the runner.
add_library(flutter_wrapper_app STATIC
${CPP_WRAPPER_SOURCES_CORE}
${CPP_WRAPPER_SOURCES_APP}
)
apply_standard_settings(flutter_wrapper_app)
target_link_libraries(flutter_wrapper_app PUBLIC flutter)
target_include_directories(flutter_wrapper_app PUBLIC
"${WRAPPER_ROOT}/include"
)
add_dependencies(flutter_wrapper_app flutter_assemble)
# === Flutter tool backend ===
# _phony_ is a non-existent file to force this command to run every time,
# since currently there's no way to get a full input/output list from the
# flutter tool.
set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_")
set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE)
add_custom_command(
OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}
${CPP_WRAPPER_SOURCES_APP}
${PHONY_OUTPUT}
COMMAND ${CMAKE_COMMAND} -E env
${FLUTTER_TOOL_ENVIRONMENT}
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
${FLUTTER_TARGET_PLATFORM} $<CONFIG>
VERBATIM
)
add_custom_target(flutter_assemble DEPENDS
"${FLUTTER_LIBRARY}"
${FLUTTER_LIBRARY_HEADERS}
${CPP_WRAPPER_SOURCES_CORE}
${CPP_WRAPPER_SOURCES_PLUGIN}
${CPP_WRAPPER_SOURCES_APP}
)

View File

@@ -0,0 +1,23 @@
#
# Generated file, do not edit.
#
list(APPEND FLUTTER_PLUGIN_LIST
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
)
set(PLUGIN_BUNDLED_LIBRARIES)
foreach(plugin ${FLUTTER_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin})
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
endforeach(plugin)
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin})
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
endforeach(ffi_plugin)

View File

@@ -0,0 +1,18 @@
cmake_minimum_required(VERSION 3.15)
project(runner LANGUAGES CXX)
add_executable(${BINARY_NAME} WIN32
"flutter_window.cpp"
"main.cpp"
"run_loop.cpp"
"utils.cpp"
"win32_window.cpp"
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
"Runner.rc"
"runner.exe.manifest"
)
apply_standard_settings(${BINARY_NAME})
target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
add_dependencies(${BINARY_NAME} flutter_assemble)

View File

@@ -0,0 +1,121 @@
// Microsoft Visual C++ generated resource script.
//
#pragma code_page(65001)
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (United States) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""winres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_APP_ICON ICON "resources\\app_icon.ico"
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD)
#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD
#else
#define VERSION_AS_NUMBER 1,0,0,0
#endif
#if defined(FLUTTER_VERSION)
#define VERSION_AS_STRING FLUTTER_VERSION
#else
#define VERSION_AS_STRING "1.0.0"
#endif
VS_VERSION_INFO VERSIONINFO
FILEVERSION VERSION_AS_NUMBER
PRODUCTVERSION VERSION_AS_NUMBER
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
#ifdef _DEBUG
FILEFLAGS VS_FF_DEBUG
#else
FILEFLAGS 0x0L
#endif
FILEOS VOS__WINDOWS32
FILETYPE VFT_APP
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904e4"
BEGIN
VALUE "CompanyName", "com.example" "\0"
VALUE "FileDescription", "A new Flutter project." "\0"
VALUE "FileVersion", VERSION_AS_STRING "\0"
VALUE "InternalName", "example" "\0"
VALUE "LegalCopyright", "Copyright (C) 2020 com.example. All rights reserved." "\0"
VALUE "OriginalFilename", "example.exe" "\0"
VALUE "ProductName", "example" "\0"
VALUE "ProductVersion", VERSION_AS_STRING "\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1252
END
END
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

View File

@@ -0,0 +1,68 @@
// 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.
#include "flutter_window.h"
#include <optional>
#include "flutter/generated_plugin_registrant.h"
FlutterWindow::FlutterWindow(RunLoop* run_loop,
const flutter::DartProject& project)
: run_loop_(run_loop), project_(project) {}
FlutterWindow::~FlutterWindow() {}
bool FlutterWindow::OnCreate() {
if (!Win32Window::OnCreate()) {
return false;
}
RECT frame = GetClientArea();
// The size here must match the window dimensions to avoid unnecessary surface
// creation / destruction in the startup path.
flutter_controller_ = std::make_unique<flutter::FlutterViewController>(
frame.right - frame.left, frame.bottom - frame.top, project_);
// Ensure that basic setup of the controller was successful.
if (!flutter_controller_->engine() || !flutter_controller_->view()) {
return false;
}
RegisterPlugins(flutter_controller_->engine());
run_loop_->RegisterFlutterInstance(flutter_controller_->engine());
SetChildContent(flutter_controller_->view()->GetNativeWindow());
return true;
}
void FlutterWindow::OnDestroy() {
if (flutter_controller_) {
run_loop_->UnregisterFlutterInstance(flutter_controller_->engine());
flutter_controller_ = nullptr;
}
Win32Window::OnDestroy();
}
LRESULT
FlutterWindow::MessageHandler(HWND hwnd, UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept {
// Give Flutter, including plugins, an opporutunity to handle window messages.
if (flutter_controller_) {
std::optional<LRESULT> result =
flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,
lparam);
if (result) {
return *result;
}
}
switch (message) {
case WM_FONTCHANGE:
flutter_controller_->engine()->ReloadSystemFonts();
break;
}
return Win32Window::MessageHandler(hwnd, message, wparam, lparam);
}

View File

@@ -0,0 +1,43 @@
// 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.
#ifndef RUNNER_FLUTTER_WINDOW_H_
#define RUNNER_FLUTTER_WINDOW_H_
#include <flutter/dart_project.h>
#include <flutter/flutter_view_controller.h>
#include <memory>
#include "run_loop.h"
#include "win32_window.h"
// A window that does nothing but host a Flutter view.
class FlutterWindow : public Win32Window {
public:
// Creates a new FlutterWindow driven by the |run_loop|, hosting a
// Flutter view running |project|.
explicit FlutterWindow(RunLoop* run_loop,
const flutter::DartProject& project);
virtual ~FlutterWindow();
protected:
// Win32Window:
bool OnCreate() override;
void OnDestroy() override;
LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,
LPARAM const lparam) noexcept override;
private:
// The run loop driving events for this window.
RunLoop* run_loop_;
// The project to run.
flutter::DartProject project_;
// The Flutter instance hosted by this window.
std::unique_ptr<flutter::FlutterViewController> flutter_controller_;
};
#endif // RUNNER_FLUTTER_WINDOW_H_

View File

@@ -0,0 +1,40 @@
// 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.
#include <flutter/dart_project.h>
#include <flutter/flutter_view_controller.h>
#include <windows.h>
#include "flutter_window.h"
#include "run_loop.h"
#include "utils.h"
int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
_In_ wchar_t* command_line, _In_ int show_command) {
// Attach to console when present (e.g., 'flutter run') or create a
// new console when running with a debugger.
if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
CreateAndAttachConsole();
}
// Initialize COM, so that it is available for use in the library and/or
// plugins.
::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
RunLoop run_loop;
flutter::DartProject project(L"data");
FlutterWindow window(&run_loop, project);
Win32Window::Point origin(10, 10);
Win32Window::Size size(1280, 720);
if (!window.CreateAndShow(L"example", origin, size)) {
return EXIT_FAILURE;
}
window.SetQuitOnClose(true);
run_loop.Run();
::CoUninitialize();
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,16 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by Runner.rc
//
#define IDI_APP_ICON 101
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View File

@@ -0,0 +1,70 @@
// 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.
#include "run_loop.h"
#include <windows.h>
#include <algorithm>
RunLoop::RunLoop() {}
RunLoop::~RunLoop() {}
void RunLoop::Run() {
bool keep_running = true;
TimePoint next_flutter_event_time = TimePoint::clock::now();
while (keep_running) {
std::chrono::nanoseconds wait_duration =
std::max(std::chrono::nanoseconds(0),
next_flutter_event_time - TimePoint::clock::now());
::MsgWaitForMultipleObjects(
0, nullptr, FALSE, static_cast<DWORD>(wait_duration.count() / 1000),
QS_ALLINPUT);
bool processed_events = false;
MSG message;
// All pending Windows messages must be processed; MsgWaitForMultipleObjects
// won't return again for items left in the queue after PeekMessage.
while (::PeekMessage(&message, nullptr, 0, 0, PM_REMOVE)) {
processed_events = true;
if (message.message == WM_QUIT) {
keep_running = false;
break;
}
::TranslateMessage(&message);
::DispatchMessage(&message);
// Allow Flutter to process messages each time a Windows message is
// processed, to prevent starvation.
next_flutter_event_time =
std::min(next_flutter_event_time, ProcessFlutterMessages());
}
// If the PeekMessage loop didn't run, process Flutter messages.
if (!processed_events) {
next_flutter_event_time =
std::min(next_flutter_event_time, ProcessFlutterMessages());
}
}
}
void RunLoop::RegisterFlutterInstance(
flutter::FlutterEngine* flutter_instance) {
flutter_instances_.insert(flutter_instance);
}
void RunLoop::UnregisterFlutterInstance(
flutter::FlutterEngine* flutter_instance) {
flutter_instances_.erase(flutter_instance);
}
RunLoop::TimePoint RunLoop::ProcessFlutterMessages() {
TimePoint next_event_time = TimePoint::max();
for (auto instance : flutter_instances_) {
std::chrono::nanoseconds wait_duration = instance->ProcessMessages();
if (wait_duration != std::chrono::nanoseconds::max()) {
next_event_time =
std::min(next_event_time, TimePoint::clock::now() + wait_duration);
}
}
return next_event_time;
}

View File

@@ -0,0 +1,42 @@
// 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.
#ifndef RUNNER_RUN_LOOP_H_
#define RUNNER_RUN_LOOP_H_
#include <flutter/flutter_engine.h>
#include <chrono>
#include <set>
// A runloop that will service events for Flutter instances as well
// as native messages.
class RunLoop {
public:
RunLoop();
~RunLoop();
// Prevent copying
RunLoop(RunLoop const&) = delete;
RunLoop& operator=(RunLoop const&) = delete;
// Runs the run loop until the application quits.
void Run();
// Registers the given Flutter instance for event servicing.
void RegisterFlutterInstance(flutter::FlutterEngine* flutter_instance);
// Unregisters the given Flutter instance from event servicing.
void UnregisterFlutterInstance(flutter::FlutterEngine* flutter_instance);
private:
using TimePoint = std::chrono::steady_clock::time_point;
// Processes all currently pending messages for registered Flutter instances.
TimePoint ProcessFlutterMessages();
std::set<flutter::FlutterEngine*> flutter_instances_;
};
#endif // RUNNER_RUN_LOOP_H_

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
</windowsSettings>
</application>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
</application>
</compatibility>
</assembly>

View File

@@ -0,0 +1,26 @@
// 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.
#include "utils.h"
#include <flutter_windows.h>
#include <io.h>
#include <stdio.h>
#include <windows.h>
#include <iostream>
void CreateAndAttachConsole() {
if (::AllocConsole()) {
FILE* unused;
if (freopen_s(&unused, "CONOUT$", "w", stdout)) {
_dup2(_fileno(stdout), 1);
}
if (freopen_s(&unused, "CONOUT$", "w", stderr)) {
_dup2(_fileno(stdout), 2);
}
std::ios::sync_with_stdio();
FlutterDesktopResyncOutputStreams();
}
}

View File

@@ -0,0 +1,12 @@
// 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.
#ifndef RUNNER_UTILS_H_
#define RUNNER_UTILS_H_
// Creates a console for the process, and redirects stdout and stderr to
// it for both the runner and the Flutter library.
void CreateAndAttachConsole();
#endif // RUNNER_UTILS_H_

View File

@@ -0,0 +1,240 @@
// 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.
#include "win32_window.h"
#include <flutter_windows.h>
#include "resource.h"
namespace {
constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
// The number of Win32Window objects that currently exist.
static int g_active_window_count = 0;
using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);
// Scale helper to convert logical scaler values to physical using passed in
// scale factor
int Scale(int source, double scale_factor) {
return static_cast<int>(source * scale_factor);
}
// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.
// This API is only needed for PerMonitor V1 awareness mode.
void EnableFullDpiSupportIfAvailable(HWND hwnd) {
HMODULE user32_module = LoadLibraryA("User32.dll");
if (!user32_module) {
return;
}
auto enable_non_client_dpi_scaling =
reinterpret_cast<EnableNonClientDpiScaling*>(
GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
if (enable_non_client_dpi_scaling != nullptr) {
enable_non_client_dpi_scaling(hwnd);
FreeLibrary(user32_module);
}
}
} // namespace
// Manages the Win32Window's window class registration.
class WindowClassRegistrar {
public:
~WindowClassRegistrar() = default;
// Returns the singleton registar instance.
static WindowClassRegistrar* GetInstance() {
if (!instance_) {
instance_ = new WindowClassRegistrar();
}
return instance_;
}
// Returns the name of the window class, registering the class if it hasn't
// previously been registered.
const wchar_t* GetWindowClass();
// Unregisters the window class. Should only be called if there are no
// instances of the window.
void UnregisterWindowClass();
private:
WindowClassRegistrar() = default;
static WindowClassRegistrar* instance_;
bool class_registered_ = false;
};
WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr;
const wchar_t* WindowClassRegistrar::GetWindowClass() {
if (!class_registered_) {
WNDCLASS window_class{};
window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
window_class.lpszClassName = kWindowClassName;
window_class.style = CS_HREDRAW | CS_VREDRAW;
window_class.cbClsExtra = 0;
window_class.cbWndExtra = 0;
window_class.hInstance = GetModuleHandle(nullptr);
window_class.hIcon =
LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
window_class.hbrBackground = 0;
window_class.lpszMenuName = nullptr;
window_class.lpfnWndProc = Win32Window::WndProc;
RegisterClass(&window_class);
class_registered_ = true;
}
return kWindowClassName;
}
void WindowClassRegistrar::UnregisterWindowClass() {
UnregisterClass(kWindowClassName, nullptr);
class_registered_ = false;
}
Win32Window::Win32Window() { ++g_active_window_count; }
Win32Window::~Win32Window() {
--g_active_window_count;
Destroy();
}
bool Win32Window::CreateAndShow(const std::wstring& title, const Point& origin,
const Size& size) {
Destroy();
const wchar_t* window_class =
WindowClassRegistrar::GetInstance()->GetWindowClass();
const POINT target_point = {static_cast<LONG>(origin.x),
static_cast<LONG>(origin.y)};
HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);
UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);
double scale_factor = dpi / 96.0;
HWND window = CreateWindow(
window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE,
Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
Scale(size.width, scale_factor), Scale(size.height, scale_factor),
nullptr, nullptr, GetModuleHandle(nullptr), this);
if (!window) {
return false;
}
return OnCreate();
}
// static
LRESULT CALLBACK Win32Window::WndProc(HWND const window, UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept {
if (message == WM_NCCREATE) {
auto window_struct = reinterpret_cast<CREATESTRUCT*>(lparam);
SetWindowLongPtr(window, GWLP_USERDATA,
reinterpret_cast<LONG_PTR>(window_struct->lpCreateParams));
auto that = static_cast<Win32Window*>(window_struct->lpCreateParams);
EnableFullDpiSupportIfAvailable(window);
that->window_handle_ = window;
} else if (Win32Window* that = GetThisFromHandle(window)) {
return that->MessageHandler(window, message, wparam, lparam);
}
return DefWindowProc(window, message, wparam, lparam);
}
LRESULT
Win32Window::MessageHandler(HWND hwnd, UINT const message, WPARAM const wparam,
LPARAM const lparam) noexcept {
switch (message) {
case WM_DESTROY:
window_handle_ = nullptr;
Destroy();
if (quit_on_close_) {
PostQuitMessage(0);
}
return 0;
case WM_DPICHANGED: {
auto newRectSize = reinterpret_cast<RECT*>(lparam);
LONG newWidth = newRectSize->right - newRectSize->left;
LONG newHeight = newRectSize->bottom - newRectSize->top;
SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth,
newHeight, SWP_NOZORDER | SWP_NOACTIVATE);
return 0;
}
case WM_SIZE:
RECT rect = GetClientArea();
if (child_content_ != nullptr) {
// Size and position the child window.
MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left,
rect.bottom - rect.top, TRUE);
}
return 0;
case WM_ACTIVATE:
if (child_content_ != nullptr) {
SetFocus(child_content_);
}
return 0;
}
return DefWindowProc(window_handle_, message, wparam, lparam);
}
void Win32Window::Destroy() {
OnDestroy();
if (window_handle_) {
DestroyWindow(window_handle_);
window_handle_ = nullptr;
}
if (g_active_window_count == 0) {
WindowClassRegistrar::GetInstance()->UnregisterWindowClass();
}
}
Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {
return reinterpret_cast<Win32Window*>(
GetWindowLongPtr(window, GWLP_USERDATA));
}
void Win32Window::SetChildContent(HWND content) {
child_content_ = content;
SetParent(content, window_handle_);
RECT frame = GetClientArea();
MoveWindow(content, frame.left, frame.top, frame.right - frame.left,
frame.bottom - frame.top, true);
SetFocus(child_content_);
}
RECT Win32Window::GetClientArea() {
RECT frame;
GetClientRect(window_handle_, &frame);
return frame;
}
HWND Win32Window::GetHandle() { return window_handle_; }
void Win32Window::SetQuitOnClose(bool quit_on_close) {
quit_on_close_ = quit_on_close;
}
bool Win32Window::OnCreate() {
// No-op; provided for subclasses.
return true;
}
void Win32Window::OnDestroy() {
// No-op; provided for subclasses.
}

View File

@@ -0,0 +1,99 @@
// 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.
#ifndef RUNNER_WIN32_WINDOW_H_
#define RUNNER_WIN32_WINDOW_H_
#include <windows.h>
#include <functional>
#include <memory>
#include <string>
// A class abstraction for a high DPI-aware Win32 Window. Intended to be
// inherited from by classes that wish to specialize with custom
// rendering and input handling
class Win32Window {
public:
struct Point {
unsigned int x;
unsigned int y;
Point(unsigned int x, unsigned int y) : x(x), y(y) {}
};
struct Size {
unsigned int width;
unsigned int height;
Size(unsigned int width, unsigned int height)
: width(width), height(height) {}
};
Win32Window();
virtual ~Win32Window();
// Creates and shows a win32 window with |title| and position and size using
// |origin| and |size|. New windows are created on the default monitor. Window
// sizes are specified to the OS in physical pixels, hence to ensure a
// consistent size to will treat the width height passed in to this function
// as logical pixels and scale to appropriate for the default monitor. Returns
// true if the window was created successfully.
bool CreateAndShow(const std::wstring& title, const Point& origin,
const Size& size);
// Release OS resources associated with window.
void Destroy();
// Inserts |content| into the window tree.
void SetChildContent(HWND content);
// Returns the backing Window handle to enable clients to set icon and other
// window properties. Returns nullptr if the window has been destroyed.
HWND GetHandle();
// If true, closing this window will quit the application.
void SetQuitOnClose(bool quit_on_close);
// Return a RECT representing the bounds of the current client area.
RECT GetClientArea();
protected:
// Processes and route salient window messages for mouse handling,
// size change and DPI. Delegates handling of these to member overloads that
// inheriting classes can handle.
virtual LRESULT MessageHandler(HWND window, UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept;
// Called when CreateAndShow is called, allowing subclass window-related
// setup. Subclasses should return false if setup fails.
virtual bool OnCreate();
// Called when Destroy is called.
virtual void OnDestroy();
private:
friend class WindowClassRegistrar;
// OS callback called by message pump. Handles the WM_NCCREATE message which
// is passed when the non-client area is being created and enables automatic
// non-client DPI scaling so that the non-client area automatically
// responsponds to changes in DPI. All other messages are handled by
// MessageHandler.
static LRESULT CALLBACK WndProc(HWND const window, UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept;
// Retrieves a class instance pointer for |window|
static Win32Window* GetThisFromHandle(HWND const window) noexcept;
bool quit_on_close_ = false;
// window handle for top level window.
HWND window_handle_ = nullptr;
// window handle for hosted content.
HWND child_content_ = nullptr;
};
#endif // RUNNER_WIN32_WINDOW_H_

View File

@@ -0,0 +1,10 @@
// 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.
// path_provider_windows is implemented using FFI; export a stub for platforms
// that don't support FFI (e.g., web) to avoid having transitive dependencies
// break web compilation.
export 'src/folders_stub.dart' if (dart.library.ffi) 'src/folders.dart';
export 'src/path_provider_windows_stub.dart'
if (dart.library.ffi) 'src/path_provider_windows_real.dart';

View File

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

View File

@@ -0,0 +1,6 @@
// 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.
/// Stub version of the actual class.
class WindowsKnownFolder {}

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

@@ -0,0 +1,280 @@
// 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:io';
import 'package:ffi/ffi.dart';
import 'package:flutter/foundation.dart' show visibleForTesting;
import 'package:flutter/services.dart';
import 'package:path/path.dart' as path;
import 'package:path_provider_platform_interface/path_provider_platform_interface.dart';
import 'folders.dart';
import 'guid.dart';
import 'win32_wrappers.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 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/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;
}
final Pointer<Utf16> keyPath =
'\\StringFileInfo\\$language$encoding\\$key'.toNativeUtf16();
final Pointer<UINT> length = calloc<UINT>();
final Pointer<Pointer<Utf16>> valueAddress = calloc<Pointer<Utf16>>();
try {
if (VerQueryValue(versionInfo, keyPath, valueAddress, length) == 0) {
return null;
}
return valueAddress.value.toDartString();
} finally {
calloc.free(keyPath);
calloc.free(length);
calloc.free(valueAddress);
}
}
}
/// The Windows implementation of [PathProviderPlatform]
///
/// This class implements the `package:path_provider` functionality for Windows.
class PathProviderWindows extends PathProviderPlatform {
/// Registers the Windows implementation.
static void registerWith() {
PathProviderPlatform.instance = PathProviderWindows();
}
/// The object to use for performing VerQueryValue calls.
@visibleForTesting
VersionInfoQuerier versionInfoQuerier = VersionInfoQuerier();
/// This is typically the same as the TMP environment variable.
@override
Future<String?> getTemporaryPath() async {
final Pointer<Utf16> buffer = calloc<Uint16>(MAX_PATH + 1).cast<Utf16>();
String path;
try {
final int length = GetTempPath(MAX_PATH, buffer);
if (length == 0) {
final int error = GetLastError();
throw _createWin32Exception(error);
} else {
path = buffer.toDartString();
// GetTempPath adds a trailing backslash, but SHGetKnownFolderPath does
// not. Strip off trailing backslash for consistency with other methods
// here.
if (path.endsWith(r'\')) {
path = path.substring(0, path.length - 1);
}
}
// Ensure that the directory exists, since GetTempPath doesn't.
final Directory directory = Directory(path);
if (!directory.existsSync()) {
await directory.create(recursive: true);
}
return path;
} finally {
calloc.free(buffer);
}
}
@override
Future<String?> getApplicationSupportPath() =>
_createApplicationSubdirectory(WindowsKnownFolder.RoamingAppData);
@override
Future<String?> getApplicationDocumentsPath() =>
getPath(WindowsKnownFolder.Documents);
@override
Future<String?> getApplicationCachePath() =>
_createApplicationSubdirectory(WindowsKnownFolder.LocalAppData);
@override
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 Pointer<Pointer<Utf16>> pathPtrPtr = calloc<Pointer<Utf16>>();
final Pointer<GUID> knownFolderID = calloc<GUID>()..ref.parse(folderID);
try {
final int hr = SHGetKnownFolderPath(
knownFolderID,
KF_FLAG_DEFAULT,
NULL,
pathPtrPtr,
);
if (FAILED(hr)) {
if (hr == E_INVALIDARG || hr == E_FAIL) {
throw _createWin32Exception(hr);
}
return Future<String?>.value();
}
final String path = pathPtrPtr.value.toDartString();
return Future<String>.value(path);
} finally {
calloc.free(pathPtrPtr);
calloc.free(knownFolderID);
}
}
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.
///
/// The convention is to use company-name\product-name\. This will use that if
/// possible, using the data in the VERSIONINFO resource, with the following
/// fallbacks:
/// - If the company name isn't there, that component will be dropped.
/// - If the product name isn't there, it will use the exe's filename (without
/// extension).
String _getApplicationSpecificSubdirectory() {
String? companyName;
String? productName;
final Pointer<Utf16> moduleNameBuffer =
calloc<WCHAR>(MAX_PATH + 1).cast<Utf16>();
final Pointer<DWORD> unused = calloc<DWORD>();
Pointer<BYTE>? infoBuffer;
try {
// Get the module name.
final int moduleNameLength =
GetModuleFileName(0, moduleNameBuffer, MAX_PATH);
if (moduleNameLength == 0) {
final int error = GetLastError();
throw _createWin32Exception(error);
}
// From that, load the VERSIONINFO resource
final int infoSize = GetFileVersionInfoSize(moduleNameBuffer, unused);
if (infoSize != 0) {
infoBuffer = calloc<BYTE>(infoSize);
if (GetFileVersionInfo(moduleNameBuffer, 0, infoSize, infoBuffer) ==
0) {
calloc.free(infoBuffer);
infoBuffer = null;
}
}
companyName =
_sanitizedDirectoryName(_getStringValue(infoBuffer, 'CompanyName'));
productName =
_sanitizedDirectoryName(_getStringValue(infoBuffer, 'ProductName'));
// If there was no product name, use the executable name.
productName ??=
path.basenameWithoutExtension(moduleNameBuffer.toDartString());
return companyName != null
? path.join(companyName, productName)
: productName;
} finally {
calloc.free(moduleNameBuffer);
calloc.free(unused);
if (infoBuffer != null) {
calloc.free(infoBuffer);
}
}
}
/// Makes [rawString] safe as a directory component. See
/// https://docs.microsoft.com/windows/win32/fileio/naming-a-file#naming-conventions
///
/// If after sanitizing the string is empty, returns null.
String? _sanitizedDirectoryName(String? rawString) {
if (rawString == null) {
return null;
}
String sanitized = rawString
// Replace banned characters.
.replaceAll(RegExp(r'[<>:"/\\|?*]'), '_')
// Remove trailing whitespace.
.trimRight()
// Ensure that it does not end with a '.'.
.replaceAll(RegExp(r'[.]+$'), '');
const int kMaxComponentLength = 255;
if (sanitized.length > kMaxComponentLength) {
sanitized = sanitized.substring(0, kMaxComponentLength);
}
return sanitized.isEmpty ? null : sanitized;
}
Future<String?> _createApplicationSubdirectory(String folderId) async {
final String? baseDir = await getPath(folderId);
if (baseDir == null) {
return null;
}
final Directory directory =
Directory(path.join(baseDir, _getApplicationSpecificSubdirectory()));
// Ensure that the directory exists if possible, since it will on other
// platforms. If the name is longer than MAXPATH, creating will fail, so
// skip that step; it's up to the client to decide what to do with the path
// in that case (e.g., using a short path).
if (directory.path.length <= MAX_PATH) {
if (!directory.existsSync()) {
await directory.create(recursive: true);
}
}
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,34 @@
// 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 'package:path_provider_platform_interface/path_provider_platform_interface.dart';
/// A stub implementation to satisfy compilation of multi-platform packages that
/// depend on path_provider_windows. This should never actually be created.
///
/// Notably, because path_provider needs to manually register
/// path_provider_windows, anything with a transitive dependency on
/// path_provider will also depend on path_provider_windows, not just at the
/// pubspec level but the code level.
class PathProviderWindows extends PathProviderPlatform {
/// Errors on attempted instantiation of the stub. It exists only to satisfy
/// compile-time dependencies, and should never actually be created.
PathProviderWindows() : assert(false);
/// Registers the Windows implementation.
static void registerWith() {
PathProviderPlatform.instance = PathProviderWindows();
}
/// Stub; see comment on VersionInfoQuerier.
VersionInfoQuerier versionInfoQuerier = VersionInfoQuerier();
/// Match PathProviderWindows so that the analyzer won't report invalid
/// overrides if tests provide fake PathProviderWindows implementations.
Future<String> getPath(String folderID) async => '';
}
/// Stub to satisfy the analyzer, which doesn't seem to handle conditional
/// exports correctly.
class VersionInfoQuerier {}

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

@@ -0,0 +1,32 @@
name: path_provider_windows
description: Windows implementation of the path_provider plugin
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
version: 2.3.0
environment:
sdk: ^3.2.0
flutter: ">=3.16.0"
flutter:
plugin:
implements: path_provider
platforms:
windows:
dartPluginClass: PathProviderWindows
dependencies:
ffi: ^2.0.0
flutter:
sdk: flutter
path: ^1.8.0
path_provider_platform_interface: ^2.1.0
dev_dependencies:
flutter_test:
sdk: flutter
topics:
- files
- path-provider
- paths

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

@@ -0,0 +1,190 @@
// 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:io';
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 encodingCP1252, encodingUnicode, languageEn;
// A fake VersionInfoQuerier that just returns preset responses.
class FakeVersionInfoQuerier implements VersionInfoQuerier {
FakeVersionInfoQuerier(
this.responses, {
this.language = languageEn,
this.encoding = encodingUnicode,
});
final String language;
final String encoding;
final Map<String, String> responses;
// ignore: unreachable_from_main
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() {
test('registered instance', () {
PathProviderWindows.registerWith();
expect(PathProviderPlatform.instance, isA<PathProviderWindows>());
});
test('getTemporaryPath', () async {
final PathProviderWindows pathProvider = PathProviderWindows();
expect(await pathProvider.getTemporaryPath(), contains(r'C:\'));
}, skip: !Platform.isWindows);
test('getApplicationSupportPath with no version info', () async {
final PathProviderWindows pathProvider = PathProviderWindows();
pathProvider.versionInfoQuerier =
FakeVersionInfoQuerier(<String, String>{});
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 full version info in CP1252', () async {
final PathProviderWindows pathProvider = PathProviderWindows();
pathProvider.versionInfoQuerier = FakeVersionInfoQuerier(<String, String>{
'CompanyName': 'A Company',
'ProductName': 'Amazing App',
}, encoding: encodingCP1252);
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 Unicode', () async {
final PathProviderWindows pathProvider = PathProviderWindows();
pathProvider.versionInfoQuerier = FakeVersionInfoQuerier(<String, String>{
'CompanyName': 'A Company',
'ProductName': 'Amazing App',
});
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>{
'ProductName': 'Amazing App',
});
final String? path = await pathProvider.getApplicationSupportPath();
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 {
final PathProviderWindows pathProvider = PathProviderWindows();
pathProvider.versionInfoQuerier = FakeVersionInfoQuerier(<String, String>{
'CompanyName': r'A <Bad> Company: Name.',
'ProductName': r'A"/Terrible\|App?*Name',
});
final String? path = await pathProvider.getApplicationSupportPath();
expect(path, isNotNull);
if (path != null) {
expect(
path,
endsWith(
r'AppData\Roaming\A _Bad_ Company_ Name\A__Terrible__App__Name'));
expect(Directory(path).existsSync(), isTrue);
}
}, skip: !Platform.isWindows);
test('getApplicationSupportPath with a completely invalid company', () async {
final PathProviderWindows pathProvider = PathProviderWindows();
pathProvider.versionInfoQuerier = FakeVersionInfoQuerier(<String, String>{
'CompanyName': r'..',
'ProductName': r'Amazing App',
});
final String? path = await pathProvider.getApplicationSupportPath();
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 {
final PathProviderWindows pathProvider = PathProviderWindows();
final String truncatedName = 'A' * 255;
pathProvider.versionInfoQuerier = FakeVersionInfoQuerier(<String, String>{
'CompanyName': 'A Company',
'ProductName': truncatedName * 2,
});
final String? path = await pathProvider.getApplicationSupportPath();
expect(path, endsWith('\\$truncatedName'));
// The directory won't exist, since it's longer than MAXPATH, so don't check
// that here.
}, skip: !Platform.isWindows);
test('getApplicationDocumentsPath', () async {
final PathProviderWindows pathProvider = PathProviderWindows();
final String? path = await pathProvider.getApplicationDocumentsPath();
expect(path, contains(r'C:\'));
expect(path, contains(r'Documents'));
}, skip: !Platform.isWindows);
test('getApplicationCachePath', () async {
final PathProviderWindows pathProvider = PathProviderWindows();
pathProvider.versionInfoQuerier = FakeVersionInfoQuerier(<String, String>{
'CompanyName': 'A Company',
'ProductName': 'Amazing App',
}, encoding: encodingCP1252);
final String? path = await pathProvider.getApplicationCachePath();
expect(path, isNotNull);
if (path != null) {
expect(path, endsWith(r'AppData\Local\A Company\Amazing App'));
expect(Directory(path).existsSync(), isTrue);
}
}, skip: !Platform.isWindows);
test('getDownloadsPath', () async {
final PathProviderWindows pathProvider = PathProviderWindows();
final String? path = await pathProvider.getDownloadsPath();
expect(path, contains(r'C:\'));
expect(path, contains(r'Downloads'));
}, skip: !Platform.isWindows);
}