diff --git a/client/LoginServer.cpp b/client/LoginServer.cpp index 952bcf0..51ff2bc 100644 --- a/client/LoginServer.cpp +++ b/client/LoginServer.cpp @@ -378,13 +378,29 @@ LOGIN_INFOR GetLoginInfo(DWORD dwSpeed, CONNECT_ADDRESS& conn, const std::string std::string pubIP = cfg.GetStr("settings", "public_ip", ""); auto ip_time = cfg.GetInt("settings", "ip_time"); bool timeout = ip_time <= 0 || (time(0) - ip_time > 7 * 86400); + // 升级后强制重新查询一次:旧版缓存的 location 是 GBK/CP1252,或已把 ö/ä/ß 等替换成 '?'; + // 新版 GetGeoLocation 改为 UTF-8 上报(见 common/location.h),需重新拉取才能修正。 + // 版本号即编译日期 __DATE__(见 common/commands.h 的 DLL_VERSION):编译期宏,内存 DLL + // 也能取到,且每次编译都会变,天然标识“升级”,无需手动递增,也不依赖 exe 版本资源。 + bool cacheVerChanged = (cfg.GetStr("settings", "loc_cache_ver", "") != __DATE__); IPConverter cvt; - if (loc.empty() || pubIP.empty() || timeout) { - pubIP = cvt.getPublicIP(); - loc = cvt.GetGeoLocation(pubIP); - cfg.SetStr("settings", "location", loc); - cfg.SetStr("settings", "public_ip", pubIP); - cfg.SetInt("settings", "ip_time", time(0)); + if (loc.empty() || pubIP.empty() || timeout || cacheVerChanged) { + std::string newPubIP = pubIP; + // 升级重查 location 时复用已缓存的公网 IP;仅当 IP 缺失或 7 天过期才重新探测, + // 否则离线/无外网机器每次重连都会轮询 5 个公网 IP 源、空等数秒到十几秒。 + if (pubIP.empty() || timeout) { + newPubIP = cvt.getPublicIP(); + } + std::string newLoc = cvt.GetGeoLocation(newPubIP); + // 仅查询成功才覆盖缓存并推进版本号;失败保留旧值,下次登录仍会重试。 + if (!newLoc.empty() && !newPubIP.empty()) { + pubIP = newPubIP; + loc = newLoc; + cfg.SetStr("settings", "location", loc); + cfg.SetStr("settings", "public_ip", pubIP); + cfg.SetInt("settings", "ip_time", time(0)); + cfg.SetStr("settings", "loc_cache_ver", __DATE__); + } } LoginInfor.AddReserved(loc.c_str()); LoginInfor.AddReserved(pubIP.c_str()); diff --git a/common/location.h b/common/location.h index cfc4de4..004aeb0 100644 --- a/common/location.h +++ b/common/location.h @@ -328,8 +328,10 @@ private: } } - std::string city = Utf8ToAnsi(json[cityField].asString()); - std::string country = Utf8ToAnsi(json[countryField].asString()); + // 地理位置 API 返回 UTF-8;不再转 CP_ACP——那会把 ö/ä/ß 等变音符替换成 '?'。 + // GetGeoLocation 现统一返回 UTF-8,消费方按需用 NormalizeLocationToUtf8 / SafeUtf8ToAnsi 转。 + std::string city = json[cityField].asString(); + std::string country = json[countryField].asString(); if (!city.empty() && !country.empty()) return city + ", " + country; if (!city.empty()) return city; diff --git a/server/2015Remote/2015RemoteDlg.cpp b/server/2015Remote/2015RemoteDlg.cpp index b3b7bbe..05624ad 100644 --- a/server/2015Remote/2015RemoteDlg.cpp +++ b/server/2015Remote/2015RemoteDlg.cpp @@ -261,6 +261,47 @@ UINT GetProcessListEncoding(context* ctx) { return 936; } +// 判断字符串是否为合法 UTF-8(MB_ERR_INVALID_CHARS 严格校验,避免把 GBK 误判成 UTF-8)。 +static bool IsValidUtf8Str(const char* s) +{ + if (!s || !s[0]) return false; + return MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s, -1, NULL, 0) > 0; +} + +// 把按 cp 编码的字符串转成 UTF-8(cp == CP_UTF8 时原样返回)。 +static CString LocationToUtf8(const CString& str, UINT cp) +{ + if (str.IsEmpty() || cp == CP_UTF8) return str; + int wlen = MultiByteToWideChar(cp, 0, str, -1, NULL, 0); + if (wlen <= 1) return str; + std::wstring w(wlen - 1, L'\0'); + MultiByteToWideChar(cp, 0, str, -1, &w[0], wlen); + int u8len = WideCharToMultiByte(CP_UTF8, 0, w.c_str(), -1, NULL, 0, NULL, NULL); + if (u8len <= 1) return str; + std::string u8(u8len - 1, '\0'); + WideCharToMultiByte(CP_UTF8, 0, w.c_str(), -1, &u8[0], u8len, NULL, NULL); + return CString(u8.c_str()); +} + +// 兜底规整地理位置为 UTF-8:合法 UTF-8(LNX/MAC 自报、迁移后的缓存)原样保留, +// 否则视为 GBK/936(Windows 自报、本地库 QQWry/Ip2Region、老缓存)转 UTF-8。 +static CString NormalizeLocationToUtf8(const CString& str) +{ + if (str.IsEmpty() || IsValidUtf8Str(str.GetString())) return str; + return LocationToUtf8(str, 936); +} + +// 把按 cp 编码的字符串转成宽字符串(cp == CP_UTF8 用于地理位置,CP_ACP 用于 GBK/ASCII 部分)。 +static CStringW AnsiToWide(const char* s, UINT cp = CP_ACP) +{ + if (!s || !s[0]) return L""; + int wlen = MultiByteToWideChar(cp, 0, s, -1, NULL, 0); + if (wlen <= 1) return L""; + std::wstring w(wlen - 1, L'\0'); + MultiByteToWideChar(cp, 0, s, -1, &w[0], wlen); + return CStringW(w.c_str()); +} + // 授权日志频率控制:首次必须记录,状态变化必须记录,相同状态每小时记录一次 static bool ShouldLogAuth(const std::string& sn, int success) { struct AuthLogState { @@ -1638,7 +1679,7 @@ VOID CMy2015RemoteDlg::AddList(CString strIP, CString strAddr, CString strPCName v[RES_CLIENT_ID] = std::to_string(id); } - bool modify = false, needConvert = true; + bool modify = false; // 新客户端 V2 ID 算法首次上线时,把老 ID 下的元数据迁过来。 if (TryMigrateClientMetadata(id, strPCName, path)) { @@ -1647,20 +1688,21 @@ VOID CMy2015RemoteDlg::AddList(CString strIP, CString strAddr, CString strPCName // 优先级:① 客户端刚发来的有效地理位置(非空非"?") // ② 服务端缓存(排除历史遗留的"?"无效值) // ③ 服务端按连接 IP 自行查询 + // 存储统一为 UTF-8:列表用宽字符显示、HostJson/邮件导出直接透传, + // 不再 SafeUtf8ToAnsi 转 GBK(GBK 无法编码 ö/ä/ß 等变音符,会替换成 '?')。 CString clientLoc = v[RES_CLIENT_LOC].c_str(); CString cachedLoc = m_ClientMap->GetClientMapData(id, MAP_LOCATION); CString loc; if (!clientLoc.IsEmpty() && clientLoc != "?") { - loc = clientLoc; + // 客户端自报:GetGeoLocation 已改为返回 UTF-8(见 common/location.h), + // 新客户端各类型都上报 UTF-8;老版本/旧缓存的客户端可能仍是 GBK/936。统一按 UTF-8 规整。 + loc = NormalizeLocationToUtf8(clientLoc); } else if (!cachedLoc.IsEmpty() && cachedLoc != "?") { - loc = cachedLoc; + loc = NormalizeLocationToUtf8(cachedLoc); } else { - loc = m_IPConverter->GetGeoLocation(data[ONLINELIST_IP].GetString()).c_str(); - needConvert = !m_HasLocDB; + std::string geo = m_IPConverter->GetGeoLocation(data[ONLINELIST_IP].GetString()); + loc = NormalizeLocationToUtf8(CString(geo.c_str())); } - // TODO: Remove SafeUtf8ToAnsi after migrating to UTF-8 - if (needConvert) - loc = SafeUtf8ToAnsi(loc.GetString()).c_str(); bool flag = strIP == "127.0.0.1" && !v[RES_CLIENT_PUBIP].empty(); data[ONLINELIST_IP] = flag ? v[RES_CLIENT_PUBIP].c_str() : strIP; data[ONLINELIST_LOCATION] = loc; @@ -1734,7 +1776,13 @@ VOID CMy2015RemoteDlg::AddList(CString strIP, CString strAddr, CString strPCName } std::string tip = flag ? " (" + v[RES_CLIENT_PUBIP] + ") " : ""; - ShowMessage(_TR("操作成功"), strIP + tip.c_str() + " " + _L(_T("主机上线")) + "[" + loc + "][" + groupName.c_str() + "]"); + // 地理位置 loc 是 UTF-8,其余是 GBK/ASCII;拼成宽字符串走 ShowMessageW, + // 否则 UTF-8 会在 ANSI 日志列表里被按 GBK 显示成乱码(Böblingen → B枚blingen)。 + CString type = _TR("操作成功"); + CString prefix = strIP + tip.c_str() + " " + _L(_T("主机上线")) + "["; + CString suffix = "][" + CString(groupName.c_str()) + "]"; + ShowMessageW(AnsiToWide(type.GetString()), + AnsiToWide(prefix.GetString(), CP_ACP) + AnsiToWide(loc.GetString(), CP_UTF8) + AnsiToWide(suffix.GetString(), CP_ACP)); CharMsg *title = new CharMsg(_TR("主机上线")); CharMsg *text = new CharMsg(strIP + CString(tip.c_str()) + _T(" ") + _L(_T("主机上线")) + _T(" [") + loc + _T("]")); @@ -1881,6 +1929,61 @@ VOID CMy2015RemoteDlg::ShowMessage(CString strType, CString strMsg) m_StatusBar.SetPaneText(0,strStatusMsg); //在状态条上显示文字 } +// 宽字符版 ShowMessage:消息里嵌入了 UTF-8 地理位置时,SetItemText 会把 UTF-8 按 GBK +// 显示成乱码(Böblingen → B枚blingen)。这里把消息列经 LVM_SETITEMTEXTW 直接写宽字符串。 +// 类型/时间列与线程安全镜像仍保持 GBK,与其它条目及 McpServer get_audit_log 的 ToUtf8(msg,936) 一致。 +VOID CMy2015RemoteDlg::ShowMessageW(const CStringW& strTypeW, const CStringW& strMsgW) +{ + AUTO_TICK(200, ""); + CTime Timer = CTime::GetCurrentTime(); + CString strTime = Timer.Format("%Y-%m-%d %H:%M:%S"); + + int count = m_CList_Message.GetItemCount(); + if (count >= MAX_MESSAGE_COUNT) { + m_CList_Message.DeleteItem(count - 1); + } + + // 宽 → GBK 仅用于 InsertItem(类型列)、颜色判定与线程安全镜像;ö 等变音符会退化为 '?', + // 但 UI 消息列用的是下面的宽字符写,不受影响。 + CString strType(strTypeW); + CString strMsg(strMsgW); + + m_CList_Message.InsertItem(0, strType); + m_CList_Message.SetItemText(0, 1, strTime); + + LVITEMW lv = {}; + lv.mask = LVIF_TEXT; + lv.iItem = 0; + lv.iSubItem = 2; + lv.pszText = const_cast(strMsgW.GetString()); + ::SendMessageW(m_CList_Message.GetSafeHwnd(), LVM_SETITEMTEXTW, 0, (LPARAM)&lv); + + m_CList_Message.SetItemData(0, (DWORD_PTR)MsgColorLevel(strType, strMsg)); + + // 同步线程安全镜像(GBK,与其它条目一致) + { + EnterCriticalSection(&m_cs); + LogMessage entry; + entry.type = strType.GetString(); + entry.time = strTime.GetString(); + entry.msg = strMsg.GetString(); + m_MessageLog.insert(m_MessageLog.begin(), entry); + if ((int)m_MessageLog.size() > MAX_MESSAGE_COUNT) + m_MessageLog.pop_back(); + LeaveCriticalSection(&m_cs); + } + + CString strStatusMsg; + EnterCriticalSection(&m_cs); + int m_iCount = m_CList_Online.GetItemCount(); + int totalCount = m_HostList.size(); + LeaveCriticalSection(&m_cs); + strStatusMsg.FormatL("有%d个主机在线", m_iCount); + strStatusMsg += CString("[Total: ") + std::to_string(totalCount).c_str() + "]"; + if (m_StatusBar.GetSafeHwnd()) + m_StatusBar.SetPaneText(0, strStatusMsg); +} + // 试用版 IP 段触发:OnAccept 发现入站连接对端是公网 IP(已透过 Proxy Protocol v2 解出真实 IP)。 // 与 OnTrialRttAbuse 共用 IOCPServer::s_TrialAbuseWarned latch,本函数每进程最多调一次。 LRESULT CMy2015RemoteDlg::OnTrialWanIpAbuse(WPARAM wParam, LPARAM lParam) @@ -4259,6 +4362,10 @@ void CMy2015RemoteDlg::OnGetDispInfo(NMHDR* pNMHDR, LRESULT* pResult) text = ctx->GetClientData(slot); if (text.IsEmpty()) text = "?"; } + // 地理位置列已统一按 UTF-8 存储(见 AddList);A 版回调只能填 CP_ACP 文本,转回 CP_ACP。 + if (slot == ONLINELIST_LOCATION) { + text = Utf8ToAnsi(std::string(text.GetString())).c_str(); + } lstrcpyn(pItem->pszText, text, pItem->cchTextMax); } } @@ -4325,7 +4432,8 @@ void CMy2015RemoteDlg::OnGetDispInfoW(NMHDR* pNMHDR, LRESULT* pResult) } if (wtext.empty()) wtext = L"?"; } else { - // 其它列:CString 仍是 ANSI(CP_ACP),按 CP_ACP 转宽供控件显示 + // 其它列:CString 仍是 ANSI(CP_ACP),按 CP_ACP 转宽供控件显示; + // 地理位置列已统一按 UTF-8 存储(见 AddList),按 UTF-8 转宽,避免把 UTF-8 字节当 GBK。 CString text; if (nCol == ONLINELIST_COMPUTER_NAME) { CString note = m_ClientMap->GetClientMapData(ctx->GetClientID(), MAP_NOTE); @@ -4334,10 +4442,11 @@ void CMy2015RemoteDlg::OnGetDispInfoW(NMHDR* pNMHDR, LRESULT* pResult) text = ctx->GetClientData(nCol); if (text.IsEmpty()) text = "?"; } - int wlen = MultiByteToWideChar(CP_ACP, 0, text, -1, NULL, 0); + UINT cp = (nCol == ONLINELIST_LOCATION) ? CP_UTF8 : CP_ACP; + int wlen = MultiByteToWideChar(cp, 0, text, -1, NULL, 0); if (wlen > 0) { wtext.resize(wlen - 1); - MultiByteToWideChar(CP_ACP, 0, text, -1, &wtext[0], wlen); + MultiByteToWideChar(cp, 0, text, -1, &wtext[0], wlen); } } @@ -4550,6 +4659,10 @@ void CMy2015RemoteDlg::OnOnlineMessage() void CMy2015RemoteDlg::OnChat() { + // 二次确认:避免误点工具栏“远程交谈”就立刻向被控端发起双向聊天 + if (IDYES != MessageBoxL(_T("确定开始远程交谈吗?"), _T("提示"), MB_ICONQUESTION | MB_YESNO)) + return; + BYTE bToken = COMMAND_CHAT; //向被控端发送开始远程交谈(双向聊天) SendSelectedCommand(&bToken, sizeof(BYTE)); } @@ -5509,7 +5622,7 @@ int CMy2015RemoteDlg::AuthorizeClient(context* ctx, const std::string& sn, const if (outExpired) *outExpired = true; // 签名有效但已过期 if (ctx != nullptr) { std::string ip = ctx->GetClientData(ONLINELIST_IP); - std::string location = m_IPConverter ? m_IPConverter->GetGeoLocation(ip) : ""; + std::string location = m_IPConverter ? SafeUtf8ToAnsi(m_IPConverter->GetGeoLocation(ip)) : ""; std::string machineName = ctx->GetClientData(ONLINELIST_COMPUTER_NAME); UpdateLicenseActivity(sn, passcode, hmacStr, ip, location, machineName); } else { @@ -5527,7 +5640,7 @@ int CMy2015RemoteDlg::AuthorizeClient(context* ctx, const std::string& sn, const // 授权成功时更新 license 活跃信息 if (ctx != nullptr) { std::string ip = ctx->GetClientData(ONLINELIST_IP); - std::string location = m_IPConverter ? m_IPConverter->GetGeoLocation(ip) : ""; + std::string location = m_IPConverter ? SafeUtf8ToAnsi(m_IPConverter->GetGeoLocation(ip)) : ""; std::string machineName = ctx->GetClientData(ONLINELIST_COMPUTER_NAME); UpdateLicenseActivity(sn, passcode, hmacStr, ip, location, machineName); } else { @@ -5576,7 +5689,7 @@ int CMy2015RemoteDlg::AuthorizeClientV2(context* ctx, const std::string& sn, con if (outExpired) *outExpired = true; // 签名有效但已过期 if (ctx != nullptr) { std::string ip = ctx->GetClientData(ONLINELIST_IP); - std::string location = m_IPConverter ? m_IPConverter->GetGeoLocation(ip) : ""; + std::string location = m_IPConverter ? SafeUtf8ToAnsi(m_IPConverter->GetGeoLocation(ip)) : ""; std::string machineName = ctx->GetClientData(ONLINELIST_COMPUTER_NAME); UpdateLicenseActivity(sn, passcode, hmacV2, ip, location, machineName); } else { @@ -5594,7 +5707,7 @@ int CMy2015RemoteDlg::AuthorizeClientV2(context* ctx, const std::string& sn, con // 授权成功时更新 license 活跃信息 if (ctx != nullptr) { std::string ip = ctx->GetClientData(ONLINELIST_IP); - std::string location = m_IPConverter ? m_IPConverter->GetGeoLocation(ip) : ""; + std::string location = m_IPConverter ? SafeUtf8ToAnsi(m_IPConverter->GetGeoLocation(ip)) : ""; std::string machineName = ctx->GetClientData(ONLINELIST_COMPUTER_NAME); UpdateLicenseActivity(sn, passcode, hmacV2, ip, location, machineName); } else { @@ -12660,36 +12773,47 @@ LRESULT CMy2015RemoteDlg::OnSplitterReleased(WPARAM, LPARAM) void CMy2015RemoteDlg::OnCopyClientInfo() { - CString csv; + // 用宽字符拼 CSV 后以 CF_UNICODETEXT 写入剪贴板,逐列按源编码转宽,保证无损。 + // 地理位置/程序路径已统一按 UTF-8 存储(见 AddList),活动窗口编码随客户端能力位; + // 其余列仍是 CP_ACP(GBK)。此前直接 dump 原始字节,UTF-8 列会被当 GBK 造成乱码。 + std::wstring csv; EnterCriticalSection(&m_cs); int nItem = m_CList_Online.GetNextItem(-1, LVNI_SELECTED); while (nItem != -1) { context* ctx = GetContextByListIndex(nItem); if (ctx) { - CString line; + std::wstring line; for (int col = 1; col < g_Column_Count_Online; ++col) { - if (col > 1) line += _T(","); - line += ctx->GetClientData(col - 1); + if (col > 1) line += L","; + int slot = col - 1; + CString text = ctx->GetClientData(slot); + UINT cp = CP_ACP; + if (slot == ONLINELIST_LOCATION || slot == ONLINELIST_PATH) + cp = CP_UTF8; + else if (slot == ONLINELIST_LOGINTIME) + cp = GetClientEncoding(ctx); + int wlen = MultiByteToWideChar(cp, 0, text, -1, NULL, 0); + if (wlen > 1) { + std::wstring w(wlen - 1, L'\0'); + MultiByteToWideChar(cp, 0, text, -1, &w[0], wlen); + line += w; + } } - csv += line + _T("\r\n"); + csv += line + L"\r\n"; } nItem = m_CList_Online.GetNextItem(nItem, LVNI_SELECTED); } LeaveCriticalSection(&m_cs); - if (csv.IsEmpty()) return; + if (csv.empty()) return; if (!OpenClipboard()) return; EmptyClipboard(); - int len = (csv.GetLength() + 1) * sizeof(TCHAR); + int len = (int)((csv.size() + 1) * sizeof(wchar_t)); HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, len); if (hMem) { - memcpy(GlobalLock(hMem), (LPCTSTR)csv, len); + memcpy(GlobalLock(hMem), csv.c_str(), len); GlobalUnlock(hMem); -#ifdef UNICODE SetClipboardData(CF_UNICODETEXT, hMem); -#else - SetClipboardData(CF_TEXT, hMem); -#endif } CloseClipboard(); } diff --git a/server/2015Remote/2015RemoteDlg.h b/server/2015Remote/2015RemoteDlg.h index e811092..909dd23 100644 --- a/server/2015Remote/2015RemoteDlg.h +++ b/server/2015Remote/2015RemoteDlg.h @@ -223,6 +223,9 @@ public: VOID AddList(CString strIP, CString strAddr, CString strPCName, CString strOS, CString strCPU, CString strVideo, CString strPing, CString ver, CString startTime, std::vector& v, CONTEXT_OBJECT* ContextObject); VOID ShowMessage(CString strType, CString strMsg); + // 消息中含 UTF-8(如地理位置)时使用:消息列经 LVM_SETITEMTEXTW 写宽字符串, + // 避免 UTF-8 在 ANSI 日志列表里被按 GBK 显示成乱码(Böblingen → B枚blingen)。 + VOID ShowMessageW(const CStringW& strType, const CStringW& strMsg); VOID CreatStatusBar(); VOID CreateToolBar(); VOID CreateNotifyBar(); diff --git a/server/2015Remote/CClientListDlg.cpp b/server/2015Remote/CClientListDlg.cpp index 01a8f74..6834965 100644 --- a/server/2015Remote/CClientListDlg.cpp +++ b/server/2015Remote/CClientListDlg.cpp @@ -94,10 +94,10 @@ static CString GetFieldValue(const ClientValue& val, GroupField field) } } -// 客户端自报的程序路径是 UTF-8(见 client/LoginServer.cpp 的 RES_FILE_PATH), +// 程序路径与地理位置均以 UTF-8 存储(见 AddList / client 的 RES_FILE_PATH、RES_CLIENT_LOC), // 这里用 LVM_SETITEMTEXTW 直接写宽字符串,绕开 ANSI→CP_ACP 回转, -// 即使服务端运行在非中文 ACP(如德语)上,中文路径也能正常显示(与窗口/进程管理同法)。 -static void SetProgramPathText(CListCtrl& list, int iItem, int iSubItem, const char* sz) +// 即使服务端运行在非中文 ACP(如德语)上,中文/带变音符的文本也能正常显示(与窗口/进程管理同法)。 +static void SetUtf8ItemText(CListCtrl& list, int iItem, int iSubItem, const char* sz) { std::wstring w; if (sz && sz[0]) { @@ -316,12 +316,12 @@ void CClientListDlg::DisplayClients() m_ClientList.SetItemText(nItem, COL_ID, strID); m_ClientList.SetItemText(nItem, COL_NOTE, val.Note); m_ClientList.SetItemText(nItem, COL_COMPUTER_NAME, CString(val.ComputerName)); - m_ClientList.SetItemText(nItem, COL_LOCATION, val.Location); + SetUtf8ItemText(m_ClientList, nItem, COL_LOCATION, val.Location); m_ClientList.SetItemText(nItem, COL_IP, val.IP); m_ClientList.SetItemText(nItem, COL_OS, val.OsName); m_ClientList.SetItemText(nItem, COL_INSTALL_TIME, val.InstallTime); m_ClientList.SetItemText(nItem, COL_LAST_LOGIN, val.LastLoginTime); - SetProgramPathText(m_ClientList, nItem, COL_PROGRAM_PATH, val.ProgramPath); + SetUtf8ItemText(m_ClientList, nItem, COL_PROGRAM_PATH, val.ProgramPath); m_ClientList.SetItemText(nItem, COL_LEVEL, strLevel); m_ClientList.SetItemText(nItem, COL_AUTH, strAuth); m_ClientList.SetItemData(nItem, (DWORD_PTR)key); @@ -368,12 +368,12 @@ void CClientListDlg::DisplayClients() m_ClientList.SetItemText(nItem, COL_ID, strID); m_ClientList.SetItemText(nItem, COL_NOTE, val.Note); m_ClientList.SetItemText(nItem, COL_COMPUTER_NAME, CString(val.ComputerName)); - m_ClientList.SetItemText(nItem, COL_LOCATION, val.Location); + SetUtf8ItemText(m_ClientList, nItem, COL_LOCATION, val.Location); m_ClientList.SetItemText(nItem, COL_IP, val.IP); m_ClientList.SetItemText(nItem, COL_OS, val.OsName); m_ClientList.SetItemText(nItem, COL_INSTALL_TIME, val.InstallTime); m_ClientList.SetItemText(nItem, COL_LAST_LOGIN, val.LastLoginTime); - SetProgramPathText(m_ClientList, nItem, COL_PROGRAM_PATH, val.ProgramPath); + SetUtf8ItemText(m_ClientList, nItem, COL_PROGRAM_PATH, val.ProgramPath); m_ClientList.SetItemText(nItem, COL_LEVEL, strLevel); m_ClientList.SetItemText(nItem, COL_AUTH, strAuth); m_ClientList.SetItemData(nItem, (DWORD_PTR)key); diff --git a/server/2015Remote/HostJson.cpp b/server/2015Remote/HostJson.cpp index acd6a44..11438d2 100644 --- a/server/2015Remote/HostJson.cpp +++ b/server/2015Remote/HostJson.cpp @@ -69,8 +69,9 @@ Json::Value BuildHostJson(context* ctx, _ClientList* clientMap) { CString os = ctx->GetClientData(ONLINELIST_OS); device["os"] = AnsiToUtf8(os); + // 地理位置已统一按 UTF-8 存储(见 AddList),直接透传,不再 CP_ACP→UTF-8 二次转换。 CString location = ctx->GetClientData(ONLINELIST_LOCATION); - device["location"] = AnsiToUtf8(location); + device["location"] = std::string(location.GetString()); CString rtt = ctx->GetClientData(ONLINELIST_PING); device["rtt"] = AnsiToUtf8(rtt); diff --git a/server/2015Remote/NotifyManager.cpp b/server/2015Remote/NotifyManager.cpp index 578cba0..708da7a 100644 --- a/server/2015Remote/NotifyManager.cpp +++ b/server/2015Remote/NotifyManager.cpp @@ -205,7 +205,8 @@ void NotifyManager::BuildHostOnlineEmail(context* ctx, const std::string& matche // Get host info std::string computerName = CT2A(ctx->GetClientData(ONLINELIST_COMPUTER_NAME), CP_UTF8); std::string ip = CT2A(ctx->GetClientData(ONLINELIST_IP), CP_UTF8); - std::string location = CT2A(ctx->GetClientData(ONLINELIST_LOCATION), CP_UTF8); + // 地理位置已统一按 UTF-8 存储(见 AddList),直接透传,不再 CP_ACP→UTF-8 二次转换。 + std::string location = ctx->GetClientData(ONLINELIST_LOCATION).GetString(); std::string os = CT2A(ctx->GetClientData(ONLINELIST_OS), CP_UTF8); std::string version = CT2A(ctx->GetClientData(ONLINELIST_VERSION), CP_UTF8); diff --git a/server/2015Remote/SearchBarDlg.cpp b/server/2015Remote/SearchBarDlg.cpp index d72697d..5382e6b 100644 --- a/server/2015Remote/SearchBarDlg.cpp +++ b/server/2015Remote/SearchBarDlg.cpp @@ -277,6 +277,12 @@ void CSearchBarDlg::DoSearch() colText = ctx->GetClientData(col); } + // 地理位置列已统一按 UTF-8 存储(见 AddList),而搜索输入是 CP_ACP; + // 转回 CP_ACP 再比对,否则按中文位置搜索会因编码不一致而失配。 + if (col == ONLINELIST_LOCATION) { + colText = CString(Utf8ToAnsi(std::string(colText.GetString())).c_str()); + } + colText.MakeLower(); if (colText.Find(searchText) >= 0) { matched = true; diff --git a/server/2015Remote/lang/de_DE.ini b/server/2015Remote/lang/de_DE.ini index 1d27867..f208c71 100644 --- a/server/2015Remote/lang/de_DE.ini +++ b/server/2015Remote/lang/de_DE.ini @@ -306,6 +306,7 @@ Web ; ============================================ ·¢ËÍÏûϢ=Nachricht senden ½áÊø½»̸=Chat beenden +ȷ¶¨¿ªʼԶ³̽»̸Âð?=Remote-Chat starten? Ëø¶¨ÆÁĻ\nÆÁ±ι¦Äܼü=Bildschirm sperren\nTasten blockieren ½â³ýËø¶¨=Entsperren diff --git a/server/2015Remote/lang/en_US.ini b/server/2015Remote/lang/en_US.ini index f0836e3..402da00 100644 --- a/server/2015Remote/lang/en_US.ini +++ b/server/2015Remote/lang/en_US.ini @@ -309,6 +309,7 @@ Web ; ============================================ ·¢ËÍÏûϢ=Send Msg ½áÊø½»̸=End Chat +ȷ¶¨¿ªʼԶ³̽»̸Âð?=Start remote chat? Ëø¶¨ÆÁĻ\nÆÁ±ι¦Äܼü=Lock Screen\nBlock Keys ½â³ýËø¶¨=Unlock diff --git a/server/2015Remote/lang/zh_TW.ini b/server/2015Remote/lang/zh_TW.ini index 94a23d8..32cd1fd 100644 --- a/server/2015Remote/lang/zh_TW.ini +++ b/server/2015Remote/lang/zh_TW.ini @@ -309,6 +309,7 @@ Web ; ============================================ ·¢ËÍÏûϢ=‚÷ËÍӍϢ ½áÊø½»̸=½YÊø½»Մ +ȷ¶¨¿ªʼԶ³̽»̸Âð?=´_¶¨é_ʼßh³̽»Մ†á? Ëø¶¨ÆÁĻ\nÆÁ±ι¦Äܼü=æi¶¨ΞĻ\nÆÁ±ι¦ÄÜæI ½â³ýËø¶¨=½â³ýæi¶¨