fix: 更新诸多bug

This commit is contained in:
2026-01-08 02:01:21 -08:00
parent 9563a81a6e
commit 8477c2e343
96 changed files with 1338 additions and 5695 deletions
+47
View File
@@ -0,0 +1,47 @@
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:dio/io.dart';
import 'package:kaer_with_panels/app/services/singbox_imp/kr_sing_box_imp.dart';
import 'package:kaer_with_panels/app/utils/kr_log_util.dart';
class KRHttpAdapterUtil {
/// 创建标准配置的 HttpClientAdapter
///
/// [useSingBoxProxy] 是否集成 sing-box 代理规则,默认 true
/// [forceDirect] 是否强制直连(不使用任何代理),默认 false
static HttpClientAdapter createAdapter({
bool useSingBoxProxy = true,
bool forceDirect = false,
Duration timeout = const Duration(seconds: 10),
}) {
return IOHttpClientAdapter(
createHttpClient: () {
final client = HttpClient();
client.connectionTimeout = timeout;
// ✅ 统一修复:允许所有 SSL 证书
client.badCertificateCallback = (cert, host, port) => true;
if (forceDirect) {
client.findProxy = (url) {
KRLogUtil.kr_i('🔍 请求使用直连: $url', tag: 'KRHttpAdapterUtil');
return 'DIRECT';
};
} else if (useSingBoxProxy) {
client.findProxy = (url) {
try {
final proxyConfig = KRSingBoxImp.instance.kr_buildProxyRule();
KRLogUtil.kr_i('🔍 请求使用代理: $proxyConfig, url: $url', tag: 'KRHttpAdapterUtil');
return proxyConfig;
} catch (e) {
KRLogUtil.kr_w('⚠️ 获取代理配置异常,回退到 DIRECT: $e', tag: 'KRHttpAdapterUtil');
return 'DIRECT';
}
};
}
return client;
},
);
}
}
+11 -6
View File
@@ -25,8 +25,9 @@ class KRWindowManager with WindowListener, TrayListener {
KRLogUtil.kr_i('kr_initWindowManager: 窗口管理器已初始化');
const WindowOptions windowOptions = WindowOptions(
size: Size(800, 668),
minimumSize: Size(400, 334),
size: Size(420, 800),
minimumSize: Size(420, 800),
maximumSize: Size(420, 800),
center: true,
backgroundColor: Colors.white,
skipTaskbar: false,
@@ -46,16 +47,20 @@ class KRWindowManager with WindowListener, TrayListener {
if (Platform.isWindows) {
await windowManager.setTitleBarStyle(TitleBarStyle.normal);
await windowManager.setTitle('HiFastVPN');
await windowManager.setSize(const Size(800, 668));
await windowManager.setMinimumSize(const Size(400, 334));
await windowManager.setSize(const Size(420, 800));
await windowManager.setMinimumSize(const Size(420, 800));
await windowManager.setMaximumSize(const Size(420, 800));
await windowManager.setResizable(false);
await windowManager.center();
await windowManager.show();
// 阻止窗口关闭
await windowManager.setPreventClose(true);
} else {
await windowManager.setTitle('HiFastVPN');
await windowManager.setSize(const Size(800, 668));
await windowManager.setMinimumSize(const Size(400, 334));
await windowManager.setSize(const Size(420, 800));
await windowManager.setMinimumSize(const Size(420, 800));
await windowManager.setMaximumSize(const Size(420, 800));
await windowManager.setResizable(false);
await windowManager.center();
}
+123 -38
View File
@@ -1,5 +1,6 @@
import 'dart:io';
import 'package:kaer_with_panels/app/utils/kr_log_util.dart';
import 'package:kaer_with_panels/app/utils/kr_windows_process_util.dart';
/// Windows DNS 管理工具类
///
@@ -38,28 +39,48 @@ class KRWindowsDnsUtil {
}
try {
KRLogUtil.kr_i('📦 开始备份 Windows DNS 设置...', tag: 'WindowsDNS');
// 🔒 添加5秒超时保护
return await Future.value(() async {
KRLogUtil.kr_i('📦 开始备份 Windows DNS 设置...', tag: 'WindowsDNS');
// 1. 获取主网络接口
final interfaceName = await _kr_getPrimaryNetworkInterface();
if (interfaceName == null) {
KRLogUtil.kr_e('❌ 无法获取主网络接口', tag: 'WindowsDNS');
return false;
}
_primaryInterfaceName = interfaceName;
KRLogUtil.kr_i('🔍 主网络接口: $_primaryInterfaceName', tag: 'WindowsDNS');
// 1. 获取主网络接口
final interfaceName = await _kr_getPrimaryNetworkInterface();
if (interfaceName == null) {
KRLogUtil.kr_e('❌ 无法获取主网络接口', tag: 'WindowsDNS');
return false;
}
_primaryInterfaceName = interfaceName;
KRLogUtil.kr_i('🔍 主网络接口: $_primaryInterfaceName', tag: 'WindowsDNS');
// 2. 获取当前 DNS 服务器
final dnsServers = await _kr_getCurrentDnsServers(interfaceName);
if (dnsServers.isEmpty) {
KRLogUtil.kr_w('⚠️ 当前 DNS 为空,可能是自动获取', tag: 'WindowsDNS');
_originalDnsServers = []; // 空列表表示 DHCP 自动获取
} else {
_originalDnsServers = dnsServers;
KRLogUtil.kr_i('✅ 已备份 DNS: ${dnsServers.join(", ")}', tag: 'WindowsDNS');
}
// 2. 获取当前 DNS 服务器
final dnsServers = await _kr_getCurrentDnsServers(interfaceName);
return true;
// 🔧 P0修复1: 过滤掉 127.0.0.1 (sing-box 的本地 DNS)
// 原因:如果备份了 127.0.0.1,关闭 VPN 后恢复为 127.0.0.1,但 sing-box 已停止,导致 DNS 无法解析
final validDnsServers = dnsServers.where((dns) => !dns.startsWith('127.')).toList();
if (validDnsServers.isEmpty) {
KRLogUtil.kr_w('⚠️ 当前 DNS 为空或全是本地地址,设为 DHCP 自动获取', tag: 'WindowsDNS');
if (dnsServers.isNotEmpty) {
KRLogUtil.kr_i(' (已过滤的本地DNS: ${dnsServers.join(", ")})', tag: 'WindowsDNS');
}
_originalDnsServers = []; // 空列表表示 DHCP 自动获取
} else {
_originalDnsServers = validDnsServers;
KRLogUtil.kr_i('✅ 已备份有效 DNS: ${validDnsServers.join(", ")}', tag: 'WindowsDNS');
if (dnsServers.length != validDnsServers.length) {
KRLogUtil.kr_i(' (已过滤掉 ${dnsServers.length - validDnsServers.length} 个本地地址)', tag: 'WindowsDNS');
}
}
return true;
}()).timeout(
const Duration(seconds: 5),
onTimeout: () {
KRLogUtil.kr_w('⏱️ DNS 备份操作超时(5秒),跳过备份', tag: 'WindowsDNS');
return false;
},
);
} catch (e) {
KRLogUtil.kr_e('❌ 备份 DNS 设置失败: $e', tag: 'WindowsDNS');
return false;
@@ -80,22 +101,33 @@ class KRWindowsDnsUtil {
try {
KRLogUtil.kr_i('🔄 开始恢复 Windows DNS 设置...', tag: 'WindowsDNS');
// 1. 检查是否有备份
if (_primaryInterfaceName == null) {
KRLogUtil.kr_w('⚠️ 没有备份的网络接口,尝试自动检测', tag: 'WindowsDNS');
_primaryInterfaceName = await _kr_getPrimaryNetworkInterface();
if (_primaryInterfaceName == null) {
KRLogUtil.kr_e('❌ 无法检测网络接口,执行兜底恢复', tag: 'WindowsDNS');
return await _kr_fallbackRestoreDns();
}
// 🔧 P1修复: 恢复时重新检测主接口,防止网络切换导致恢复错误接口
final currentInterface = await _kr_getPrimaryNetworkInterface();
if (currentInterface == null) {
KRLogUtil.kr_e('❌ 无法检测当前网络接口,执行兜底恢复', tag: 'WindowsDNS');
return await _kr_fallbackRestoreDns();
}
// 2. 恢复原始 DNS
// 检查接口是否变化
if (_primaryInterfaceName != null && _primaryInterfaceName != currentInterface) {
KRLogUtil.kr_w('⚠️ 网络接口已变化: $_primaryInterfaceName$currentInterface', tag: 'WindowsDNS');
KRLogUtil.kr_w(' 执行兜底恢复以确保当前接口DNS正常', tag: 'WindowsDNS');
_primaryInterfaceName = currentInterface; // 更新为当前接口
return await _kr_fallbackRestoreDns();
}
// 使用当前检测到的接口
_primaryInterfaceName = currentInterface;
KRLogUtil.kr_i('🔍 当前网络接口: $_primaryInterfaceName', tag: 'WindowsDNS');
// 1. 检查是否有备份的DNS
if (_originalDnsServers == null) {
KRLogUtil.kr_w('⚠️ 没有备份的 DNS,执行兜底恢复', tag: 'WindowsDNS');
return await _kr_fallbackRestoreDns();
}
// 2. 恢复原始 DNS
if (_originalDnsServers!.isEmpty) {
// 原本是 DHCP 自动获取
KRLogUtil.kr_i('🔄 恢复为 DHCP 自动获取 DNS', tag: 'WindowsDNS');
@@ -130,6 +162,15 @@ class KRWindowsDnsUtil {
return await _kr_fallbackRestoreDns();
}
// 🔧 P2优化: 测试 DNS 解析是否真正可用
KRLogUtil.kr_i('🧪 测试 DNS 解析功能...', tag: 'WindowsDNS');
final canResolve = await _kr_testDnsResolution();
if (!canResolve) {
KRLogUtil.kr_w('⚠️ DNS 解析测试失败,执行兜底恢复', tag: 'WindowsDNS');
return await _kr_fallbackRestoreDns();
}
KRLogUtil.kr_i('✅ DNS 解析测试通过', tag: 'WindowsDNS');
return true;
} catch (e) {
KRLogUtil.kr_e('❌ 恢复 DNS 设置失败: $e', tag: 'WindowsDNS');
@@ -187,7 +228,7 @@ class KRWindowsDnsUtil {
Future<String?> _kr_getPrimaryNetworkInterface() async {
try {
// 使用 netsh 获取接口列表
final result = await Process.run('netsh', ['interface', 'show', 'interface']);
final result = await KRWindowsProcessUtil.runHidden('netsh', ['interface', 'show', 'interface']);
if (result.exitCode != 0) {
KRLogUtil.kr_e('❌ 获取网络接口失败: ${result.stderr}', tag: 'WindowsDNS');
@@ -271,7 +312,7 @@ class KRWindowsDnsUtil {
/// 返回:DNS 服务器列表
Future<List<String>> _kr_getCurrentDnsServers(String interfaceName) async {
try {
final result = await Process.run('netsh', [
final result = await KRWindowsProcessUtil.runHidden('netsh', [
'interface',
'ipv4',
'show',
@@ -294,10 +335,9 @@ class KRWindowsDnsUtil {
final ipMatch = RegExp(r'\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b').firstMatch(line);
if (ipMatch != null) {
final ip = ipMatch.group(0)!;
// 排除本地回环地址
if (!ip.startsWith('127.')) {
dnsServers.add(ip);
}
// 🔧 关键修复:不过滤127.0.0.1,以便正确检测DNS是否还在使用sing-box的本地DNS
// 这样在恢复DNS时,第126行的验证才能正确检测到127.0.0.1并触发兜底恢复
dnsServers.add(ip);
}
}
@@ -325,7 +365,7 @@ class KRWindowsDnsUtil {
// 1. 设置主 DNS
KRLogUtil.kr_i('🔧 设置主 DNS: ${dnsServers[0]}', tag: 'WindowsDNS');
var result = await Process.run('netsh', [
var result = await KRWindowsProcessUtil.runHidden('netsh', [
'interface',
'ipv4',
'set',
@@ -345,7 +385,7 @@ class KRWindowsDnsUtil {
if (dnsServers.length > 1) {
for (int i = 1; i < dnsServers.length; i++) {
KRLogUtil.kr_i('🔧 设置备用 DNS ${i}: ${dnsServers[i]}', tag: 'WindowsDNS');
result = await Process.run('netsh', [
result = await KRWindowsProcessUtil.runHidden('netsh', [
'interface',
'ipv4',
'add',
@@ -384,7 +424,7 @@ class KRWindowsDnsUtil {
try {
KRLogUtil.kr_i('🔧 设置 DNS 为自动获取 (DHCP)', tag: 'WindowsDNS');
final result = await Process.run('netsh', [
final result = await KRWindowsProcessUtil.runHidden('netsh', [
'interface',
'ipv4',
'set',
@@ -416,7 +456,7 @@ class KRWindowsDnsUtil {
try {
KRLogUtil.kr_i('🔄 刷新 DNS 缓存...', tag: 'WindowsDNS');
final result = await Process.run('ipconfig', ['/flushdns']);
final result = await KRWindowsProcessUtil.runHidden('ipconfig', ['/flushdns']);
if (result.exitCode == 0) {
KRLogUtil.kr_i('✅ DNS 缓存已刷新', tag: 'WindowsDNS');
@@ -428,6 +468,51 @@ class KRWindowsDnsUtil {
}
}
/// 🔧 P2优化: 测试 DNS 解析是否真正可用
///
/// 通过 nslookup 测试常见域名解析
/// 返回:true 表示 DNS 可用,false 表示 DNS 不可用
Future<bool> _kr_testDnsResolution() async {
try {
// 测试多个常见域名,提高成功率
final testDomains = ['www.baidu.com', 'www.qq.com', 'dns.alidns.com'];
for (var domain in testDomains) {
try {
// 使用 nslookup 测试 DNS 解析,设置 2 秒超时
final result = await KRWindowsProcessUtil.runHidden(
'nslookup',
[domain],
).timeout(
const Duration(seconds: 2),
onTimeout: () {
return ProcessResult(0, 1, '', 'Timeout');
},
);
if (result.exitCode == 0) {
final output = result.stdout.toString();
// 检查输出是否包含 IP 地址(简单验证)
if (output.contains('Address:') || output.contains('地址:')) {
KRLogUtil.kr_i('✅ DNS 解析测试通过: $domain', tag: 'WindowsDNS');
return true;
}
}
} catch (e) {
// 单个域名失败,继续测试下一个
continue;
}
}
// 所有域名都解析失败
KRLogUtil.kr_w('⚠️ 所有测试域名解析均失败', tag: 'WindowsDNS');
return false;
} catch (e) {
KRLogUtil.kr_e('❌ DNS 解析测试异常: $e', tag: 'WindowsDNS');
return false;
}
}
/// 清除备份数据
///
/// 在应用退出或不需要时调用
+594
View File
@@ -0,0 +1,594 @@
import 'dart:convert';
import 'dart:ffi';
import 'dart:io';
import 'dart:typed_data';
import 'package:ffi/ffi.dart';
class KRWindowsProcessUtil {
/// 🔍 调试标志:设为 true 可以追踪所有命令执行(用于排查黑窗问题)
static const bool _debugCommandExecution = true; // ← 开启调试
static Future<ProcessResult> runHidden(String executable, List<String> arguments) async {
if (!Platform.isWindows) {
return Process.run(executable, arguments);
}
final result = await _runHiddenWindows(executable, arguments);
return result;
}
static Future<int> startHidden(String executable, List<String> arguments) async {
if (!Platform.isWindows) {
final process = await Process.start(executable, arguments);
return process.pid;
}
return _startHiddenWindows(executable, arguments);
}
static String _buildCommandLine(String executable, List<String> arguments) {
final parts = <String>[_quoteArgument(executable)];
for (final arg in arguments) {
parts.add(_quoteArgument(arg));
}
return parts.join(' ');
}
static bool _shouldSearchPath(String executable) {
if (executable.isEmpty) {
return true;
}
return !(executable.contains('\\') || executable.contains('/') || executable.contains(':'));
}
static String _quoteArgument(String value) {
if (value.isEmpty) {
return '""';
}
final needsQuotes = value.contains(' ') || value.contains('\t') || value.contains('"');
if (!needsQuotes) {
return value;
}
final buffer = StringBuffer('"');
var backslashes = 0;
for (var i = 0; i < value.length; i++) {
final char = value[i];
if (char == '\\') {
backslashes++;
continue;
}
if (char == '"') {
buffer.write('\\' * (backslashes * 2 + 1));
buffer.write('"');
backslashes = 0;
continue;
}
if (backslashes > 0) {
buffer.write('\\' * backslashes);
backslashes = 0;
}
buffer.write(char);
}
if (backslashes > 0) {
buffer.write('\\' * (backslashes * 2));
}
buffer.write('"');
return buffer.toString();
}
static Future<ProcessResult> _runHiddenWindows(String executable, List<String> arguments) async {
final stdoutPipe = _createPipe();
final stderrPipe = _createPipe();
final startupInfo = calloc<STARTUPINFO>();
final processInfo = calloc<PROCESS_INFORMATION>();
final commandLine = _buildCommandLine(executable, arguments).toNativeUtf16();
final applicationName = _shouldSearchPath(executable) ? nullptr : executable.toNativeUtf16();
final stdInput = _getStdInputHandle();
startupInfo.ref
..cb = sizeOf<STARTUPINFO>()
..dwFlags = STARTF_USESTDHANDLES
..hStdInput = stdInput
..hStdOutput = stdoutPipe.write
..hStdError = stderrPipe.write;
final created = _CreateProcessW(
applicationName,
commandLine,
nullptr,
nullptr,
TRUE,
CREATE_NO_WINDOW,
nullptr,
nullptr,
startupInfo,
processInfo,
);
calloc.free(commandLine);
if (applicationName != nullptr) {
calloc.free(applicationName);
}
if (created == 0) {
_closeHandle(stdoutPipe.read);
_closeHandle(stdoutPipe.write);
_closeHandle(stderrPipe.read);
_closeHandle(stderrPipe.write);
calloc.free(startupInfo);
calloc.free(processInfo);
throw Exception('CreateProcessW failed: ${_GetLastError()}');
}
_closeHandle(stdoutPipe.write);
_closeHandle(stderrPipe.write);
final output = await _collectOutput(processInfo.ref.hProcess, stdoutPipe.read, stderrPipe.read);
final exitCode = _getExitCode(processInfo.ref.hProcess);
_closeHandle(stdoutPipe.read);
_closeHandle(stderrPipe.read);
_closeHandle(processInfo.ref.hThread);
_closeHandle(processInfo.ref.hProcess);
final pid = processInfo.ref.dwProcessId;
calloc.free(startupInfo);
calloc.free(processInfo);
return ProcessResult(pid, exitCode, output.stdout, output.stderr);
}
static Future<int> _startHiddenWindows(String executable, List<String> arguments) async {
final startupInfo = calloc<STARTUPINFO>();
final processInfo = calloc<PROCESS_INFORMATION>();
final commandLine = _buildCommandLine(executable, arguments).toNativeUtf16();
final applicationName = _shouldSearchPath(executable) ? nullptr : executable.toNativeUtf16();
startupInfo.ref.cb = sizeOf<STARTUPINFO>();
final created = _CreateProcessW(
applicationName,
commandLine,
nullptr,
nullptr,
FALSE,
CREATE_NO_WINDOW,
nullptr,
nullptr,
startupInfo,
processInfo,
);
calloc.free(commandLine);
if (applicationName != nullptr) {
calloc.free(applicationName);
}
if (created == 0) {
calloc.free(startupInfo);
calloc.free(processInfo);
throw Exception('CreateProcessW failed: ${_GetLastError()}');
}
final pid = processInfo.ref.dwProcessId;
_closeHandle(processInfo.ref.hThread);
_closeHandle(processInfo.ref.hProcess);
calloc.free(startupInfo);
calloc.free(processInfo);
return pid;
}
static _Pipe _createPipe() {
final readHandle = calloc<Pointer<Void>>();
final writeHandle = calloc<Pointer<Void>>();
final securityAttributes = calloc<SECURITY_ATTRIBUTES>();
securityAttributes.ref
..nLength = sizeOf<SECURITY_ATTRIBUTES>()
..bInheritHandle = TRUE
..lpSecurityDescriptor = nullptr;
final created = _CreatePipe(readHandle, writeHandle, securityAttributes, 0);
calloc.free(securityAttributes);
if (created == 0) {
calloc.free(readHandle);
calloc.free(writeHandle);
throw Exception('CreatePipe failed: ${_GetLastError()}');
}
final readValue = readHandle.value;
final writeValue = writeHandle.value;
calloc.free(readHandle);
calloc.free(writeHandle);
final infoResult = _SetHandleInformation(readValue, HANDLE_FLAG_INHERIT, 0);
if (infoResult == 0) {
_closeHandle(readValue);
_closeHandle(writeValue);
throw Exception('SetHandleInformation failed: ${_GetLastError()}');
}
return _Pipe(readValue, writeValue);
}
static Pointer<Void> _getStdInputHandle() {
final handle = _GetStdHandle(STD_INPUT_HANDLE);
if (handle == INVALID_HANDLE_VALUE || handle == 0) {
return nullptr;
}
return Pointer<Void>.fromAddress(handle);
}
static Future<_ProcessOutput> _collectOutput(
Pointer<Void> process,
Pointer<Void> stdoutHandle,
Pointer<Void> stderrHandle,
) async {
final stdoutBuilder = BytesBuilder();
final stderrBuilder = BytesBuilder();
while (true) {
final stdoutRead = _drainPipe(stdoutHandle, stdoutBuilder);
final stderrRead = _drainPipe(stderrHandle, stderrBuilder);
final waitResult = _WaitForSingleObject(process, 0);
if (waitResult == WAIT_OBJECT_0) {
break;
}
if (waitResult == WAIT_FAILED) {
throw Exception('WaitForSingleObject failed: ${_GetLastError()}');
}
if (!stdoutRead && !stderrRead) {
await Future.delayed(const Duration(milliseconds: 10));
} else {
await Future<void>.delayed(Duration.zero);
}
}
while (_drainPipe(stdoutHandle, stdoutBuilder) || _drainPipe(stderrHandle, stderrBuilder)) {
await Future<void>.delayed(Duration.zero);
}
return _ProcessOutput(
_decodeOutput(stdoutBuilder),
_decodeOutput(stderrBuilder),
);
}
static bool _drainPipe(Pointer<Void> handle, BytesBuilder builder) {
final buffer = calloc<Uint8>(4096);
final bytesRead = calloc<Uint32>();
final available = calloc<Uint32>();
var didRead = false;
while (true) {
final peekOk = _PeekNamedPipe(handle, nullptr, 0, nullptr, available, nullptr);
if (peekOk == 0 || available.value == 0) {
break;
}
final toRead = available.value < 4096 ? available.value : 4096;
final ok = _ReadFile(handle, buffer.cast<Void>(), toRead, bytesRead, nullptr);
final read = ok == 0 ? 0 : bytesRead.value;
if (read == 0) {
break;
}
builder.add(buffer.asTypedList(read));
didRead = true;
}
calloc.free(buffer);
calloc.free(bytesRead);
calloc.free(available);
return didRead;
}
static String _decodeOutput(BytesBuilder builder) {
if (builder.length == 0) {
return '';
}
final bytes = builder.toBytes();
try {
return systemEncoding.decode(bytes);
} catch (_) {
return utf8.decode(bytes, allowMalformed: true);
}
}
static int _getExitCode(Pointer<Void> process) {
final exitCode = calloc<Uint32>();
final ok = _GetExitCodeProcess(process, exitCode);
final code = ok == 0 ? -1 : exitCode.value;
calloc.free(exitCode);
return code;
}
static void _closeHandle(Pointer<Void> handle) {
if (handle == nullptr) {
return;
}
_CloseHandle(handle);
}
// 🔧 WinINet API helpers for proxy settings
/// 查询当前系统代理设置
static String? queryWindowsProxyServer() {
if (!Platform.isWindows) return null;
try {
final bufferSize = calloc<Uint32>();
bufferSize.value = sizeOf<INTERNET_PROXY_INFO>();
final proxyInfo = calloc<INTERNET_PROXY_INFO>();
final result = _InternetQueryOptionW(nullptr, INTERNET_OPTION_PROXY, proxyInfo.cast<Void>(), bufferSize);
if (result == 0) {
calloc.free(bufferSize);
calloc.free(proxyInfo);
return null;
}
final proxyServer = proxyInfo.ref.lpszProxy.toDartString();
calloc.free(bufferSize);
calloc.free(proxyInfo);
return proxyServer.isEmpty ? null : proxyServer;
} catch (e) {
return null;
}
}
/// 设置系统代理
static bool setWindowsProxyServer(String? server) {
if (!Platform.isWindows) return false;
try {
final proxyInfo = calloc<INTERNET_PROXY_INFO>();
if (server != null && server.isNotEmpty) {
// 设置代理模式
proxyInfo.ref.dwAccessType = INTERNET_OPEN_TYPE_PROXY;
proxyInfo.ref.lpszProxy = server.toNativeUtf16();
proxyInfo.ref.lpszProxyBypass = ''.toNativeUtf16();
} else {
// 禁用代理
proxyInfo.ref.dwAccessType = INTERNET_OPEN_TYPE_DIRECT;
proxyInfo.ref.lpszProxy = nullptr;
proxyInfo.ref.lpszProxyBypass = nullptr;
}
final result = _InternetSetOptionW(
nullptr,
INTERNET_OPTION_PROXY,
proxyInfo.cast<Void>(),
sizeOf<INTERNET_PROXY_INFO>(),
);
if (server != null && server.isNotEmpty) {
calloc.free(proxyInfo.ref.lpszProxy);
calloc.free(proxyInfo.ref.lpszProxyBypass);
}
calloc.free(proxyInfo);
return result != 0;
} catch (e) {
return false;
}
}
/// 禁用系统代理
static bool disableWindowsProxy() {
return setWindowsProxyServer(null);
}
}
class _Pipe {
final Pointer<Void> read;
final Pointer<Void> write;
_Pipe(this.read, this.write);
}
class _ProcessOutput {
final String stdout;
final String stderr;
_ProcessOutput(this.stdout, this.stderr);
}
const int TRUE = 1;
const int FALSE = 0;
const int STARTF_USESTDHANDLES = 0x00000100;
const int CREATE_NO_WINDOW = 0x08000000;
const int HANDLE_FLAG_INHERIT = 0x00000001;
const int WAIT_OBJECT_0 = 0x00000000;
const int WAIT_FAILED = 0xFFFFFFFF;
const int STD_INPUT_HANDLE = -10;
const int INVALID_HANDLE_VALUE = -1;
final class SECURITY_ATTRIBUTES extends Struct {
@Uint32()
external int nLength;
external Pointer<Void> lpSecurityDescriptor;
@Int32()
external int bInheritHandle;
}
final class STARTUPINFO extends Struct {
@Uint32()
external int cb;
external Pointer<Utf16> lpReserved;
external Pointer<Utf16> lpDesktop;
external Pointer<Utf16> lpTitle;
@Uint32()
external int dwX;
@Uint32()
external int dwY;
@Uint32()
external int dwXSize;
@Uint32()
external int dwYSize;
@Uint32()
external int dwXCountChars;
@Uint32()
external int dwYCountChars;
@Uint32()
external int dwFillAttribute;
@Uint32()
external int dwFlags;
@Uint16()
external int wShowWindow;
@Uint16()
external int cbReserved2;
external Pointer<Uint8> lpReserved2;
external Pointer<Void> hStdInput;
external Pointer<Void> hStdOutput;
external Pointer<Void> hStdError;
}
final class PROCESS_INFORMATION extends Struct {
external Pointer<Void> hProcess;
external Pointer<Void> hThread;
@Uint32()
external int dwProcessId;
@Uint32()
external int dwThreadId;
}
final DynamicLibrary _kernel32 = DynamicLibrary.open('kernel32.dll');
final _CreatePipe = _kernel32.lookupFunction<
Int32 Function(Pointer<Pointer<Void>>, Pointer<Pointer<Void>>, Pointer<SECURITY_ATTRIBUTES>, Uint32),
int Function(Pointer<Pointer<Void>>, Pointer<Pointer<Void>>, Pointer<SECURITY_ATTRIBUTES>, int)>(
'CreatePipe',
);
final _SetHandleInformation = _kernel32.lookupFunction<
Int32 Function(Pointer<Void>, Uint32, Uint32),
int Function(Pointer<Void>, int, int)>(
'SetHandleInformation',
);
final _CreateProcessW = _kernel32.lookupFunction<
Int32 Function(
Pointer<Utf16>,
Pointer<Utf16>,
Pointer<SECURITY_ATTRIBUTES>,
Pointer<SECURITY_ATTRIBUTES>,
Int32,
Uint32,
Pointer<Void>,
Pointer<Utf16>,
Pointer<STARTUPINFO>,
Pointer<PROCESS_INFORMATION>,
),
int Function(
Pointer<Utf16>,
Pointer<Utf16>,
Pointer<SECURITY_ATTRIBUTES>,
Pointer<SECURITY_ATTRIBUTES>,
int,
int,
Pointer<Void>,
Pointer<Utf16>,
Pointer<STARTUPINFO>,
Pointer<PROCESS_INFORMATION>,
)>(
'CreateProcessW',
);
final _PeekNamedPipe = _kernel32.lookupFunction<
Int32 Function(Pointer<Void>, Pointer<Void>, Uint32, Pointer<Uint32>, Pointer<Uint32>, Pointer<Uint32>),
int Function(Pointer<Void>, Pointer<Void>, int, Pointer<Uint32>, Pointer<Uint32>, Pointer<Uint32>)>(
'PeekNamedPipe',
);
final _ReadFile = _kernel32.lookupFunction<
Int32 Function(Pointer<Void>, Pointer<Void>, Uint32, Pointer<Uint32>, Pointer<Void>),
int Function(Pointer<Void>, Pointer<Void>, int, Pointer<Uint32>, Pointer<Void>)>(
'ReadFile',
);
final _CloseHandle = _kernel32.lookupFunction<
Int32 Function(Pointer<Void>),
int Function(Pointer<Void>)>(
'CloseHandle',
);
final _WaitForSingleObject = _kernel32.lookupFunction<
Uint32 Function(Pointer<Void>, Uint32),
int Function(Pointer<Void>, int)>(
'WaitForSingleObject',
);
final _GetStdHandle = _kernel32.lookupFunction<
IntPtr Function(Int32),
int Function(int)>(
'GetStdHandle',
);
final _GetExitCodeProcess = _kernel32.lookupFunction<
Int32 Function(Pointer<Void>, Pointer<Uint32>),
int Function(Pointer<Void>, Pointer<Uint32>)>(
'GetExitCodeProcess',
);
final _GetLastError = _kernel32.lookupFunction<
Uint32 Function(),
int Function()>(
'GetLastError',
);
// 🔧 WinINet API for proxy settings - 用于替代 reg 命令,消除黑屏
final DynamicLibrary _wininet = DynamicLibrary.open('wininet.dll');
const int INTERNET_OPTION_PROXY = 38;
const int INTERNET_OPEN_TYPE_PROXY = 3;
const int INTERNET_OPEN_TYPE_DIRECT = 1;
final class INTERNET_PROXY_INFO extends Struct {
@Int32()
external int dwAccessType;
external Pointer<Utf16> lpszProxy;
external Pointer<Utf16> lpszProxyBypass;
}
/// WinINet InternetSetOption API - 用于设置系统代理
final _InternetSetOptionW = _wininet.lookupFunction<
Int32 Function(Pointer<Void>, Uint32, Pointer<Void>, Uint32),
int Function(Pointer<Void>, int, Pointer<Void>, int)>(
'InternetSetOptionW',
);
/// WinINet InternetQueryOption API - 用于查询系统代理
final _InternetQueryOptionW = _wininet.lookupFunction<
Int32 Function(Pointer<Void>, Uint32, Pointer<Void>, Pointer<Uint32>),
int Function(Pointer<Void>, int, Pointer<Void>, Pointer<Uint32>)>(
'InternetQueryOptionW',
);