Implemented a more defensive version of WIN_SendDestroyMsg.
diff --git a/windows/win.c b/windows/win.c
index 702d036..529bb6d 100644
--- a/windows/win.c
+++ b/windows/win.c
@@ -1130,21 +1130,90 @@
if( CARET_GetHwnd() == pWnd->hwndSelf ) DestroyCaret();
CLIPBOARD_GetDriver()->pResetOwner( pWnd, TRUE );
-
+
+ /*
+ * Send the WM_DESTROY to the window.
+ */
SendMessageA( pWnd->hwndSelf, WM_DESTROY, 0, 0);
- if( IsWindow(pWnd->hwndSelf) )
+ /*
+ * This WM_DESTROY message can trigger re-entrant calls to DestroyWindow
+ * make sure that the window still exists when we come back.
+ */
+ if (IsWindow(pWnd->hwndSelf))
{
- WND* pChild = WIN_LockWndPtr(pWnd->child);
- while( pChild )
+ HWND* pWndArray = NULL;
+ WND* pChild = NULL;
+ int nKidCount = 0;
+
+ /*
+ * Now, if the window has kids, we have to send WM_DESTROY messages
+ * recursively to it's kids. It seems that those calls can also
+ * trigger re-entrant calls to DestroyWindow for the kids so we must
+ * protect against corruption of the list of siblings. We first build
+ * a list of HWNDs representing all the kids.
+ */
+ pChild = WIN_LockWndPtr(pWnd->child);
+ while( pChild )
+ {
+ nKidCount++;
+ WIN_UpdateWndPtr(&pChild,pChild->next);
+ }
+
+ /*
+ * If there are no kids, we're done.
+ */
+ if (nKidCount==0)
+ return;
+
+ pWndArray = HeapAlloc(GetProcessHeap(), 0, nKidCount*sizeof(HWND));
+
+ /*
+ * Sanity check
+ */
+ if (pWndArray==NULL)
+ return;
+
+ /*
+ * Now, enumerate all the kids in a list, since we wait to make the SendMessage
+ * call, our linked list of siblings should be safe.
+ */
+ nKidCount = 0;
+ pChild = WIN_LockWndPtr(pWnd->child);
+ while( pChild )
+ {
+ pWndArray[nKidCount] = pChild->hwndSelf;
+ nKidCount++;
+ WIN_UpdateWndPtr(&pChild,pChild->next);
+ }
+
+ /*
+ * Now that we have a list, go through that list again and send the destroy
+ * message to those windows. We are using the HWND to retrieve the
+ * WND pointer so we are effectively checking that all the kid windows are
+ * still valid before sending the message.
+ */
+ while (nKidCount>0)
+ {
+ pChild = WIN_FindWndPtr(pWndArray[nKidCount]);
+
+ if (pChild!=NULL)
{
- WIN_SendDestroyMsg( pChild );
- WIN_UpdateWndPtr(&pChild,pChild->next);
+ WIN_SendDestroyMsg( pChild );
+ WIN_ReleaseWndPtr(pChild);
}
- WIN_CheckFocus(pWnd);
+
+ nKidCount--;
+ }
+
+ /*
+ * Cleanup
+ */
+ HeapFree(GetProcessHeap(), 0, pWndArray);
+ WIN_CheckFocus(pWnd);
}
else
- WARN(win, "\tdestroyed itself while in WM_DESTROY!\n");
+ WARN(win, "\tdestroyed itself while in WM_DESTROY!\n");
}