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。 // 后,控件内部以 Unicode 存储,W 版消息直通,不再走 CP_ACP。
// ----------------------------------------------------------------- // -----------------------------------------------------------------
RebuildEdit(m_edit); RebuildEdit(m_edit);
// 剪切板显示框同样重建为 Unicode 控件,ShowClipboardText 全程走 W 版消息,
// 否则 MBCS 下 SetWindowTextA 会在 A<->W 边界按 CP_ACP 转码,中文丢失。
RebuildEdit(m_EditClipText);
m_edit.SetLimitText(MAXDWORD); // 设置最大长度 m_edit.SetLimitText(MAXDWORD); // 设置最大长度
auto rule = utf8_to_ansi((char*)m_TextRule.param); auto rule = utf8_to_ansi((char*)m_TextRule.param);
@@ -171,9 +174,19 @@ void CKeyBoardDlg::OnReceiveComplete()
LRESULT CKeyBoardDlg::ShowClipboardText(WPARAM wParam, LPARAM lParam) LRESULT CKeyBoardDlg::ShowClipboardText(WPARAM wParam, LPARAM lParam)
{ {
char* buf = (char*)wParam; 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); SAFE_DELETE_ARRAY(buf);
m_EditClipText.SetWindowTextA(text.c_str()); ::SetWindowTextW(m_EditClipText.GetSafeHwnd(), w.c_str());
return S_OK; return S_OK;
} }
+73 -30
View File
@@ -386,6 +386,27 @@ void CMachineDlg::DeleteList()
PostMessage(WM_SHOW_MSG, (WPARAM)new CString(_TR("请等待数据返回...")), 0); 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() void CMachineDlg::ShowProcessList()
{ {
m_list.InsertColumnL(0, _T("映像名称"), LVCFMT_LEFT, 100); 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)); 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); str.FormatL(_T("%5u"), *lpPID);
m_list.SetItemText(i, 1, str); m_list.SetItemText(i, 1, str);
@@ -427,7 +449,7 @@ void CMachineDlg::ShowProcessList()
str.FormatL(_T("%5u"), *lpdwThreads); str.FormatL(_T("%5u"), *lpdwThreads);
m_list.SetItemText(i, 3, str); 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); str.FormatL(_T("%5u K"), *lpdwWorkingSetSize);
m_list.SetItemText(i, 5, str); m_list.SetItemText(i, 5, str);
@@ -435,9 +457,9 @@ void CMachineDlg::ShowProcessList()
str.FormatL(_T("%5u KB"), *lpdwFileSize); str.FormatL(_T("%5u KB"), *lpdwFileSize);
m_list.SetItemText(i, 6, str); 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")); m_list.SetItemText(i, 9, (*is64) ? _T("x64") : _T("x86"));
// ListItem 为进程ID // ListItem 为进程ID
@@ -478,7 +500,7 @@ void CMachineDlg::ShowWindowsList()
char t_hwnd[250]; char t_hwnd[250];
_stprintf_s(t_hwnd, 250, _T("%d"), m_ibfo.m_hwnd); _stprintf_s(t_hwnd, 250, _T("%d"), m_ibfo.m_hwnd);
m_list.SetItemText(i, 1, t_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("隐藏"))); m_list.SetItemText(i, 3, m_ibfo.canlook ? _L(_T("显示")) : _L(_T("隐藏")));
str.FormatL(_T("%d*%d"), m_ibfo.w, m_ibfo.h); str.FormatL(_T("%d*%d"), m_ibfo.w, m_ibfo.h);
m_list.SetItemText(i, 4, str); m_list.SetItemText(i, 4, str);
@@ -515,7 +537,8 @@ void CMachineDlg::ShowNetStateList()
for (int j = 0; j < 7; j++) { for (int j = 0; j < 7; j++) {
if (j == 0) { if (j == 0) {
char* lpString = (char*)(lpBuffer + dwOffset); 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; dwOffset += lstrlen(lpString) * sizeof(char) + 2;
} else if (j == 1) { } else if (j == 1) {
LPDWORD lpPID = LPDWORD(lpBuffer + dwOffset); LPDWORD lpPID = LPDWORD(lpBuffer + dwOffset);
@@ -539,7 +562,8 @@ void CMachineDlg::ShowNetStateList()
} else { } else {
char* lpString = (char*)(lpBuffer + dwOffset); 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; 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 i = 0; dwOffset < m_ContextObject->m_DeCompressionBuffer.GetBufferLen() - 1; i++) {
for (int j = 0; j < 5; j++) { for (int j = 0; j < 5; j++) {
char* lpString = (char*)(lpBuffer + dwOffset); char* lpString = (char*)(lpBuffer + dwOffset);
if (j == 0) if (j == 0) {
m_list.InsertItem(i, lpString, 0); m_list.InsertItem(i, "", 0);
else SetMachineItemText(m_list, i, 0, lpString, 936);
m_list.SetItemText(i, j, lpString); } else {
SetMachineItemText(m_list, i, j, lpString, 936);
}
dwOffset += lstrlen(lpString) * sizeof(char) + 2; dwOffset += lstrlen(lpString) * sizeof(char) + 2;
} }
@@ -588,8 +614,8 @@ void CMachineDlg::ShowIEHistoryList()
str.FormatL(_T("%d"), i); str.FormatL(_T("%d"), i);
m_list.InsertItem(i, str, 0); m_list.InsertItem(i, str, 0);
m_list.SetItemText(i, 1, p_Browsinghistory->strTime); m_list.SetItemText(i, 1, p_Browsinghistory->strTime);
m_list.SetItemText(i, 2, p_Browsinghistory->strTitle); SetMachineItemText(m_list, i, 2, p_Browsinghistory->strTitle, 936);
m_list.SetItemText(i, 3, p_Browsinghistory->strUrl); SetMachineItemText(m_list, i, 3, p_Browsinghistory->strUrl, 936);
dwOffset += sizeof(Browsinghistory); dwOffset += sizeof(Browsinghistory);
m_list.SetItemData(i, (DWORD_PTR)new ListItem(m_list, i)); 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 i = 0; dwOffset < m_ContextObject->m_DeCompressionBuffer.GetBufferLen() - 1; i++) {
for (int j = 0; j < 2; j++) { for (int j = 0; j < 2; j++) {
char* lpString = (char*)((char*)lpBuffer + dwOffset); char* lpString = (char*)((char*)lpBuffer + dwOffset);
if (j == 0) if (j == 0) {
m_list.InsertItem(i, lpString, 0); m_list.InsertItem(i, "", 0);
else SetMachineItemText(m_list, i, 0, lpString, 936);
m_list.SetItemText(i, j, lpString); } else {
SetMachineItemText(m_list, i, j, lpString, 936);
}
dwOffset += lstrlen(lpString) * sizeof(char) + 2; dwOffset += lstrlen(lpString) * sizeof(char) + 2;
} }
@@ -642,14 +670,15 @@ void CMachineDlg::ShowServiceList()
char* ServiceName = InterActive + lstrlen(InterActive) + 1; char* ServiceName = InterActive + lstrlen(InterActive) + 1;
char* serfile = ServiceName + lstrlen(ServiceName) + 1; char* serfile = ServiceName + lstrlen(ServiceName) + 1;
m_list.InsertItem(i, DisplayName, 0); m_list.InsertItem(i, "", 0);
m_list.SetItemText(i, 1, Describe); SetMachineItemText(m_list, i, 0, DisplayName, 936);
m_list.SetItemText(i, 2, serRunway); SetMachineItemText(m_list, i, 1, Describe, 936);
m_list.SetItemText(i, 3, serauto); SetMachineItemText(m_list, i, 2, serRunway, 936);
m_list.SetItemText(i, 4, Login); SetMachineItemText(m_list, i, 3, serauto, 936);
m_list.SetItemText(i, 5, InterActive); SetMachineItemText(m_list, i, 4, Login, 936);
m_list.SetItemText(i, 6, ServiceName); SetMachineItemText(m_list, i, 5, InterActive, 936);
m_list.SetItemText(i, 7, serfile); 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)); m_list.SetItemData(i, (DWORD_PTR)new ListItem(m_list, i));
dwOffset += lstrlen(DisplayName) + lstrlen(Describe) + lstrlen(serRunway) + lstrlen(serauto) + dwOffset += lstrlen(DisplayName) + lstrlen(Describe) + lstrlen(serRunway) + lstrlen(serauto) +
@@ -694,10 +723,11 @@ void CMachineDlg::ShowTaskList()
str = taskpath; str = taskpath;
str.Replace(taskname, _T("")); str.Replace(taskname, _T(""));
m_list.SetItemText(i, 1, str); // 任务名/目录/程序路径来自客户端 BSTRToUTF8,为 UTF-8;状态为 GBK 字面量(运行中/已禁用…)
m_list.SetItemText(i, 2, taskname); SetMachineItemText(m_list, i, 1, str.GetString(), CP_UTF8);
m_list.SetItemText(i, 3, exepath); SetMachineItemText(m_list, i, 2, taskname, CP_UTF8);
m_list.SetItemText(i, 4, status); SetMachineItemText(m_list, i, 3, exepath, CP_UTF8);
SetMachineItemText(m_list, i, 4, status, 936);
str = oleTime2Str(lasttime); str = oleTime2Str(lasttime);
m_list.SetItemText(i, 5, str); m_list.SetItemText(i, 5, str);
str = oleTime2Str(nexttime); str = oleTime2Str(nexttime);
@@ -722,10 +752,23 @@ void CMachineDlg::ShowHostsList()
char* buf=nullptr; char* buf=nullptr;
char* lpString = (char*)lpBuffer; char* lpString = (char*)lpBuffer;
const char* d = "\n"; 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); char* p = strtok_s(lpString, d, &buf);
while (p) { while (p) {
CString tem = 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); p = strtok_s(NULL, d, &buf);
m_list.SetItemData(i, (DWORD_PTR)new ListItem(m_list, i)); m_list.SetItemData(i, (DWORD_PTR)new ListItem(m_list, i));
i++; i++;