192 lines
5.2 KiB
Dart
192 lines
5.2 KiB
Dart
import 'dart:ffi';
|
|
import 'dart:io';
|
|
|
|
import 'package:ffi/ffi.dart';
|
|
import 'package:fl_clash/common/common.dart';
|
|
import 'package:fl_clash/enum/enum.dart';
|
|
import 'package:path/path.dart';
|
|
|
|
class Windows {
|
|
static Windows? _instance;
|
|
late DynamicLibrary _shell32;
|
|
|
|
Windows._internal() {
|
|
_shell32 = DynamicLibrary.open('shell32.dll');
|
|
}
|
|
|
|
factory Windows() {
|
|
_instance ??= Windows._internal();
|
|
return _instance!;
|
|
}
|
|
|
|
bool runas(String command, String arguments) {
|
|
final commandPtr = command.toNativeUtf16();
|
|
final argumentsPtr = arguments.toNativeUtf16();
|
|
final operationPtr = 'runas'.toNativeUtf16();
|
|
|
|
final shellExecute = _shell32.lookupFunction<
|
|
Int32 Function(
|
|
Pointer<Utf16> hwnd,
|
|
Pointer<Utf16> lpOperation,
|
|
Pointer<Utf16> lpFile,
|
|
Pointer<Utf16> lpParameters,
|
|
Pointer<Utf16> lpDirectory,
|
|
Int32 nShowCmd),
|
|
int Function(
|
|
Pointer<Utf16> hwnd,
|
|
Pointer<Utf16> lpOperation,
|
|
Pointer<Utf16> lpFile,
|
|
Pointer<Utf16> lpParameters,
|
|
Pointer<Utf16> lpDirectory,
|
|
int nShowCmd)>('ShellExecuteW');
|
|
|
|
final result = shellExecute(
|
|
nullptr,
|
|
operationPtr,
|
|
commandPtr,
|
|
argumentsPtr,
|
|
nullptr,
|
|
1,
|
|
);
|
|
|
|
calloc.free(commandPtr);
|
|
calloc.free(argumentsPtr);
|
|
calloc.free(operationPtr);
|
|
|
|
commonPrint.log("windows runas: $command $arguments resultCode:$result");
|
|
|
|
if (result < 42) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
_killProcess(int port) async {
|
|
final result = await Process.run('netstat', ['-ano']);
|
|
final lines = result.stdout.toString().trim().split('\n');
|
|
for (final line in lines) {
|
|
if (!line.contains(":$port") || !line.contains("LISTENING")) {
|
|
continue;
|
|
}
|
|
final parts = line.trim().split(RegExp(r'\s+'));
|
|
final pid = int.tryParse(parts.last);
|
|
if (pid != null) {
|
|
await Process.run('taskkill', ['/PID', pid.toString(), '/F']);
|
|
}
|
|
}
|
|
}
|
|
|
|
Future<WindowsHelperServiceStatus> checkService() async {
|
|
// final qcResult = await Process.run('sc', ['qc', appHelperService]);
|
|
// final qcOutput = qcResult.stdout.toString();
|
|
// if (qcResult.exitCode != 0 || !qcOutput.contains(appPath.helperPath)) {
|
|
// return WindowsHelperServiceStatus.none;
|
|
// }
|
|
final result = await Process.run('sc', ['query', appHelperService]);
|
|
if(result.exitCode != 0){
|
|
return WindowsHelperServiceStatus.none;
|
|
}
|
|
final output = result.stdout.toString();
|
|
if (output.contains("RUNNING") && await request.pingHelper()) {
|
|
return WindowsHelperServiceStatus.running;
|
|
}
|
|
return WindowsHelperServiceStatus.presence;
|
|
}
|
|
|
|
Future<bool> registerService() async {
|
|
final status = await checkService();
|
|
|
|
if (status == WindowsHelperServiceStatus.running) {
|
|
return true;
|
|
}
|
|
|
|
await _killProcess(helperPort);
|
|
|
|
final command = [
|
|
"/c",
|
|
if (status == WindowsHelperServiceStatus.presence) ...[
|
|
"sc",
|
|
"delete",
|
|
appHelperService,
|
|
"/force",
|
|
"&&",
|
|
],
|
|
"sc",
|
|
"create",
|
|
appHelperService,
|
|
'binPath= "${appPath.helperPath}"',
|
|
'start= auto',
|
|
"&&",
|
|
"sc",
|
|
"start",
|
|
appHelperService,
|
|
].join(" ");
|
|
|
|
final res = runas("cmd.exe", command);
|
|
|
|
await Future.delayed(
|
|
Duration(milliseconds: 300),
|
|
);
|
|
|
|
return res;
|
|
}
|
|
|
|
Future<bool> registerTask(String appName) async {
|
|
final taskXml = '''
|
|
<?xml version="1.0" encoding="UTF-16"?>
|
|
<Task version="1.3" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
|
|
<Principals>
|
|
<Principal id="Author">
|
|
<LogonType>InteractiveToken</LogonType>
|
|
<RunLevel>HighestAvailable</RunLevel>
|
|
</Principal>
|
|
</Principals>
|
|
<Triggers>
|
|
<LogonTrigger/>
|
|
</Triggers>
|
|
<Settings>
|
|
<MultipleInstancesPolicy>Parallel</MultipleInstancesPolicy>
|
|
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
|
|
<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
|
|
<AllowHardTerminate>false</AllowHardTerminate>
|
|
<StartWhenAvailable>false</StartWhenAvailable>
|
|
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
|
|
<IdleSettings>
|
|
<StopOnIdleEnd>false</StopOnIdleEnd>
|
|
<RestartOnIdle>false</RestartOnIdle>
|
|
</IdleSettings>
|
|
<AllowStartOnDemand>true</AllowStartOnDemand>
|
|
<Enabled>true</Enabled>
|
|
<Hidden>false</Hidden>
|
|
<RunOnlyIfIdle>false</RunOnlyIfIdle>
|
|
<WakeToRun>false</WakeToRun>
|
|
<ExecutionTimeLimit>PT72H</ExecutionTimeLimit>
|
|
<Priority>7</Priority>
|
|
</Settings>
|
|
<Actions Context="Author">
|
|
<Exec>
|
|
<Command>"${Platform.resolvedExecutable}"</Command>
|
|
</Exec>
|
|
</Actions>
|
|
</Task>''';
|
|
final taskPath = join(await appPath.tempPath, "task.xml");
|
|
await File(taskPath).create(recursive: true);
|
|
await File(taskPath)
|
|
.writeAsBytes(taskXml.encodeUtf16LeWithBom, flush: true);
|
|
final commandLine = [
|
|
'/Create',
|
|
'/TN',
|
|
appName,
|
|
'/XML',
|
|
"%s",
|
|
'/F',
|
|
].join(" ");
|
|
return runas(
|
|
'schtasks',
|
|
commandLine.replaceFirst("%s", taskPath),
|
|
);
|
|
}
|
|
}
|
|
|
|
final windows = Platform.isWindows ? Windows() : null;
|