windows startup modified

This commit is contained in:
leedongmyung 2025-01-05 06:50:21 +09:00
parent 6a99f29695
commit 64ac027005
8 changed files with 304 additions and 143 deletions

11
.vscode/launch.json vendored
View file

@ -5,9 +5,16 @@
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
"name": "dart_framework", "name": "Dart",
"type": "dart",
"request": "launch", "request": "launch",
"type": "dart" "program": "bin/main.dart"
},
{
"name": "Unit test",
"request": "launch",
"type": "dart",
"args": ["test"]
} }
] ]
} }

View file

@ -1,16 +1,23 @@
import 'dart:io'; import 'dart:io';
import 'package:dart_framework/platform/process.dart';
import 'package:dart_framework/subtitle/parser_smi.dart'; import 'package:dart_framework/subtitle/parser_smi.dart';
import 'package:dart_framework/subtitle/parser_srt.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: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/core/application.dart';
import 'package:dart_framework/charset.dart'; import 'package:dart_framework/charset.dart';
void main() async { void main() async {
Application('dartframework', 'com.toki-labs.dartframework', () 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'); // var md5 = generateMd5('test');
// print('$md5 / ${md5.length}'); // print('$md5 / ${md5.length}');
@ -55,3 +62,90 @@ void testParse() {
} }
} }
} }
/*
@override
Future<bool> 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 = <String>[];
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<bool> registStartup(String executableFilePath, String label,
{List<String>? 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<bool> 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);
}
*/

View file

