From 4bcf23d1ded3d2fc104ba9e85b88fb1a59150183 Mon Sep 17 00:00:00 2001 From: Katayama Hirofumi MZ Date: Wed, 20 Apr 2022 16:10:06 +0900 Subject: [PATCH] [NTUSER] Destroy the default IME window (#4462) - Add IntFindNonImeRelatedWndOfSameThread, IntImeCanDestroyDefIMEforChild, and IntImeCanDestroyDefIME helper functions. - Do assignment unlock spwndDefaultIme at co_UserFreeWindow. - Destroy the default IME window of the specified window if necessary at co_UserDestroyWindow. CORE-11700 --- win32ss/user/ntuser/ime.c | 197 +++++++++++++++++++++++++++++++++-- win32ss/user/ntuser/window.c | 21 +++- win32ss/user/ntuser/window.h | 13 +++ 3 files changed, 221 insertions(+), 10 deletions(-) diff --git a/win32ss/user/ntuser/ime.c b/win32ss/user/ntuser/ime.c index d7377c32f0b..e869608724f 100644 --- a/win32ss/user/ntuser/ime.c +++ b/win32ss/user/ntuser/ime.c @@ -21,15 +21,6 @@ DBG_DEFAULT_CHANNEL(UserMisc); #define LANGID_CHINESE_TRADITIONAL MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL) #define LANGID_NEUTRAL MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL) -// The IME-like windows are the IME windows and the IME UI windows. -// The IME window's class name is "IME". -// The IME UI window behaves the User Interface of IME for the user. -#define IS_WND_IMELIKE(pwnd) \ - (((pwnd)->pcls->style & CS_IME) || \ - ((pwnd)->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME])) -#define IS_WND_MENU(pWnd) \ - ((pWnd)->pcls->atomClassName == gpsi->atomSysClass[ICLS_MENU]) - // The special virtual keys for Japanese: Used for key states. // https://www.kthree.co.jp/kihelp/index.html?page=app/vkey&type=html #define VK_DBE_ALPHANUMERIC 0xF0 @@ -1820,4 +1811,192 @@ Quit: return ret; } +// Searchs a non-IME-related window of the same thread of pwndTarget, +// other than pwndTarget, around pwndParent. Returns TRUE if found. +// +// Win: IsChildSameThread +BOOL IntFindNonImeRelatedWndOfSameThread(PWND pwndParent, PWND pwndTarget) +{ + PWND pwnd, pwndOwner, pwndNode; + PTHREADINFO ptiTarget = pwndTarget->head.pti; + + // For all the children of pwndParent, ... + for (pwnd = pwndParent->spwndChild; pwnd; pwnd = pwnd->spwndNext) + { + if (pwnd == pwndTarget || pwnd->head.pti != ptiTarget || IS_WND_MENU(pwnd)) + continue; + + if (!IS_WND_CHILD(pwnd)) + { + // Check if any IME-like owner. + BOOL bFound1 = FALSE; + for (pwndOwner = pwnd; pwndOwner; pwndOwner = pwndOwner->spwndOwner) + { + if (IS_WND_IMELIKE(pwndOwner)) + { + bFound1 = TRUE; + break; + } + } + if (bFound1) + continue; // Skip if any IME-like owner. + } + + pwndNode = pwnd; + + if (IS_WND_CHILD(pwndNode)) + { + // Check if any same-thread IME-like ancestor. + BOOL bFound2 = FALSE; + for (; IS_WND_CHILD(pwndNode); pwndNode = pwndNode->spwndParent) + { + if (pwndNode->head.pti != ptiTarget) + break; + + if (IS_WND_IMELIKE(pwndNode)) + { + bFound2 = TRUE; + break; + } + } + if (bFound2) + continue; + // Now, pwndNode is non-child or non-same-thread window. + } + + if (!IS_WND_CHILD(pwndNode)) // pwndNode is non-child + { + // Check if any same-thread IME-like owner. + BOOL bFound3 = FALSE; + for (; pwndNode; pwndNode = pwndNode->spwndOwner) + { + if (pwndNode->head.pti != ptiTarget) + break; + + if (IS_WND_IMELIKE(pwndNode)) + { + bFound3 = TRUE; + break; + } + } + if (bFound3) + continue; + } + + return TRUE; + } + + return FALSE; +} + +// Can we destroy the default IME window for the target child window? +// Win: ImeCanDestroyDefIMEforChild +BOOL FASTCALL IntImeCanDestroyDefIMEforChild(PWND pImeWnd, PWND pwndTarget) +{ + PWND pwndNode; + PIMEUI pimeui; + IMEUI SafeImeUI; + + pimeui = ((PIMEWND)pImeWnd)->pimeui; + if (!pimeui || (LONG_PTR)pimeui == (LONG_PTR)-1) + return FALSE; + + // Check IMEUI.fChildThreadDef + _SEH2_TRY + { + ProbeForRead(pimeui, sizeof(IMEUI), 1); + SafeImeUI = *pimeui; + if (!SafeImeUI.fChildThreadDef) + return FALSE; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + ; + } + _SEH2_END; + + // The parent of pwndTarget is NULL or of the same thread of pwndTarget? + if (pwndTarget->spwndParent == NULL || + pwndTarget->head.pti == pwndTarget->spwndParent->head.pti) + { + return FALSE; + } + + for (pwndNode = pwndTarget; pwndNode; pwndNode = pwndNode->spwndParent) + { + if (pwndNode == pwndNode->head.rpdesk->pDeskInfo->spwnd) + break; + + if (IntFindNonImeRelatedWndOfSameThread(pwndNode->spwndParent, pwndTarget)) + return FALSE; + } + + return TRUE; +} + +// Can we destroy the default IME window for the non-child target window? +// If so, this function sets spwndOwner to NULL. +// Win: ImeCanDestroyDefIME +BOOL FASTCALL IntImeCanDestroyDefIME(PWND pImeWnd, PWND pwndTarget) +{ + PWND pwndNode; + PIMEUI pimeui; + IMEUI SafeImeUI; + + pimeui = ((PIMEWND)pImeWnd)->pimeui; + if (!pimeui || (LONG_PTR)pimeui == (LONG_PTR)-1) + return FALSE; + + // Check IMEUI.fDestroy + _SEH2_TRY + { + ProbeForRead(pimeui, sizeof(IMEUI), 1); + SafeImeUI = *pimeui; + if (SafeImeUI.fDestroy) + return FALSE; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + ; + } + _SEH2_END; + + // Any ancestor of pImeWnd is pwndTarget? + if (pImeWnd->spwndOwner) + { + for (pwndNode = pImeWnd->spwndOwner; pwndNode; pwndNode = pwndNode->spwndOwner) + { + if (pwndNode == pwndTarget) + break; + } + + if (!pwndNode) + return FALSE; + } + + // Any ancestor of pwndTarget is IME-like? + for (pwndNode = pwndTarget; pwndNode; pwndNode = pwndNode->spwndOwner) + { + if (IS_WND_IMELIKE(pwndNode)) + return FALSE; + } + + // Adjust the ordering and top-mode status + IntImeSetFutureOwner(pImeWnd, pwndTarget); + for (pwndNode = pImeWnd->spwndOwner; pwndNode; pwndNode = pwndNode->spwndNext) + { + if (pwndNode == pImeWnd) + break; + } + if (pwndNode == pImeWnd) + IntImeCheckTopmost(pImeWnd); + + // Is the owner of pImeWnd NULL or pwndTarget? + if (pImeWnd->spwndOwner && pwndTarget != pImeWnd->spwndOwner) + return FALSE; + + pImeWnd->spwndOwner = NULL; + return TRUE; +} + /* EOF */ diff --git a/win32ss/user/ntuser/window.c b/win32ss/user/ntuser/window.c index da8da6cf68e..9ec554b4a24 100644 --- a/win32ss/user/ntuser/window.c +++ b/win32ss/user/ntuser/window.c @@ -658,6 +658,9 @@ LRESULT co_UserFreeWindow(PWND Window, ThreadData->rpdesk->rpwinstaParent->ShellListView = NULL; } + if (IS_IMM_MODE() && Window == ThreadData->spwndDefaultIme) + UserAssignmentUnlock((PVOID*)&(ThreadData->spwndDefaultIme)); + /* Fixes dialog test_focus breakage due to r66237. */ if (ThreadData->MessageQueue->spwndFocus == Window) ThreadData->MessageQueue->spwndFocus = NULL; @@ -2762,7 +2765,7 @@ BOOLEAN co_UserDestroyWindow(PVOID Object) TRACE("co_UserDestroyWindow(Window = 0x%p, hWnd = 0x%p)\n", Window, hWnd); /* Check for owner thread */ - if ( Window->head.pti != PsGetCurrentThreadWin32Thread()) + if (Window->head.pti != ti) { /* Check if we are destroying the desktop window */ if (! ((Window->head.rpdesk->dwDTFlags & DF_DESTROYED) && Window == Window->head.rpdesk->pDeskInfo->spwnd)) @@ -2918,6 +2921,22 @@ BOOLEAN co_UserDestroyWindow(PVOID Object) /* Send destroy messages */ IntSendDestroyMsg(UserHMGetHandle(Window)); + // Destroy the default IME window if necessary + if (IS_IMM_MODE() && !(ti->TIF_flags & TIF_INCLEANUP) && + ti->spwndDefaultIme && !IS_WND_IMELIKE(Window) && !(Window->state & WNDS_DESTROYED)) + { + if (IS_WND_CHILD(Window)) + { + if (IntImeCanDestroyDefIMEforChild(ti->spwndDefaultIme, Window)) + co_UserDestroyWindow(ti->spwndDefaultIme); + } + else + { + if (IntImeCanDestroyDefIME(ti->spwndDefaultIme, Window)) + co_UserDestroyWindow(ti->spwndDefaultIme); + } + } + if (!IntIsWindow(UserHMGetHandle(Window))) { return TRUE; diff --git a/win32ss/user/ntuser/window.h b/win32ss/user/ntuser/window.h index 7086af36fc5..86daff24d97 100644 --- a/win32ss/user/ntuser/window.h +++ b/win32ss/user/ntuser/window.h @@ -102,4 +102,17 @@ VOID FASTCALL IntFreeHwndList(PWINDOWLIST pwlTarget); /* Undocumented dwFlags for IntBuildHwndList */ #define IACE_LIST 0x0002 +#define IS_WND_CHILD(pWnd) ((pWnd)->style & WS_CHILD) +#define IS_WND_MENU(pWnd) ((pWnd)->pcls->atomClassName == gpsi->atomSysClass[ICLS_MENU]) + +// The IME-like windows are the IME windows and the IME UI windows. +// The IME window's class name is "IME". +// The IME UI window behaves the User Interface of IME for the user. +#define IS_WND_IMELIKE(pWnd) \ + (((pWnd)->pcls->style & CS_IME) || \ + ((pWnd)->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME])) + +BOOL FASTCALL IntImeCanDestroyDefIMEforChild(PWND pImeWnd, PWND pwndTarget); +BOOL FASTCALL IntImeCanDestroyDefIME(PWND pImeWnd, PWND pwndTarget); + /* EOF */