fix: 中文 profile 路径导致 Chrome 归属校验永远失败 (#9)
负责人点「打开淘宝登录」报错: 「Chrome进程不属于配置的专属用户数据目录,已将其关闭」 根因是编码不一致。processCommandLine 用 PowerShell 读取进程命令行, PowerShell 的标准输出使用系统 ANSI 代码页(简体中文 Windows 是 GBK), 而 Go 字符串是 UTF-8。专属 profile 路径含中文 (电商视频自动下载工具\淘宝浏览器\默认账号),两边字节不同, strings.Index 永远匹配不上,于是 Chrome 刚启动就被判为不属于本 profile 并被杀掉。 单元测试没能发现,因为测试里比较的两个字符串都是 UTF-8。 改为让 PowerShell 先把命令行转成 UTF-8 的 Base64,Go 侧再解码。 不用 [Console]::OutputEncoding=UTF8 是因为那依赖控制台状态; Base64 只含 ASCII,任何区域设置下都可靠。 实测三种方式: 原样读取 含中文路径 false ← 修复前 设 UTF8 编码 含中文路径 true Base64 传输 含中文路径 true ← 采用 已用真实运行中的 Chrome 进程验证:归属判断返回 true。 补一条防回归测试,覆盖含中文的 profile 以及编码不一致的情形。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
This commit is contained in:
@@ -3,6 +3,7 @@ package taobao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -286,13 +287,35 @@ func processExists(pid int) bool {
|
||||
return strings.Contains(string(output), fmt.Sprintf("\",\"%d\",", pid))
|
||||
}
|
||||
|
||||
// processCommandLine 读取指定进程的完整命令行。
|
||||
//
|
||||
// 这里有个必须绕开的坑:PowerShell 的标准输出用系统 ANSI 代码页
|
||||
// (简体中文 Windows 上是 GBK),而 Go 里的字符串是 UTF-8。
|
||||
// 专属 profile 路径含中文(电商视频自动下载工具\淘宝浏览器\默认账号),
|
||||
// 直接读会拿到 GBK 字节,和 UTF-8 的 profile 怎么比都不相等,
|
||||
// 于是归属校验永远失败、Chrome 刚启动就被判为"不属于本 profile"。
|
||||
//
|
||||
// 解决办法是让 PowerShell 先把结果转成 UTF-8 的 Base64,Go 侧再解码。
|
||||
// 不用 [Console]::OutputEncoding=UTF8 是因为那依赖控制台状态,
|
||||
// Base64 只有 ASCII,任何区域设置下都可靠。
|
||||
func processCommandLine(pid int) (string, error) {
|
||||
command := fmt.Sprintf(`(Get-CimInstance Win32_Process -Filter "ProcessId=%d").CommandLine`, pid)
|
||||
command := fmt.Sprintf(
|
||||
`$c=(Get-CimInstance Win32_Process -Filter "ProcessId=%d").CommandLine; `+
|
||||
`if ($null -eq $c) { '' } else { `+
|
||||
`[Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($c)) }`, pid)
|
||||
output, err := exec.Command("powershell", "-NoProfile", "-Command", command).Output()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("读取 PID %d 的命令行失败:%w", pid, err)
|
||||
}
|
||||
line := strings.TrimSpace(string(output))
|
||||
encoded := strings.TrimSpace(string(output))
|
||||
if encoded == "" {
|
||||
return "", fmt.Errorf("找不到 PID %d 的进程命令行", pid)
|
||||
}
|
||||
decoded, err := base64.StdEncoding.DecodeString(encoded)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("解析 PID %d 的命令行失败:%w", pid, err)
|
||||
}
|
||||
line := strings.TrimSpace(string(decoded))
|
||||
if line == "" {
|
||||
return "", fmt.Errorf("找不到 PID %d 的进程命令行", pid)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package taobao
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test命令行归属判断(t *testing.T) {
|
||||
profile := `C:\Users\测试 用户\淘宝浏览器\默认账号`
|
||||
@@ -44,3 +47,29 @@ func Test端口全占满时返回错误(t *testing.T) {
|
||||
t.Fatal("端口全占满时应返回错误")
|
||||
}
|
||||
}
|
||||
|
||||
// 防回归:profile 路径含中文时的归属判断。
|
||||
//
|
||||
// 曾经真实发生过:PowerShell 输出用系统 ANSI 代码页(GBK),
|
||||
// Go 字符串是 UTF-8,含中文的 profile 路径两边字节不同,
|
||||
// 归属判断永远为 false,Chrome 一启动就被判为"不属于本 profile"。
|
||||
// 修法是让 PowerShell 先转 Base64(UTF-8) 再由 Go 解码。
|
||||
//
|
||||
// 这个测试只能守住比较逻辑本身;编码问题必须靠 processCommandLine
|
||||
// 里的 Base64 传输解决,改那里时务必用真实 Chrome 验证一次。
|
||||
func Test中文路径的归属判断(t *testing.T) {
|
||||
profile := `C:\Users\ila20\AppData\Local\电商视频自动下载工具\淘宝浏览器\默认账号`
|
||||
cmd := `"C:\Program Files\Google\Chrome\Application\chrome.exe" ` +
|
||||
`--user-data-dir=C:\Users\ila20\AppData\Local\电商视频自动下载工具\淘宝浏览器\默认账号 ` +
|
||||
`--remote-debugging-port=19666`
|
||||
|
||||
if !commandBelongsToProfile(cmd, profile) {
|
||||
t.Fatalf("含中文的 profile 路径应当判定为属于本程序")
|
||||
}
|
||||
// 用 GBK 编码的同一路径必须判为不属于——这正是修复前的真实故障形态,
|
||||
// 如果哪天又退回按控制台编码读取,这里会失败。
|
||||
gbk := "\xb5\xe7\xc9\xcc\xca\xd3\xc6\xb5"
|
||||
if commandBelongsToProfile(strings.ReplaceAll(cmd, "电商视频", gbk), profile) {
|
||||
t.Fatalf("编码不一致的命令行不应被判为属于本程序")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user