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:
QiuSW
2026-09-03 09:22:53 +08:00
co-authored by Claude Opus 5
parent 656b7a8c49
commit e7c3c6849c
2 changed files with 55 additions and 3 deletions
+25 -2
View File
@@ -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)
}
+30 -1
View File
@@ -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("编码不一致的命令行不应被判为属于本程序")
}
}