Fix: Decode host-management and clipboard text as GBK/UTF-8 on non-Chinese systems

The 主机管理 dialog wrote client data (process names, window titles, net
connection state, software entries, IE history, favorites, services,
scheduled tasks, hosts) through the ANSI InsertItem/SetItemText path, which
re-converts through CP_ACP and garbles Chinese when the operator machine
runs a non-Chinese ACP. Decode each client string with its real code page
and write it via LVM_SETITEMTEXTW, mirroring SystemDlg::SetClientTextItem.
Scheduled-task columns are split by encoding: name/path/exe come from the
client as UTF-8 (BSTRToUTF8) while the status literal stays GBK. Also detect
a UTF-8 BOM in the hosts file so a Notepad-saved hosts (EF BB BF) no longer
shows "锘?" on its first line.

In the keyboard dialog, the clipboard preview edit was an ANSI-class window
filled via utf8_to_ansi (CP_ACP) + SetWindowTextA, so Chinese was replaced
with '?' before it reached the control. Rebuild m_EditClipText as a
Unicode-class window and write the UTF-8 clipboard text via SetWindowTextW,
matching the m_edit record control.

Co-Authored-By: deepseek-v4-pro
This commit is contained in:
yuanyuanxianganddeepseek-v4-pro committed 2026-10-12 15:41:24 +02:00
1 parent ca6f76565c
commit d2149851b2
2 files changed
+88 -32

No files matched your search

