From 3740ee4daf01dbc68e3e32d6602623ae659c68cc Mon Sep 17 00:00:00 2001 From: Kristen McWilliam <9575627+Merrit@users.noreply.github.com> Date: Wed, 2 Oct 2024 15:54:44 -0400 Subject: [PATCH 1/2] chore: automatic migration for windows files Flutter applied these changes automatically when running on Windows. --- pubspec.lock | 32 ++++++++++++++++---------------- windows/flutter/CMakeLists.txt | 7 ++++++- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 5902671d..a7c7d367 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -98,10 +98,10 @@ packages: dependency: "direct dev" description: name: build_runner - sha256: dd09dd4e2b078992f42aac7f1a622f01882a8492fef08486b27ddde929c19f04 + sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d" url: "https://pub.dev" source: hosted - version: "2.4.12" + version: "2.4.13" build_runner_core: dependency: transitive description: @@ -210,10 +210,10 @@ packages: dependency: transitive description: name: dart_style - sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9" + sha256: "7856d364b589d1f08986e140938578ed36ed948581fbc3bc9aef1805039ac5ab" url: "https://pub.dev" source: hosted - version: "2.3.6" + version: "2.3.7" dbus: dependency: transitive description: @@ -640,10 +640,10 @@ packages: dependency: transitive description: name: mime - sha256: "801fd0b26f14a4a58ccb09d5892c3fbdeff209594300a542492cf13fba9d247a" + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" url: "https://pub.dev" source: hosted - version: "1.0.6" + version: "2.0.0" mockito: dependency: "direct dev" description: @@ -880,10 +880,10 @@ packages: dependency: transitive description: name: shelf_static - sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e + sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3 url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.3" shelf_web_socket: dependency: transitive description: @@ -1029,10 +1029,10 @@ packages: dependency: "direct main" description: name: tray_manager - sha256: c9a63fd88bd3546287a7eb8ccc978d707eef82c775397af17dda3a4f4c039e64 + sha256: bdc3ac6c36f3d12d871459e4a9822705ce5a1165a17fa837103bc842719bf3f7 url: "https://pub.dev" source: hosted - version: "0.2.3" + version: "0.2.4" typed_data: dependency: transitive description: @@ -1077,10 +1077,10 @@ packages: dependency: transitive description: name: url_launcher_macos - sha256: "9a1a42d5d2d95400c795b2914c36fdcb525870c752569438e4ebb09a2b5d90de" + sha256: "769549c999acdb42b8bcfa7c43d72bf79a382ca7441ab18a808e101149daf672" url: "https://pub.dev" source: hosted - version: "3.2.0" + version: "3.2.1" url_launcher_platform_interface: dependency: transitive description: @@ -1173,18 +1173,18 @@ packages: dependency: "direct main" description: name: win32 - sha256: "68d1e89a91ed61ad9c370f9f8b6effed9ae5e0ede22a270bdfa6daf79fc2290a" + sha256: "4d45dc9069dba4619dc0ebd93c7cec5e66d8482cb625a370ac806dcc8165f2ec" url: "https://pub.dev" source: hosted - version: "5.5.4" + version: "5.5.5" win32_registry: dependency: transitive description: name: win32_registry - sha256: "723b7f851e5724c55409bb3d5a32b203b3afe8587eaf5dafb93a5fed8ecda0d6" + sha256: "21ec76dfc731550fd3e2ce7a33a9ea90b828fdf19a5c3bcf556fa992cfa99852" url: "https://pub.dev" source: hosted - version: "1.1.4" + version: "1.1.5" win32_suspend_process: dependency: "direct main" description: diff --git a/windows/flutter/CMakeLists.txt b/windows/flutter/CMakeLists.txt index 744f08a9..0a917772 100644 --- a/windows/flutter/CMakeLists.txt +++ b/windows/flutter/CMakeLists.txt @@ -9,6 +9,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # 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") @@ -90,7 +95,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ + ${FLUTTER_TARGET_PLATFORM} $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS From cf171b8e98a73535d378c975063b610043b68009 Mon Sep 17 00:00:00 2001 From: Kristen McWilliam <9575627+Merrit@users.noreply.github.com> Date: Wed, 2 Oct 2024 19:43:58 -0400 Subject: [PATCH 2/2] feat(windows): suspend entire process tree Suspend / resume child processes recursively on Windows, the same as we do on Linux. Improves reliability and performance gains for processes that spawn many child processes. --- .../process/repository/src/win32/win32.dart | 53 +++++++++++++++++++ .../src/win32_process_repository.dart | 47 ++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 lib/native_platform/src/process/repository/src/win32/win32.dart diff --git a/lib/native_platform/src/process/repository/src/win32/win32.dart b/lib/native_platform/src/process/repository/src/win32/win32.dart new file mode 100644 index 00000000..ab5ab37a --- /dev/null +++ b/lib/native_platform/src/process/repository/src/win32/win32.dart @@ -0,0 +1,53 @@ +// ignore_for_file: non_constant_identifier_names, constant_identifier_names + +import 'dart:convert'; +import 'dart:ffi'; + +final _kernel32 = DynamicLibrary.open('kernel32.dll'); + +final CreateToolhelp32Snapshot = + _kernel32.lookupFunction( + 'CreateToolhelp32Snapshot'); + +final Process32First = _kernel32.lookupFunction< + Int32 Function(IntPtr hSnapshot, Pointer lppe), + int Function(int hSnapshot, Pointer lppe)>('Process32First'); + +final Process32Next = _kernel32.lookupFunction< + Int32 Function(IntPtr hSnapshot, Pointer lppe), + int Function(int hSnapshot, Pointer lppe)>('Process32Next'); + +final class PROCESSENTRY32 extends Struct { + @Int32() + external int dwSize; + @Int32() + external int cntUsage; + @Int32() + external int th32ProcessID; + external Pointer th32DefaultHeapID; + @Int32() + external int th32ModuleID; + @Int32() + external int cntThreads; + @Int32() + external int th32ParentProcessID; + @Int32() + external int pcPriClassBase; + @Int32() + external int dwFlags; + @Array(260) + external Array _szExeFile; + String get szExeFile => _unwrap(_szExeFile); +} + +String _unwrap(Array bytes) { + String buf = ""; + int i = 0; + while (bytes[i] != 0) { + buf += utf8.decode([bytes[i]]); + i += 1; + } + return buf; +} + +const TH32CS_SNAPPROCESS = 0x00000002; diff --git a/lib/native_platform/src/process/repository/src/win32_process_repository.dart b/lib/native_platform/src/process/repository/src/win32_process_repository.dart index 82b93453..4d41c04b 100644 --- a/lib/native_platform/src/process/repository/src/win32_process_repository.dart +++ b/lib/native_platform/src/process/repository/src/win32_process_repository.dart @@ -7,6 +7,7 @@ import 'package:win32_suspend_process/win32_suspend_process.dart'; import '../../../../../logs/logs.dart'; import '../../process.dart'; +import 'win32/win32.dart'; /// Provides interaction access with host system processes on Windows. class Win32ProcessRepository extends ProcessRepository { @@ -101,6 +102,15 @@ class Win32ProcessRepository extends ProcessRepository { final successful = (result == 0); log.i('Resuming $pid was successful: $successful'); CloseHandle(processHandle); + + if (successful) { + // Resume child processes recursively. + final childPids = await _getChildProcesses(pid); + for (final childPid in childPids) { + await resume(childPid); + } + } + return successful; } @@ -116,9 +126,46 @@ class Win32ProcessRepository extends ProcessRepository { final successful = (result == 0); log.i('Suspending $pid was successful: $successful'); CloseHandle(processHandle); + + if (successful) { + // Suspend child processes recursively. + final childPids = await _getChildProcesses(pid); + for (final childPid in childPids) { + await suspend(childPid); + } + } + return successful; } + /// Returns a list of child processes for the provided [pid]. + Future> _getChildProcesses(int pid) async { + final childPids = []; + final snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (snapshot == INVALID_HANDLE_VALUE) { + log.w('Failed to create snapshot: ${GetLastError()}'); + return childPids; + } + + final processEntry = calloc(); + processEntry.ref.dwSize = sizeOf(); + + final isProcessFound = Process32First(snapshot, processEntry) == TRUE; + if (isProcessFound) { + do { + if (processEntry.ref.th32ParentProcessID == pid) { + childPids.add(processEntry.ref.th32ProcessID); + } + } while (Process32Next(snapshot, processEntry) == TRUE); + } else { + log.w('Failed to retrieve first process: ${GetLastError()}'); + } + + CloseHandle(snapshot); + calloc.free(processEntry); + return childPids; + } + /// Returns true if the process is suspended, false otherwise. bool _isProcessSuspended(int pid) { return _isProcessSuspendedNative(pid) == 1;