import 'dart:async'; import 'dart:io'; import 'dart:convert'; import 'dart:math'; import 'dart:typed_data'; import 'package:dart_framework/charset/euc_kr.dart'; class ProcessData { late Process process; late File file; late bool isRunning; late int exitCode; Function(ProcessData)? _endListener; late StringBuffer stdout; late StringBuffer stderr; bool _enableStdout; bool _enableStderr; // ignore: no_leading_underscores_for_local_identifiers ProcessData(this._enableStdout, this._enableStderr); Function(ProcessData)? get endListener { return _endListener; } bool get isSuccess { return ProcessExecutor.getSuccess(exitCode); } List get stdoutArr { return getBufferToArray(stdout); } List get stderrArr { return getBufferToArray(stderr); } List getBufferToArray(StringBuffer buffer) { var list = []; var arr = buffer.toString().split('\n'); var length = arr.length; for (var i = 0; i < length; ++i) { if (arr[i].isNotEmpty) { list.add(arr[i]); } } return list; } set processValue(Process value) { process = value; stdout = StringBuffer(); stderr = StringBuffer(); process.stdout.transform(utf8.decoder).listen((event) { stdout.writeln(event); if (_enableStdout) { print(event); } }); process.stderr.transform(utf8.decoder).listen((event) { stderr.writeln(event); if (_enableStderr) { print('======================= $event'); } }); isRunning = true; checkExit(); } void addEndListener(Function(ProcessData) listener) { _endListener = listener; } void removeEndListener() { _endListener = null; } void checkExit() async { exitCode = await process.exitCode; deleteFile(); isRunning = false; if (_endListener != null) _endListener!(this); } void terminate() { process.kill(); } void deleteFile() async { if (await file.exists()) { await file.delete(); } } void dispose() async { deleteFile(); } } class ProcessExecutor { static Future startExe(String exe, List args, {String? workspace, bool? printStdout, bool? printStderr}) async { var c = Completer(); var arg = '$exe ${args.join(' ')}'; var processData = ProcessData(printStdout ?? true, printStderr ?? true); if (Platform.isMacOS || Platform.isWindows) { workspace = workspace ?? Directory.systemTemp.path; print('Execute Shell: $arg'); try { processData.processValue = await Process.start(exe, args, workingDirectory: workspace); } on Exception catch (e) { print(e); } } else { print('This platform not support process executor.'); } c.complete(processData); return c.future; } static Future start(StringBuffer shell, {String? workspace, bool? printStdout, bool? printStderr}) async { var c = Completer(); try { var processData = ProcessData(printStdout ?? true, printStderr ?? true); if (Platform.isMacOS || Platform.isLinux) { workspace = workspace ?? Directory.systemTemp.path; var file = File('$workspace/$tempFileName'); processData.file = file; print('Execute Shell: ${file.path} \n ${shell.toString()}'); if (await file.exists()) { await file.delete(); } file.createSync(); file.writeAsStringSync(shell.toString()); try { processData.processValue = await Process.start('zsh', [file.path], workingDirectory: workspace, runInShell: false); } on Exception catch (e) { print(e); } } else if (Platform.isWindows) { workspace = workspace ?? Directory.systemTemp.path; var file = File('$workspace/$tempFileName'); processData.file = file; print('Execute Shell: ${file.path} \n ${shell.toString()}'); if (await file.exists()) { await file.delete(); } file.createSync(); file.writeAsStringSync(shell.toString()); try { processData.processValue = await Process.start( 'cmd', ['/C', file.path], workingDirectory: workspace, runInShell: false); } on Exception catch (e) { print(e); } } else { print('This platform not support process executor.'); } c.complete(processData); } on Exception catch (e) { print('General Exception in ProcessExecutor.start: $e'); } return c.future; } static Future run(StringBuffer shell, {String? workspace, bool printStdout = true}) async { var c = Completer(); ProcessResult? processResult; if (Platform.isMacOS || Platform.isLinux) { workspace = workspace ?? Directory.systemTemp.path; var file = File('$workspace/$tempFileName'); print('Execute Shell: ${file.path} \n ${shell.toString()}'); file.createSync(); file.writeAsStringSync(shell.toString()); try { await Process.run('zsh', [file.path], workingDirectory: workspace) .then((ProcessResult result) { processResult = result; }); if (printStdout) { print( // ignore: prefer_interpolation_to_compose_strings '[ProcessExecutor] Exit Code: ${processResult!.exitCode}\nResult: ${processResult!.stdout}\nError: ${processResult!.stderr}'); } } on Exception catch (e) { print(e); } finally { file.deleteSync(); } c.complete(processResult); } else if (Platform.isWindows) { workspace = workspace ?? Directory.systemTemp.path; var file = File('$workspace/$tempFileName'); print('Execute Bat: ${file.path} \n ${shell.toString()}'); file.createSync(); file.writeAsStringSync(shell.toString()); await Process.run('cmd', ['/C', file.path], workingDirectory: workspace) .then((ProcessResult result) { processResult = result; }); if (printStdout) { print( // ignore: prefer_interpolation_to_compose_strings '[ProcessExecutor] Exit Code: ${processResult!.exitCode}\n' + '==============================================================\n' + 'Result: ${eucKr.decode(processResult!.stdout)}\n' + '==============================================================\n' + 'Error: ${eucKr.decode(processResult!.stderr)}'); } file.deleteSync(); c.complete(processResult); } else { print('This platform not support process executor.'); c.complete(null); } return c.future; } static String get tempFileName { if (Platform.isMacOS || Platform.isLinux) { return 'temp_${DateTime.now().microsecondsSinceEpoch}.sh'; } else { return 'temp_${DateTime.now().microsecondsSinceEpoch}.bat'; } } static bool getSuccess(int exitCode) { if (Platform.isMacOS || Platform.isLinux) return exitCode == 0; if (Platform.isWindows) return exitCode == 1; return false; } } class NetStatData { late String type; late String from; late String to; late String state; late int pid; NetStatData(List categories) { var count = 0; var length = categories.length; for (var i = 0; i < length; ++i) { var category = categories[i].replaceAll(' ', ''); if (category.isNotEmpty) { switch (count) { case 0: type = category; break; case 1: from = category; break; case 2: to = category; break; case 3: state = category; break; case 4: var value = int.tryParse(category); value ??= -1; pid = value; break; } count++; } } } } bool _removedBat = false; Future removeTempBat() async { var c = Completer(); if (!_removedBat) { _removedBat = true; var reg = RegExp(r'(temp_bat_)([0-9_]+)(.bat)'); var list = Directory.current.listSync(); for (var item in list) { var match = reg.stringMatch(item.path); if (match != null) { await item.delete(); } } await Future.delayed(Duration(milliseconds: 500)); } c.complete(); return c.future; } Future> findNetStat(String searchWord) async { await removeTempBat(); var c = Completer>(); var shell = StringBuffer('netstat -ano | findstr $searchWord'); var stdout = await ProcessSilentExecutor.run(shell); var netList = []; var returnPattern = RegExp('\n|\r'); var list = stdout.split(returnPattern); for (var item in list) { item = item.replaceAll(returnPattern, ''); if (item.isNotEmpty) { var categories = item.split(' '); if (categories.isNotEmpty) { netList.add(NetStatData(categories)); } } } c.complete(netList); return c.future; } Future> findProcess(String searchWord) async { await removeTempBat(); var c = Completer>(); var shell = StringBuffer('tasklist /fo "csv" | findstr $searchWord'); var stdout = await ProcessSilentExecutor.run(shell); var pList = parseProcessStd(stdout); c.complete(pList); return c.future; } List parseProcessStd(String stdout) { var pList = []; var returnPattern = RegExp('\n|\r'); var list = stdout.split(returnPattern); for (var item in list) { item = item.replaceAll(returnPattern, ''); if (item.isNotEmpty) { var categories = item.split(','); if (categories.length > 4) { pList.add(ProcessInfo(categories)); } } } return pList; } Future> findProcessByPid(int pid) async { // await removeTempBat(); var c = Completer>(); var batch = getTempBat(fixedName: pid.toString()); if (!batch.existsSync()) { batch.writeAsStringSync( 'tasklist /fi "pid eq ${pid.toString()}" /nh /fo "csv"'); } String stdout = await ProcessSilentExecutor.runBat(batch.path); var pList = parseProcessStd(stdout); c.complete(pList); return c.future; } class ProcessSilentExecutor { static bool _initialized = false; static Future initialize() async { if (!_initialized) { _initialized = true; //remove before files var regBat = RegExp(r'(silent_bat_)([0-9_]+)(.bat)'); var regLog = RegExp(r'(silent_log_)([0-9_]+)(.txt)'); var list = Directory.current.listSync(); for (var item in list) { var matchBat = regBat.stringMatch(item.path); var matchLog = regLog.stringMatch(item.path); if (matchBat != null || matchLog != null) { item.deleteSync(); } } } } static Future run(StringBuffer shell) async { initialize(); var c = Completer(); var batch = File('${Directory.current.path}/silent_bat_${getRnadomPostfix()}.bat'); batch.writeAsStringSync(shell.toString()); var result = await runBat(batch.path); await batch.delete(); c.complete(result); return c.future; } static Future runBat(String batchPath) async { initialize(); var c = Completer(); var current = Directory.current; String result; var log = File('${current.path}/silent_log_${getRnadomPostfix()}.txt'); await Process.run('${current.path}/silentbatch.exe', [batchPath, log.path]) .then((resultData) { Process.killPid(resultData.pid); // print('"$batchPath" => exitCode: ${resultData.exitCode}'); }); try { result = await log.readAsString(); } on Exception catch (e) { print(e); result = ''; } await log.delete(); c.complete(result); return c.future; } } String getRnadomPostfix() { return '${DateTime.now().microsecondsSinceEpoch}_${Random().nextInt(100)}'; } File getTempBat({String? fixedName}) { fixedName ??= getRnadomPostfix(); File batch = File('${Directory.current.path}/temp_bat_$fixedName.bat'); return batch; } class ProcessInfoListener { late ProcessInfo? _process; bool isRunning = false; late Timer? _timer; late final int _checkInterval; late Function(ProcessInfoListener, bool)? _connectionListener; set _isRunning(bool value) { if (isRunning != value) { isRunning = value; changedConnection(); } } ProcessInfoListener(this._process, this._checkInterval) { checkLive(); } void checkLive() async { stopChecker(); _isRunning = true; _timer = Timer.periodic(Duration(seconds: _checkInterval), (time) async { var enable = false; if (_process != null) { var list = await findProcessByPid(_process!.pid); if (list.isNotEmpty) { print('process live'); enable = true; _isRunning = true; } } if (!enable) { _isRunning = false; stopChecker(); } }); } void stopChecker() { if (_timer != null) { _timer!.cancel(); _timer = null; } } void changedConnection() { if (_connectionListener != null) { _connectionListener!(this, isRunning); } } Function(ProcessInfoListener, bool)? get endListener { return _connectionListener; } void addEndListener(Function(ProcessInfoListener, bool)? listener) { _connectionListener = listener; } void removeConnectionListener() { _connectionListener = null; } void dispose() { stopChecker(); removeConnectionListener(); _isRunning = false; _process = null; } void terminate() { if (isRunning && _process != null) { Process.killPid(_process?.pid as int); } } } class ProcessInfo { late String name; late int pid; late String sessionName; late int session; late String memory; ProcessInfo(List categories) { var count = 0; var length = categories.length; for (var i = 0; i < length; ++i) { var category = categories[i].replaceAll('"', ''); if (category.isNotEmpty) { switch (count) { case 0: name = category; break; case 1: var value = int.tryParse(category); value ??= -1; pid = value; break; case 2: sessionName = category; break; case 3: var value = int.tryParse(category); value ??= -1; session = value; break; case 4: memory = category; break; } count++; } } } }