From b732f841d055f71a51e866c2c99e2522be445b65 Mon Sep 17 00:00:00 2001 From: yuanyuanxiang <962914132@qq.com> Date: Sun, 3 May 2026 15:25:03 +0200 Subject: [PATCH] fix(server): Prevent crash from dangling pointers in file dialog map Fixed two bugs when closing ScreenSpyDlg with file transfer dialogs: 1. Access violation (0xC0000005): CDlgFileSend self-destructs via PostNcDestroy (delete this) when closed, leaving dangling pointers in m_FileRecvDlgs map. 2. Double-free: Original code called DestroyWindow() then delete, but DestroyWindow already triggers delete this via PostNcDestroy. Solution: - Store {HWND, pointer} pairs instead of raw pointers - Check HWND validity with IsWindow() before accessing pointer - Use SendMessage(WM_CLOSE) to let dialog self-destruct safely - Always erase map entries to prevent accumulation of invalid data Co-Authored-By: Claude Opus 4.5 --- server/2015Remote/ScreenSpyDlg.cpp | 24 +++++++++++++++++------- server/2015Remote/ScreenSpyDlg.h | 3 ++- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/server/2015Remote/ScreenSpyDlg.cpp b/server/2015Remote/ScreenSpyDlg.cpp index 3d326a2..86862d6 100644 --- a/server/2015Remote/ScreenSpyDlg.cpp +++ b/server/2015Remote/ScreenSpyDlg.cpp @@ -238,10 +238,14 @@ CScreenSpyDlg::~CScreenSpyDlg() StopAudioPlayback(); // 清理所有文件接收对话框 - for (auto& pair : m_FileRecvDlgs) { - if (pair.second) { - pair.second->DestroyWindow(); - delete pair.second; + // 注意:对话框可能已经被用户关闭并自我销毁(PostNcDestroy 中 delete this) + // 存储了 HWND 用于安全检查,避免访问野指针 + for (auto& entry : m_FileRecvDlgs) { + HWND hWnd = entry.second.first; + if (hWnd && ::IsWindow(hWnd)) { + // 通过 HWND 同步发送关闭消息,确保对话框在析构前完全关闭 + // 使用 SendMessage 而非 PostMessage,避免异步问题 + ::SendMessage(hWnd, WM_CLOSE, 0, 0); } } m_FileRecvDlgs.clear(); @@ -861,13 +865,15 @@ LRESULT CScreenSpyDlg::OnRecvFileV2Chunk(WPARAM wParam, LPARAM lParam) uint64_t transferID = msgData->transferID; // 创建或获取进度对话框(按 transferID 管理) - CDlgFileSend*& dlg = m_FileRecvDlgs[transferID]; - if (dlg == nullptr) { + auto& entry = m_FileRecvDlgs[transferID]; + CDlgFileSend* dlg = entry.second; + if (dlg == nullptr || !::IsWindow(entry.first)) { dlg = new CDlgFileSend(m_pParent, m_ContextObject->GetServer(), m_ContextObject, FALSE); dlg->Create(IDD_DIALOG_FILESEND, GetDesktopWindow()); dlg->SetWindowTextA(_TR("接收文件")); dlg->ShowWindow(SW_SHOW); dlg->m_bKeepConnection = TRUE; // 不断开连接 + entry = { dlg->GetSafeHwnd(), dlg }; } // 接收文件 @@ -910,7 +916,11 @@ LRESULT CScreenSpyDlg::OnRecvFileV2Complete(WPARAM wParam, LPARAM lParam) // 关闭进度对话框 auto it = m_FileRecvDlgs.find(transferID); if (it != m_FileRecvDlgs.end()) { - it->second->FinishFileSend(verifyOk); + // 只有窗口有效时才调用 FinishFileSend + if (::IsWindow(it->second.first)) { + it->second.second->FinishFileSend(verifyOk); + } + // 无论窗口是否有效,都要移除条目(避免累积无效条目) m_FileRecvDlgs.erase(it); } diff --git a/server/2015Remote/ScreenSpyDlg.h b/server/2015Remote/ScreenSpyDlg.h index ac588e6..86a0f88 100644 --- a/server/2015Remote/ScreenSpyDlg.h +++ b/server/2015Remote/ScreenSpyDlg.h @@ -198,7 +198,8 @@ public: // 文件接收进度对话框(用于 Linux Ctrl+C -> 服务端 Ctrl+V) // 按 transferID 管理多个并发传输 - std::map m_FileRecvDlgs; + // 存储 {HWND, 指针} 对,HWND 用于安全检查(指针可能变成野指针) + std::map> m_FileRecvDlgs; void SaveSnapshot(void); // 对话框数据