Fix: Decode client file path correctly in client list and info popup

The client reports its own exe path as UTF-8 in RES_FILE_PATH. Two
display sites re-decoded those bytes with the wrong code page and turned
Chinese paths into mojibake on non-Chinese servers:

- The client-list "程序路径" column wrote ProgramPath through the ANSI
  SetItemText path, so the UTF-8 bytes were misread as the system ACP.
  Decode UTF-8 to wide and write with LVM_SETITEMTEXTW instead (same
  pattern as the window/process lists).

- The double-click info popup normalized the client RES_* fields to the
  system ACP and rendered strText with CP_ACP -> wide. The app's internal
  ANSI convention is fixed GBK/936 (/execution-charset:.936 plus
  setlocale(".936")), so on non-Chinese systems the path collapsed to '?'.
  Normalize to 936 and decode 936 instead, which also fixes the popup's
  GBK labels and username in the same pass.

Co-Authored-By: deepseek-v4-pro
This commit is contained in:
yuanyuanxianganddeepseek-v4-pro committed 2026-10-09 15:00:39 +02:00
1 parent ff6b290580
commit f3a8406050
2 files changed
+35 -13

No files matched your search

+11 -11
View File
@@ -8779,22 +8779,21 @@ void CMy2015RemoteDlg::OnListClick(NMHDR* pNMHDR, LRESULT* pResult)
CString startTime = ctx->GetClientData(ONLINELIST_STARTTIME); CString startTime = ctx->GetClientData(ONLINELIST_STARTTIME);
ctx->GetAdditionalData(res); ctx->GetAdditionalData(res);
// 客户端 RES_* 字符串编码取决于客户端能力位:UTF-8 客户端(Linux/macOS/新 Win) // 客户端 RES_* 字符串编码取决于客户端能力位:UTF-8 客户端(Linux/macOS/新 Win)
// 发的是 UTF-8 字节,老客户端是 CP_ACP。这里统一规整到 CP_ACP,让下游 FormatL // 发的是 UTF-8 字节,老客户端是 GBK。这里统一规整到 GBK(936) —— 工程内部 ANSI
// 与既有 ANSI 字符串拼接以及最终 CP_ACP→wide 的浮窗渲染都能正确识别。 // 约定即 GBK(/execution-charset:.936 + setlocale(".936")),不随系统 ACP 变。
// 若服务端运行系统的 ANSI 代码页不能容纳客户端字符(如德语服务端遇到中文路径), // 下游 FormatL 拼接、以及最终 936→wide 的浮窗渲染都能据此正确识别中文。
// 不可表示的字符会变 '?' —— 与项目其它路径的既有限制一致,不在本次修复范围。
UINT cp = GetClientEncoding(ctx); UINT cp = GetClientEncoding(ctx);
if (cp != CP_ACP) { if (cp != 936) {
for (int i = 0; i < RES_MAX; i++) { for (int i = 0; i < RES_MAX; i++) {
if (res[i].IsEmpty()) continue; if (res[i].IsEmpty()) continue;
int wlen = MultiByteToWideChar(cp, 0, res[i].GetString(), -1, NULL, 0); int wlen = MultiByteToWideChar(cp, 0, res[i].GetString(), -1, NULL, 0);
if (wlen <= 1) continue; if (wlen <= 1) continue;
std::wstring wbuf(wlen - 1, L'\0'); std::wstring wbuf(wlen - 1, L'\0');
MultiByteToWideChar(cp, 0, res[i].GetString(), -1, &wbuf[0], wlen); MultiByteToWideChar(cp, 0, res[i].GetString(), -1, &wbuf[0], wlen);
int alen = WideCharToMultiByte(CP_ACP, 0, wbuf.c_str(), -1, NULL, 0, NULL, NULL); int alen = WideCharToMultiByte(936, 0, wbuf.c_str(), -1, NULL, 0, NULL, NULL);
if (alen <= 1) continue; if (alen <= 1) continue;
CString out; CString out;
WideCharToMultiByte(CP_ACP, 0, wbuf.c_str(), -1, WideCharToMultiByte(936, 0, wbuf.c_str(), -1,
out.GetBufferSetLength(alen - 1), alen, NULL, NULL); out.GetBufferSetLength(alen - 1), alen, NULL, NULL);
out.ReleaseBuffer(alen - 1); out.ReleaseBuffer(alen - 1);
res[i] = out; res[i] = out;
@@ -8849,13 +8848,14 @@ void CMy2015RemoteDlg::OnListClick(NMHDR* pNMHDR, LRESULT* pResult)
} }
auto* tip = new CPreviewTipWnd(); auto* tip = new CPreviewTipWnd();
// 项目是 MBCS(CP_ACP),浮窗内部用宽字符渲染(避免跨语言系统乱码)。 // 浮窗内部用宽字符渲染。strText 里的中文(标签来自 GBK 源字面量,路径/用户名
// 这里把 strText (CStringA) 按 CP_ACP 转 wide。 // 已在上方规整为 GBK)统一是 GBK(936),按 936 转 wide 而非系统 CP_ACP ——
// 否则非中文 ACP(德语/英语等)服务端会把中文解成乱码。
CStringW textW; CStringW textW;
{ {
int wlen = MultiByteToWideChar(CP_ACP, 0, strText.GetString(), -1, NULL, 0); int wlen = MultiByteToWideChar(936, 0, strText.GetString(), -1, NULL, 0);
if (wlen > 1) { if (wlen > 1) {
MultiByteToWideChar(CP_ACP, 0, strText.GetString(), -1, textW.GetBufferSetLength(wlen - 1), wlen); MultiByteToWideChar(936, 0, strText.GetString(), -1, textW.GetBufferSetLength(wlen - 1), wlen);
textW.ReleaseBuffer(wlen - 1); textW.ReleaseBuffer(wlen - 1);
} }
} }
+24 -2
View File
@@ -5,6 +5,7 @@
#include "afxdialogex.h" #include "afxdialogex.h"
#include "CClientListDlg.h" #include "CClientListDlg.h"
#include "2015Remote.h" #include "2015Remote.h"
#include <string>
// CClientListDlg 对话框 // CClientListDlg 对话框
@@ -93,6 +94,27 @@ static CString GetFieldValue(const ClientValue& val, GroupField field)
} }
} }
// 客户端自报的程序路径是 UTF-8(见 client/LoginServer.cpp 的 RES_FILE_PATH),
// 这里用 LVM_SETITEMTEXTW 直接写宽字符串,绕开 ANSI→CP_ACP 回转,
// 即使服务端运行在非中文 ACP(如德语)上,中文路径也能正常显示(与窗口/进程管理同法)。
static void SetProgramPathText(CListCtrl& list, int iItem, int iSubItem, const char* sz)
{
std::wstring w;
if (sz && sz[0]) {
int wlen = MultiByteToWideChar(CP_UTF8, 0, sz, -1, NULL, 0);
if (wlen > 1) {
w.resize(wlen - 1);
MultiByteToWideChar(CP_UTF8, 0, sz, -1, &w[0], wlen);
}
}
LVITEMW lv = {};
lv.mask = LVIF_TEXT;
lv.iItem = iItem;
lv.iSubItem = iSubItem;
lv.pszText = w.empty() ? const_cast<LPWSTR>(L"") : &w[0];
::SendMessageW(list.GetSafeHwnd(), LVM_SETITEMTEXTW, (WPARAM)iItem, (LPARAM)&lv);
}
// 比较两个客户端的指定列,返回 <0, 0, >0 // 比较两个客户端的指定列,返回 <0, 0, >0
static int CompareClientByColumn(const std::pair<ClientKey, ClientValue>& a, static int CompareClientByColumn(const std::pair<ClientKey, ClientValue>& a,
const std::pair<ClientKey, ClientValue>& b, const std::pair<ClientKey, ClientValue>& b,
@@ -299,7 +321,7 @@ void CClientListDlg::DisplayClients()
m_ClientList.SetItemText(nItem, COL_OS, val.OsName); m_ClientList.SetItemText(nItem, COL_OS, val.OsName);
m_ClientList.SetItemText(nItem, COL_INSTALL_TIME, val.InstallTime); m_ClientList.SetItemText(nItem, COL_INSTALL_TIME, val.InstallTime);
m_ClientList.SetItemText(nItem, COL_LAST_LOGIN, val.LastLoginTime); m_ClientList.SetItemText(nItem, COL_LAST_LOGIN, val.LastLoginTime);
m_ClientList.SetItemText(nItem, COL_PROGRAM_PATH, CString(val.ProgramPath)); SetProgramPathText(m_ClientList, nItem, COL_PROGRAM_PATH, val.ProgramPath);
m_ClientList.SetItemText(nItem, COL_LEVEL, strLevel); m_ClientList.SetItemText(nItem, COL_LEVEL, strLevel);
m_ClientList.SetItemText(nItem, COL_AUTH, strAuth); m_ClientList.SetItemText(nItem, COL_AUTH, strAuth);
m_ClientList.SetItemData(nItem, (DWORD_PTR)key); m_ClientList.SetItemData(nItem, (DWORD_PTR)key);
@@ -351,7 +373,7 @@ void CClientListDlg::DisplayClients()
m_ClientList.SetItemText(nItem, COL_OS, val.OsName); m_ClientList.SetItemText(nItem, COL_OS, val.OsName);
m_ClientList.SetItemText(nItem, COL_INSTALL_TIME, val.InstallTime); m_ClientList.SetItemText(nItem, COL_INSTALL_TIME, val.InstallTime);
m_ClientList.SetItemText(nItem, COL_LAST_LOGIN, val.LastLoginTime); m_ClientList.SetItemText(nItem, COL_LAST_LOGIN, val.LastLoginTime);
m_ClientList.SetItemText(nItem, COL_PROGRAM_PATH, CString(val.ProgramPath)); SetProgramPathText(m_ClientList, nItem, COL_PROGRAM_PATH, val.ProgramPath);
m_ClientList.SetItemText(nItem, COL_LEVEL, strLevel); m_ClientList.SetItemText(nItem, COL_LEVEL, strLevel);
m_ClientList.SetItemText(nItem, COL_AUTH, strAuth); m_ClientList.SetItemText(nItem, COL_AUTH, strAuth);
m_ClientList.SetItemData(nItem, (DWORD_PTR)key); m_ClientList.SetItemData(nItem, (DWORD_PTR)key);