[PATCH v2 2/5] win32u: Move NtUserDestroyWindow implementation from user32.

Huw Davies huw at codeweavers.com
Thu Mar 17 10:29:30 CDT 2022


From: Jacek Caban <jacek at codeweavers.com>

Signed-off-by: Jacek Caban <jacek at codeweavers.com>
Signed-off-by: Huw Davies <huw at codeweavers.com>
---
 dlls/user32/clipboard.c      |  23 ---
 dlls/user32/combo.c          |   2 +-
 dlls/user32/dde_misc.c       |   6 +-
 dlls/user32/dde_server.c     |   2 +-
 dlls/user32/defwnd.c         |   2 +-
 dlls/user32/dialog.c         |   4 +-
 dlls/user32/driver.c         |   7 +-
 dlls/user32/mdi.c            |   2 +-
 dlls/user32/menu.c           |   8 +-
 dlls/user32/message.c        |   2 -
 dlls/user32/msgbox.c         |  70 ++++----
 dlls/user32/painting.c       |  12 --
 dlls/user32/user32.spec      |   2 +-
 dlls/user32/user_main.c      |  10 ++
 dlls/user32/win.c            | 248 +--------------------------
 dlls/user32/win.h            |   1 -
 dlls/win32u/clipboard.c      |  25 +++
 dlls/win32u/driver.c         |   1 +
 dlls/win32u/gdiobj.c         |   1 +
 dlls/win32u/message.c        |   2 +
 dlls/win32u/ntuser_private.h |   4 +
 dlls/win32u/sysparams.c      |  18 +-
 dlls/win32u/win32u.spec      |   2 +-
 dlls/win32u/win32u_private.h |   6 +
 dlls/win32u/window.c         | 315 +++++++++++++++++++++++++++++++++++
 dlls/win32u/wrappers.c       |   6 +
 include/ntuser.h             |   4 +-
 27 files changed, 440 insertions(+), 345 deletions(-)

diff --git a/dlls/user32/clipboard.c b/dlls/user32/clipboard.c
index 1a69421ea17..b45ddf9c7c0 100644
--- a/dlls/user32/clipboard.c
+++ b/dlls/user32/clipboard.c
@@ -616,29 +616,6 @@ static HANDLE render_synthesized_format( UINT format, UINT from )
     return data;
 }
 
-/**************************************************************************
- *	CLIPBOARD_ReleaseOwner
- */
-void CLIPBOARD_ReleaseOwner( HWND hwnd )
-{
-    HWND viewer = 0, owner = 0;
-
-    SendMessageW( hwnd, WM_RENDERALLFORMATS, 0, 0 );
-
-    SERVER_START_REQ( release_clipboard )
-    {
-        req->owner = wine_server_user_handle( hwnd );
-        if (!wine_server_call( req ))
-        {
-            viewer = wine_server_ptr_handle( reply->viewer );
-            owner = wine_server_ptr_handle( reply->owner );
-        }
-    }
-    SERVER_END_REQ;
-
-    if (viewer) SendNotifyMessageW( viewer, WM_DRAWCLIPBOARD, (WPARAM)owner, 0 );
-}
-
 
 /**************************************************************************
  *		RegisterClipboardFormatW (USER32.@)
diff --git a/dlls/user32/combo.c b/dlls/user32/combo.c
index 77cb24be064..b375de5080c 100644
--- a/dlls/user32/combo.c
+++ b/dlls/user32/combo.c
@@ -173,7 +173,7 @@ static LRESULT COMBO_NCDestroy( LPHEADCOMBO lphc )
        TRACE("[%p]: freeing storage\n", lphc->self);
 
        if( (CB_GETTYPE(lphc) != CBS_SIMPLE) && lphc->hWndLBox )
-   	   DestroyWindow( lphc->hWndLBox );
+           NtUserDestroyWindow( lphc->hWndLBox );
 
        SetWindowLongPtrW( lphc->self, 0, 0 );
        HeapFree( GetProcessHeap(), 0, lphc );
diff --git a/dlls/user32/dde_misc.c b/dlls/user32/dde_misc.c
index 41181d099cc..7bc1656432e 100644
--- a/dlls/user32/dde_misc.c
+++ b/dlls/user32/dde_misc.c
@@ -1158,7 +1158,7 @@ BOOL WINAPI DdeUninitialize(DWORD idInst)
      */
     WDML_FreeAllHSZ(pInstance);
 
-    DestroyWindow(pInstance->hwndEvent);
+    NtUserDestroyWindow( pInstance->hwndEvent );
 
     /* OK now delete the instance handle itself */
 
@@ -1711,7 +1711,7 @@ void WDML_RemoveServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic)
 		pPrev->next = pServer->next;
 	    }
 
-	    DestroyWindow(pServer->hwndServer);
+	    NtUserDestroyWindow(pServer->hwndServer);
 	    WDML_DecHSZ(pInstance, pServer->hszServiceSpec);
 	    WDML_DecHSZ(pInstance, pServer->hszService);
 
@@ -2105,7 +2105,7 @@ void WDML_RemoveConv(WDML_CONV* pRef, WDML_SIDE side)
     hWnd = (side == WDML_CLIENT_SIDE) ? pRef->hwndClient : pRef->hwndServer;
     SetWindowLongPtrW(hWnd, GWL_WDML_CONVERSATION, 0);
 
-    DestroyWindow((side == WDML_CLIENT_SIDE) ? pRef->hwndClient : pRef->hwndServer);
+    NtUserDestroyWindow((side == WDML_CLIENT_SIDE) ? pRef->hwndClient : pRef->hwndServer);
 
     WDML_DecHSZ(pRef->instance, pRef->hszService);
     WDML_DecHSZ(pRef->instance, pRef->hszTopic);
diff --git a/dlls/user32/dde_server.c b/dlls/user32/dde_server.c
index 3867e0f014b..26494b835e5 100644
--- a/dlls/user32/dde_server.c
+++ b/dlls/user32/dde_server.c
@@ -360,7 +360,7 @@ static WDML_CONV* WDML_CreateServerConv(WDML_INSTANCE* pInstance, HWND hwndClien
     }
     else
     {
-	DestroyWindow(hwndServerConv);
+	NtUserDestroyWindow(hwndServerConv);
     }
     return pConv;
 }
diff --git a/dlls/user32/defwnd.c b/dlls/user32/defwnd.c
index f78236cb194..271644bb953 100644
--- a/dlls/user32/defwnd.c
+++ b/dlls/user32/defwnd.c
@@ -400,7 +400,7 @@ static LRESULT DEFWND_DefWinProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPa
         return 0;
 
     case WM_CLOSE:
-        DestroyWindow( hwnd );
+        NtUserDestroyWindow( hwnd );
         return 0;
 
     case WM_MOUSEACTIVATE:
diff --git a/dlls/user32/dialog.c b/dlls/user32/dialog.c
index e6e5b91587c..f9d388d0104 100644
--- a/dlls/user32/dialog.c
+++ b/dlls/user32/dialog.c
@@ -707,7 +707,7 @@ static HWND DIALOG_CreateIndirect( HINSTANCE hInst, LPCVOID dlgTemplate,
         return hwnd;
     }
     if (disabled_owner) EnableWindow( disabled_owner, TRUE );
-    if( IsWindow(hwnd) ) DestroyWindow( hwnd );
+    if (IsWindow(hwnd)) NtUserDestroyWindow( hwnd );
     return 0;
 }
 
