3 Commits
Author SHA1 Message Date
yuanyuanxiangandClaude Opus 4.8 11e8b122fe Fix: Client log dialog stops updating past edit-control default limit
The Win32 EDIT control caps text at ~32K/64K chars by default, so the
client log dialog silently stopped accepting appends long before the
512KB truncation logic could run. Call SetLimitText(0) in both
CClientLog and CActivityDialog to lift the cap.

Also unify newline handling in Logger: each entry now ends with exactly
one '\n' at the source, so the in-memory ring-buffer dump (used by the
TCP log query) splits into lines correctly instead of relying on every
Mprintf caller ending its format with '\n'. writeToFile drops the extra
std::endl accordingly.

Additionally fix a CFont leak in CClientLog::OnInitDialog by moving the
font to a member.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-20 17:54:33 +02:00
yuanyuanxianganddeepseek-v4-pro 0a1ecfc4ab Improve: Show host remark instead of IP in remote desktop dialog titles
When an online host has a remark set in the client list, use it in place of
the IP in the remote desktop ("远程桌面控制") and virtual screen ("远程虚拟屏幕")
dialog titles, falling back to the IP when no remark is present.

Add a GetTitleHostName() helper to both dialogs. CScreenSpyDlg resolves the
remark through its parent dialog's client map, while CHideScreenSpyDlg uses
the global g_2015RemoteDlg pointer; each falls back to m_IPAddress when the
host has no remark.

Co-Authored-By: deepseek-v4-pro
2026-08-20 16:36:30 +02:00
yuanyuanxianganddeepseek-v4-pro c08315cd79 Fix remote desktop cursor cross-contamination across dialogs
The view-mode "forbidden" cursor was applied with SetClassLongPtr(GCLP_HCURSOR),
which mutates the cursor for the whole window class instead of the single dialog
instance. Opening a second remote-desktop dialog without remote control overwrote
that shared class cursor with IDC_NO, so the already-controlled dialog also began
showing the forbidden icon even though it remained operable.

Move the view-mode cursor decision into OnSetCursor, which Windows routes per
window, and remove the now-unused class-cursor assignments. Remote-control mode
and the remote cursor-shape path are left unchanged.

Also drop the dead m_hRemoteCursor member and the leftover GetIconInfo block
(which leaked hbmMask/hbmColor in ScreenSpyDlg), plus the now-unreferenced
m_bMouseTracking flag.

Co-Authored-By: deepseek-v4-pro
2026-08-20 16:26:12 +02:00
8 changed files with 88 additions and 38 deletions

No files matched your search

