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:
1 parent
ff6b290580
commit
f3a8406050
2 files changed
+35
-13
No files matched your search
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in new issue
Block a user