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:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -198,7 +198,8 @@ public:
|
||||
|
||||
// 文件接收进度对话框(用于 Linux Ctrl+C -> 服务端 Ctrl+V)
|
||||
// 按 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);
|
||||
// 对话框数据
|
||||
|
||||
Reference in New Issue
Block a user