@@ -827,7 +827,7 @@ INT DIALOG_DoDialogBox( HWND hwnd, HWND owner )
         }
     }
     retval = dlgInfo->idResult;
-    DestroyWindow( hwnd );
+    NtUserDestroyWindow( hwnd );
     return retval;
 }
 
diff --git a/dlls/user32/driver.c b/dlls/user32/driver.c
index 72ea5ad17bd..ada0b8ca2f7 100644
--- a/dlls/user32/driver.c
+++ b/dlls/user32/driver.c
@@ -79,10 +79,6 @@ static BOOL CDECL nulldrv_CreateWindow( HWND hwnd )
     return TRUE;
 }
 
-static void CDECL nulldrv_DestroyWindow( HWND hwnd )
-{
-}
-
 static DWORD CDECL nulldrv_MsgWaitForMultipleObjectsEx( DWORD count, const HANDLE *handles, DWORD timeout,
                                                         DWORD mask, DWORD flags )
 {
@@ -174,7 +170,7 @@ static struct user_driver_funcs lazy_load_driver =
     /* windowing functions */
     NULL,
     loaderdrv_CreateWindow,
-    nulldrv_DestroyWindow,
+    NULL,
     NULL,
     NULL,
     nulldrv_MsgWaitForMultipleObjectsEx,
@@ -223,7 +219,6 @@ void CDECL __wine_set_user_driver( const struct user_driver_funcs *funcs, UINT v
     SET_USER_FUNC(SetCursorPos);
     SET_USER_FUNC(UpdateClipboard);
     SET_USER_FUNC(CreateWindow);
-    SET_USER_FUNC(DestroyWindow);
     SET_USER_FUNC(MsgWaitForMultipleObjectsEx);
     SET_USER_FUNC(SetWindowIcon);
     SET_USER_FUNC(SetWindowText);
diff --git a/dlls/user32/mdi.c b/dlls/user32/mdi.c
index 6bab3e96adf..3d85487f808 100644
--- a/dlls/user32/mdi.c
+++ b/dlls/user32/mdi.c
@@ -580,7 +580,7 @@ static LRESULT MDIDestroyChild( HWND client, MDICLIENTINFO *ci,
     {
         SendMessageW(client, WM_MDIREFRESHMENU, 0, 0);
         MDI_PostUpdate(GetParent(child), ci, SB_BOTH+1);
-        DestroyWindow(child);
+        NtUserDestroyWindow(child);
     }
 
     TRACE("child destroyed - %p\n", child);
diff --git a/dlls/user32/menu.c b/dlls/user32/menu.c
index 1d680009747..113e6103cd9 100644
--- a/dlls/user32/menu.c
+++ b/dlls/user32/menu.c
@@ -2333,7 +2333,7 @@ static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
 	if (!(submenu = MENU_GetMenu( hsubmenu ))) return;
 	MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE, wFlags );
 	MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
-        DestroyWindow( submenu->hWnd );
+        NtUserDestroyWindow( submenu->hWnd );
         submenu->hWnd = 0;
 
         if (!(wFlags & TPM_NONOTIFY))
@@ -3319,7 +3319,7 @@ static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
 
 	    if (menu && (menu->wFlags & MF_POPUP))
 	    {
-                DestroyWindow( menu->hWnd );
+                NtUserDestroyWindow( menu->hWnd );
                 menu->hWnd = 0;
 
                 if (!(wFlags & TPM_NONOTIFY))
@@ -3531,7 +3531,7 @@ BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
 
         if (menu->hWnd)
         {
-            DestroyWindow( menu->hWnd );
+            NtUserDestroyWindow( menu->hWnd );
             menu->hWnd = 0;
 
             if (!(wFlags & TPM_NONOTIFY))
@@ -4269,7 +4269,7 @@ BOOL WINAPI DestroyMenu( HMENU hMenu )
     /* DestroyMenu should not destroy system menu popup owner */
     if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
     {
-        DestroyWindow( lppop->hWnd );
+        NtUserDestroyWindow( lppop->hWnd );
         lppop->hWnd = 0;
     }
 
diff --git a/dlls/user32/message.c b/dlls/user32/message.c
index 365f703c56e..0ba540d2e5d 100644
--- a/dlls/user32/message.c
+++ b/dlls/user32/message.c
@@ -1859,8 +1859,6 @@ LRESULT handle_internal_message( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpar
 {
     switch(msg)
     {
-    case WM_WINE_DESTROYWINDOW:
-        return WIN_DestroyWindow( hwnd );
     case WM_WINE_SHOWWINDOW:
         if (is_desktop_window( hwnd )) return 0;
         return ShowWindow( hwnd, wparam );
diff --git a/dlls/user32/msgbox.c b/dlls/user32/msgbox.c
index 195bab2384b..c74d33092f0 100644
--- a/dlls/user32/msgbox.c
+++ b/dlls/user32/msgbox.c
@@ -116,57 +116,57 @@ static void MSGBOX_OnInit(HWND hwnd, LPMSGBOXPARAMSW lpmb)
     hItem = 0;
     switch(lpmb->dwStyle & MB_TYPEMASK) {
     case MB_OK:
-	DestroyWindow(GetDlgItem(hwnd, IDCANCEL));
+	NtUserDestroyWindow(GetDlgItem(hwnd, IDCANCEL));
 	/* fall through */
     case MB_OKCANCEL:
 	hItem = GetDlgItem(hwnd, IDOK);
-	DestroyWindow(GetDlgItem(hwnd, IDABORT));
-	DestroyWindow(GetDlgItem(hwnd, IDRETRY));
-	DestroyWindow(GetDlgItem(hwnd, IDIGNORE));
-	DestroyWindow(GetDlgItem(hwnd, IDYES));
-	DestroyWindow(GetDlgItem(hwnd, IDNO));
-	DestroyWindow(GetDlgItem(hwnd, IDTRYAGAIN));
-	DestroyWindow(GetDlgItem(hwnd, IDCONTINUE));
+	NtUserDestroyWindow(GetDlgItem(hwnd, IDABORT));
+	NtUserDestroyWindow(GetDlgItem(hwnd, IDRETRY));
+	NtUserDestroyWindow(GetDlgItem(hwnd, IDIGNORE));
+	NtUserDestroyWindow(GetDlgItem(hwnd, IDYES));
+	NtUserDestroyWindow(GetDlgItem(hwnd, IDNO));
+	NtUserDestroyWindow(GetDlgItem(hwnd, IDTRYAGAIN));
+	NtUserDestroyWindow(GetDlgItem(hwnd, IDCONTINUE));
 	break;
     case MB_ABORTRETRYIGNORE:
 	hItem = GetDlgItem(hwnd, IDABORT);
-	DestroyWindow(GetDlgItem(hwnd, IDOK));
-	DestroyWindow(GetDlgItem(hwnd, IDCANCEL));
-	DestroyWindow(GetDlgItem(hwnd, IDYES));
-	DestroyWindow(GetDlgItem(hwnd, IDNO));
-	DestroyWindow(GetDlgItem(hwnd, IDCONTINUE));
-	DestroyWindow(GetDlgItem(hwnd, IDTRYAGAIN));
+	NtUserDestroyWindow(GetDlgItem(hwnd, IDOK));
+	NtUserDestroyWindow(GetDlgItem(hwnd, IDCANCEL));
+	NtUserDestroyWindow(GetDlgItem(hwnd, IDYES));
+	NtUserDestroyWindow(GetDlgItem(hwnd, IDNO));
+	NtUserDestroyWindow(GetDlgItem(hwnd, IDCONTINUE));
+	NtUserDestroyWindow(GetDlgItem(hwnd, IDTRYAGAIN));
 	break;
     case MB_YESNO:
-	DestroyWindow(GetDlgItem(hwnd, IDCANCEL));
+	NtUserDestroyWindow(GetDlgItem(hwnd, IDCANCEL));
 	/* fall through */
     case MB_YESNOCANCEL:
 	hItem = GetDlgItem(hwnd, IDYES);
-	DestroyWindow(GetDlgItem(hwnd, IDOK));
-	DestroyWindow(GetDlgItem(hwnd, IDABORT));
-	DestroyWindow(GetDlgItem(hwnd, IDRETRY));
-	DestroyWindow(GetDlgItem(hwnd, IDIGNORE));
-	DestroyWindow(GetDlgItem(hwnd, IDCONTINUE));
-	DestroyWindow(GetDlgItem(hwnd, IDTRYAGAIN));
+	NtUserDestroyWindow(GetDlgItem(hwnd, IDOK));
+	NtUserDestroyWindow(GetDlgItem(hwnd, IDABORT));
+	NtUserDestroyWindow(GetDlgItem(hwnd, IDRETRY));
+	NtUserDestroyWindow(GetDlgItem(hwnd, IDIGNORE));
+	NtUserDestroyWindow(GetDlgItem(hwnd, IDCONTINUE));
+	NtUserDestroyWindow(GetDlgItem(hwnd, IDTRYAGAIN));
 	break;
     case MB_RETRYCANCEL:
 	hItem = GetDlgItem(hwnd, IDRETRY);
-	DestroyWindow(GetDlgItem(hwnd, IDOK));
-	DestroyWindow(GetDlgItem(hwnd, IDABORT));
-	DestroyWindow(GetDlgItem(hwnd, IDIGNORE));
-	DestroyWindow(GetDlgItem(hwnd, IDYES));
-	DestroyWindow(GetDlgItem(hwnd, IDNO));
-	DestroyWindow(GetDlgItem(hwnd, IDCONTINUE));
-	DestroyWindow(GetDlgItem(hwnd, IDTRYAGAIN));
+	NtUserDestroyWindow(GetDlgItem(hwnd, IDOK));
+	NtUserDestroyWindow(GetDlgItem(hwnd, IDABORT));
+	NtUserDestroyWindow(GetDlgItem(hwnd, IDIGNORE));
+	NtUserDestroyWindow(GetDlgItem(hwnd, IDYES));
+	NtUserDestroyWindow(GetDlgItem(hwnd, IDNO));
+	NtUserDestroyWindow(GetDlgItem(hwnd, IDCONTINUE));
+	NtUserDestroyWindow(GetDlgItem(hwnd, IDTRYAGAIN));
 	break;
     case MB_CANCELTRYCONTINUE:
 	hItem = GetDlgItem(hwnd, IDCANCEL);
-	DestroyWindow(GetDlgItem(hwnd, IDOK));
-	DestroyWindow(GetDlgItem(hwnd, IDABORT));
-	DestroyWindow(GetDlgItem(hwnd, IDIGNORE));
-	DestroyWindow(GetDlgItem(hwnd, IDYES));
-	DestroyWindow(GetDlgItem(hwnd, IDNO));
-	DestroyWindow(GetDlgItem(hwnd, IDRETRY));
+	NtUserDestroyWindow(GetDlgItem(hwnd, IDOK));
+	NtUserDestroyWindow(GetDlgItem(hwnd, IDABORT));
+	NtUserDestroyWindow(GetDlgItem(hwnd, IDIGNORE));
+	NtUserDestroyWindow(GetDlgItem(hwnd, IDYES));
+	NtUserDestroyWindow(GetDlgItem(hwnd, IDNO));
+	NtUserDestroyWindow(GetDlgItem(hwnd, IDRETRY));
     }
 
     if (hItem) SetWindowLongW(hItem, GWL_STYLE, GetWindowLongW(hItem, GWL_STYLE) | WS_GROUP);
@@ -202,7 +202,7 @@ static void MSGBOX_OnInit(HWND hwnd, LPMSGBOXPARAMSW lpmb)
 
     /* Remove Help button unless MB_HELP supplied */
     if (!(lpmb->dwStyle & MB_HELP)) {
-        DestroyWindow(GetDlgItem(hwnd, IDHELP));
+        NtUserDestroyWindow(GetDlgItem(hwnd, IDHELP));
     }
 
     /* Position everything */
diff --git a/dlls/user32/painting.c b/dlls/user32/painting.c
index 8722cb68f12..ee9d23b1f71 100644
--- a/dlls/user32/painting.c
+++ b/dlls/user32/painting.c
@@ -35,18 +35,6 @@
 WINE_DEFAULT_DEBUG_CHANNEL(win);
 
 
-/***********************************************************************
- *           free_dce
- *
- * Free a class or window DCE.
- */
-void free_dce( struct dce *dce, HWND hwnd )
-{
-    /* FIXME: move callers to win32u */
-    NtUserCallTwoParam( (UINT_PTR)dce, HandleToUlong(hwnd), NtUserFreeDCE );
-}
-
-
 /***********************************************************************
  *   invalidate_dce
  *
diff --git a/dlls/user32/user32.spec b/dlls/user32/user32.spec
index 7c27d9400db..cb18d9415c9 100644
--- a/dlls/user32/user32.spec
+++ b/dlls/user32/user32.spec
@@ -161,7 +161,7 @@
 @ stdcall DestroyIcon(long)
 @ stdcall DestroyMenu(long)
 # @ stub DestroyReasons
-@ stdcall DestroyWindow(long)
+@ stdcall DestroyWindow(long) NtUserDestroyWindow
 # @ stub DeviceEventWorker
 @ stdcall DialogBoxIndirectParamA(long ptr long ptr long)
 @ stdcall DialogBoxIndirectParamAorW(long ptr long ptr long long)
diff --git a/dlls/user32/user_main.c b/dlls/user32/user_main.c
index ff9b469dae6..bc0dec6b579 100644
--- a/dlls/user32/user_main.c
+++ b/dlls/user32/user_main.c
@@ -140,6 +140,11 @@ static void CDECL notify_ime( HWND hwnd, UINT param )
     if (ime_default) SendMessageW( ime_default, WM_IME_INTERNAL, param, HandleToUlong(hwnd) );
 }
 
+void WINAPI unregister_imm( HWND hwnd )
+{
+    imm_unregister_window( hwnd );
+}
+
 static void CDECL free_win_ptr( WND *win )
 {
     HeapFree( GetProcessHeap(), 0, win->text );
@@ -150,7 +155,9 @@ static void CDECL free_win_ptr( WND *win )
 static const struct user_callbacks user_funcs =
 {
     CopyImage,
+    DestroyCaret,
     DestroyMenu,
+    EndMenu,
     HideCaret,
     PostMessageW,
     SendInput,
@@ -161,12 +168,14 @@ static const struct user_callbacks user_funcs =
     ShowWindow,
     WaitForInputIdle,
     free_win_ptr,
+    MENU_IsMenuActive,
     notify_ime,
     register_builtin_classes,
     MSG_SendInternalMessageTimeout,
     SCROLL_SetStandardScrollPainted,
     (void *)__wine_set_user_driver,
     set_window_pos,
+    unregister_imm,
 };
 
 static void WINAPI User32CallFreeIcon( ULONG *param, ULONG size )
@@ -227,6 +236,7 @@ static void thread_detach(void)
     struct user_thread_info *thread_info = get_user_thread_info();
 
     exiting_thread_id = GetCurrentThreadId();
+    NtUserCallNoParam( NtUserExitingThread );
 
     WDML_NotifyThreadDetach();
 
diff --git a/dlls/user32/win.c b/dlls/user32/win.c
index b57d6bd7566..b0e6b33eb56 100644
--- a/dlls/user32/win.c
+++ b/dlls/user32/win.c
@@ -558,38 +558,6 @@ HWND WIN_GetFullHandle( HWND hwnd )
 }
 
 
-/***********************************************************************
- *           WIN_SetOwner
- *
- * Change the owner of a window.
- */
-static HWND WIN_SetOwner( HWND hwnd, HWND owner )
-{
-    WND *win = WIN_GetPtr( hwnd );
-    HWND ret = 0;
-
-    if (!win || win == WND_DESKTOP) return 0;
-    if (win == WND_OTHER_PROCESS)
-    {
-        if (IsWindow(hwnd)) ERR( "cannot set owner %p on other process window %p\n", owner, hwnd );
-        return 0;
-    }
-    SERVER_START_REQ( set_window_owner )
-    {
-        req->handle = wine_server_user_handle( hwnd );
-        req->owner  = wine_server_user_handle( owner );
-        if (!wine_server_call( req ))
-        {
-            win->owner = wine_server_ptr_handle( reply->full_owner );
-            ret = wine_server_ptr_handle( reply->prev_owner );
-        }
-    }
-    SERVER_END_REQ;
-    WIN_ReleasePtr( win );
-    return ret;
-}
-
-
 /***********************************************************************
  *           WIN_SetStyle
  *
@@ -739,87 +707,6 @@ other_process:
 }
 
 
-/***********************************************************************
- *           WIN_DestroyWindow
- *
- * Destroy storage associated to a window. "Internals" p.358
- */
-LRESULT WIN_DestroyWindow( HWND hwnd )
-{
-    WND *wndPtr;
-    HWND *list;
-    HMENU menu = 0, sys_menu;
-    struct window_surface *surface;
-
-    TRACE("%p\n", hwnd );
-
-    /* destroy default IME window */
-    if (win_set_flags( hwnd, 0, WIN_HAS_IME_WIN ) & WIN_HAS_IME_WIN)
-    {
-        TRACE("unregister IME window for %p\n", hwnd);
-        imm_unregister_window( hwnd );
-    }
-
-    /* free child windows */
-    if ((list = WIN_ListChildren( hwnd )))
-    {
-        int i;
-        for (i = 0; list[i]; i++)
-        {
-            if (WIN_IsCurrentThread( list[i] )) WIN_DestroyWindow( list[i] );
-            else SendNotifyMessageW( list[i], WM_WINE_DESTROYWINDOW, 0, 0 );
-        }
-        HeapFree( GetProcessHeap(), 0, list );
-    }
-
-    /* Unlink now so we won't bother with the children later on */
-    SERVER_START_REQ( set_parent )
-    {
-        req->handle = wine_server_user_handle( hwnd );
-        req->parent = 0;
-        wine_server_call( req );
-    }
-    SERVER_END_REQ;
-
-    /*
-     * Send the WM_NCDESTROY to the window being destroyed.
-     */
-    SendMessageW( hwnd, WM_NCDESTROY, 0, 0 );
-
-    /* FIXME: do we need to fake QS_MOUSEMOVE wakebit? */
-
-    /* free resources associated with the window */
-
-    if (!(wndPtr = WIN_GetPtr( hwnd )) || wndPtr == WND_OTHER_PROCESS) return 0;
-    if ((wndPtr->dwStyle & (WS_CHILD | WS_POPUP)) != WS_CHILD)
-        menu = (HMENU)wndPtr->wIDmenu;
-    sys_menu = wndPtr->hSysMenu;
-    free_dce( wndPtr->dce, hwnd );
-    wndPtr->dce = NULL;
-    HeapFree( GetProcessHeap(), 0, wndPtr->text );
-    wndPtr->text = NULL;
-    HeapFree( GetProcessHeap(), 0, wndPtr->pScroll );
-    wndPtr->pScroll = NULL;
-    DestroyIcon( wndPtr->hIconSmall2 );
-    surface = wndPtr->surface;
-    wndPtr->surface = NULL;
-    WIN_ReleasePtr( wndPtr );
-
-    if (menu) DestroyMenu( menu );
-    if (sys_menu) DestroyMenu( sys_menu );
-    if (surface)
-    {
-        register_window_surface( surface, NULL );
-        window_surface_release( surface );
-    }
-
-    USER_Driver->pDestroyWindow( hwnd );
-
-    free_window_handle( hwnd );
-    return 0;
-}
-
-
 /***********************************************************************
  *           WIN_FixCoordinates
  *
@@ -1435,7 +1322,7 @@ HWND WIN_CreateWindowEx( CREATESTRUCTW *cs, LPCWSTR className, HINSTANCE module,
     return hwnd;
 
 failed:
-    WIN_DestroyWindow( hwnd );
+    NtUserCallHwnd( hwnd, NtUserDestroyWindowHandle );
     SetThreadDpiAwarenessContext( context );
     return 0;
 }
@@ -1506,139 +1393,6 @@ HWND WINAPI DECLSPEC_HOTPATCH CreateWindowExW( DWORD exStyle, LPCWSTR className,
 }
 
 
-/***********************************************************************
- *           WIN_SendDestroyMsg
- */
-static void WIN_SendDestroyMsg( HWND hwnd )
-{
-    GUITHREADINFO info;
-
-    info.cbSize = sizeof(info);
-    if (NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ))
-    {
-        if (hwnd == info.hwndCaret) DestroyCaret();
-        if (hwnd == info.hwndActive) WINPOS_ActivateOtherWindow( hwnd );
-    }
-
-    if (hwnd == NtUserGetClipboardOwner()) CLIPBOARD_ReleaseOwner( hwnd );
-
-    /*
-     * Send the WM_DESTROY to the window.
-     */
-    SendMessageW( hwnd, WM_DESTROY, 0, 0);
-
-    /*
-     * This WM_DESTROY message can trigger re-entrant calls to DestroyWindow
-     * make sure that the window still exists when we come back.
-     */
-    if (IsWindow(hwnd))
-    {
-        HWND* pWndArray;
-        int i;
-
-        if (!(pWndArray = WIN_ListChildren( hwnd ))) return;
-
-        for (i = 0; pWndArray[i]; i++)
-        {
-            if (IsWindow( pWndArray[i] )) WIN_SendDestroyMsg( pWndArray[i] );
-        }
-        HeapFree( GetProcessHeap(), 0, pWndArray );
-    }
-    else
-      WARN("\tdestroyed itself while in WM_DESTROY!\n");
-}
-
-
-/***********************************************************************
- *		DestroyWindow (USER32.@)
- */
-BOOL WINAPI DestroyWindow( HWND hwnd )
-{
-    BOOL is_child;
-
-    if (!(hwnd = WIN_IsCurrentThread( hwnd )) || is_desktop_window( hwnd ))
-    {
-        SetLastError( ERROR_ACCESS_DENIED );
-        return FALSE;
-    }
-
-    TRACE("(%p)\n", hwnd);
-
-      /* Call hooks */
-
-    if (HOOK_CallHooks( WH_CBT, HCBT_DESTROYWND, (WPARAM)hwnd, 0, TRUE )) return FALSE;
-
-    if (MENU_IsMenuActive() == hwnd)
-        EndMenu();
-
-    is_child = (GetWindowLongW( hwnd, GWL_STYLE ) & WS_CHILD) != 0;
-
-    if (is_child)
-    {
-        if (!USER_IsExitingThread( GetCurrentThreadId() ))
-            send_parent_notify( hwnd, WM_DESTROY );
-    }
-    else if (!GetWindow( hwnd, GW_OWNER ))
-    {
-        HOOK_CallHooks( WH_SHELL, HSHELL_WINDOWDESTROYED, (WPARAM)hwnd, 0L, TRUE );
-        /* FIXME: clean up palette - see "Internals" p.352 */
-    }
-
-    if (!IsWindow(hwnd)) return TRUE;
-
-      /* Hide the window */
-    if (GetWindowLongW( hwnd, GWL_STYLE ) & WS_VISIBLE)
-    {
-        /* Only child windows receive WM_SHOWWINDOW in DestroyWindow() */
-        if (is_child)
-            ShowWindow( hwnd, SW_HIDE );
-        else
-            NtUserSetWindowPos( hwnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE |
-                                SWP_NOZORDER | SWP_NOACTIVATE | SWP_HIDEWINDOW );
-    }
-
-    if (!IsWindow(hwnd)) return TRUE;
-
-      /* Recursively destroy owned windows */
-
-    if (!is_child)
-    {
-        for (;;)
-        {
-            int i;
-            BOOL got_one = FALSE;
-            HWND *list = WIN_ListChildren( GetDesktopWindow() );
-            if (list)
-            {
-                for (i = 0; list[i]; i++)
-                {
-                    if (GetWindow( list[i], GW_OWNER ) != hwnd) continue;
-                    if (WIN_IsCurrentThread( list[i] ))
-                    {
-                        DestroyWindow( list[i] );
-                        got_one = TRUE;
-                        continue;
-                    }
-                    WIN_SetOwner( list[i], 0 );
-                }
-                HeapFree( GetProcessHeap(), 0, list );
-            }
-            if (!got_one) break;
-        }
-    }
-
-      /* Send destroy messages */
-
-    WIN_SendDestroyMsg( hwnd );
-    if (!IsWindow( hwnd )) return TRUE;
-
-      /* Destroy the window storage */
-
-    WIN_DestroyWindow( hwnd );
-    return TRUE;
-}
-
-
 /***********************************************************************
  *		CloseWindow (USER32.@)
  */
diff --git a/dlls/user32/win.h b/dlls/user32/win.h
index 3e6885ab090..89e91a255f9 100644
--- a/dlls/user32/win.h
+++ b/dlls/user32/win.h
@@ -45,7 +45,6 @@ extern HWND WIN_IsCurrentThread( HWND hwnd ) DECLSPEC_HIDDEN;
 extern UINT win_set_flags( HWND hwnd, UINT set_mask, UINT clear_mask ) DECLSPEC_HIDDEN;
 extern ULONG WIN_SetStyle( HWND hwnd, ULONG set_bits, ULONG clear_bits ) DECLSPEC_HIDDEN;
 extern BOOL WIN_GetRectangles( HWND hwnd, enum coords_relative relative, RECT *rectWindow, RECT *rectClient ) DECLSPEC_HIDDEN;
-extern LRESULT WIN_DestroyWindow( HWND hwnd ) DECLSPEC_HIDDEN;
 extern HWND WIN_CreateWindowEx( CREATESTRUCTW *cs, LPCWSTR className, HINSTANCE module, BOOL unicode ) DECLSPEC_HIDDEN;
 extern BOOL WIN_IsWindowDrawable( HWND hwnd, BOOL ) DECLSPEC_HIDDEN;
 extern HWND *WIN_ListChildren( HWND hwnd ) DECLSPEC_HIDDEN;
diff --git a/dlls/win32u/clipboard.c b/dlls/win32u/clipboard.c
index e132f981344..585a2aae631 100644
--- a/dlls/win32u/clipboard.c
+++ b/dlls/win32u/clipboard.c
@@ -288,3 +288,28 @@ BOOL WINAPI NtUserRemoveClipboardFormatListener( HWND hwnd )
     SERVER_END_REQ;
     return ret;
 }
+
+/**************************************************************************
+ *           release_clipboard_owner
+ */
+void release_clipboard_owner( HWND hwnd )
+{
+    HWND viewer = 0, owner = 0;
+
+    send_message( hwnd, WM_RENDERALLFORMATS, 0, 0 );
+
+    SERVER_START_REQ( release_clipboard )
+    {
+        req->owner = wine_server_user_handle( hwnd );
+        if (!wine_server_call( req ))
+        {
+            viewer = wine_server_ptr_handle( reply->viewer );
+            owner = wine_server_ptr_handle( reply->owner );
+        }
+    }
+    SERVER_END_REQ;
+
+    if (viewer)
+        NtUserMessageCall( viewer, WM_DRAWCLIPBOARD, (WPARAM)owner, 0,
+                           0, FNID_SENDNOTIFYMESSAGE, FALSE );
+}
diff --git a/dlls/win32u/driver.c b/dlls/win32u/driver.c
index afe44e1c4a3..166c2009dbc 100644
--- a/dlls/win32u/driver.c
+++ b/dlls/win32u/driver.c
@@ -1190,6 +1190,7 @@ static const struct user_driver_funcs lazy_load_driver =
     .pUpdateDisplayDevices = loaderdrv_UpdateDisplayDevices,
     /* windowing functions */
     .pCreateDesktopWindow = loaderdrv_CreateDesktopWindow,
+    .pDestroyWindow = nulldrv_DestroyWindow,
     .pFlashWindowEx = loaderdrv_FlashWindowEx,
     .pGetDC = loaderdrv_GetDC,
     .pSetCapture = nulldrv_SetCapture,
diff --git a/dlls/win32u/gdiobj.c b/dlls/win32u/gdiobj.c
index adb162ab7ff..69607f0e3de 100644
--- a/dlls/win32u/gdiobj.c
+++ b/dlls/win32u/gdiobj.c
@@ -1162,6 +1162,7 @@ static struct unix_funcs unix_funcs =
     NtUserCountClipboardFormats,
     NtUserDeferWindowPosAndBand,
     NtUserDestroyCursor,
+    NtUserDestroyWindow,
     NtUserDrawIconEx,
     NtUserEndDeferWindowPosEx,
     NtUserEndPaint,
diff --git a/dlls/win32u/message.c b/dlls/win32u/message.c
index d00178e6aa4..690de6cdf8a 100644
--- a/dlls/win32u/message.c
+++ b/dlls/win32u/message.c
@@ -41,6 +41,8 @@ LRESULT handle_internal_message( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpar
 {
     switch(msg)
     {
+    case WM_WINE_DESTROYWINDOW:
+        return destroy_window( hwnd );
     case WM_WINE_SETWINDOWPOS:
         if (is_desktop_window( hwnd )) return 0;
         return set_window_pos( (WINDOWPOS *)lparam, 0, 0 );
diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h
index aa9b45b1542..b3cafe791df 100644
--- a/dlls/win32u/ntuser_private.h
+++ b/dlls/win32u/ntuser_private.h
@@ -31,7 +31,9 @@ struct tagWND;
 struct user_callbacks
 {
     HANDLE (WINAPI *pCopyImage)( HANDLE, UINT, INT, INT, UINT );
+    BOOL (WINAPI *pDestroyCaret)(void);
     BOOL (WINAPI *pDestroyMenu)( HMENU );
+    BOOL (WINAPI *pEndMenu)(void);
     BOOL (WINAPI *pHideCaret)( HWND hwnd );
     BOOL (WINAPI *pPostMessageW)( HWND, UINT, WPARAM, LPARAM );
     UINT (WINAPI *pSendInput)( UINT count, INPUT *inputs, int size );
@@ -42,6 +44,7 @@ struct user_callbacks
     BOOL (WINAPI *pShowWindow)( HWND, INT );
     DWORD (WINAPI *pWaitForInputIdle)( HANDLE, DWORD );
     void (CDECL *free_win_ptr)( struct tagWND *win );
+    HWND (CDECL *is_menu_active)(void);
     void (CDECL *notify_ime)( HWND hwnd, UINT param );
     void (CDECL *register_builtin_classes)(void);
     LRESULT (WINAPI *send_ll_message)( DWORD, DWORD, UINT, WPARAM, LPARAM, UINT, UINT, PDWORD_PTR );
@@ -50,6 +53,7 @@ struct user_callbacks
     BOOL (CDECL *set_window_pos)( HWND hwnd, HWND insert_after, UINT swp_flags,
                                   const RECT *window_rect, const RECT *client_rect,
                                   const RECT *valid_rects );
+    void (WINAPI *unregister_imm)( HWND hwnd );
 };
 
 #define WM_SYSTIMER         0x0118
diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c
index d9ddfe3400e..0166b64c76a 100644
--- a/dlls/win32u/sysparams.c
+++ b/dlls/win32u/sysparams.c
@@ -4591,6 +4591,16 @@ static BOOL message_beep( UINT i )
     return TRUE;
 }
 
+static DWORD exiting_thread_id;
+
+/**********************************************************************
+ *           is_exiting_thread
+ */
+BOOL is_exiting_thread( DWORD tid )
+{
+    return tid == exiting_thread_id;
+}
+
 static void thread_detach(void)
 {
     struct user_thread_info *thread_info = get_user_thread_info();
@@ -4602,6 +4612,8 @@ static void thread_detach(void)
 
     destroy_thread_windows();
     NtClose( thread_info->server_queue );
+
+    exiting_thread_id = 0;
 }
 
 /***********************************************************************
@@ -4618,6 +4630,9 @@ ULONG_PTR WINAPI NtUserCallNoParam( ULONG code )
     case NtUserReleaseCapture:
         return release_capture();
     /* temporary exports */
+    case NtUserExitingThread:
+        exiting_thread_id = GetCurrentThreadId();
+        return 0;
     case NtUserThreadDetach:
         thread_detach();
         return 0;
@@ -4724,9 +4739,6 @@ ULONG_PTR WINAPI NtUserCallTwoParam( ULONG_PTR arg1, ULONG_PTR arg2, ULONG code
         return HandleToUlong( alloc_user_handle( (struct user_object *)arg1, arg2 ));
     case NtUserAllocWinProc:
         return (UINT_PTR)alloc_winproc( (WNDPROC)arg1, arg2 );
-    case NtUserFreeDCE:
-        free_dce( (struct dce *)arg1, UlongToHandle(arg2) );
-        return 0;
     case NtUserFreeHandle:
         return (UINT_PTR)free_user_handle( UlongToHandle(arg1), arg2 );
     case NtUserGetHandlePtr:
diff --git a/dlls/win32u/win32u.spec b/dlls/win32u/win32u.spec
index 96d8d4833b0..aacfebd1b15 100644
--- a/dlls/win32u/win32u.spec
+++ b/dlls/win32u/win32u.spec
@@ -834,7 +834,7 @@
 @ stub NtUserDestroyInputContext
 @ stub NtUserDestroyMenu
 @ stub NtUserDestroyPalmRejectionDelayZone
-@ stub NtUserDestroyWindow
+@ stdcall NtUserDestroyWindow(long)
 @ stub NtUserDisableImmersiveOwner
 @ stub NtUserDisableProcessWindowFiltering
 @ stub NtUserDisableThreadIme
diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h
index 516f4540315..83b0e093a20 100644
--- a/dlls/win32u/win32u_private.h
+++ b/dlls/win32u/win32u_private.h
@@ -199,6 +199,7 @@ struct unix_funcs
                                                      INT x, INT y, INT cx, INT cy,
                                                      UINT flags, UINT unk1, UINT unk2 );
     BOOL     (WINAPI *pNtUserDestroyCursor)( HCURSOR cursor, ULONG arg );
+    BOOL     (WINAPI *pNtUserDestroyWindow)( HWND hwnd );
     BOOL     (WINAPI *pNtUserDrawIconEx)( HDC hdc, INT x0, INT y0, HICON icon, INT width,
                                           INT height, UINT istep, HBRUSH hbr, UINT flags );
     BOOL     (WINAPI *pNtUserEndDeferWindowPosEx)( HDWP hdwp, BOOL async );
@@ -286,6 +287,9 @@ struct unix_funcs
                                       struct window_surface *surface );
 };
 
+/* clipboard.c */
+extern void release_clipboard_owner( HWND hwnd ) DECLSPEC_HIDDEN;
+
 /* cursoricon.c */
 extern HICON alloc_cursoricon_handle( BOOL is_icon ) DECLSPEC_HIDDEN;
 extern BOOL get_clip_cursor( RECT *rect ) DECLSPEC_HIDDEN;
@@ -327,6 +331,7 @@ extern UINT get_system_dpi(void) DECLSPEC_HIDDEN;
 extern int get_system_metrics( int index ) DECLSPEC_HIDDEN;
 extern UINT get_thread_dpi(void) DECLSPEC_HIDDEN;
 extern RECT get_virtual_screen_rect( UINT dpi ) DECLSPEC_HIDDEN;
+extern BOOL is_exiting_thread( DWORD tid ) DECLSPEC_HIDDEN;
 extern POINT map_dpi_point( POINT pt, UINT dpi_from, UINT dpi_to ) DECLSPEC_HIDDEN;
 extern RECT map_dpi_rect( RECT rect, UINT dpi_from, UINT dpi_to ) DECLSPEC_HIDDEN;
 extern HMONITOR monitor_from_point( POINT pt, DWORD flags, UINT dpi ) DECLSPEC_HIDDEN;
@@ -344,6 +349,7 @@ extern void erase_now( HWND hwnd, UINT rdw_flags ) DECLSPEC_HIDDEN;
 struct tagWND;
 extern HDWP begin_defer_window_pos( INT count ) DECLSPEC_HIDDEN;
 extern void destroy_thread_windows(void) DECLSPEC_HIDDEN;
+extern LRESULT destroy_window( HWND hwnd ) DECLSPEC_HIDDEN;
 extern HWND get_desktop_window(void) DECLSPEC_HIDDEN;
 extern UINT get_dpi_for_window( HWND hwnd ) DECLSPEC_HIDDEN;
 extern HWND get_full_window_handle( HWND hwnd ) DECLSPEC_HIDDEN;
diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c
index a28b6ae9ebf..f8d147b785c 100644
--- a/dlls/win32u/window.c
+++ b/dlls/win32u/window.c
@@ -3174,6 +3174,99 @@ BOOL WINAPI NtUserEndDeferWindowPosEx( HDWP hdwp, BOOL async )
     return TRUE;
 }
 
+/***********************************************************************
+ *           win_set_flags
+ *
+ * Set the flags of a window and return the previous value.
+ */
+static UINT win_set_flags( HWND hwnd, UINT set_mask, UINT clear_mask )
+{
+    WND *win = get_win_ptr( hwnd );
+    UINT ret;
+
+    if (!win || win == WND_OTHER_PROCESS || win == WND_DESKTOP) return 0;
+    ret = win->flags;
+    win->flags = (ret & ~clear_mask) | set_mask;
+    release_win_ptr( win );
+    return ret;
+}
+
+/*******************************************************************
+ *         can_activate_window
+ *
+ * Check if we can activate the specified window.
+ */
+static BOOL can_activate_window( HWND hwnd )
+{
+    LONG style;
+
+    if (!hwnd) return FALSE;
+    style = get_window_long( hwnd, GWL_STYLE );
+    if (!(style & WS_VISIBLE)) return FALSE;
+    if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE;
+    return !(style & WS_DISABLED);
+}
+
+/*******************************************************************
+ *         activate_other_window
+ *
+ * Activates window other than hwnd.
+ */
+static void activate_other_window( HWND hwnd )
+{
+    HWND hwnd_to, fg;
+
+    if ((get_window_long( hwnd, GWL_STYLE ) & WS_POPUP) &&
+        (hwnd_to = get_window_relative( hwnd, GW_OWNER )))
+    {
+        hwnd_to = NtUserGetAncestor( hwnd_to, GA_ROOT );
+        if (can_activate_window( hwnd_to )) goto done;
+    }
+
+    hwnd_to = hwnd;
+    for (;;)
+    {
+        if (!(hwnd_to = get_window_relative( hwnd_to, GW_HWNDNEXT ))) break;
+        if (can_activate_window( hwnd_to )) goto done;
+    }
+
+    hwnd_to = get_window_relative( get_desktop_window(), GW_CHILD );
+    for (;;)
+    {
+        if (hwnd_to == hwnd)
+        {
+            hwnd_to = 0;
+            break;
+        }
+        if (can_activate_window( hwnd_to )) goto done;
+        if (!(hwnd_to = get_window_relative( hwnd_to, GW_HWNDNEXT ))) break;
+    }
+
+ done:
+    fg = NtUserGetForegroundWindow();
+    TRACE( "win = %p fg = %p\n", hwnd_to, fg );
+    if (!fg || hwnd == fg)
+    {
+        if (set_foreground_window( hwnd_to, FALSE )) return;
+    }
+    if (NtUserSetActiveWindow( hwnd_to )) NtUserSetActiveWindow( 0 );
+}
+
+/*******************************************************************
+ *           send_parent_notify
+ */
+static void send_parent_notify( HWND hwnd, UINT msg )
+{
+    if ((get_window_long( hwnd, GWL_STYLE ) & (WS_CHILD | WS_POPUP)) == WS_CHILD &&
+        !(get_window_long( hwnd, GWL_EXSTYLE ) & WS_EX_NOPARENTNOTIFY))
+    {
+        HWND parent = get_parent( hwnd );
+        if (parent && parent != get_desktop_window())
+            send_message( parent, WM_PARENTNOTIFY,
+                          MAKEWPARAM( msg, get_window_long( hwnd, GWLP_ID )), (LPARAM)hwnd );
+    }
+}
+
 /*******************************************************************
  *           update_window_state
  *
@@ -3260,6 +3353,226 @@ BOOL WINAPI NtUserFlashWindowEx( FLASHWINFO *info )
 }
 
 /***********************************************************************
+ *           send_destroy_message
+ */
+static void send_destroy_message( HWND hwnd )
+{
+    GUITHREADINFO info;
+
+    info.cbSize = sizeof(info);
+    if (NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ))
+    {
+        if (hwnd == info.hwndCaret && user_callbacks) user_callbacks->pDestroyCaret();
+        if (hwnd == info.hwndActive) activate_other_window( hwnd );
+    }
+
+    if (hwnd == NtUserGetClipboardOwner()) release_clipboard_owner( hwnd );
+
+    send_message( hwnd, WM_DESTROY, 0, 0);
+
+    /*
+     * This WM_DESTROY message can trigger re-entrant calls to DestroyWindow
+     * make sure that the window still exists when we come back.
+     */
+    if (is_window(hwnd))
+    {
+        HWND *children;
+        int i;
+
+        if (!(children = list_window_children( 0, hwnd, NULL, 0 ))) return;
+
+        for (i = 0; children[i]; i++)
+        {
+            if (is_window( children[i] )) send_destroy_message( children[i] );
+        }
+        free( children );
+    }
+    else
+        WARN( "\tdestroyed itself while in WM_DESTROY!\n" );
+}
+
+/***********************************************************************
+ *           free_window_handle
+ *
+ * Free a window handle.
+ */
+static void free_window_handle( HWND hwnd )
+{
+    WND *win;
+
+    TRACE( "\n" );
+
+    if ((win = get_user_handle_ptr( hwnd, NTUSER_OBJ_WINDOW )) && win != OBJ_OTHER_PROCESS)
+    {
+        SERVER_START_REQ( destroy_window )
+        {
+            req->handle = wine_server_user_handle( hwnd );
+            wine_server_call( req );
+            set_user_handle_ptr( hwnd, NULL );
+        }
+        SERVER_END_REQ;
+        user_unlock();
+        if (user_callbacks) user_callbacks->free_win_ptr( win );
+    }
+}
+
+/***********************************************************************
+ *           destroy_window
+ */
+LRESULT destroy_window( HWND hwnd )
+{
+    struct window_surface *surface;
+    HMENU menu = 0, sys_menu;
+    WND *win;
+    HWND *children;
+
+    TRACE( "%p\n", hwnd );
+
+    /* destroy default IME window */
+    if (win_set_flags( hwnd, 0, WIN_HAS_IME_WIN ) & WIN_HAS_IME_WIN)
+    {
+        TRACE("unregister IME window for %p\n", hwnd);
+        if (user_callbacks) user_callbacks->unregister_imm( hwnd );
+    }
+
+    /* free child windows */
+    if ((children = list_window_children( 0, hwnd, NULL, 0 )))
+    {
+        int i;
+        for (i = 0; children[i]; i++)
+        {
+            if (is_current_thread_window( children[i] ))
+                destroy_window( children[i] );
+            else
+                NtUserMessageCall( children[i], WM_WINE_DESTROYWINDOW, 0, 0,
+                                   0, FNID_SENDNOTIFYMESSAGE, FALSE );
+        }
+        free( children );
+    }
+
+    /* Unlink now so we won't bother with the children later on */
+    SERVER_START_REQ( set_parent )
+    {
+        req->handle = wine_server_user_handle( hwnd );
+        req->parent = 0;
+        wine_server_call( req );
+    }
+    SERVER_END_REQ;
+
+    send_message( hwnd, WM_NCDESTROY, 0, 0 );
+
+    /* FIXME: do we need to fake QS_MOUSEMOVE wakebit? */
+
+    /* free resources associated with the window */
+
+    if (!(win = get_win_ptr( hwnd )) || win == WND_OTHER_PROCESS) return 0;
+    if ((win->dwStyle & (WS_CHILD | WS_POPUP)) != WS_CHILD)
+        menu = (HMENU)win->wIDmenu;
+    sys_menu = win->hSysMenu;
+    free_dce( win->dce, hwnd );
+    win->dce = NULL;
+    NtUserDestroyCursor( win->hIconSmall2, 0 );
+    surface = win->surface;
+    win->surface = NULL;
+    release_win_ptr( win );
+
+    NtUserDestroyMenu( menu );
+    NtUserDestroyMenu( sys_menu );
+    if (surface)
+    {
+        register_window_surface( surface, NULL );
+        window_surface_release( surface );
+    }
+
+    user_driver->pDestroyWindow( hwnd );
+
+    free_window_handle( hwnd );
+    return 0;
+}
+
+/***********************************************************************
+ *           NtUserDestroyWindow (win32u.@)
+ */
+BOOL WINAPI NtUserDestroyWindow( HWND hwnd )
+{
+    BOOL is_child;
+
+    if (!(hwnd = is_current_thread_window( hwnd )) || is_desktop_window( hwnd ))
+    {
+        SetLastError( ERROR_ACCESS_DENIED );
+        return FALSE;
+    }
+
+    TRACE( "(%p)\n", hwnd );
+
+    if (call_hooks( WH_CBT, HCBT_DESTROYWND, (WPARAM)hwnd, 0, TRUE )) return FALSE;
+
+    if (user_callbacks && user_callbacks->is_menu_active() == hwnd)
+        user_callbacks->pEndMenu();
+
+    is_child = (get_window_long( hwnd, GWL_STYLE ) & WS_CHILD) != 0;
+
+    if (is_child)
+    {
+        if (!is_exiting_thread( GetCurrentThreadId() ))
+            send_parent_notify( hwnd, WM_DESTROY );
+    }
+    else if (!get_window_relative( hwnd, GW_OWNER ))
+    {
+        call_hooks( WH_SHELL, HSHELL_WINDOWDESTROYED, (WPARAM)hwnd, 0L, TRUE );
+        /* FIXME: clean up palette - see "Internals" p.352 */
+    }
+
+    if (!is_window( hwnd )) return TRUE;
+
+    /* Hide the window */
+    if (get_window_long( hwnd, GWL_STYLE ) & WS_VISIBLE)
+    {
+        /* Only child windows receive WM_SHOWWINDOW in DestroyWindow() */
+        if (is_child)
+            NtUserShowWindow( hwnd, SW_HIDE );
+        else
+            NtUserSetWindowPos( hwnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE |
+                                SWP_NOZORDER | SWP_NOACTIVATE | SWP_HIDEWINDOW );
+    }
+
+    if (!is_window( hwnd )) return TRUE;
+
+    /* Recursively destroy child windows */
+    if (!is_child)
+    {
+        for (;;)
+        {
+            BOOL got_one = FALSE;
+            HWND *children;
+            unsigned int i;
+
+            if (!(children = list_window_children( 0, get_desktop_window(), NULL, 0 ))) break;
+
+            for (i = 0; children[i]; i++)
+            {
+                if (get_window_relative( children[i], GW_OWNER ) != hwnd) continue;
+                if (is_current_thread_window( children[i] ))
+                {
+                    NtUserDestroyWindow( children[i] );
+                    got_one = TRUE;
+                    continue;
+                }
+                set_window_owner( children[i], 0 );
+            }
+            free( children );
+            if (!got_one) break;
+        }
+    }
+
+    send_destroy_message( hwnd );
+    if (!is_window( hwnd )) return TRUE;
+
+    destroy_window( hwnd );
+    return TRUE;
+}
+
+/*****************************************************************************
  *           destroy_thread_windows
  *
  * Destroy all window owned by the current thread.
@@ -3329,6 +3642,8 @@ ULONG_PTR WINAPI NtUserCallHwnd( HWND hwnd, DWORD code )
     /* temporary exports */
     case NtUserCreateDesktopWindow:
         return user_driver->pCreateDesktopWindow( hwnd );
+    case NtUserDestroyWindowHandle:
+        return destroy_window( hwnd );
     case NtUserGetDummySurface:
         return (UINT_PTR)&dummy_surface;
     default:
diff --git a/dlls/win32u/wrappers.c b/dlls/win32u/wrappers.c
index 258483ad8de..da1957c8c88 100644
--- a/dlls/win32u/wrappers.c
+++ b/dlls/win32u/wrappers.c
@@ -783,6 +783,12 @@ BOOL WINAPI NtUserDestroyCursor( HCURSOR cursor, ULONG arg )
     return unix_funcs->pNtUserDestroyCursor( cursor, arg );
 }
 
+BOOL WINAPI NtUserDestroyWindow( HWND hwnd )
+{
+    if (!unix_funcs) return FALSE;
+    return unix_funcs->pNtUserDestroyWindow( hwnd );
+}
+
 BOOL WINAPI NtUserDrawIconEx( HDC hdc, INT x0, INT y0, HICON icon, INT width,
                               INT height, UINT istep, HBRUSH hbr, UINT flags )
 {
diff --git a/include/ntuser.h b/include/ntuser.h
index aeb3e9ae2cc..de958caa735 100644
--- a/include/ntuser.h
+++ b/include/ntuser.h
@@ -91,6 +91,7 @@ enum
     NtUserGetInputState,
     NtUserReleaseCapture,
     /* temporary exports */
+    NtUserExitingThread,
     NtUserThreadDetach,
 };
 
@@ -134,7 +135,6 @@ enum
     /* temporary exports */
     NtUserAllocHandle,
     NtUserAllocWinProc,
-    NtUserFreeDCE,
     NtUserFreeHandle,
     NtUserGetHandlePtr,
     NtUserInvalidateDCE,
@@ -154,6 +154,7 @@ enum
     NtUserIsWindowVisible,
     /* temporary exports */
     NtUserCreateDesktopWindow,
+    NtUserDestroyWindowHandle,
     NtUserGetDummySurface,
 };
 
@@ -302,6 +303,7 @@ HDWP    WINAPI NtUserDeferWindowPosAndBand( HDWP hdwp, HWND hwnd, HWND after, IN
 BOOL    WINAPI NtUserDestroyAcceleratorTable( HACCEL handle );
 BOOL    WINAPI NtUserDestroyCursor( HCURSOR cursor, ULONG arg );
 BOOL    WINAPI NtUserDestroyMenu( HMENU menu );
+BOOL    WINAPI NtUserDestroyWindow( HWND hwnd );
 BOOL    WINAPI NtUserDrawIconEx( HDC hdc, INT x0, INT y0, HICON icon, INT width,
                                  INT height, UINT istep, HBRUSH hbr, UINT flags );
 BOOL    WINAPI NtUserEndDeferWindowPosEx( HDWP hdwp, BOOL async );
-- 
2.23.0




More information about the wine-devel mailing list