fix: windows识别邀请码
This commit is contained in:
parent
d60f1d3c5d
commit
6da7649720
@ -91,6 +91,15 @@ class KRDeviceUtil {
|
||||
String? code = _kr_extractCode(executablePath);
|
||||
if (code != null) return code;
|
||||
|
||||
// 策略 1.5: (Windows 独有) 检测安装目录下的 channel.txt (支持 Inno Setup 方案)
|
||||
if (Platform.isWindows) {
|
||||
code = await _kr_getInviteCodeFromChannelFile(executablePath);
|
||||
if (code != null) {
|
||||
KRLogUtil.kr_i('🎯 从 channel.txt 获取到邀请码: $code', tag: 'DeviceUtil');
|
||||
return code;
|
||||
}
|
||||
}
|
||||
|
||||
if (Platform.isMacOS) {
|
||||
// 策略 2: 如果在 /Volumes 下运行,尝试找 DMG 原文件名
|
||||
if (executablePath.contains('/Volumes/')) {
|
||||
@ -107,29 +116,31 @@ class KRDeviceUtil {
|
||||
KRLogUtil.kr_i('🎯 从 mdls (文件元数据) 获取到邀请码: $code', tag: 'DeviceUtil');
|
||||
return code;
|
||||
}
|
||||
|
||||
// 策略 4: 在下载目录下寻找最近的带有 ic- 的 DMG
|
||||
code = await _kr_searchDownloadsForInviteCode();
|
||||
if (code != null) {
|
||||
KRLogUtil.kr_i('🎯 从下载目录搜索获取到邀请码: $code', tag: 'DeviceUtil');
|
||||
return code;
|
||||
}
|
||||
|
||||
KRLogUtil.kr_i('⚠️ 深度检测完成,未发现有效的邀请码标识', tag: 'DeviceUtil');
|
||||
}
|
||||
|
||||
// 策略 4: 在下载目录下寻找最近的带有 ic- 的程序 (MacOS/Windows 通用)
|
||||
code = await _kr_searchDownloadsForInviteCode();
|
||||
if (code != null) {
|
||||
KRLogUtil.kr_i('🎯 从下载目录搜索获取到邀请码: $code', tag: 'DeviceUtil');
|
||||
return code;
|
||||
}
|
||||
|
||||
KRLogUtil.kr_i('⚠️ 深度检测完成,未发现有效的邀请码标识', tag: 'DeviceUtil');
|
||||
} catch (e) {
|
||||
KRLogUtil.kr_e('解析桌面邀请码异常: $e', tag: 'DeviceUtil');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/// (MacOS) 在下载目录下搜寻最近的、符合命名规范的 DMG
|
||||
/// (MacOS/Windows) 在下载目录下搜寻最近的、符合命名规范的安装包
|
||||
Future<String?> _kr_searchDownloadsForInviteCode() async {
|
||||
try {
|
||||
final String home = Platform.environment['HOME'] ?? '';
|
||||
// 适配不同平台的家目录环境变量
|
||||
final String home = Platform.environment['HOME'] ??
|
||||
Platform.environment['USERPROFILE'] ?? '';
|
||||
if (home.isEmpty) return null;
|
||||
|
||||
final Directory downloads = Directory('$home/Downloads');
|
||||
final Directory downloads = Directory('${home}${Platform.pathSeparator}Downloads');
|
||||
if (!await downloads.exists()) return null;
|
||||
|
||||
final List<FileSystemEntity> files = await downloads.list().toList();
|
||||
@ -154,7 +165,31 @@ class KRDeviceUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
/// 正则提取 ic- 模式
|
||||
/// (Windows) 从安装目录下的 channel.txt 中读取邀请码 (针对 Inno Setup 方案)
|
||||
Future<String?> _kr_getInviteCodeFromChannelFile(String execPath) async {
|
||||
try {
|
||||
final File exeFile = File(execPath);
|
||||
final String dirPath = exeFile.parent.path;
|
||||
final File channelFile = File('$dirPath${Platform.pathSeparator}channel.txt');
|
||||
|
||||
if (await channelFile.exists()) {
|
||||
final String content = await channelFile.readAsString();
|
||||
final String channel = content.trim();
|
||||
if (channel.isNotEmpty) {
|
||||
// 兼容两种格式:纯邀请码 或 带 ic- 标识的邀请码
|
||||
if (channel.contains('ic-')) {
|
||||
return _kr_extractCode(channel);
|
||||
}
|
||||
return channel; // 如果 Inno Setup 已经截取好了纯 code,直接返回
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
KRLogUtil.kr_e('读取 Windows channel.txt 失败: $e', tag: 'DeviceUtil');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// 正则提取 ic-模式
|
||||
String? _kr_extractCode(String source) {
|
||||
final RegExp regExp = RegExp(r'ic-([A-Za-z0-9-_]+)');
|
||||
final Match? match = regExp.firstMatch(source);
|
||||
|
||||
@ -138,6 +138,35 @@ begin
|
||||
Result := True;
|
||||
end;
|
||||
|
||||
procedure CurStepChanged(CurStep: TSetupStep);
|
||||
var
|
||||
FileName: String;
|
||||
ChannelName: String;
|
||||
P: Integer;
|
||||
begin
|
||||
if CurStep = ssPostInstall then
|
||||
begin
|
||||
// 获取当前安装包的文件名 (例如 Setup_ic-888.exe)
|
||||
FileName := ExpandConstant('{srcexe}');
|
||||
FileName := ExtractFileName(FileName);
|
||||
|
||||
// 逻辑:寻找 ic- 标识并提取
|
||||
P := Pos('ic-', FileName);
|
||||
if P > 0 then
|
||||
begin
|
||||
// 提取从 ic- 开始到结尾的部分
|
||||
ChannelName := Copy(FileName, P, Length(FileName) - P + 1);
|
||||
// 去掉 .exe 后缀
|
||||
P := Pos('.exe', LowerCase(ChannelName));
|
||||
if P > 0 then
|
||||
Delete(ChannelName, P, Length(ChannelName));
|
||||
|
||||
// 在安装目录下生成一个 channel.txt,供 Flutter 读取
|
||||
SaveStringToFile(ExpandConstant('{app}\channel.txt'), ChannelName, False);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function InitializeUninstall(): Boolean;
|
||||
begin
|
||||
TerminateProcesses();
|
||||
|
||||
7
邀请文档.md
7
邀请文档.md
@ -23,9 +23,10 @@
|
||||
- **识别规范**:文件名或路径中包含 `ic-<code>`,例如 `HiFastVPN-ic-888.dmg`。
|
||||
- **解析逻辑(分层检测策略)**:
|
||||
1. **直接路径匹配 (Win/Mac 通用)**:从当前运行的 `.exe` 或 `.app` 全路径中正则提取。这是最直接、最高效的手段。
|
||||
2. **挂载源追溯 (macOS 独有)**:通过 `hdiutil info` 关联运行中的 App 及其原始 `.dmg` 文件名。专门解决“程序未改名但在已命名的镜像中运行”的场景。
|
||||
3. **下载元数据提取 (macOS 独有)**:通过 `mdls` 读取文件扩展属性。即便安装包已清理,只要系统保留了下载来源记录,仍可识别。
|
||||
4. **下载目录智能扫描 (Win/Mac 通用)**:作为最后兜底,扫描用户下载目录(Downloads)下最近修改的、符合命名规范的镜像或安装包(`.dmg`/`.exe`)。
|
||||
2. **安装渠道配置文件 (Windows 独有)**:读取程序安装目录下的 `channel.txt`。支持 Inno Setup 在安装时通过 `{srcexe}` 获取安装包名并自动生成该文件。这为 Windows 提供了最稳定的安装后识别方案。
|
||||
3. **挂载源追溯 (macOS 独有)**:通过 `hdiutil info` 关联运行中的 App 及其原始 `.dmg` 文件名。专门解决“程序未改名但在已命名的镜像中运行”的场景。
|
||||
4. **下载元数据提取 (macOS 独有)**:通过 `mdls` 读取文件扩展属性。即便安装包已清理,只要系统保留了下载来源记录,仍可识别。
|
||||
5. **下载目录智能扫描 (Win/Mac 通用)**:作为最后兜底,扫描用户下载目录(Downloads)下最近修改的、符合命名规范的镜像或安装包(`.dmg`/`.exe`)。这一策略能有效解决用户下载后虽然安装程序没改名,但原始安装包依然留在下载文件夹中的场景。
|
||||
- **调试支持**:当 `inviteDebugMode` 开启且识别到邀请码时,桌面端会弹出确认对话框。
|
||||
|
||||
### 网络层标识(silentInvite)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user