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;