From ff6b290580166c138beb2c07c029a4c72bb223f2 Mon Sep 17 00:00:00 2001 From: yuanyuanxiang <962914132@qq.com> Date: Fri, 9 Oct 2026 12:43:11 +0200 Subject: [PATCH] Fix: Decode process list names/paths as GBK on Windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Process management decoded the client's process name/path bytes with GetClientEncoding, which returns UTF-8 for CLIENT_CAP_UTF8 clients. But the Windows client builds the process list with the ANSI A-interface (Process32FirstA / QueryFullProcessImageNameA), which emits the client's system code page (GBK on Chinese Windows) regardless of the UTF-8 capability -- unlike window titles, which were migrated to the W-interface plus UTF-8. Decoding GBK as UTF-8 garbled Chinese process names and paths. Add GetProcessListEncoding(): 936 for Windows clients, UTF-8 for LNX/MAC, matching McpServer::BuildListProcesses, and use it in ShowProcessList (which still writes via LVM_SETITEMTEXTW). Also raise the IP history cap to 1000 and rename the experimental "隐私屏幕" menu item to "虚拟屏幕". Co-Authored-By: deepseek-v4-pro --- server/2015Remote/2015Remote.rc | Bin 178156 -> 178156 bytes server/2015Remote/2015RemoteDlg.cpp | 22 ++++++++++++++++++++ server/2015Remote/2015RemoteDlg.h | 7 +++++++ server/2015Remote/CPasswordDlg.cpp | 2 +- server/2015Remote/SystemDlg.cpp | 31 +++++++++++++++++++++++++--- 5 files changed, 58 insertions(+), 4 deletions(-) diff --git a/server/2015Remote/2015Remote.rc b/server/2015Remote/2015Remote.rc index b32effda82e03f55abfbd2ebb9d6009b08cb39c4..329cb53f1a85c9795c70f16d2a9b1ab4d8f748a9 100644 GIT binary patch delta 22 ecmaF!p6ktfu7)j)_wG;s@|4kOyTSv;GuHu_)eOr3 delta 22 ecmaF!p6ktfu7)j)_wG;cddBFqUEu-and<GetAdditionalData(RES_CLIENT_TYPE); + if (clientType == "LNX" || clientType == "MAC") return CP_UTF8; + // 子连接情形:自身 RES_CLIENT_TYPE 为空 -> 找主连接取 clientType + if (g_2015RemoteDlg) { + context* mainCtx = g_2015RemoteDlg->FindHostByClientID(ctx->GetClientID()); + if (!mainCtx) { + mainCtx = g_2015RemoteDlg->FindHostByIP(ctx->GetPeerName()); + } + if (mainCtx) { + CString mainType = mainCtx->GetAdditionalData(RES_CLIENT_TYPE); + if (mainType == "LNX" || mainType == "MAC") return CP_UTF8; + } + } + return 936; +} + // 授权日志频率控制:首次必须记录,状态变化必须记录,相同状态每小时记录一次 static bool ShouldLogAuth(const std::string& sn, int success) { struct AuthLogState { diff --git a/server/2015Remote/2015RemoteDlg.h b/server/2015Remote/2015RemoteDlg.h index 037c440..e811092 100644 --- a/server/2015Remote/2015RemoteDlg.h +++ b/server/2015Remote/2015RemoteDlg.h @@ -109,6 +109,13 @@ bool SupportsFileTransferV2(context* ctx); // 找不到主连接或老客户端:默认 CP936(覆盖 95% 简中/英语 ASCII 老客户端)。 UINT GetClientEncoding(context* ctx); +// 获取客户端「进程名/路径」编码 (CP_UTF8 或 936)。 +// 与 GetClientEncoding 不同:Windows 客户端的进程名/路径由 A 接口 +// (Process32FirstA / QueryFullProcessImageNameA 等)生成,恒为客户端系统 +// ANSI(简中=GBK/936),不随 CLIENT_CAP_UTF8 转 UTF-8;仅 LNX/MAC 走 UTF-8。 +// 与 McpServer::BuildListProcesses 的判定保持一致。 +UINT GetProcessListEncoding(context* ctx); + // 服务端待续传的传输信息 struct PendingTransferV2 { uint64_t clientID; diff --git a/server/2015Remote/CPasswordDlg.cpp b/server/2015Remote/CPasswordDlg.cpp index 5217d01..d3a5775 100644 --- a/server/2015Remote/CPasswordDlg.cpp +++ b/server/2015Remote/CPasswordDlg.cpp @@ -250,7 +250,7 @@ bool UpdateLicenseAuthorization(const std::string& deviceID, const std::string& } // IP 列表管理常量 -#define MAX_IP_HISTORY 500 // 最多保留 几 个不同的 IP +#define MAX_IP_HISTORY 1000 // 最多保留 几 个不同的 IP // 解析 IP 列表字符串为 vector> // 格式: "192.168.1.1|260218, 10.0.0.1|260215" (yyMMdd) diff --git a/server/2015Remote/SystemDlg.cpp b/server/2015Remote/SystemDlg.cpp index 93c480d..61c5a3e 100644 --- a/server/2015Remote/SystemDlg.cpp +++ b/server/2015Remote/SystemDlg.cpp @@ -232,6 +232,26 @@ void CSystemDlg::ShowWindowsList(void) ApplySort(); // 应用记忆的排序偏好 } +// 按客户端编码 (936 或 UTF-8) 解码字节串,并用 LVM_SETITEMTEXTW 写入列表单元格, +// 绕开 ANSI→CP_ACP 回转(非中文 ACP 服务端上中文也能正常显示,与窗口管理同法)。 +static void SetClientTextItem(CListCtrl& list, int iItem, int iSubItem, const char* sz, UINT cp) +{ + std::wstring w; + if (sz && sz[0]) { + int wlen = MultiByteToWideChar(cp, 0, sz, -1, NULL, 0); + if (wlen > 1) { + w.resize(wlen - 1); + MultiByteToWideChar(cp, 0, sz, -1, &w[0], wlen); + } + } + LVITEMW lv = {}; + lv.mask = LVIF_TEXT; + lv.iItem = iItem; + lv.iSubItem = iSubItem; + lv.pszText = w.empty() ? const_cast(L"") : &w[0]; + ::SendMessageW(list.GetSafeHwnd(), LVM_SETITEMTEXTW, (WPARAM)iItem, (LPARAM)&lv); +} + void CSystemDlg::ShowProcessList(void) { // GetMyBuffer(1) 深拷贝数据,跳过 TOKEN 字节 @@ -248,6 +268,10 @@ void CSystemDlg::ShowProcessList(void) const char *szProcessFullPath; DWORD dwOffset = 0; CString str; + // 进程名/路径编码:Windows 客户端 A 接口生成,恒为 GBK/936;仅 LNX/MAC 为 UTF-8。 + // 不能用 GetClientEncoding(它对新客户端返回 CP_UTF8,会把 GBK 进程名解成乱码)。 + UINT cp = GetProcessListEncoding(m_ContextObject); + DeleteAllItems(); //遍历发送来的每一个字符别忘了他的数据结构啊 Id+进程名+0+完整名+0 int i; @@ -258,10 +282,11 @@ void CSystemDlg::ShowProcessList(void) szProcessFullPath = szExeFile + lstrlen(szExeFile) + 1; //完整名就是进程名之后的啦 //他的数据结构的构建很巧妙 - m_ControlList.InsertItem(i, arr[0].c_str()); //将得到的数据加入到列表当中 + m_ControlList.InsertItem(i, ""); // 占位,首列随后用 Unicode 写入 str.FormatL("%5u", *PID); - m_ControlList.SetItemText(i, 1, str); - m_ControlList.SetItemText(i, 2, szProcessFullPath); + m_ControlList.SetItemText(i, 1, str); // PID (ASCII) + SetClientTextItem(m_ControlList, i, 0, arr[0].c_str(), cp); // 进程名(按客户端编码) + SetClientTextItem(m_ControlList, i, 2, szProcessFullPath, cp); // 程序路径(按客户端编码) m_ControlList.SetItemText(i, 3, arr[1].empty() ? "N/A" : arr[1].c_str()); // ItemData 为进程ID;Data[3] 需与第 4 列"架构"一致,否则按架构排序拿到空串而无意义 auto data = new ItemData{ *PID, {arr[0].c_str(), str, szProcessFullPath, arr[1].empty() ? "N/A" : arr[1].c_str()}, arr[1].c_str() };