diff --git a/.vscode/launch.json b/.vscode/launch.json index 857c250..216e382 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,9 +5,16 @@ "version": "0.2.0", "configurations": [ { - "name": "dart_framework", + "name": "Dart", + "type": "dart", "request": "launch", - "type": "dart" + "program": "bin/main.dart" + }, + { + "name": "Unit test", + "request": "launch", + "type": "dart", + "args": ["test"] } ] } \ No newline at end of file diff --git a/bin/main.dart b/bin/main.dart index ab7ce49..5870a52 100644 --- a/bin/main.dart +++ b/bin/main.dart @@ -1,16 +1,23 @@ import 'dart:io'; -import 'package:dart_framework/platform/process.dart'; import 'package:dart_framework/subtitle/parser_smi.dart'; import 'package:dart_framework/subtitle/parser_srt.dart'; +import 'package:dart_framework/utils/os_startup.dart'; +import 'package:dart_framework/utils/system_util.dart'; import 'package:path/path.dart' as path; -import 'package:dart_framework/platform/isolate_manager.dart' as isolate; import 'package:dart_framework/core/application.dart'; import 'package:dart_framework/charset.dart'; void main() async { Application('dartframework', 'com.toki-labs.dartframework', () async { - isolate.test(); + // print(await OSStartup.unregistStartup(appIdendifier)); + + // print(await OSStartup.isRegistedStartup(appIdendifier)); + + print(await OSStartup.registStartup( + Platform.resolvedExecutable, appIdendifier, + logStandardFilePath: '$dataPath\\startup_log.txt', + logErrorFilePath: '$dataPath\\startup_err.txt')); // var md5 = generateMd5('test'); // print('$md5 / ${md5.length}'); @@ -55,3 +62,90 @@ void testParse() { } } } + + +/* +@override + Future isRegistedStartup(String label) async { + var shell = 'Get-ScheduledTask | Where-Object {\$_.TaskName -eq "$label"}'; + var process = await Process.run('powershell.exe', [shell]); + var exist = false; + if (process.exitCode == 0) { + var existLine = []; + var stdoutList = process.stdout.toString().split('\n'); + for (var item in stdoutList) { + var result = item.trim(); + if (result.isNotEmpty) { + existLine.add(item); + } + } + if (existLine.length > 2) { + exist = true; + } + } + return dataFutrue(exist); + } + + //shell file must have '#!/bin/sh' + @override + Future registStartup(String executableFilePath, String label, + {List? arguments, + String? logStandardFilePath, + String? logErrorFilePath}) async { + var scriptPath = '${dataPath.replaceAll('/', '\\')}\\$label.ps1'; + var startupScript = startupScriptTemplate.replaceAll( + '{EXECUTABLE_PATH}', executableFilePath.replaceAll('/', '\\')); + var args = ''; + if (arguments != null) { + args = '''-ArgumentList "${arguments.join(' ')}" ` + '''; + } + startupScript = startupScript.replaceAll('{ARGUMENTS}', args); + var startupFile = File(scriptPath); + startupFile.createSync(recursive: true); + startupFile.writeAsStringSync(startupScript, flush: true); + + var schedulerScript = schedulerScriptTemplate + .replaceAll('{SCRIPT_PATH}', scriptPath) + .replaceAll('{TASK_NAME}', label); + + var logStdout = ''; + if (logStandardFilePath != null) { + logStdout = ' > ${logStandardFilePath.replaceAll('/', '\\')}'; + } + schedulerScript = schedulerScript.replaceAll('{LOG_STDOUT}', logStdout); + + var logStderr = ''; + if (logErrorFilePath != null) { + logStderr = ' 2> ${logErrorFilePath.replaceAll('/', '\\')}'; + } + schedulerScript = schedulerScript.replaceAll('{LOG_STDERR}', logStderr); + + var tempFile = + File('${Directory.systemTemp.path}\\${ProcessExecutor.tempFileName}'); + tempFile.createSync(recursive: true); + tempFile.writeAsStringSync(schedulerScript, flush: true); + + var powershellCommand = + 'Start-Process powershell.exe -ArgumentList "-NoProfile -ExecutionPolicy Bypass -Command `& \'${tempFile.path}\'; exit" -Verb RunAs -WindowStyle Hidden'; + + var process = + await Process.run('powershell', ['-Command', powershellCommand]); + + print(process.stdout.toString()); + print(process.stderr.toString()); + print(process.exitCode); + + return dataFutrue(process.exitCode == 0); + } + + @override + Future unregistStartup(String label) async { + var shell = 'Unregister-ScheduledTask -TaskName "$label" -Confirm:\$false'; + var process = await Process.run('powershell.exe', [shell]); + print(process.stdout.toString()); + print(process.stderr.toString()); + print(process.exitCode); + return dataFutrue(process.exitCode == 0); + } + */ \ No newline at end of file diff --git a/lib/platform/process.dart b/lib/platform/process.dart index 71397a1..1199f16 100644 --- a/lib/platform/process.dart +++ b/lib/platform/process.dart @@ -5,8 +5,6 @@ import 'dart:io'; import 'dart:convert'; import 'dart:math'; -import 'package:args/args.dart'; -import 'package:dart_framework/cp949.dart'; import 'package:dart_framework/log/log.dart'; import 'package:dart_framework/utils/system_util.dart'; @@ -129,6 +127,26 @@ class ProcessData { } class ProcessExecutor { + static bool _powershellGranted = false; + static Future getPowershellGrant() async { + if (!_powershellGranted) { + _powershellGranted = true; + var result = await Process.run( + 'powershell.exe', + [ + '-Command', + 'Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned' + ], + runInShell: true, + ); + + if (result.stderr.toString().trim().isNotEmpty) { + print('[Powershell Grant] Error: ${result.stderr}'); + } + } + return simpleFuture; + } + static Future startExe(String exe, List args, {String? workspace, bool? printStdout, @@ -156,26 +174,6 @@ class ProcessExecutor { return dataFutrue(processData); } - static bool _powershellGranted = false; - static Future getPowershellGrant() async { - if (!_powershellGranted) { - _powershellGranted = true; - var result = await Process.run( - 'powershell.exe', - [ - '-Command', - 'Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned' - ], - runInShell: true, - ); - - if (result.stderr.toString().trim().isNotEmpty) { - print('[Powershell Grant] Error: ${result.stderr}'); - } - } - return simpleFuture; - } - static Future start(StringBuffer shell, {Converter, String>? decoder, String? workspace, @@ -227,6 +225,9 @@ class ProcessExecutor { } file.createSync(); file.writeAsStringSync(shell.toString(), flush: true); + + await getPowershellGrant(); + try { processData.process = await Process.start( 'powershell.exe', ['-NoProfile', '-File', file.absolute.path]); @@ -285,6 +286,8 @@ class ProcessExecutor { file.createSync(); file.writeAsStringSync(shell.toString()); + print(file.absolute.path); + await getPowershellGrant(); processResult = await Process.run( @@ -456,7 +459,7 @@ Future> findProcess(String searchWord) async { var shell = StringBuffer( 'Get-CimInstance -ClassName Win32_Process | Where-Object { \$_.Name.Contains("$searchWord") } | Select-Object CommandLine, ProcessId | Export-Csv -Path ${csvPath.absolute.path} -NoTypeInformation'); var result = await ProcessExecutor.run(shell); - if (csvPath.existsSync()) { + if (result.exitCode == 0 && csvPath.existsSync()) { pList = parseProcessStd(csvPath.readAsStringSync()); csvPath.deleteSync(); } diff --git a/lib/utils/os_startup.dart b/lib/utils/os_startup.dart index 71762d7..af0a3a1 100644 --- a/lib/utils/os_startup.dart +++ b/lib/utils/os_startup.dart @@ -3,7 +3,6 @@ import 'dart:async'; import 'package:dart_framework/utils/path.dart'; import 'package:dart_framework/utils/system_util.dart'; -import 'package:path/path.dart' as path; import 'package:dart_framework/platform/process.dart'; class OSStartup { @@ -140,16 +139,17 @@ fi final appDir = Directory('$userPath/Library/$label'); final file = File('${appDir.path}/launch.sh'); - if(!appDir.existsSync()) { + if (!appDir.existsSync()) { appDir.createSync(recursive: true); } var param = arguments == null ? '' : ' ${arguments.join(' ')}'; - var launchContent = pathTemplate.replaceAll('{COMMAND}', '$executableFilePath$param'); + var launchContent = + pathTemplate.replaceAll('{COMMAND}', '$executableFilePath$param'); file.writeAsStringSync(launchContent); //create plist in LaunchAgents - + final tagLogStandard = '{LOG_STANDARD}'; final tagLogError = '{LOG_ERROR}'; var plistContent = plistTemplate @@ -171,8 +171,8 @@ fi await plistFile.create(); await plistFile.writeAsString(plistContent); - var result = await ProcessExecutor.run( - StringBuffer('chmod +x "$executableFilePath" && chmod +x "${file.path}" ')); + var result = await ProcessExecutor.run(StringBuffer( + 'chmod +x "$executableFilePath" && chmod +x "${file.path}" ')); success = result.exitCode == 0; return dataFutrue(success); @@ -291,35 +291,50 @@ WantedBy=default.target"""; /// ************* WINDOWS *************** class _StartupWindows extends _Startup { - final windowsStartupPath = - '\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\'; + final startupScriptTemplate = '''Start-Process -FilePath "{EXECUTABLE_PATH}" ` + {ARGUMENTS}-WindowStyle Hidden{LOG_STDOUT}{LOG_STDERR} ` + -Wait'''; - final windowRoamingPath = '\\AppData\\Roaming\\'; + final schedulerScriptTemplate = ''' +# 실행할 PS1 스크립트 경로 +\$ScriptPath = "{SCRIPT_PATH}" - final vbsTemplate = """Set WshShell = CreateObject("WScript.Shell") -WshShell.Run chr(34) & "{FILE_PATH}" & Chr(34) & {PARAMETERS}, 0 -Set WshShell = Nothing -"""; +# 작업 이름 설정 +\$TaskName = "{TASK_NAME}" + +# 트리거 정의 (사용자 로그온 시 실행) +\$Trigger = New-ScheduledTaskTrigger -AtLogOn +\$Trigger + +# 작업 동작 정의 +\$Action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-ExecutionPolicy Bypass -File `"\$ScriptPath`"" +\$Action + +# 작업 등록 +Register-ScheduledTask -TaskName \$TaskName -Action \$Action -Trigger \$Trigger + +Exit +'''; @override Future isRegistedStartup(String label) async { - var c = Completer(); - //For Windows 8, 10 + var shell = 'Get-ScheduledTask | Where-Object {\$_.TaskName -eq "$label"}'; + var process = await Process.run('powershell.exe', [shell]); var exist = false; - var userPath = await Path.userPath; - var startupDir = Directory('$userPath$windowsStartupPath'); - var files = startupDir.listSync(); - for (var fileEntry in files) { - var fileName = path.basename(fileEntry.path); - var extension = path.extension(fileName); - fileName = fileName.replaceAll(extension, ''); - if (fileName == label) { + if (process.exitCode == 0) { + var existLine = []; + var stdoutList = process.stdout.toString().split('\n'); + for (var item in stdoutList) { + var result = item.trim(); + if (result.isNotEmpty) { + existLine.add(item); + } + } + if (existLine.length > 2) { exist = true; - break; } } - c.complete(exist); - return c.future; + return dataFutrue(exist); } //shell file must have '#!/bin/sh' @@ -328,77 +343,69 @@ Set WshShell = Nothing {List? arguments, String? logStandardFilePath, String? logErrorFilePath}) async { - var c = Completer(); - var file = File(executableFilePath); - ProcessResult? result; - //For Windows 8, 10 - var userPath = Platform.environment['UserProfile']; - - //compose vbs content - var parameters = ''; + var scriptPath = '${dataPath.replaceAll('/', '\\')}\\$label.ps1'; + var startupScript = startupScriptTemplate.replaceAll( + '{EXECUTABLE_PATH}', executableFilePath.replaceAll('/', '\\')); + var args = ''; if (arguments != null) { - for (var arg in arguments) { - parameters += '" $arg"'; - } + args = '''-ArgumentList "${arguments.join(' ')}" ` + '''; } - var vbsContent = vbsTemplate - .replaceAll('{FILE_PATH}', executableFilePath) - .replaceAll('{PARAMETERS}', parameters); + startupScript = startupScript.replaceAll('{ARGUMENTS}', args); + var logStdout = ''; + if (logStandardFilePath != null) { + logStdout = ''' ` + -RedirectStandardOutput "$logStandardFilePath"'''; + } + startupScript = startupScript.replaceAll('{LOG_STDOUT}', logStdout); - //create vbs file - var vbsInstallDir = Directory('$userPath$windowRoamingPath$label'); - if (!await vbsInstallDir.exists()) { - await vbsInstallDir.create(recursive: true); + var logStderr = ''; + if (logErrorFilePath != null) { + logStderr = ''' ` + -RedirectStandardError "$logErrorFilePath"'''; } - var vbsFile = File(path.join(vbsInstallDir.path, '$label.vbs')); - if (await vbsFile.exists()) { - await vbsFile.delete(); - } - await vbsFile.create(); - await vbsFile.writeAsString(vbsContent); + startupScript = startupScript.replaceAll('{LOG_STDERR}', logStderr); - //create symbolic - var symbolicPath = '$userPath$windowsStartupPath$label.vbs'; - result = await ProcessExecutor.run( - StringBuffer('mklink "$symbolicPath" "${vbsFile.path}"')); - c.complete(result.exitCode == 0); - return c.future; + var startupFile = File(scriptPath); + startupFile.createSync(recursive: true); + startupFile.writeAsStringSync(startupScript, flush: true); + + var schedulerScript = schedulerScriptTemplate + .replaceAll('{SCRIPT_PATH}', scriptPath) + .replaceAll('{TASK_NAME}', label); + + var tempFile = + File('${Directory.systemTemp.path}\\${ProcessExecutor.tempFileName}'); + tempFile.createSync(recursive: true); + tempFile.writeAsStringSync(schedulerScript, flush: true); + + var powershellCommand = + 'Start-Process powershell.exe -ArgumentList "-NoProfile -ExecutionPolicy Bypass -Command `& \'${tempFile.path}\'; exit" -Verb RunAs -WindowStyle Hidden'; + + var process = await Process.run( + 'powershell', ['-Command', powershellCommand, '-Wait']); + + print(process.stdout.toString()); + print(process.stderr.toString()); + print(process.exitCode); + + return dataFutrue(process.exitCode == 0); } @override Future unregistStartup(String label) async { - var c = Completer(); - var success = false; - //For Windows 8, 10 - var userPath = Platform.environment['UserProfile']; - - //delete vbs file - var vbsInstallDir = Directory('$userPath$windowRoamingPath$label'); - var vbsFile = File(path.join(vbsInstallDir.path, '$label.vbs')); - if (await vbsFile.exists()) { - await vbsFile.delete(); - var list = vbsInstallDir.listSync(recursive: true); - if (list.isEmpty) { - if (await vbsInstallDir.exists()) { - await vbsInstallDir.delete(recursive: true); - } - } - } - - //delete shorcut file - var startupDir = Directory('$userPath$windowsStartupPath'); - var files = startupDir.listSync(); - for (var fileEntry in files) { - var fileName = path.basename(fileEntry.path); - var extension = path.extension(fileName); - fileName = fileName.replaceAll(extension, ''); - if (fileName == label) { - await File(fileEntry.path).delete(); - success = true; - break; - } - } - c.complete(success); - return c.future; + var shell = 'Unregister-ScheduledTask -TaskName "$label" -Confirm:\$false'; + var process = await Process.run( + 'powershell.exe', + [ + '-Command', + 'Start-Process powershell -ArgumentList \'-NoProfile -ExecutionPolicy Bypass -Command "$shell"\' -Verb RunAs -WindowStyle Hidden' + ], + runInShell: true, + ); + print(process.stdout.toString()); + print(process.stderr.toString()); + print(process.exitCode); + return dataFutrue(process.exitCode == 0); } } diff --git a/lib/utils/path.dart b/lib/utils/path.dart index d30d4ed..08d69ab 100644 --- a/lib/utils/path.dart +++ b/lib/utils/path.dart @@ -8,9 +8,9 @@ enum FileType { none, file, directory, link } class Path { static Future get userPath async { String path = ''; - if(Platform.isWindows) { + if (Platform.isWindows) { path = Platform.environment['USERPROFILE'] as String; - } else if(Platform.isMacOS || Platform.isLinux) { + } else if (Platform.isMacOS || Platform.isLinux) { path = Platform.environment['HOME'] as String; } return dataFutrue(path); @@ -25,8 +25,9 @@ class Path { } else if (Platform.isWindows) { path = '$path/AppData/Local/$appIdendifier'; } + if (Platform.isWindows) path = path.replaceAll('/', '\\'); return dataFutrue(path); -} + } static String? getParent(String path) { path = getNormal(path); @@ -68,7 +69,8 @@ class Path { static Future copy(String startPath, String destPath, bool isDir, {required List? ignorePattern, required List? ignoredList, - bool isMove = false, bool isOverwrite = true}) async { + bool isMove = false, + bool isOverwrite = true}) async { var c = Completer(); var result = true; if (ignorePattern != null) { @@ -94,11 +96,17 @@ class Path { for (var file in files) { if (file is File) { await copy(file.path, destDir.path, false, - ignorePattern: ignorePattern, ignoredList: ignoredList, isMove: isMove, isOverwrite: isOverwrite); + ignorePattern: ignorePattern, + ignoredList: ignoredList, + isMove: isMove, + isOverwrite: isOverwrite); } if (file is Directory) { await copy(file.path, destDir.path, true, - ignorePattern: ignorePattern, ignoredList: ignoredList, isMove: isMove, isOverwrite: isOverwrite); + ignorePattern: ignorePattern, + ignoredList: ignoredList, + isMove: isMove, + isOverwrite: isOverwrite); } } } else { @@ -107,8 +115,7 @@ class Path { print('File copy: $startPath --> $destChildPath'); var file = File(startPath); var fileDest = File(destChildPath); - if(fileDest.existsSync()) - { + if (fileDest.existsSync()) { await fileDest.delete(); } await file.copy(destChildPath); diff --git a/pubspec.yaml b/pubspec.yaml index a5fbc49..56a2973 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -23,6 +23,6 @@ dependencies: dev_dependencies: lints: ^3.0.0 - test: ^1.21.0 + test: ^1.24.0 build_runner: ^2.4.8 json_serializable: ^6.7.1 diff --git a/test/main_test.dart b/test/main_test.dart new file mode 100644 index 0000000..4ec8b4a --- /dev/null +++ b/test/main_test.dart @@ -0,0 +1,60 @@ +import 'dart:io'; + +import 'package:dart_framework/core/application.dart'; +import 'package:dart_framework/dart_framework.dart'; +import 'package:dart_framework/utils/os_startup.dart'; +import 'package:dart_framework/utils/system_util.dart'; +import 'package:test/test.dart'; +import 'package:dart_framework/platform/isolate_manager.dart' as isolate; + +void main() { + final appID = 'com.toki-labs.dartframework'; + + group('Application', () { + final awesome = Awesome(); + + setUp(() { + // Additional setup goes here. + print('Set up!!'); + Application('dartframework', appID, () async {}, (error, stack) {}); + }); + + test('First Test', () { + expect(awesome.isAwesome, isTrue); + }); + }); + + group('Isolate', () { + setUp(() { + // Additional setup goes here. + print('Set up!!'); + }); + + test('test', () async { + isolate.test(); + }); + }); + + group('OSStart', () { + setUp(() { + // Additional setup goes here. + print('Set up!!'); + }); + + test('OSStart test', () async { + var isRegisted = await OSStartup.isRegistedStartup(appID); + expect(isRegisted, isFalse); + print('isRegisted: $isRegisted'); + + var result = await OSStartup.registStartup( + Platform.resolvedExecutable, appID, + logStandardFilePath: '$dataPath\\startup_log.txt'); + expect(result, isTrue); + print('Regist Result: $result'); + + /*expect(await OSStartup.unregistStartup(appID), isTrue); + + expect(await OSStartup.isRegistedStartup(appID), isFalse);*/ + }); + }); +} diff --git a/test/test.dart b/test/test.dart deleted file mode 100644 index c9f805e..0000000 --- a/test/test.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:dart_framework/dart_framework.dart'; -import 'package:test/test.dart'; - -void main() { - group('A group of tests', () { - final awesome = Awesome(); - - setUp(() { - // Additional setup goes here. - print('Set up!!'); - }); - - test('First Test', () { - expect(awesome.isAwesome, isTrue); - }); - }); -}