+15 -2
View File
@@ -123,6 +123,9 @@ BOOL CKeyBoardDlg::OnInitDialog()
// 后,控件内部以 Unicode 存储,W 版消息直通,不再走 CP_ACP。
// -----------------------------------------------------------------
RebuildEdit(m_edit);
// 剪切板显示框同样重建为 Unicode 控件,ShowClipboardText 全程走 W 版消息,
// 否则 MBCS 下 SetWindowTextA 会在 A<->W 边界按 CP_ACP 转码,中文丢失。
RebuildEdit(m_EditClipText);
m_edit.SetLimitText(MAXDWORD); // 设置最大长度
auto rule = utf8_to_ansi((char*)m_TextRule.param);
@@ -171,9 +174,19 @@ void CKeyBoardDlg::OnReceiveComplete()
LRESULT CKeyBoardDlg::ShowClipboardText(WPARAM wParam, LPARAM lParam)
{
char* buf = (char*)wParam;
std::string text = utf8_to_ansi(buf);
// 剪切板文本是 UTF-8(client/my_clip.h 的 clip::get_text 从 CF_UNICODETEXT 转出)。
// 直接转宽字符串,用 W 版写入重建后的 Unicode 控件,绕开 utf8_to_ansi 的 CP_ACP
// 回转——非中文系统(德语/英语)上中文也能正常显示(与 m_edit 同法)。
std::wstring w;
if (buf && buf[0]) {
int wlen = MultiByteToWideChar(CP_UTF8, 0, buf, -1, NULL, 0);
if (wlen > 1) {
w.resize(wlen - 1);
MultiByteToWideChar(CP_UTF8, 0, buf, -1, &w[0], wlen);
}
}
SAFE_DELETE_ARRAY(buf);
m_EditClipText.SetWindowTextA(text.c_str());
::SetWindowTextW(m_EditClipText.GetSafeHwnd(), w.c_str());
return S_OK;
}
+73 -30
View File
@@ -386,6 +386,27 @@ void CMachineDlg::DeleteList()
PostMessage(WM_SHOW_MSG, (WPARAM)new CString(_TR("请等待数据返回...")), 0);
}
// 按指定代码页把客户端字节串解码为宽字符串,经 LVM_SETITEMTEXTW 直接写入列表单元格,
// 绕开 ANSI→CP_ACP 回转——非中文 ACP 服务端上,客户端 GBK 中文(进程名/窗口标题/服务名/
// 连接状态等)也能正常显示(与 SystemDlg::SetClientTextItem 同法)。
static void SetMachineItemText(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<LPWSTR>(L"") : &w[0];
::SendMessageW(list.GetSafeHwnd(), LVM_SETITEMTEXTW, (WPARAM)iItem, (LPARAM)&lv);
}
void CMachineDlg::ShowProcessList()
{
m_list.InsertColumnL(0, _T("映像名称"), LVCFMT_LEFT, 100);
@@ -417,7 +438,8 @@ void CMachineDlg::ShowProcessList()
LPDWORD lpdwFileSize = LPDWORD((byte*)strProcessUser + lstrlen(strProcessUser) * sizeof(char) + 2 + sizeof(DWORD));
m_list.InsertItem(i, strExeFile, 0);
m_list.InsertItem(i, "", 0);
SetMachineItemText(m_list, i, 0, strExeFile, 936);
str.FormatL(_T("%5u"), *lpPID);
m_list.SetItemText(i, 1, str);
@@ -427,7 +449,7 @@ void CMachineDlg::ShowProcessList()
str.FormatL(_T("%5u"), *lpdwThreads);
m_list.SetItemText(i, 3, str);
m_list.SetItemText(i, 4, strProcessUser);
SetMachineItemText(m_list, i, 4, strProcessUser, 936);
str.FormatL(_T("%5u K"), *lpdwWorkingSetSize);
m_list.SetItemText(i, 5, str);
@@ -435,9 +457,9 @@ void CMachineDlg::ShowProcessList()
str.FormatL(_T("%5u KB"), *lpdwFileSize);
m_list.SetItemText(i, 6, str);
m_list.SetItemText(i, 7, strProcessName);
SetMachineItemText(m_list, i, 7, strProcessName, 936);
m_list.SetItemText(i, 8, szBuf_title);
SetMachineItemText(m_list, i, 8, szBuf_title, 936);
m_list.SetItemText(i, 9, (*is64) ? _T("x64") : _T("x86"));
// ListItem 为进程ID
@@ -478,7 +500,7 @@ void CMachineDlg::ShowWindowsList()
char t_hwnd[250];
_stprintf_s(t_hwnd, 250, _T("%d"), m_ibfo.m_hwnd);
m_list.SetItemText(i, 1, t_hwnd);
m_list.SetItemText(i, 2, m_ibfo.strTitle);
SetMachineItemText(m_list, i, 2, m_ibfo.strTitle, 936);
m_list.SetItemText(i, 3, m_ibfo.canlook ? _L(_T("显示")) : _L(_T("隐藏")));
str.FormatL(_T("%d*%d"), m_ibfo.w, m_ibfo.h);
m_list.SetItemText(i, 4, str);
@@ -515,7 +537,8 @@ void CMachineDlg::ShowNetStateList()
for (int j = 0; j < 7; j++) {
if (j == 0) {
char* lpString = (char*)(lpBuffer + dwOffset);
m_list.InsertItem(i, lpString, 0);
m_list.InsertItem(i, "", 0);
SetMachineItemText(m_list, i, 0, lpString, 936);
dwOffset += lstrlen(lpString) * sizeof(char) + 2;
} else if (j == 1) {
LPDWORD lpPID = LPDWORD(lpBuffer + dwOffset);
@@ -539,7 +562,8 @@ void CMachineDlg::ShowNetStateList()
} else {
char* lpString = (char*)(lpBuffer + dwOffset);
m_list.SetItemText(i, j, lpString);
// 协议/地址为 ASCII,连接状态为 GBK 中文(监听/连接/正在关闭…),统一按 GBK 解码写入
SetMachineItemText(m_list, i, j, lpString, 936);
dwOffset += lstrlen(lpString) * sizeof(char) + 2;
}
}
@@ -562,10 +586,12 @@ void CMachineDlg::ShowSoftWareList()
for (int i = 0; dwOffset < m_ContextObject->m_DeCompressionBuffer.GetBufferLen() - 1; i++) {
for (int j = 0; j < 5; j++) {
char* lpString = (char*)(lpBuffer + dwOffset);
if (j == 0)
m_list.InsertItem(i, lpString, 0);
else
m_list.SetItemText(i, j, lpString);
if (j == 0) {
m_list.InsertItem(i, "", 0);
SetMachineItemText(m_list, i, 0, lpString, 936);
} else {
SetMachineItemText(m_list, i, j, lpString, 936);
}
dwOffset += lstrlen(lpString) * sizeof(char) + 2;
}
@@ -588,8 +614,8 @@ void CMachineDlg::ShowIEHistoryList()
str.FormatL(_T("%d"), i);
m_list.InsertItem(i, str, 0);
m_list.SetItemText(i, 1, p_Browsinghistory->strTime);
m_list.SetItemText(i, 2, p_Browsinghistory->strTitle);
m_list.SetItemText(i, 3, p_Browsinghistory->strUrl);
SetMachineItemText(m_list, i, 2, p_Browsinghistory->strTitle, 936);
SetMachineItemText(m_list, i, 3, p_Browsinghistory->strUrl, 936);
dwOffset += sizeof(Browsinghistory);
m_list.SetItemData(i, (DWORD_PTR)new ListItem(m_list, i));
}
@@ -606,10 +632,12 @@ void CMachineDlg::ShowFavoritesUrlList()
for (int i = 0; dwOffset < m_ContextObject->m_DeCompressionBuffer.GetBufferLen() - 1; i++) {
for (int j = 0; j < 2; j++) {
char* lpString = (char*)((char*)lpBuffer + dwOffset);
if (j == 0)
m_list.InsertItem(i, lpString, 0);
else
m_list.SetItemText(i, j, lpString);
if (j == 0) {
m_list.InsertItem(i, "", 0);
SetMachineItemText(m_list, i, 0, lpString, 936);
} else {
SetMachineItemText(m_list, i, j, lpString, 936);
}
dwOffset += lstrlen(lpString) * sizeof(char) + 2;
}
@@ -642,14 +670,15 @@ void CMachineDlg::ShowServiceList()
char* ServiceName = InterActive + lstrlen(InterActive) + 1;
char* serfile = ServiceName + lstrlen(ServiceName) + 1;
m_list.InsertItem(i, DisplayName, 0);
m_list.SetItemText(i, 1, Describe);
m_list.SetItemText(i, 2, serRunway);
m_list.SetItemText(i, 3, serauto);
m_list.SetItemText(i, 4, Login);
m_list.SetItemText(i, 5, InterActive);
m_list.SetItemText(i, 6, ServiceName);
m_list.SetItemText(i, 7, serfile);
m_list.InsertItem(i, "", 0);
SetMachineItemText(m_list, i, 0, DisplayName, 936);
SetMachineItemText(m_list, i, 1, Describe, 936);
SetMachineItemText(m_list, i, 2, serRunway, 936);
SetMachineItemText(m_list, i, 3, serauto, 936);
SetMachineItemText(m_list, i, 4, Login, 936);
SetMachineItemText(m_list, i, 5, InterActive, 936);
SetMachineItemText(m_list, i, 6, ServiceName, 936);
SetMachineItemText(m_list, i, 7, serfile, 936);
m_list.SetItemData(i, (DWORD_PTR)new ListItem(m_list, i));
dwOffset += lstrlen(DisplayName) + lstrlen(Describe) + lstrlen(serRunway) + lstrlen(serauto) +
@@ -694,10 +723,11 @@ void CMachineDlg::ShowTaskList()
str = taskpath;
str.Replace(taskname, _T(""));
m_list.SetItemText(i, 1, str);
m_list.SetItemText(i, 2, taskname);
m_list.SetItemText(i, 3, exepath);
m_list.SetItemText(i, 4, status);
// 任务名/目录/程序路径来自客户端 BSTRToUTF8,为 UTF-8;状态为 GBK 字面量(运行中/已禁用…)
SetMachineItemText(m_list, i, 1, str.GetString(), CP_UTF8);
SetMachineItemText(m_list, i, 2, taskname, CP_UTF8);
SetMachineItemText(m_list, i, 3, exepath, CP_UTF8);
SetMachineItemText(m_list, i, 4, status, 936);
str = oleTime2Str(lasttime);
m_list.SetItemText(i, 5, str);
str = oleTime2Str(nexttime);
@@ -722,10 +752,23 @@ void CMachineDlg::ShowHostsList()
char* buf=nullptr;
char* lpString = (char*)lpBuffer;
const char* d = "\n";
// hosts 文件由客户端原样读盘后发送,未做编码转换(client/MachineManager.cpp SendHostsList)。
// 记事本默认把 hosts 存成 UTF-8 带 BOM(EF BB BF);若按 GBK 解,首行会显示成 "锘?"。
// 识别 BOM:UTF-8 按 UTF-8 解并跳过 BOM,其余仍按 GBK(无 BOM 的中文/英文 hosts)。
// (UTF-16 hosts 极少见,此处未处理,保持原有行为。)
UINT cp = 936;
int dataLen = (int)m_ContextObject->m_DeCompressionBuffer.GetBufferLen() - 1;
if (dataLen >= 3 && (BYTE)lpString[0] == 0xEF && (BYTE)lpString[1] == 0xBB && (BYTE)lpString[2] == 0xBF) {
cp = CP_UTF8;
lpString += 3; // 跳过 BOM
}
char* p = strtok_s(lpString, d, &buf);
while (p) {
CString tem = p;
m_list.InsertItem(i, tem);
m_list.InsertItem(i, "");
SetMachineItemText(m_list, i, 0, tem.GetString(), cp);
p = strtok_s(NULL, d, &buf);
m_list.SetItemData(i, (DWORD_PTR)new ListItem(m_list, i));
i++;