From d2149851b24fd0a6c01ee88575f39ba6486e631b Mon Sep 17 00:00:00 2001 From: yuanyuanxiang <962914132@qq.com> Date: Mon, 12 Oct 2026 15:41:24 +0200 Subject: [PATCH] Fix: Decode host-management and clipboard text as GBK/UTF-8 on non-Chinese systems MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- server/2015Remote/KeyBoardDlg.cpp | 17 ++++- server/2015Remote/sys/MachineDlg.cpp | 103 +++++++++++++++++++-------- 2 files changed, 88 insertions(+), 32 deletions(-) diff --git a/server/2015Remote/KeyBoardDlg.cpp b/server/2015Remote/KeyBoardDlg.cpp index f5ed83a..7e51595 100644 --- a/server/2015Remote/KeyBoardDlg.cpp +++ b/server/2015Remote/KeyBoardDlg.cpp @@ -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; } diff --git a/server/2015Remote/sys/MachineDlg.cpp b/server/2015Remote/sys/MachineDlg.cpp index 9a9a9fa..310c780 100644 --- a/server/2015Remote/sys/MachineDlg.cpp +++ b/server/2015Remote/sys/MachineDlg.cpp @@ -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(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++;