@ -5,8 +5,6 @@ import 'dart:io';
import 'dart:convert'; import 'dart:convert';
import 'dart:math'; 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/log/log.dart';
import 'package:dart_framework/utils/system_util.dart'; import 'package:dart_framework/utils/system_util.dart';
@ -129,6 +127,26 @@ class ProcessData {
} }
class ProcessExecutor { 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<ProcessData> startExe(String exe, List<String> args, static Future<ProcessData> startExe(String exe, List<String> args,
{String? workspace, {String? workspace,
bool? printStdout, bool? printStdout,
@ -156,26 +174,6 @@ class ProcessExecutor {
return dataFutrue(processData); 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<ProcessData> start(StringBuffer shell, static Future<ProcessData> start(StringBuffer shell,
{Converter<List<int>, String>? decoder, {Converter<List<int>, String>? decoder,
String? workspace, String? workspace,
@ -227,6 +225,9 @@ class ProcessExecutor {
} }
file.createSync(); file.createSync();
file.writeAsStringSync(shell.toString(), flush: true); file.writeAsStringSync(shell.toString(), flush: true);
await getPowershellGrant();
try { try {
processData.process = await Process.start( processData.process = await Process.start(
'powershell.exe', ['-NoProfile', '-File', file.absolute.path]); 'powershell.exe', ['-NoProfile', '-File', file.absolute.path]);
@ -285,6 +286,8 @@ class ProcessExecutor {
file.createSync(); file.createSync();
file.writeAsStringSync(shell.toString()); file.writeAsStringSync(shell.toString());
print(file.absolute.path);
await getPowershellGrant(); await getPowershellGrant();
processResult = await Process.run( processResult = await Process.run(
@ -456,7 +459,7 @@ Future<List<ProcessInfo>> findProcess(String searchWord) async {
var shell = StringBuffer( var shell = StringBuffer(
'Get-CimInstance -ClassName Win32_Process | Where-Object { \$_.Name.Contains("$searchWord") } | Select-Object CommandLine, ProcessId | Export-Csv -Path ${csvPath.absolute.path} -NoTypeInformation'); '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); var result = await ProcessExecutor.run(shell);
if (csvPath.existsSync()) { if (result.exitCode == 0 && csvPath.existsSync()) {
pList = parseProcessStd(csvPath.readAsStringSync()); pList = parseProcessStd(csvPath.readAsStringSync());
csvPath.deleteSync(); csvPath.deleteSync();
} }

View file

@ -3,7 +3,6 @@ import 'dart:async';
import 'package:dart_framework/utils/path.dart'; import 'package:dart_framework/utils/path.dart';
import 'package:dart_framework/utils/system_util.dart'; import 'package:dart_framework/utils/system_util.dart';
import 'package:path/path.dart' as path;
import 'package:dart_framework/platform/process.dart'; import 'package:dart_framework/platform/process.dart';
class OSStartup { class OSStartup {
@ -145,7 +144,8 @@ fi
} }
var param = arguments == null ? '' : ' ${arguments.join(' ')}'; var param = arguments == null ? '' : ' ${arguments.join(' ')}';
var launchContent = pathTemplate.replaceAll('{COMMAND}', '$executableFilePath$param'); var launchContent =
pathTemplate.replaceAll('{COMMAND}', '$executableFilePath$param');
file.writeAsStringSync(launchContent); file.writeAsStringSync(launchContent);
//create plist in LaunchAgents //create plist in LaunchAgents
@ -171,8 +171,8 @@ fi
await plistFile.create(); await plistFile.create();
await plistFile.writeAsString(plistContent); await plistFile.writeAsString(plistContent);
var result = await ProcessExecutor.run( var result = await ProcessExecutor.run(StringBuffer(
StringBuffer('chmod +x "$executableFilePath" && chmod +x "${file.path}" ')); 'chmod +x "$executableFilePath" && chmod +x "${file.path}" '));
success = result.exitCode == 0; success = result.exitCode == 0;
return dataFutrue(success); return dataFutrue(success);
@ -291,35 +291,50 @@ WantedBy=default.target""";
/// ************* WINDOWS *************** /// ************* WINDOWS ***************
class _StartupWindows extends _Startup { class _StartupWindows extends _Startup {
final windowsStartupPath = final startupScriptTemplate = '''Start-Process -FilePath "{EXECUTABLE_PATH}" `
'\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\'; {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 \$TaskName = "{TASK_NAME}"
Set WshShell = Nothing
"""; # ( )
\$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 @override
Future<bool> isRegistedStartup(String label) async { Future<bool> isRegistedStartup(String label) async {
var c = Completer<bool>(); var shell = 'Get-ScheduledTask | Where-Object {\$_.TaskName -eq "$label"}';
//For Windows 8, 10 var process = await Process.run('powershell.exe', [shell]);
var exist = false; var exist = false;
var userPath = await Path.userPath; if (process.exitCode == 0) {
var startupDir = Directory('$userPath$windowsStartupPath'); var existLine = <String>[];
var files = startupDir.listSync(); var stdoutList = process.stdout.toString().split('\n');
for (var fileEntry in files) { for (var item in stdoutList) {
var fileName = path.basename(fileEntry.path); var result = item.trim();
var extension = path.extension(fileName); if (result.isNotEmpty) {
fileName = fileName.replaceAll(extension, ''); existLine.add(item);
if (fileName == label) { }
}
if (existLine.length > 2) {
exist = true; exist = true;
break;
} }
} }
c.complete(exist); return dataFutrue(exist);
return c.future;
} }
//shell file must have '#!/bin/sh' //shell file must have '#!/bin/sh'
@ -328,77 +343,69 @@ Set WshShell = Nothing
{List<String>? arguments, {List<String>? arguments,
String? logStandardFilePath, String? logStandardFilePath,
String? logErrorFilePath}) async { String? logErrorFilePath}) async {
var c = Completer<bool>(); var scriptPath = '${dataPath.replaceAll('/', '\\')}\\$label.ps1';
var file = File(executableFilePath); var startupScript = startupScriptTemplate.replaceAll(
ProcessResult? result; '{EXECUTABLE_PATH}', executableFilePath.replaceAll('/', '\\'));
//For Windows 8, 10 var args = '';
var userPath = Platform.environment['UserProfile'];
//compose vbs content
var parameters = '';
if (arguments != null) { if (arguments != null) {
for (var arg in arguments) { args = '''-ArgumentList "${arguments.join(' ')}" `
parameters += '" $arg"'; ''';
} }
startupScript = startupScript.replaceAll('{ARGUMENTS}', args);
var logStdout = '';
if (logStandardFilePath != null) {
logStdout = ''' `
-RedirectStandardOutput "$logStandardFilePath"''';
} }
var vbsContent = vbsTemplate startupScript = startupScript.replaceAll('{LOG_STDOUT}', logStdout);
.replaceAll('{FILE_PATH}', executableFilePath)
.replaceAll('{PARAMETERS}', parameters);
//create vbs file var logStderr = '';
var vbsInstallDir = Directory('$userPath$windowRoamingPath$label'); if (logErrorFilePath != null) {
if (!await vbsInstallDir.exists()) { logStderr = ''' `
await vbsInstallDir.create(recursive: true); -RedirectStandardError "$logErrorFilePath"''';
} }
var vbsFile = File(path.join(vbsInstallDir.path, '$label.vbs')); startupScript = startupScript.replaceAll('{LOG_STDERR}', logStderr);
if (await vbsFile.exists()) {
await vbsFile.delete();
}
await vbsFile.create();
await vbsFile.writeAsString(vbsContent);
//create symbolic var startupFile = File(scriptPath);
var symbolicPath = '$userPath$windowsStartupPath$label.vbs'; startupFile.createSync(recursive: true);
result = await ProcessExecutor.run( startupFile.writeAsStringSync(startupScript, flush: true);
StringBuffer('mklink "$symbolicPath" "${vbsFile.path}"'));
c.complete(result.exitCode == 0); var schedulerScript = schedulerScriptTemplate
return c.future; .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 @override
Future<bool> unregistStartup(String label) async { Future<bool> unregistStartup(String label) async {
var c = Completer<bool>(); var shell = 'Unregister-ScheduledTask -TaskName "$label" -Confirm:\$false';
var success = false; var process = await Process.run(
//For Windows 8, 10 'powershell.exe',
var userPath = Platform.environment['UserProfile']; [
'-Command',
//delete vbs file 'Start-Process powershell -ArgumentList \'-NoProfile -ExecutionPolicy Bypass -Command "$shell"\' -Verb RunAs -WindowStyle Hidden'
var vbsInstallDir = Directory('$userPath$windowRoamingPath$label'); ],
var vbsFile = File(path.join(vbsInstallDir.path, '$label.vbs')); runInShell: true,
if (await vbsFile.exists()) { );
await vbsFile.delete(); print(process.stdout.toString());
var list = vbsInstallDir.listSync(recursive: true); print(process.stderr.toString());
if (list.isEmpty) { print(process.exitCode);
if (await vbsInstallDir.exists()) { return dataFutrue(process.exitCode == 0);
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;
} }
} }

View file

@ -25,6 +25,7 @@ class Path {
} else if (Platform.isWindows) { } else if (Platform.isWindows) {
path = '$path/AppData/Local/$appIdendifier'; path = '$path/AppData/Local/$appIdendifier';
} }
if (Platform.isWindows) path = path.replaceAll('/', '\\');
return dataFutrue(path); return dataFutrue(path);
} }
@ -68,7 +69,8 @@ class Path {
static Future<bool> copy(String startPath, String destPath, bool isDir, static Future<bool> copy(String startPath, String destPath, bool isDir,
{required List<String>? ignorePattern, {required List<String>? ignorePattern,
required List<String>? ignoredList, required List<String>? ignoredList,
bool isMove = false, bool isOverwrite = true}) async { bool isMove = false,
bool isOverwrite = true}) async {
var c = Completer<bool>(); var c = Completer<bool>();
var result = true; var result = true;
if (ignorePattern != null) { if (ignorePattern != null) {
@ -94,11 +96,17 @@ class Path {
for (var file in files) { for (var file in files) {
if (file is File) { if (file is File) {
await copy(file.path, destDir.path, false, 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) { if (file is Directory) {
await copy(file.path, destDir.path, true, await copy(file.path, destDir.path, true,
ignorePattern: ignorePattern, ignoredList: ignoredList, isMove: isMove, isOverwrite: isOverwrite); ignorePattern: ignorePattern,
ignoredList: ignoredList,
isMove: isMove,
isOverwrite: isOverwrite);
} }
} }
} else { } else {
@ -107,8 +115,7 @@ class Path {
print('File copy: $startPath --> $destChildPath'); print('File copy: $startPath --> $destChildPath');
var file = File(startPath); var file = File(startPath);
var fileDest = File(destChildPath); var fileDest = File(destChildPath);
if(fileDest.existsSync()) if (fileDest.existsSync()) {
{
await fileDest.delete(); await fileDest.delete();
} }
await file.copy(destChildPath); await file.copy(destChildPath);

View file

@ -23,6 +23,6 @@ dependencies:
dev_dependencies: dev_dependencies:
lints: ^3.0.0 lints: ^3.0.0
test: ^1.21.0 test: ^1.24.0
build_runner: ^2.4.8 build_runner: ^2.4.8
json_serializable: ^6.7.1 json_serializable: ^6.7.1

60
test/main_test.dart Normal file
View file

@ -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);*/
});
});
}

View file

@ -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);
});
});
}