4 Commits
Author SHA1 Message Date
yuanyuanxianganddeepseek-v4-pro 1a9312d2d1 Fix: Decode remote file-list names/paths from GBK/UTF-8 on non-Chinese systems
The Windows ghost client lists directories with FindFirstFileA, so remote
file names arrive as GBK/936 bytes. Both file-manager dialogs wrote them
back through ANSI InsertItem/SetItemText, which re-converts through CP_ACP
and garbles Chinese names when the operator machine runs a non-Chinese ACP.

Decode the name/path from the client encoding (GBK for Windows, UTF-8 for
LNX/MAC, chosen by RES_CLIENT_TYPE) and write the text via LVM_SETITEMTEXTW,
keeping the icon through an empty InsertItem - the same pattern used for the
process list, window list and client-list path.

Co-Authored-By: deepseek-v4-pro
2026-10-09 16:23:23 +02:00
yuanyuanxianganddeepseek-v4-pro f3a8406050 Fix: Decode client file path correctly in client list and info popup
The client reports its own exe path as UTF-8 in RES_FILE_PATH. Two
display sites re-decoded those bytes with the wrong code page and turned
Chinese paths into mojibake on non-Chinese servers:

- The client-list "程序路径" column wrote ProgramPath through the ANSI
  SetItemText path, so the UTF-8 bytes were misread as the system ACP.
  Decode UTF-8 to wide and write with LVM_SETITEMTEXTW instead (same
  pattern as the window/process lists).

- The double-click info popup normalized the client RES_* fields to the
  system ACP and rendered strText with CP_ACP -> wide. The app's internal
  ANSI convention is fixed GBK/936 (/execution-charset:.936 plus
  setlocale(".936")), so on non-Chinese systems the path collapsed to '?'.
  Normalize to 936 and decode 936 instead, which also fixes the popup's
  GBK labels and username in the same pass.

Co-Authored-By: deepseek-v4-pro
2026-10-09 15:00:39 +02:00
yuanyuanxianganddeepseek-v4-pro ff6b290580 Fix: Decode process list names/paths as GBK on Windows
Process management decoded the client's process name/path bytes with
GetClientEncoding, which returns UTF-8 for CLIENT_CAP_UTF8 clients. But
the Windows client builds the process list with the ANSI A-interface
(Process32FirstA / QueryFullProcessImageNameA), which emits the client's
system code page (GBK on Chinese Windows) regardless of the UTF-8
capability -- unlike window titles, which were migrated to the
W-interface plus UTF-8. Decoding GBK as UTF-8 garbled Chinese process
names and paths.

Add GetProcessListEncoding(): 936 for Windows clients, UTF-8 for
LNX/MAC, matching McpServer::BuildListProcesses, and use it in
ShowProcessList (which still writes via LVM_SETITEMTEXTW).

Also raise the IP history cap to 1000 and rename the experimental
"隐私屏幕" menu item to "虚拟屏幕".

Co-Authored-By: deepseek-v4-pro
2026-10-09 14:05:52 +02:00
yuanyuanxianganddeepseek-v4-pro da0ec983ba Feature: Show live transfer speed in file-transfer dialog title
Add a smoothed bytes-per-second readout to the CDlgFileSend title,
refreshed about every 200 ms during transfer (e.g. "发送文件 - 5.3M/s").
The base title is captured on the first progress frame and the speed
suffix applied through Unicode APIs (GetWindowTextW / SetWindowTextW),
so the whole title stays correct on non-Chinese systems where the ANSI
message path would misdecode GBK as the system ACP.

Also add the missing "接收文件 (V2)" translation (en_US "Receive File (V2)",
zh_TW "接收檔案 (V2)"), so the V2 receive title no longer falls back to
raw Chinese on non-Chinese systems.

Co-Authored-By: deepseek-v4-pro
2026-10-08 22:15:31 +02:00
12 changed files with 225 additions and 26 deletions

No files matched your search

