Compare commits
2
Commits
1a9312d2d1
...
ca6f76565c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca6f76565c | ||
|
|
d15cd77a28 |
No files matched your search
@@ -245,14 +245,17 @@ INT_PTR CALLBACK CChatManager::ChatDialogProc(HWND hDlg, UINT uMsg, WPARAM wPara
|
||||
return TRUE;
|
||||
}
|
||||
if (LOWORD(wParam) == IDC_BUTTON_END) {
|
||||
// 被控端主动结束交谈:关闭本地聊天窗口
|
||||
SendMessage(hDlg, WM_USER + 1, NULL, NULL);
|
||||
// 被控端关闭聊天窗口:仅隐藏对话框,会话保持。关闭交谈始终由服务端发起,
|
||||
// 服务端继续发消息时 OnReceive 会用 SW_RESTORE 重新弹出本对话框。
|
||||
ShowWindow(hDlg, SW_HIDE);
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_CLOSE:
|
||||
return 0;
|
||||
// X 关闭按钮:仅隐藏对话框(与「结束交谈」一致),关闭交谈始终由服务端发起
|
||||
ShowWindow(hDlg, SW_HIDE);
|
||||
return TRUE;
|
||||
|
||||
case WM_USER + 1:
|
||||
pThis->m_hWnd = NULL;
|
||||
|
||||
+19
-3
@@ -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);
|
||||
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());
|
||||
|
||||
+4
-2
@@ -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;
|
||||
|
||||
@@ -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<LPWSTR>(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();
|
||||
}
|
||||
|
||||
@@ -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<std::string>& 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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -306,6 +306,7 @@ Web
|
||||
; ============================================
|
||||
发送消息=Nachricht senden
|
||||
结束交谈=Chat beenden
|
||||
È·¶¨¿ªÊ¼Ô¶³Ì½»Ì¸Âð?=Remote-Chat starten?
|
||||
锁定屏幕\n屏蔽功能键=Bildschirm sperren\nTasten blockieren
|
||||
解除锁定=Entsperren
|
||||
|
||||
|
||||
@@ -309,6 +309,7 @@ Web
|
||||
; ============================================
|
||||
发送消息=Send Msg
|
||||
结束交谈=End Chat
|
||||
È·¶¨¿ªÊ¼Ô¶³Ì½»Ì¸Âð?=Start remote chat?
|
||||
锁定屏幕\n屏蔽功能键=Lock Screen\nBlock Keys
|
||||
解除锁定=Unlock
|
||||
|
||||
|
||||
@@ -309,6 +309,7 @@ Web
|
||||
; ============================================
|
||||
发送消息=傳送訊息
|
||||
结束交谈=結束交談
|
||||
ȷ����ʼԶ�̽�̸��?=�_���_ʼ�h�̽�Մ��?
|
||||
锁定屏幕\n屏蔽功能键=鎖定螢幕\n屏蔽功能鍵
|
||||
解除锁定=解除鎖定
|
||||
|
||||
|
||||
Reference in new issue
Block a user