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 <noreply@anthropic.com>
This commit is contained in:
yuanyuanxiang
2026-05-03 15:25:03 +02:00
parent 1df2a7b321
commit b732f841d0
2 changed files with 19 additions and 8 deletions

View File

@@ -238,10 +238,14 @@ CScreenSpyDlg::~CScreenSpyDlg()
StopAudioPlayback(); StopAudioPlayback();
// 清理所有文件接收对话框 // 清理所有文件接收对话框
for (auto& pair : m_FileRecvDlgs) { // 注意对话框可能已经被用户关闭并自我销毁PostNcDestroy 中 delete this
if (pair.second) { // 存储了 HWND 用于安全检查,避免访问野指针
pair.second->DestroyWindow(); for (auto& entry : m_FileRecvDlgs) {
delete pair.second; HWND hWnd = entry.second.first;
if (hWnd && ::IsWindow(hWnd)) {
// 通过 HWND 同步发送关闭消息,确保对话框在析构前完全关闭
// 使用 SendMessage 而非 PostMessage避免异步问题
::SendMessage(hWnd, WM_CLOSE, 0, 0);
} }
} }
m_FileRecvDlgs.clear(); m_FileRecvDlgs.clear();
@@ -861,13 +865,15 @@ LRESULT CScreenSpyDlg::OnRecvFileV2Chunk(WPARAM wParam, LPARAM lParam)
uint64_t transferID = msgData->transferID; uint64_t transferID = msgData->transferID;
// 创建或获取进度对话框(按 transferID 管理) // 创建或获取进度对话框(按 transferID 管理)
CDlgFileSend*& dlg = m_FileRecvDlgs[transferID]; auto& entry = m_FileRecvDlgs[transferID];
if (dlg == nullptr) { CDlgFileSend* dlg = entry.second;
if (dlg == nullptr || !::IsWindow(entry.first)) {
dlg = new CDlgFileSend(m_pParent, m_ContextObject->GetServer(), m_ContextObject, FALSE); dlg = new CDlgFileSend(m_pParent, m_ContextObject->GetServer(), m_ContextObject, FALSE);
dlg->Create(IDD_DIALOG_FILESEND, GetDesktopWindow()); dlg->Create(IDD_DIALOG_FILESEND, GetDesktopWindow());
dlg->SetWindowTextA(_TR("接收文件")); dlg->SetWindowTextA(_TR("接收文件"));
dlg->ShowWindow(SW_SHOW); dlg->ShowWindow(SW_SHOW);
dlg->m_bKeepConnection = TRUE; // 不断开连接 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); auto it = m_FileRecvDlgs.find(transferID);
if (it != m_FileRecvDlgs.end()) { if (it != m_FileRecvDlgs.end()) {
it->second->FinishFileSend(verifyOk); // 只有窗口有效时才调用 FinishFileSend
if (::IsWindow(it->second.first)) {
it->second.second->FinishFileSend(verifyOk);
}
// 无论窗口是否有效,都要移除条目(避免累积无效条目)
m_FileRecvDlgs.erase(it); m_FileRecvDlgs.erase(it);
} }

View File

@@ -198,7 +198,8 @@ public:
// 文件接收进度对话框(用于 Linux Ctrl+C -> 服务端 Ctrl+V // 文件接收进度对话框(用于 Linux Ctrl+C -> 服务端 Ctrl+V
// 按 transferID 管理多个并发传输 // 按 transferID 管理多个并发传输
std::map<uint64_t, class CDlgFileSend*> m_FileRecvDlgs; // 存储 {HWND, 指针} 对HWND 用于安全检查(指针可能变成野指针)
std::map<uint64_t, std::pair<HWND, class CDlgFileSend*>> m_FileRecvDlgs;
void SaveSnapshot(void); void SaveSnapshot(void);
// 对话框数据 // 对话框数据