Binary file not shown.
+33 -11
View File
@@ -239,6 +239,28 @@ UINT GetClientEncoding(context* ctx) {
return 936;
}
// 客户端「进程名/路径」编码:Windows 用 A 接口生成,恒为客户端 ANSI(简中=GBK/936),
// 不随 CLIENT_CAP_UTF8 转 UTF-8;LNX/MAC 的 /proc 天然 UTF-8。
// 不能用 GetClientEncoding——它按能力位返回 CP_UTF8,会误解 Windows 客户端的 GBK 进程名。
// 与 McpServer::BuildListProcesses 的判定保持一致。
UINT GetProcessListEncoding(context* ctx) {
if (!ctx) return 936;
CString clientType = ctx->GetAdditionalData(RES_CLIENT_TYPE);
if (clientType == "LNX" || clientType == "MAC") return CP_UTF8;
// 子连接情形:自身 RES_CLIENT_TYPE 为空 -> 找主连接取 clientType
if (g_2015RemoteDlg) {
context* mainCtx = g_2015RemoteDlg->FindHostByClientID(ctx->GetClientID());
if (!mainCtx) {
mainCtx = g_2015RemoteDlg->FindHostByIP(ctx->GetPeerName());
}
if (mainCtx) {
CString mainType = mainCtx->GetAdditionalData(RES_CLIENT_TYPE);
if (mainType == "LNX" || mainType == "MAC") return CP_UTF8;
}
}
return 936;
}
// 授权日志频率控制:首次必须记录,状态变化必须记录,相同状态每小时记录一次
static bool ShouldLogAuth(const std::string& sn, int success) {
struct AuthLogState {
@@ -8757,22 +8779,21 @@ void CMy2015RemoteDlg::OnListClick(NMHDR* pNMHDR, LRESULT* pResult)
CString startTime = ctx->GetClientData(ONLINELIST_STARTTIME);
ctx->GetAdditionalData(res);
// 客户端 RES_* 字符串编码取决于客户端能力位:UTF-8 客户端(Linux/macOS/新 Win)
// 发的是 UTF-8 字节,老客户端是 CP_ACP。这里统一规整到 CP_ACP,让下游 FormatL
// 与既有 ANSI 字符串拼接以及最终 CP_ACP→wide 的浮窗渲染都能正确识别。
// 若服务端运行系统的 ANSI 代码页不能容纳客户端字符(如德语服务端遇到中文路径),
// 不可表示的字符会变 '?' —— 与项目其它路径的既有限制一致,不在本次修复范围。
// 发的是 UTF-8 字节,老客户端是 GBK。这里统一规整到 GBK(936) —— 工程内部 ANSI
// 约定即 GBK(/execution-charset:.936 + setlocale(".936")),不随系统 ACP 变。
// 下游 FormatL 拼接、以及最终 936→wide 的浮窗渲染都能据此正确识别中文。
UINT cp = GetClientEncoding(ctx);
if (cp != CP_ACP) {
if (cp != 936) {
for (int i = 0; i < RES_MAX; i++) {
if (res[i].IsEmpty()) continue;
int wlen = MultiByteToWideChar(cp, 0, res[i].GetString(), -1, NULL, 0);
if (wlen <= 1) continue;
std::wstring wbuf(wlen - 1, L'\0');
MultiByteToWideChar(cp, 0, res[i].GetString(), -1, &wbuf[0], wlen);
int alen = WideCharToMultiByte(CP_ACP, 0, wbuf.c_str(), -1, NULL, 0, NULL, NULL);
int alen = WideCharToMultiByte(936, 0, wbuf.c_str(), -1, NULL, 0, NULL, NULL);
if (alen <= 1) continue;
CString out;
WideCharToMultiByte(CP_ACP, 0, wbuf.c_str(), -1,
WideCharToMultiByte(936, 0, wbuf.c_str(), -1,
out.GetBufferSetLength(alen - 1), alen, NULL, NULL);
out.ReleaseBuffer(alen - 1);
res[i] = out;
@@ -8827,13 +8848,14 @@ void CMy2015RemoteDlg::OnListClick(NMHDR* pNMHDR, LRESULT* pResult)
}
auto* tip = new CPreviewTipWnd();
// 项目是 MBCS(CP_ACP),浮窗内部用宽字符渲染(避免跨语言系统乱码)。
// 这里把 strText (CStringA) 按 CP_ACP 转 wide。
// 浮窗内部用宽字符渲染。strText 里的中文(标签来自 GBK 源字面量,路径/用户名
// 已在上方规整为 GBK)统一是 GBK(936),按 936 转 wide 而非系统 CP_ACP ——
// 否则非中文 ACP(德语/英语等)服务端会把中文解成乱码。
CStringW textW;
{
int wlen = MultiByteToWideChar(CP_ACP, 0, strText.GetString(), -1, NULL, 0);
int wlen = MultiByteToWideChar(936, 0, strText.GetString(), -1, NULL, 0);
if (wlen > 1) {
MultiByteToWideChar(CP_ACP, 0, strText.GetString(), -1, textW.GetBufferSetLength(wlen - 1), wlen);
MultiByteToWideChar(936, 0, strText.GetString(), -1, textW.GetBufferSetLength(wlen - 1), wlen);
textW.ReleaseBuffer(wlen - 1);
}
}
+7
View File
@@ -109,6 +109,13 @@ bool SupportsFileTransferV2(context* ctx);
// 找不到主连接或老客户端:默认 CP936(覆盖 95% 简中/英语 ASCII 老客户端)。
UINT GetClientEncoding(context* ctx);
// 获取客户端「进程名/路径」编码 (CP_UTF8 或 936)。
// 与 GetClientEncoding 不同:Windows 客户端的进程名/路径由 A 接口
// (Process32FirstA / QueryFullProcessImageNameA 等)生成,恒为客户端系统
// ANSI(简中=GBK/936),不随 CLIENT_CAP_UTF8 转 UTF-8;仅 LNX/MAC 走 UTF-8。
// 与 McpServer::BuildListProcesses 的判定保持一致。
UINT GetProcessListEncoding(context* ctx);
// 服务端待续传的传输信息
struct PendingTransferV2 {
uint64_t clientID;
+24 -2
View File
@@ -5,6 +5,7 @@
#include "afxdialogex.h"
#include "CClientListDlg.h"
#include "2015Remote.h"
#include <string>
// CClientListDlg 对话框
@@ -93,6 +94,27 @@ static CString GetFieldValue(const ClientValue& val, GroupField field)
}
}
// 客户端自报的程序路径是 UTF-8(见 client/LoginServer.cpp 的 RES_FILE_PATH),
// 这里用 LVM_SETITEMTEXTW 直接写宽字符串,绕开 ANSI→CP_ACP 回转,
// 即使服务端运行在非中文 ACP(如德语)上,中文路径也能正常显示(与窗口/进程管理同法)。
static void SetProgramPathText(CListCtrl& list, int iItem, int iSubItem, const char* sz)
{
std::wstring w;
if (sz && sz[0]) {
int wlen = MultiByteToWideChar(CP_UTF8, 0, sz, -1, NULL, 0);
if (wlen > 1) {
w.resize(wlen - 1);
MultiByteToWideChar(CP_UTF8, 0, sz, -1, &w[0], wlen);
}
}
LVITEMW lv = {};
lv.mask = LVIF_TEXT;
lv.iItem = iItem;
lv.iSubItem = iSubItem;
lv.pszText = w.empty() ? const_cast<LPWSTR>(L"") : &w[0];
::SendMessageW(list.GetSafeHwnd(), LVM_SETITEMTEXTW, (WPARAM)iItem, (LPARAM)&lv);
}
// 比较两个客户端的指定列,返回 <0, 0, >0
static int CompareClientByColumn(const std::pair<ClientKey, ClientValue>& a,
const std::pair<ClientKey, ClientValue>& b,
@@ -299,7 +321,7 @@ void CClientListDlg::DisplayClients()
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);
m_ClientList.SetItemText(nItem, COL_PROGRAM_PATH, CString(val.ProgramPath));
SetProgramPathText(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);
@@ -351,7 +373,7 @@ void CClientListDlg::DisplayClients()
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);
m_ClientList.SetItemText(nItem, COL_PROGRAM_PATH, CString(val.ProgramPath));
SetProgramPathText(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);
+49
View File
@@ -151,6 +151,54 @@ void CDlgFileSend::UpdateProgress(CString file, const FileProgressInfo& info)
PostMessageA(WM_UPDATEFILEPROGRESS, (WPARAM)new CString(file), (LPARAM)new FileProgressInfo(info));
}
// 将字节/秒格式化为人类可读的速度串(如 "5.3M/s"、"320K/s"、"800B/s")
static CStringW FormatSpeed(double bps)
{
CStringW s;
if (bps >= 1024.0 * 1024.0)
s.Format(L"%.1fM/s", bps / (1024.0 * 1024.0));
else if (bps >= 1024.0)
s.Format(L"%.0fK/s", bps / 1024.0);
else
s.Format(L"%.0fB/s", bps);
return s;
}
// 累计字节并周期性刷新标题中的速度(如 "发送文件 - 5.3M/s")
void CDlgFileSend::UpdateTitleWithSpeed(uint64_t dataLength)
{
m_ullTotalBytes += dataLength;
ULONGLONG now = GetTickCount64();
// 首帧:用 Unicode API 捕获当前标题作为基础部分(可能已被 V2/C2C 覆盖为 "接收文件 (V2)" 等),
// 并建立采样基准;此时尚无速度可显示。
if (!m_bBaseTitleCaptured) {
WCHAR wBase[128] = { 0 };
::GetWindowTextW(m_hWnd, wBase, 128);
m_strBaseTitle = wBase;
m_bBaseTitleCaptured = true;
m_ullLastTick = now;
m_ullLastBytes = m_ullTotalBytes;
return;
}
ULONGLONG dt = now - m_ullLastTick;
if (dt < 200) return; // 采样间隔未到,仅累积字节
uint64_t delta = m_ullTotalBytes - m_ullLastBytes;
double speed = dt ? (double)delta * 1000.0 / (double)dt : 0.0;
m_dSpeedBps = (m_dSpeedBps == 0.0) ? speed : m_dSpeedBps * 0.6 + speed * 0.4; // 轻度平滑
m_ullLastTick = now;
m_ullLastBytes = m_ullTotalBytes;
// 基础标题(Unicode)+ ASCII 速度后缀,全程 Unicode API,避免非中文系统上 ANSI 消息按系统 ACP 解码乱码
CStringW title = m_strBaseTitle;
title += L" - ";
title += FormatSpeed(m_dSpeedBps);
::SetWindowTextW(m_hWnd, title);
}
void CDlgFileSend::FinishFileSend(BOOL succeed)
{
if (!GetSafeHwnd()) return;
@@ -170,6 +218,7 @@ LRESULT CDlgFileSend::OnUpdateFileProgress(WPARAM wParam, LPARAM lParam)
SetDlgItemText(IDC_STATIC_CURRENTINDEX, status);
SetDlgItemText(IDC_STATIC_CURRENT_FILE, *pFile);
m_Progress.SetPos((int)percent);
UpdateTitleWithSpeed(pInfo->dataLength);
// 只在第一次显示时置顶,后续更新不抢焦点
if (!IsWindowVisible()) {
+10
View File
@@ -51,6 +51,14 @@ public:
BOOL m_bTargetOffline = FALSE; // C2C目标已离线,已发送取消包
enum { IDD = IDD_DIALOG_FILESEND };
// 传输速度统计(用于标题实时显示速度,如 "发送文件 - 5.3M/s")
CStringW m_strBaseTitle; // 标题基础部分("发送文件"/"接收文件 (V2)" 等,Unicode),首帧进度时捕获
bool m_bBaseTitleCaptured = false;
uint64_t m_ullTotalBytes = 0; // 累计传输字节
uint64_t m_ullLastBytes = 0;
ULONGLONG m_ullLastTick = 0; // 上次速度采样时刻 (ms)
double m_dSpeedBps = 0.0; // 平滑后的速度 (B/s)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
@@ -59,6 +67,8 @@ protected:
afx_msg LRESULT OnUpdateFileProgress(WPARAM wParam, LPARAM lParam);
afx_msg LRESULT OnFinishFileSend(WPARAM wParam, LPARAM lParam);
void UpdateTitleWithSpeed(uint64_t dataLength); // 累计字节并周期性刷新标题速度
public:
CProgressCtrl m_Progress;
virtual BOOL OnInitDialog();
+1 -1
View File
@@ -250,7 +250,7 @@ bool UpdateLicenseAuthorization(const std::string& deviceID, const std::string&
}
// IP 列表管理常量
#define MAX_IP_HISTORY 500 // 最多保留 几 个不同的 IP
#define MAX_IP_HISTORY 1000 // 最多保留 几 个不同的 IP
// 解析 IP 列表字符串为 vector<pair<IP, 时间戳>>
// 格式: "192.168.1.1|260218, 10.0.0.1|260215" (yyMMdd)
+32 -3
View File
@@ -223,6 +223,27 @@ int GetIconIndex(LPCTSTR lpFileName, DWORD dwFileAttributes)
return index;
}
// 远程文件名/路径编码按 clientType 判定:Windows 走 A 接口(FindFirstFileA)=GBK/936,
// LNX/MAC 文件系统天然 UTF-8(同 McpServer.cpp::BuildListFiles)。这里用 LVM_SETITEMTEXTW
// 直接写宽字符串,绕开 ANSI→CP_ACP 回转,即使服务端运行在非中文 ACP 上中文名也能正常显示。
static void SetFileTextItem(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<LPWSTR>(L"") : &w[0];
::SendMessageW(list.GetSafeHwnd(), LVM_SETITEMTEXTW, (WPARAM)iItem, (LPARAM)&lv);
}
BOOL CFileManagerDlg::OnInitDialog()
{
__super::OnInitDialog();
@@ -1104,6 +1125,9 @@ void CFileManagerDlg::OnDblclkListRemote(NMHDR* pNMHDR, LRESULT* pResult)
void CFileManagerDlg::FixedRemoteFileList(BYTE *pbBuffer, DWORD dwBufferLen)
{
CString clientType = m_ContextObject->GetAdditionalData(RES_CLIENT_TYPE);
UINT cp = (clientType == _T("LNX") || clientType == _T("MAC")) ? CP_UTF8 : 936;
// 重新设置ImageList
SHFILEINFO sfi = {};
HIMAGELIST hImageListLarge = (HIMAGELIST)SHGetFileInfo(NULL, 0, &sfi,sizeof(SHFILEINFO), SHGFI_SYSICONINDEX | SHGFI_LARGEICON);
@@ -1151,7 +1175,8 @@ void CFileManagerDlg::FixedRemoteFileList(BYTE *pbBuffer, DWORD dwBufferLen)
pszFileName = ++pList;
if (bIsInsert) {
nItem = m_list_remote.InsertItem(nItemIndex++, pszFileName, GetIconIndex(pszFileName, nType));
nItem = m_list_remote.InsertItem(nItemIndex++, "", GetIconIndex(pszFileName, nType));
SetFileTextItem(m_list_remote, nItem, 0, pszFileName, cp);
m_list_remote.SetItemData(nItem, nType == FILE_ATTRIBUTE_DIRECTORY);
SHFILEINFO sfi = {};
SHGetFileInfo(pszFileName, FILE_ATTRIBUTE_NORMAL | nType, &sfi,sizeof(SHFILEINFO), SHGFI_TYPENAME | SHGFI_USEFILEATTRIBUTES);
@@ -1184,6 +1209,9 @@ void CFileManagerDlg::FixedRemoteFileList(BYTE *pbBuffer, DWORD dwBufferLen)
void CFileManagerDlg::FixedRemoteSearchFileList(BYTE *pbBuffer, DWORD dwBufferLen)
{
CString clientType = m_ContextObject->GetAdditionalData(RES_CLIENT_TYPE);
UINT cp = (clientType == _T("LNX") || clientType == _T("MAC")) ? CP_UTF8 : 936;
// 第一批结果到达时,初始化搜索列表
if (!m_bSearching) {
m_bSearching = true;
@@ -1241,7 +1269,8 @@ void CFileManagerDlg::FixedRemoteSearchFileList(BYTE *pbBuffer, DWORD dwBufferLe
}
}
int nItem = m_list_remote.InsertItem(m_nSearchResultCount, csFileName, nIcon);
int nItem = m_list_remote.InsertItem(m_nSearchResultCount, "", nIcon);
SetFileTextItem(m_list_remote, nItem, 0, csFileName, cp);
m_list_remote.SetItemData(nItem, nType == FILE_ATTRIBUTE_DIRECTORY);
pList += lstrlen(pszFullPath) + 1;
@@ -1261,7 +1290,7 @@ void CFileManagerDlg::FixedRemoteSearchFileList(BYTE *pbBuffer, DWORD dwBufferLe
m_list_remote.SetItemText(nItem, 2, time.FormatL("%Y-%m-%d %H:%M"));
// 路径
m_list_remote.SetItemText(nItem, 3, csDir);
SetFileTextItem(m_list_remote, nItem, 3, csDir, cp);
pList += 16;
m_nSearchResultCount++;
+28 -3
View File
@@ -232,6 +232,26 @@ void CSystemDlg::ShowWindowsList(void)
ApplySort(); // 应用记忆的排序偏好
}
// 按客户端编码 (936 或 UTF-8) 解码字节串,并用 LVM_SETITEMTEXTW 写入列表单元格,
// 绕开 ANSI→CP_ACP 回转(非中文 ACP 服务端上中文也能正常显示,与窗口管理同法)。
static void SetClientTextItem(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<LPWSTR>(L"") : &w[0];
::SendMessageW(list.GetSafeHwnd(), LVM_SETITEMTEXTW, (WPARAM)iItem, (LPARAM)&lv);
}
void CSystemDlg::ShowProcessList(void)
{
// GetMyBuffer(1) 深拷贝数据,跳过 TOKEN 字节
@@ -248,6 +268,10 @@ void CSystemDlg::ShowProcessList(void)
const char *szProcessFullPath;
DWORD dwOffset = 0;
CString str;
// 进程名/路径编码:Windows 客户端 A 接口生成,恒为 GBK/936;仅 LNX/MAC 为 UTF-8。
// 不能用 GetClientEncoding(它对新客户端返回 CP_UTF8,会把 GBK 进程名解成乱码)。
UINT cp = GetProcessListEncoding(m_ContextObject);
DeleteAllItems();
//遍历发送来的每一个字符别忘了他的数据结构啊 Id+进程名+0+完整名+0
int i;
@@ -258,10 +282,11 @@ void CSystemDlg::ShowProcessList(void)
szProcessFullPath = szExeFile + lstrlen(szExeFile) + 1; //完整名就是进程名之后的啦
//他的数据结构的构建很巧妙
m_ControlList.InsertItem(i, arr[0].c_str()); //将得到的数据加入到列表当中
m_ControlList.InsertItem(i, ""); // 占位,首列随后用 Unicode 写入
str.FormatL("%5u", *PID);
m_ControlList.SetItemText(i, 1, str);
m_ControlList.SetItemText(i, 2, szProcessFullPath);
m_ControlList.SetItemText(i, 1, str); // PID (ASCII)
SetClientTextItem(m_ControlList, i, 0, arr[0].c_str(), cp); // 进程名(按客户端编码)
SetClientTextItem(m_ControlList, i, 2, szProcessFullPath, cp); // 程序路径(按客户端编码)
m_ControlList.SetItemText(i, 3, arr[1].empty() ? "N/A" : arr[1].c_str());
// ItemData 为进程ID;Data[3] 需与第 4 列"架构"一致,否则按架构排序拿到空串而无意义
auto data = new ItemData{ *PID, {arr[0].c_str(), str, szProcessFullPath, arr[1].empty() ? "N/A" : arr[1].c_str()}, arr[1].c_str() };
+39 -6
View File
@@ -7,6 +7,7 @@
#include "InputDlg.h"
#include <windows.h>
#include <Shlwapi.h>
#include <string>
#ifdef _DEBUG
#define new DEBUG_NEW
@@ -818,8 +819,32 @@ void CFileManagerDlg::OnclickListSearch(NMHDR* pNMHDR, LRESULT* pResult)
int GetIconIndex(LPCTSTR lpFileName, DWORD dwFileAttributes);
// 远程文件名/路径编码按 clientType 判定:Windows 走 A 接口(FindFirstFileA)=GBK/936,
// LNX/MAC 文件系统天然 UTF-8(同 McpServer.cpp::BuildListFiles)。这里用 LVM_SETITEMTEXTW
// 直接写宽字符串,绕开 ANSI→CP_ACP 回转,即使服务端运行在非中文 ACP 上中文名也能正常显示。
static void SetFileTextItem(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<LPWSTR>(L"") : &w[0];
::SendMessageW(list.GetSafeHwnd(), LVM_SETITEMTEXTW, (WPARAM)iItem, (LPARAM)&lv);
}
void CFileManagerDlg::FixedRemoteFileList(BYTE* pbBuffer, DWORD dwBufferLen)
{
CString clientType = m_ContextObject->GetAdditionalData(RES_CLIENT_TYPE);
UINT cp = (clientType == _T("LNX") || clientType == _T("MAC")) ? CP_UTF8 : 936;
// 重建标题
m_list_remote.DeleteAllItems();
int nItemIndex = 0;
@@ -855,7 +880,8 @@ void CFileManagerDlg::FixedRemoteFileList(BYTE* pbBuffer, DWORD dwBufferLen)
if (bIsInsert) {
int iicon = GetIconIndex(pszFileName, nType);
nItem = m_list_remote.InsertItem(nItemIndex++, pszFileName, iicon);
nItem = m_list_remote.InsertItem(nItemIndex++, _T(""), iicon);
SetFileTextItem(m_list_remote, nItem, 0, pszFileName, cp);
m_list_remote.SetItemData(nItem, nType == FILE_ATTRIBUTE_DIRECTORY);
SHFILEINFO sfi = {};
SHGetFileInfo(pszFileName, FILE_ATTRIBUTE_NORMAL | nType, &sfi, sizeof(SHFILEINFO), SHGFI_TYPENAME | SHGFI_USEFILEATTRIBUTES);
@@ -2421,6 +2447,9 @@ void CFileManagerDlg::SearchEnd()
void CFileManagerDlg::FixedRemoteSearchFileList(BYTE* pbBuffer, DWORD dwBufferLen)
{
CString clientType = m_ContextObject->GetAdditionalData(RES_CLIENT_TYPE);
UINT cp = (clientType == _T("LNX") || clientType == _T("MAC")) ? CP_UTF8 : 936;
byte* pList = pbBuffer + 1;
int nItemIndex = 0;
for (byte* pBase = pList; (int)(pList - pBase) < (int)(dwBufferLen - 1);) {
@@ -2439,7 +2468,8 @@ void CFileManagerDlg::FixedRemoteSearchFileList(BYTE* pbBuffer, DWORD dwBufferLe
int nPos = csFilePath.ReverseFind(_T('\\'));
csFileFullName = csFilePath.Right(csFilePath.GetLength() - nPos - 1);// 获取文件全名,包括文件名和扩展名
nItem = m_list_remote_search.InsertItem(nItemIndex++, csFileFullName, GetIconIndex(pszFileName, nType));
nItem = m_list_remote_search.InsertItem(nItemIndex++, _T(""), GetIconIndex(pszFileName, nType));
SetFileTextItem(m_list_remote_search, nItem, 0, csFileFullName, cp);
m_list_remote_search.SetItemData(nItem, nType == FILE_ATTRIBUTE_DIRECTORY);
pList += (lstrlen(pszFileName) + 1) * sizeof(TCHAR);
@@ -2455,7 +2485,7 @@ void CFileManagerDlg::FixedRemoteSearchFileList(BYTE* pbBuffer, DWORD dwBufferLe
m_list_remote_search.SetItemText(nItem, 2, time.FormatL(_T("%Y-%m-%d %H:%M")));
PathRemoveFileSpec(pszFileName); //去除文件名获取文件路径
m_list_remote_search.SetItemText(nItem, 3, pszFileName);
SetFileTextItem(m_list_remote_search, nItem, 3, pszFileName, cp);
pList += 16;
}
@@ -2491,6 +2521,9 @@ void CFileManagerDlg::ShowSearchPlugList()
char* Filename;
char* Pathname;
CString clientType = m_ContextObject->GetAdditionalData(RES_CLIENT_TYPE);
UINT cp = (clientType == _T("LNX") || clientType == _T("MAC")) ? CP_UTF8 : 936;
DWORD dwOffset = 0;
m_list_remote_search.DeleteAllItems();
int i = 0;
@@ -2498,13 +2531,13 @@ void CFileManagerDlg::ShowSearchPlugList()
Filename = lpBuffer + dwOffset;
Pathname = Filename + strlen(Filename) + 1;
CString CS_tem;
CS_tem = Filename;
m_list_remote_search.InsertItem(i, CS_tem, 0);
m_list_remote_search.InsertItem(i, _T(""), 0);
SetFileTextItem(m_list_remote_search, i, 0, Filename, cp);
CS_tem = Pathname;
int index = CS_tem.ReverseFind('\\');
if (index >= 0)
CS_tem = CS_tem.Left(index);
m_list_remote_search.SetItemText(i,3, CS_tem);
SetFileTextItem(m_list_remote_search, i, 3, CS_tem, cp);
dwOffset += strlen(Filename) + strlen(Pathname) + 2;
}
m_list_remote_search.EnableWindow(TRUE);
+1
View File
@@ -1400,6 +1400,7 @@ RDP
C2C 文件传输=C2C File Transfer
续传文件 (V2)=Resume File Upload (V2)
发送文件 (V2)=Send File (V2)
接收文件 (V2)=Receive File (V2)
发送文件(%d/%d): %.2f%%=Send Files(%d/%d): %.2f%%
接收文件(%d/%d): %.2f%%=Recv Files(%d/%d): %.2f%%
[远程右键复制, 本地 Ctrl+V 粘贴]= [Remote Right-click Copy, Local Ctrl+V Paste]
+1
View File
@@ -1396,6 +1396,7 @@ RDP
C2C 文件传输=C2C 檔案傳輸
续传文件 (V2)=續傳檔案 (V2)
发送文件 (V2)=傳送檔案 (V2)
接收文件 (V2)=接收檔案 (V2)
发送文件(%d/%d): %.2f%%=发送文件(%d/%d): %.2f%%
接收文件(%d/%d): %.2f%%=接收文件(%d/%d): %.2f%%
[远程右键复制, 本地 Ctrl+V 粘贴]= [遠端右鍵複製, 本地 Ctrl+V 貼上]