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
This commit is contained in:
yuanyuanxiang
2026-08-20 16:23:39 +02:00
parent 5611ba621c
commit c08315cd79
4 changed files with 40 additions and 30 deletions

View File

@@ -90,11 +90,33 @@ BEGIN_MESSAGE_MAP(CHideScreenSpyDlg, CDialog)
ON_WM_PAINT() ON_WM_PAINT()
ON_WM_TIMER() ON_WM_TIMER()
ON_WM_CLOSE() ON_WM_CLOSE()
ON_WM_SETCURSOR()
END_MESSAGE_MAP() END_MESSAGE_MAP()
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// CHideScreenSpyDlg message handlers // 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() void CHideScreenSpyDlg::OnClose()
{ {
if (!m_aviFile.IsEmpty()) { if (!m_aviFile.IsEmpty()) {
@@ -165,7 +187,6 @@ BOOL CHideScreenSpyDlg::OnInitDialog()
// when the application's main window is not a dialog // when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon SetIcon(m_hIcon, FALSE); // Set small icon
SetClassLongPtr(m_hWnd, GCLP_HCURSOR, (LONG_PTR)LoadCursor(NULL, IDC_NO));
CMenu* pSysMenu = GetSystemMenu(FALSE); CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL) { if (pSysMenu != NULL) {
pSysMenu->AppendMenuSeparator(MF_SEPARATOR); pSysMenu->AppendMenuSeparator(MF_SEPARATOR);
@@ -214,15 +235,7 @@ BOOL CHideScreenSpyDlg::OnInitDialog()
} }
// TODO: Add extra initialization here // 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); 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_hFullDC = ::GetDC(m_hWnd);
m_hFullMemDC = CreateCompatibleDC(m_hFullDC); m_hFullMemDC = CreateCompatibleDC(m_hFullDC);
@@ -440,11 +453,6 @@ void CHideScreenSpyDlg::OnSysCommand(UINT nID, LPARAM lParam)
case IDM_CONTROL: { case IDM_CONTROL: {
m_bIsCtrl = !m_bIsCtrl; m_bIsCtrl = !m_bIsCtrl;
pSysMenu->CheckMenuItem(IDM_CONTROL, m_bIsCtrl ? MF_CHECKED : MF_UNCHECKED); 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; break;

View File

@@ -59,6 +59,7 @@ public:
afx_msg void OnPaint(); afx_msg void OnPaint();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam); afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnSize(UINT nType, int cx, int cy); 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); virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
@@ -75,7 +76,6 @@ protected:
HBITMAP m_BitmapHandle; HBITMAP m_BitmapHandle;
LPVOID m_BitmapData_Full; LPVOID m_BitmapData_Full;
LPBITMAPINFO m_BitmapInfor_Full; LPBITMAPINFO m_BitmapInfor_Full;
HCURSOR m_hRemoteCursor;
CCursorInfo m_CursorInfo; CCursorInfo m_CursorInfo;
BOOL m_bIsFirst; BOOL m_bIsFirst;
BOOL m_bIsCtrl; BOOL m_bIsCtrl;

View File

@@ -843,13 +843,9 @@ BOOL CScreenSpyDlg::OnInitDialog()
m_ClientCursorPos.y = 0; m_ClientCursorPos.y = 0;
m_bCursorIndex = 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_CONTROL, m_bIsCtrl ? MF_CHECKED : MF_UNCHECKED);
SysMenu->CheckMenuItem(IDM_ADAPTIVE_SIZE, m_bAdaptiveSize ? 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); 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); ShowScrollBar(SB_BOTH, !m_bAdaptiveSize);
// 设置合理的"正常"窗口大小,显示在主程序所在的显示器上 // 设置合理的"正常"窗口大小,显示在主程序所在的显示器上
@@ -1990,7 +1986,23 @@ void CScreenSpyDlg::OnPaint()
BOOL CScreenSpyDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) 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); // 只要在客户区,始终隐藏系统光标 ::SetCursor(NULL); // 只要在客户区,始终隐藏系统光标
return TRUE; // 告诉 Windows 我们处理过了 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_CONTROL, m_bIsCtrl ? MF_CHECKED : MF_UNCHECKED);
SysMenu->CheckMenuItem(IDM_TRACE_CURSOR, m_bIsTraceCursor ? 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 // 控制模式:禁用本地 IME查看模式启用本地 IME
ImmAssociateContext(m_hWnd, m_bIsCtrl ? NULL : m_hOldIMC); ImmAssociateContext(m_hWnd, m_bIsCtrl ? NULL : m_hOldIMC);
break; break;
@@ -3473,9 +3484,6 @@ void CScreenSpyDlg::OnMouseMove(UINT nFlags, CPoint point)
// 关键:在控制模式下,强制设置光标为空,隐藏本地物理箭头 // 关键:在控制模式下,强制设置光标为空,隐藏本地物理箭头
::SetCursor(NULL); ::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); __super::OnMouseMove(nFlags, point);
@@ -3484,9 +3492,6 @@ void CScreenSpyDlg::OnMouseMove(UINT nFlags, CPoint point)
void CScreenSpyDlg::OnMouseLeave() void CScreenSpyDlg::OnMouseLeave()
{ {
CWnd::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) void CScreenSpyDlg::OnKillFocus(CWnd* pNewWnd)
@@ -3566,7 +3571,6 @@ void CScreenSpyDlg::UpdateCtrlStatus(BOOL ctrl)
Invalidate(FALSE); Invalidate(FALSE);
} }
} }
SetClassLongPtr(m_hWnd, GCLP_HCURSOR, m_bIsCtrl ? (LONG_PTR)m_hRemoteCursor : (LONG_PTR)LoadCursor(NULL, IDC_NO));
// 控制模式:禁用本地 IME查看模式启用本地 IME // 控制模式:禁用本地 IME查看模式启用本地 IME
ImmAssociateContext(m_hWnd, m_bIsCtrl ? NULL : m_hOldIMC); ImmAssociateContext(m_hWnd, m_bIsCtrl ? NULL : m_hOldIMC);
CMenu* SysMenu = GetSystemMenu(FALSE); CMenu* SysMenu = GetSystemMenu(FALSE);

View File

@@ -274,13 +274,11 @@ public:
clock_t m_lastMouseMove; // 鼠标移动时间 clock_t m_lastMouseMove; // 鼠标移动时间
POINT m_lastMousePoint;// 上次鼠标位置 POINT m_lastMousePoint;// 上次鼠标位置
BOOL m_bAdaptiveSize = TRUE; BOOL m_bAdaptiveSize = TRUE;
HCURSOR m_hRemoteCursor = NULL;
HCURSOR m_hCustomCursor = NULL; // 缓存的自定义光标 HCURSOR m_hCustomCursor = NULL; // 缓存的自定义光标
DWORD m_dwCustomCursorHash = 0; // 当前自定义光标哈希 DWORD m_dwCustomCursorHash = 0; // 当前自定义光标哈希
CRect m_CRect; CRect m_CRect;
double m_wZoom=1, m_hZoom=1; double m_wZoom=1, m_hZoom=1;
int m_offsetX=0, m_offsetY=0; // 自适应模式黑边偏移(像素) int m_offsetX=0, m_offsetY=0; // 自适应模式黑边偏移(像素)
bool m_bMouseTracking = false;
// 自适应黑边合成缓冲(按窗口尺寸缓存,避免每帧分配 GDI 资源) // 自适应黑边合成缓冲(按窗口尺寸缓存,避免每帧分配 GDI 资源)
HDC m_hComposeDC = NULL; HDC m_hComposeDC = NULL;