+7 -1
View File
@@ -152,6 +152,12 @@ public:
id + "[" + timestamp + "] [" + file + ":" + std::to_string(line) + "] " + message:
id + "[" + timestamp + "] " + message;
// 方案B:在源头统一保证每条日志以单个 '\n' 结尾,文件与内存 ring buffer
// 共用同一份文本;写文件时不再额外追加换行,内存导出直接拼接即可正确分行。
while (!logEntry.empty() && (logEntry.back() == '\n' || logEntry.back() == '\r'))
logEntry.pop_back();
logEntry += '\n';
// Always record to in-memory ring buffer regardless of enable flag.
{
std::lock_guard<std::mutex> lock(m_memMutex);
@@ -305,7 +311,7 @@ private:
std::lock_guard<std::mutex> lock(fileMutex);
std::ofstream logFile(logFileName, std::ios::app);
if (logFile.is_open()) {
logFile << logEntry << std::endl;
logFile << logEntry; // logEntry 已在 log() 中以单个 '\n' 结尾
}
}
+2
View File
@@ -39,6 +39,8 @@ BOOL CActivityDialog::OnInitDialog()
m_editLog.SetFont(&m_font);
// 重建为 Unicode 编辑框:这是 ✻/✦ 能正确显示的关键。重建时已保留上面的字体。
RebuildEdit(m_editLog);
// 解除 EDIT 控件默认约 64KB 长度上限,否则大快照经 SetWindowTextW 会被静默截断。
m_editLog.SetLimitText(0);
return TRUE;
}
+3 -3
View File
@@ -37,9 +37,9 @@ BOOL CClientLog::OnInitDialog()
HICON hIcon = AfxGetApp()->LoadIcon(IDI_CLIENTLOG);
SetIcon(hIcon, TRUE);
SetIcon(hIcon, FALSE);
CFont* pFont = new CFont();
pFont->CreatePointFont(85, _T("Consolas"));
m_editLog.SetFont(pFont);
m_font.CreatePointFont(85, _T("Consolas"));
m_editLog.SetFont(&m_font);
m_editLog.SetLimitText(0); // 解除 EDIT 控件默认长度上限,改由 AppendLog 的 512KB 逻辑管理
return TRUE;
}
+1
View File
@@ -24,6 +24,7 @@ protected:
private:
CEdit m_editLog;
CFont m_font;
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_DIALOG_CLIENT_LOG };
+37 -15
View File
@@ -6,6 +6,7 @@
#include "InputDlg.h"
#include "CTextDlg.h"
#include "HideScreenSpyDlg.h"
#include "2015RemoteDlg.h"
#include <windows.h>
#ifdef _DEBUG
@@ -43,6 +44,8 @@ IMPLEMENT_DYNAMIC(CHideScreenSpyDlg, CDialog)
bool DirectoryExists(const char* path);
std::string GetScreenShotPath(CWnd* parent, const CString& ip, const CString& filter, const CString& suffix);
extern CMy2015RemoteDlg* g_2015RemoteDlg;
CHideScreenSpyDlg::CHideScreenSpyDlg(CWnd* pParent, Server* pIOCPServer, ClientContext* pContext)
: DialogBase(CHideScreenSpyDlg::IDD, pParent, pIOCPServer, pContext, IDI_SCREENSYP)
{
@@ -90,11 +93,33 @@ BEGIN_MESSAGE_MAP(CHideScreenSpyDlg, CDialog)
ON_WM_PAINT()
ON_WM_TIMER()
ON_WM_CLOSE()
ON_WM_SETCURSOR()
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CHideScreenSpyDlg message handlers
BOOL CHideScreenSpyDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
// 查看模式(未开启远程控制):客户区显示禁止图标;标题栏及系统按钮保持正常箭头,
// 便于拖动窗口或通过标题栏右键菜单开启远程控制。
// 这里按窗口自身状态判断,避免用窗口类光标(GCLP_HCURSOR)时被同类的其他对话框实例
// 共享串扰——第二个对话框切到查看模式后,第一个控制中的对话框也被误显示成禁止图标。
if (!m_bIsCtrl && nHitTest == HTCLIENT) {
::SetCursor(LoadCursor(NULL, IDC_NO));
return TRUE;
}
if (!m_bIsCtrl &&
(nHitTest == HTCAPTION || nHitTest == HTSYSMENU || nHitTest == HTMENU ||
nHitTest == HTCLOSE || nHitTest == HTMINBUTTON || nHitTest == HTMAXBUTTON ||
nHitTest == HTHELP)) {
::SetCursor(LoadCursor(NULL, IDC_ARROW));
return TRUE;
}
// 控制模式:交父类处理,显示窗口类光标(远程光标形状)
return __super::OnSetCursor(pWnd, nHitTest, message);
}
void CHideScreenSpyDlg::OnClose()
{
if (!m_aviFile.IsEmpty()) {
@@ -153,11 +178,22 @@ bool CHideScreenSpyDlg::SaveSnapshot()
return true;
}
CString CHideScreenSpyDlg::GetTitleHostName() const
{
// 标题栏显示的主机名:有备注用备注,否则用 IP
if (g_2015RemoteDlg && g_2015RemoteDlg->m_ClientMap) {
CString note = g_2015RemoteDlg->m_ClientMap->GetClientMapData(GetClientID(), MAP_NOTE);
if (!note.IsEmpty())
return note;
}
return m_IPAddress;
}
BOOL CHideScreenSpyDlg::OnInitDialog()
{
__super::OnInitDialog();
CString strString;
strString.FormatL("%s - 远程虚拟屏幕 %d×%d", m_IPAddress,
strString.FormatL("%s - 远程虚拟屏幕 %d×%d", GetTitleHostName().GetString(),
m_BitmapInfor_Full->bmiHeader.biWidth, m_BitmapInfor_Full->bmiHeader.biHeight);
SetWindowText(strString);
@@ -165,7 +201,6 @@ BOOL CHideScreenSpyDlg::OnInitDialog()
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
SetClassLongPtr(m_hWnd, GCLP_HCURSOR, (LONG_PTR)LoadCursor(NULL, IDC_NO));
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL) {
pSysMenu->AppendMenuSeparator(MF_SEPARATOR);
@@ -214,15 +249,7 @@ BOOL CHideScreenSpyDlg::OnInitDialog()
}
// TODO: Add extra initialization here
m_hRemoteCursor = LoadCursor(NULL, IDC_ARROW);
ICONINFO CursorInfo;
::GetIconInfo(m_hRemoteCursor, &CursorInfo);
pSysMenu->CheckMenuItem(IDM_CONTROL, m_bIsCtrl ? MF_CHECKED : MF_UNCHECKED);
SetClassLongPtr(m_hWnd, GCLP_HCURSOR, (LONG_PTR)m_hRemoteCursor);
if (CursorInfo.hbmMask != NULL)
::DeleteObject(CursorInfo.hbmMask);
if (CursorInfo.hbmColor != NULL)
::DeleteObject(CursorInfo.hbmColor);
// 初始化窗口大小结构
m_hFullDC = ::GetDC(m_hWnd);
m_hFullMemDC = CreateCompatibleDC(m_hFullDC);
@@ -440,11 +467,6 @@ void CHideScreenSpyDlg::OnSysCommand(UINT nID, LPARAM lParam)
case IDM_CONTROL: {
m_bIsCtrl = !m_bIsCtrl;
pSysMenu->CheckMenuItem(IDM_CONTROL, m_bIsCtrl ? MF_CHECKED : MF_UNCHECKED);
if (m_bIsCtrl) {
SetClassLongPtr(m_hWnd, GCLP_HCURSOR, (LONG_PTR)m_hRemoteCursor);
} else
SetClassLongPtr(m_hWnd, GCLP_HCURSOR, (LONG_PTR)LoadCursor(NULL, IDC_NO));
}
break;
+4 -1
View File
@@ -59,6 +59,7 @@ public:
afx_msg void OnPaint();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnSize(UINT nType, int cx, int cy);
afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message);
virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
@@ -71,11 +72,13 @@ protected:
bool JPG_BMP(int cbit, void* input, int inlen, void* output);
void ResetScreen();
// 标题栏显示的主机名:有备注用备注,否则用 IP
CString GetTitleHostName() const;
HDC m_hFullDC, m_hFullMemDC;
HBITMAP m_BitmapHandle;
LPVOID m_BitmapData_Full;
LPBITMAPINFO m_BitmapInfor_Full;
HCURSOR m_hRemoteCursor;
CCursorInfo m_CursorInfo;
BOOL m_bIsFirst;
BOOL m_bIsCtrl;
+31 -16
View File
@@ -648,7 +648,7 @@ void CScreenSpyDlg::PrepareDrawing(const LPBITMAPINFO bmp)
m_BitmapData_Full = NULL;
CString strString;
strString.FormatL("%s - 远程桌面控制 %d×%d", m_IPAddress, bmp->bmiHeader.biWidth, bmp->bmiHeader.biHeight);
strString.FormatL("%s - 远程桌面控制 %d×%d", GetTitleHostName().GetString(), bmp->bmiHeader.biWidth, bmp->bmiHeader.biHeight);
SetWindowText(strString);
uint64_t dlg = (uint64_t)this;
Mprintf("%s [对话框ID: %llu]\n", strString.GetString(), dlg);
@@ -843,13 +843,9 @@ BOOL CScreenSpyDlg::OnInitDialog()
m_ClientCursorPos.y = 0;
m_bCursorIndex = 0;
m_hRemoteCursor = LoadCursor(NULL, IDC_ARROW);
ICONINFO CursorInfo;
::GetIconInfo(m_hRemoteCursor, &CursorInfo);
SysMenu->CheckMenuItem(IDM_CONTROL, m_bIsCtrl ? MF_CHECKED : MF_UNCHECKED);
SysMenu->CheckMenuItem(IDM_ADAPTIVE_SIZE, m_bAdaptiveSize ? MF_CHECKED : MF_UNCHECKED);
SysMenu->CheckMenuItem(IDM_TRACE_CURSOR, m_bIsTraceCursor ? MF_CHECKED : MF_UNCHECKED);
SetClassLongPtr(m_hWnd, GCLP_HCURSOR, m_bIsCtrl ? (LONG_PTR)m_hRemoteCursor : (LONG_PTR)LoadCursor(NULL, IDC_NO));
ShowScrollBar(SB_BOTH, !m_bAdaptiveSize);
// 设置合理的"正常"窗口大小,显示在主程序所在的显示器上
@@ -1990,7 +1986,23 @@ void CScreenSpyDlg::OnPaint()
BOOL CScreenSpyDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
if ((m_bIsCtrl && m_Settings.RemoteCursor) && nHitTest == HTCLIENT) {
// 查看模式(未开启远程控制):客户区显示禁止图标;标题栏及系统按钮保持正常箭头,
// 便于拖动窗口或通过标题栏右键菜单开启远程控制。
// 这里按窗口自身状态判断,避免用窗口类光标(GCLP_HCURSOR)时被同类的其他对话框实例
// 共享串扰——打开第二个未控制的对话框后,第一个已控制的对话框也被误显示成禁止图标。
if (!m_bIsCtrl && nHitTest == HTCLIENT) {
::SetCursor(LoadCursor(NULL, IDC_NO));
return TRUE;
}
if (!m_bIsCtrl &&
(nHitTest == HTCAPTION || nHitTest == HTSYSMENU || nHitTest == HTMENU ||
nHitTest == HTCLOSE || nHitTest == HTMINBUTTON || nHitTest == HTMAXBUTTON ||
nHitTest == HTHELP)) {
::SetCursor(LoadCursor(NULL, IDC_ARROW));
return TRUE;
}
// 控制模式:保持原有逻辑——显示远程光标时客户区隐藏本地物理光标,否则交父类显示远程形状
if (m_bIsCtrl && m_Settings.RemoteCursor && nHitTest == HTCLIENT) {
::SetCursor(NULL); // 只要在客户区,始终隐藏系统光标
return TRUE; // 告诉 Windows 我们处理过了
}
@@ -2084,7 +2096,6 @@ void CScreenSpyDlg::OnSysCommand(UINT nID, LPARAM lParam)
}
SysMenu->CheckMenuItem(IDM_CONTROL, m_bIsCtrl ? MF_CHECKED : MF_UNCHECKED);
SysMenu->CheckMenuItem(IDM_TRACE_CURSOR, m_bIsTraceCursor ? MF_CHECKED : MF_UNCHECKED);
SetClassLongPtr(m_hWnd, GCLP_HCURSOR, m_bIsCtrl ? (LONG_PTR)m_hRemoteCursor : (LONG_PTR)LoadCursor(NULL, IDC_NO));
// 控制模式:禁用本地 IME;查看模式:启用本地 IME
ImmAssociateContext(m_hWnd, m_bIsCtrl ? NULL : m_hOldIMC);
break;
@@ -2429,6 +2440,17 @@ void CScreenSpyDlg::OnTimer(UINT_PTR nIDEvent)
__super::OnTimer(nIDEvent);
}
CString CScreenSpyDlg::GetTitleHostName() const
{
// 标题栏显示的主机名:有备注用备注,否则用 IP
if (m_pParent && m_pParent->m_ClientMap) {
CString note = m_pParent->m_ClientMap->GetClientMapData(GetClientID(), MAP_NOTE);
if (!note.IsEmpty())
return note;
}
return m_IPAddress;
}
void CScreenSpyDlg::UpdateWindowTitle()
{
if (!m_BitmapInfor_Full) return;
@@ -2444,10 +2466,10 @@ void CScreenSpyDlg::UpdateWindowTitle()
UINT fps = (UINT)(m_dFrameRate + 0.5); // 四舍五入显示
if (m_dTransferRate >= 1024) {
strTitle.FormatL("%s - 远程桌面控制 %d×%d | %u FPS | %.1f MB/s%s",
m_IPAddress, width, height, fps, m_dTransferRate / 1024, qualityName.GetString());
GetTitleHostName().GetString(), width, height, fps, m_dTransferRate / 1024, qualityName.GetString());
} else {
strTitle.FormatL("%s - 远程桌面控制 %d×%d | %u FPS | %.0f KB/s%s",
m_IPAddress, width, height, fps, m_dTransferRate, qualityName.GetString());
GetTitleHostName().GetString(), width, height, fps, m_dTransferRate, qualityName.GetString());
}
// 追加剪贴板操作提示
strTitle += (m_Settings.ScreenType == 2) ?
@@ -3473,9 +3495,6 @@ void CScreenSpyDlg::OnMouseMove(UINT nFlags, CPoint point)
// 关键:在控制模式下,强制设置光标为空,隐藏本地物理箭头
::SetCursor(NULL);
}
} else if (!m_bMouseTracking) {
m_bMouseTracking = true;
SetClassLongPtr(m_hWnd, GCLP_HCURSOR, m_bIsCtrl ? (LONG_PTR)m_hRemoteCursor : (LONG_PTR)LoadCursor(NULL, IDC_NO));
}
__super::OnMouseMove(nFlags, point);
@@ -3484,9 +3503,6 @@ void CScreenSpyDlg::OnMouseMove(UINT nFlags, CPoint point)
void CScreenSpyDlg::OnMouseLeave()
{
CWnd::OnMouseLeave();
m_bMouseTracking = false;
SetClassLongPtr(m_hWnd, GCLP_HCURSOR, m_bIsCtrl ? (LONG_PTR)m_hRemoteCursor : (LONG_PTR)LoadCursor(NULL, IDC_NO));
}
void CScreenSpyDlg::OnKillFocus(CWnd* pNewWnd)
@@ -3566,7 +3582,6 @@ void CScreenSpyDlg::UpdateCtrlStatus(BOOL ctrl)
Invalidate(FALSE);
}
}
SetClassLongPtr(m_hWnd, GCLP_HCURSOR, m_bIsCtrl ? (LONG_PTR)m_hRemoteCursor : (LONG_PTR)LoadCursor(NULL, IDC_NO));
// 控制模式:禁用本地 IME;查看模式:启用本地 IME
ImmAssociateContext(m_hWnd, m_bIsCtrl ? NULL : m_hOldIMC);
CMenu* SysMenu = GetSystemMenu(FALSE);
+3 -2
View File
@@ -234,6 +234,9 @@ public:
// fillMode: 0=不填充, 1=全黑, 2=半透明
VOID DrawTipString(CString strString, int fillMode=1);
// 标题栏显示的主机名:有备注用备注,否则用 IP
CString GetTitleHostName() const;
POINT m_ClientCursorPos;
BYTE m_bCursorIndex;
BOOL m_bIsTraceCursor;
@@ -274,13 +277,11 @@ public:
clock_t m_lastMouseMove; // 鼠标移动时间
POINT m_lastMousePoint;// 上次鼠标位置
BOOL m_bAdaptiveSize = TRUE;
HCURSOR m_hRemoteCursor = NULL;
HCURSOR m_hCustomCursor = NULL; // 缓存的自定义光标
DWORD m_dwCustomCursorHash = 0; // 当前自定义光标哈希
CRect m_CRect;
double m_wZoom=1, m_hZoom=1;
int m_offsetX=0, m_offsetY=0; // 自适应模式黑边偏移(像素)
bool m_bMouseTracking = false;
// 自适应黑边合成缓冲(按窗口尺寸缓存,避免每帧分配 GDI 资源)
HDC m_hComposeDC = NULL;