windows路径问题

(cherry picked from commit 9cefb1b9e009575ee7f6a5aef631cb344b6e1df8)
This commit is contained in:
2025-10-29 16:07:16 +08:00
committed by speakeloudest
parent 0067017ca6
commit 9c2f9be6c5
3 changed files with 210 additions and 7 deletions
@@ -177,36 +177,225 @@ class KRSingBoxImp {
final workingDir =
Platform.isAndroid ? await getExternalStorageDirectory() : baseDir;
final tempDir = await getTemporaryDirectory();
// Windows 路径规范化:确保使用正确的路径分隔符
Directory normalizePath(Directory dir) {
if (Platform.isWindows) {
final normalized = dir.path.replaceAll('/', '\\');
if (normalized != dir.path) {
KRLogUtil.kr_i('路径规范化: ${dir.path} -> $normalized', tag: 'SingBox');
return Directory(normalized);
}
}
return dir;
}
kr_configDics = (
baseDir: baseDir,
workingDir: workingDir!,
tempDir: tempDir,
baseDir: normalizePath(baseDir),
workingDir: normalizePath(workingDir!),
tempDir: normalizePath(tempDir),
);
KRLogUtil.kr_i('其他平台路径初始化完成');
}
KRLogUtil.kr_i('开始创建目录');
KRLogUtil.kr_i('baseDir: ${kr_configDics.baseDir.path}', tag: 'SingBox');
KRLogUtil.kr_i('workingDir: ${kr_configDics.workingDir.path}', tag: 'SingBox');
KRLogUtil.kr_i('tempDir: ${kr_configDics.tempDir.path}', tag: 'SingBox');
// 确保所有目录都存在
if (!kr_configDics.baseDir.existsSync()) {
await kr_configDics.baseDir.create(recursive: true);
KRLogUtil.kr_i('已创建 baseDir', tag: 'SingBox');
}
if (!kr_configDics.workingDir.existsSync()) {
await kr_configDics.workingDir.create(recursive: true);
KRLogUtil.kr_i('已创建 workingDir', tag: 'SingBox');
}
if (!kr_configDics.tempDir.existsSync()) {
await kr_configDics.tempDir.create(recursive: true);
KRLogUtil.kr_i('已创建 tempDir', tag: 'SingBox');
}
// 创建 libcore 数据库所需的 data 目录(在 workingDir 下)
// 注意:libcore 的 Setup 会调用 os.Chdir(workingPath),所以 data 目录必须在 workingDir 下
final dataDir = Directory(p.join(kr_configDics.workingDir.path, 'data'));
// 强制确保 data 目录存在(Windows 可能需要多次尝试)
int retryCount = 0;
const maxRetries = 5;
while (!dataDir.existsSync() && retryCount < maxRetries) {
try {
await dataDir.create(recursive: true);
// 等待文件系统同步(Windows 上可能需要一点时间)
await Future.delayed(const Duration(milliseconds: 100));
// 验证目录确实创建成功
if (dataDir.existsSync()) {
KRLogUtil.kr_i('✅ 已创建 data 目录: ${dataDir.path}', tag: 'SingBox');
break;
} else {
retryCount++;
KRLogUtil.kr_i('⚠️ data 目录创建后验证失败,重试 $retryCount/$maxRetries', tag: 'SingBox');
await Future.delayed(const Duration(milliseconds: 200));
}
} catch (e) {
retryCount++;
KRLogUtil.kr_e('❌ 创建 data 目录失败 (尝试 $retryCount/$maxRetries): $e', tag: 'SingBox');
if (retryCount >= maxRetries) {
throw Exception('无法创建 libcore 数据库目录: ${dataDir.path},错误: $e');
}
final delayMs = 200 * retryCount;
await Future.delayed(Duration(milliseconds: delayMs));
}
}
if (!dataDir.existsSync()) {
final error = 'data 目录不存在: ${dataDir.path}';
KRLogUtil.kr_e('$error', tag: 'SingBox');
throw Exception(error);
}
// 验证目录权限(尝试创建一个测试文件)
try {
final testFile = File(p.join(dataDir.path, '.test_write'));
await testFile.writeAsString('test');
await testFile.delete();
KRLogUtil.kr_i('✅ data 目录写入权限验证通过', tag: 'SingBox');
} catch (e) {
KRLogUtil.kr_e('⚠️ data 目录写入权限验证失败: $e', tag: 'SingBox');
// 不抛出异常,让 libcore 自己处理
}
// 在 Windows 上额外等待,确保文件系统操作完成
if (Platform.isWindows) {
await Future.delayed(const Duration(milliseconds: 300));
KRLogUtil.kr_i('⏳ Windows 文件系统同步等待完成', tag: 'SingBox');
}
// 最终验证:在 setup() 之前再次确认 workingDir 和 data 目录都存在且可访问
// libcore 的 Setup() 会调用 os.Chdir(workingPath),然后使用相对路径 "./data"
// 如果 os.Chdir() 失败(路径不存在或权限问题),后续的相对路径访问会失败
if (!kr_configDics.workingDir.existsSync()) {
final error = '❌ workingDir 不存在,无法调用 setup(): ${kr_configDics.workingDir.path}';
KRLogUtil.kr_e(error, tag: 'SingBox');
throw Exception(error);
}
// 验证 workingDir 可读可写
try {
final testWorkingFile = File(p.join(kr_configDics.workingDir.path, '.test_working_dir'));
await testWorkingFile.writeAsString('test');
await testWorkingFile.delete();
KRLogUtil.kr_i('✅ workingDir 写入权限验证通过', tag: 'SingBox');
} catch (e) {
final error = '❌ workingDir 无写入权限: ${kr_configDics.workingDir.path}, 错误: $e';
KRLogUtil.kr_e(error, tag: 'SingBox');
throw Exception(error);
}
final finalDataDir = Directory(p.join(kr_configDics.workingDir.path, 'data'));
if (!finalDataDir.existsSync()) {
KRLogUtil.kr_e('❌ 最终验证失败:data 目录不存在', tag: 'SingBox');
KRLogUtil.kr_e('路径: ${finalDataDir.path}', tag: 'SingBox');
KRLogUtil.kr_e('workingDir 是否存在: ${kr_configDics.workingDir.existsSync()}', tag: 'SingBox');
if (kr_configDics.workingDir.existsSync()) {
try {
final workingDirContents = kr_configDics.workingDir.listSync();
KRLogUtil.kr_e('workingDir 内容: ${workingDirContents.map((e) => e.path.split(Platform.pathSeparator).last).join(", ")}', tag: 'SingBox');
} catch (e) {
KRLogUtil.kr_e('无法列出 workingDir 内容: $e', tag: 'SingBox');
}
}
throw Exception('data 目录在 setup() 前验证失败: ${finalDataDir.path}');
}
// 再次尝试写入测试,确保目录确实可用
try {
final verifyFile = File(p.join(finalDataDir.path, '.verify_setup'));
await verifyFile.writeAsString('verify');
await verifyFile.delete();
KRLogUtil.kr_i('✅ setup() 前最终验证通过', tag: 'SingBox');
} catch (e) {
KRLogUtil.kr_e('❌ setup() 前最终验证失败: $e', tag: 'SingBox');
// 不抛出异常,让 setup() 自己处理
}
if (!directory.existsSync()) {
await directory.create(recursive: true);
KRLogUtil.kr_i('已创建 configs 目录', tag: 'SingBox');
}
KRLogUtil.kr_i('目录创建完成');
KRLogUtil.kr_i('目录创建完成', tag: 'SingBox');
KRLogUtil.kr_i('开始设置 SingBox');
KRLogUtil.kr_i('开始设置 SingBox', tag: 'SingBox');
KRLogUtil.kr_i(' - baseDir: ${kr_configDics.baseDir.path}', tag: 'SingBox');
KRLogUtil.kr_i(' - workingDir: ${kr_configDics.workingDir.path}', tag: 'SingBox');
KRLogUtil.kr_i(' - tempDir: ${kr_configDics.tempDir.path}', tag: 'SingBox');
KRLogUtil.kr_i(' - data 目录: ${p.join(kr_configDics.workingDir.path, "data")}', tag: 'SingBox');
KRLogUtil.kr_i(' - data 目录存在: ${finalDataDir.existsSync()}', tag: 'SingBox');
// 在 Windows 上,列出 data 目录内容(如果有文件)
if (Platform.isWindows && finalDataDir.existsSync()) {
try {
final dataContents = finalDataDir.listSync();
if (dataContents.isNotEmpty) {
KRLogUtil.kr_i(' - data 目录现有文件: ${dataContents.map((e) => e.path.split(Platform.pathSeparator).last).join(", ")}', tag: 'SingBox');
} else {
KRLogUtil.kr_i(' - data 目录为空', tag: 'SingBox');
}
} catch (e) {
KRLogUtil.kr_e(' - 无法列出 data 目录内容: $e', tag: 'SingBox');
}
}
KRLogUtil.kr_i(' - libcore 将通过 os.Chdir() 切换到: ${kr_configDics.workingDir.path}', tag: 'SingBox');
KRLogUtil.kr_i(' - 然后使用相对路径 "./data" 访问数据库', tag: 'SingBox');
// Windows 特定:验证路径格式是否正确
if (Platform.isWindows) {
final workingPath = kr_configDics.workingDir.path;
if (workingPath.contains('/')) {
KRLogUtil.kr_e('⚠️ 警告:Windows 路径包含正斜杠,可能导致问题: $workingPath', tag: 'SingBox');
}
// 确保路径使用反斜杠(Windows 标准)
final normalizedPath = workingPath.replaceAll('/', '\\');
if (normalizedPath != workingPath) {
KRLogUtil.kr_e('⚠️ 路径格式可能需要规范化: $workingPath -> $normalizedPath', tag: 'SingBox');
}
}
await kr_singBox.setup(kr_configDics, false).map((r) {
KRLogUtil.kr_i('SingBox 设置成功');
KRLogUtil.kr_i('SingBox setup() 调用成功', tag: 'SingBox');
// setup() 返回成功,但可能数据库初始化还在进行中
// 在 Windows 上额外等待,确保 libcore 内部的服务初始化完成
if (Platform.isWindows) {
KRLogUtil.kr_i('⏳ Windows: 等待 libcore 服务初始化完成...', tag: 'SingBox');
}
return r;
}).mapLeft((err) {
KRLogUtil.kr_e('SingBox 设置失败: $err');
KRLogUtil.kr_e('SingBox setup() 调用失败: $err', tag: 'SingBox');
KRLogUtil.kr_e('诊断信息:', tag: 'SingBox');
KRLogUtil.kr_e(' - workingDir: ${kr_configDics.workingDir.path}', tag: 'SingBox');
KRLogUtil.kr_e(' - workingDir 存在: ${kr_configDics.workingDir.existsSync()}', tag: 'SingBox');
KRLogUtil.kr_e(' - data 目录绝对路径: ${p.join(kr_configDics.workingDir.path, "data")}', tag: 'SingBox');
KRLogUtil.kr_e(' - data 目录存在: ${finalDataDir.existsSync()}', tag: 'SingBox');
if (finalDataDir.existsSync()) {
try {
final files = finalDataDir.listSync();
KRLogUtil.kr_e(' - data 目录文件数量: ${files.length}', tag: 'SingBox');
} catch (e) {
KRLogUtil.kr_e(' - 无法列出 data 目录: $e', tag: 'SingBox');
}
}
throw err;
}).run();
// setup() 成功后的额外等待(Windows 上数据库初始化可能需要时间)
if (Platform.isWindows) {
await Future.delayed(const Duration(milliseconds: 500));
KRLogUtil.kr_i('⏳ Windows: libcore 初始化等待完成', tag: 'SingBox');
}
KRLogUtil.kr_i('开始更新 SingBox 选项');
KRLogUtil.kr_i('📋 SingBox 配置选项: ${oOption.toJson()}', tag: 'SingBox');
+8
View File
@@ -27,6 +27,13 @@ class KRSecureStorage {
if (Platform.isMacOS || Platform.isWindows || Platform.isLinux) {
final baseDir = await getApplicationSupportDirectory();
KRLogUtil.kr_i('初始化 Hive,路径: ${baseDir.path}', tag: 'SecureStorage');
// 确保目录存在
if (!baseDir.existsSync()) {
await baseDir.create(recursive: true);
KRLogUtil.kr_i('已创建 Hive 目录: ${baseDir.path}', tag: 'SecureStorage');
}
await Hive.initFlutter(baseDir.path);
} else {
// Android 和 iOS 使用默认路径
@@ -39,6 +46,7 @@ class KRSecureStorage {
KRLogUtil.kr_i('Hive 初始化成功', tag: 'SecureStorage');
} catch (e, stackTrace) {
KRLogUtil.kr_e('初始化 Hive 失败: $e', tag: 'SecureStorage');
KRLogUtil.kr_e('错误类型: ${e.runtimeType}', tag: 'SecureStorage');
KRLogUtil.kr_e('错误堆栈: $stackTrace', tag: 'SecureStorage');
// 对于 Windows 和 Linux,如果初始化失败,尝试删除旧文件并重试
+6
View File
@@ -2,6 +2,12 @@
cmake_minimum_required(VERSION 3.14)
project(BearVPN LANGUAGES CXX)
# 设置 CMake 策略以兼容旧版本插件
# CMP0175: add_custom_command() 拒绝无效参数(用于兼容 flutter_inappwebview_windows 插件)
if(POLICY CMP0175)
cmake_policy(SET CMP0175 OLD)
endif()
# 设置静态链接
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT")