From 6829350af9ec8219b1944330607c86d1ee3e4a8a Mon Sep 17 00:00:00 2001 From: Whindmar Saksit Date: Sat, 3 May 2025 16:08:58 +0200 Subject: [PATCH] [SHELL32][NTUSER] Implement the SEE_MASK_HOTKEY/ICON/HMONITOR flags (#7947) --- dll/win32/shell32/CShellLink.cpp | 8 +++++ dll/win32/shell32/shlexec.cpp | 28 ++++++++++++++++-- win32ss/user/ntuser/hotkey.c | 50 ++++++++++++++++++++++---------- win32ss/user/ntuser/hotkey.h | 2 +- win32ss/user/ntuser/main.c | 28 ++++++++++++++++++ win32ss/user/ntuser/window.c | 20 +++++++++++-- 6 files changed, 114 insertions(+), 22 deletions(-) diff --git a/dll/win32/shell32/CShellLink.cpp b/dll/win32/shell32/CShellLink.cpp index 5fbcc3243d0..bba214cd70e 100644 --- a/dll/win32/shell32/CShellLink.cpp +++ b/dll/win32/shell32/CShellLink.cpp @@ -2687,6 +2687,14 @@ HRESULT CShellLink::DoOpen(LPCMINVOKECOMMANDINFO lpici) else if (pszDirA && SHAnsiToUnicode(pszDirA, dir, _countof(dir))) sei.lpDirectory = dir; } + + sei.dwHotKey = lpici->dwHotKey; + sei.fMask |= CmicFlagsToSeeFlags(lpici->fMask & CMIC_MASK_HOTKEY); + if (m_Header.wHotKey) + { + sei.dwHotKey = m_Header.wHotKey; + sei.fMask |= SEE_MASK_HOTKEY; + } return (ShellExecuteExW(&sei) ? S_OK : E_FAIL); } diff --git a/dll/win32/shell32/shlexec.cpp b/dll/win32/shell32/shlexec.cpp index 8fa694faf49..91c89c10bc2 100644 --- a/dll/win32/shell32/shlexec.cpp +++ b/dll/win32/shell32/shlexec.cpp @@ -27,6 +27,9 @@ WINE_DEFAULT_DEBUG_CHANNEL(exec); EXTERN_C BOOL PathIsExeW(LPCWSTR lpszPath); +#ifndef STARTF_SHELLPRIVATE +#define STARTF_SHELLPRIVATE 0x400 // From kernel32.h +#endif #define SEE_MASK_CLASSALL (SEE_MASK_CLASSNAME | SEE_MASK_CLASSKEY) typedef UINT_PTR (*SHELL_ExecuteW32)(const WCHAR *lpCmd, WCHAR *env, BOOL shWait, @@ -512,6 +515,27 @@ static UINT_PTR SHELL_ExecuteW(const WCHAR *lpCmd, WCHAR *env, BOOL shWait, if (psei->fMask & SEE_MASK_HASLINKNAME) startup.dwFlags |= STARTF_TITLEISLINKNAME; + if (psei->fMask & SEE_MASK_HOTKEY) + { + startup.hStdInput = UlongToHandle(psei->dwHotKey); + startup.dwFlags |= STARTF_USEHOTKEY; + } + + if (psei->fMask & SEE_MASK_ICON) // hIcon has higher precedence than hMonitor + { + startup.hStdOutput = psei->hIcon; + startup.dwFlags |= STARTF_SHELLPRIVATE; + } + else if ((psei->fMask & SEE_MASK_HMONITOR) || psei->hwnd) + { + if (psei->fMask & SEE_MASK_HMONITOR) + startup.hStdOutput = psei->hMonitor; + else if (psei->hwnd) + startup.hStdOutput = MonitorFromWindow(psei->hwnd, MONITOR_DEFAULTTONEAREST); + if (startup.hStdOutput) + startup.dwFlags |= STARTF_SHELLPRIVATE; + } + if (CreateProcessW(NULL, (LPWSTR)lpCmd, NULL, NULL, FALSE, dwCreationFlags, env, lpDirectory, &startup, &info)) { @@ -1942,9 +1966,7 @@ static WCHAR *expand_environment( const WCHAR *str ) static BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc) { static const DWORD unsupportedFlags = - SEE_MASK_ICON | SEE_MASK_HOTKEY | - SEE_MASK_CONNECTNETDRV | SEE_MASK_FLAG_DDEWAIT | - SEE_MASK_ASYNCOK | SEE_MASK_HMONITOR; + SEE_MASK_CONNECTNETDRV | SEE_MASK_FLAG_DDEWAIT | SEE_MASK_ASYNCOK; DWORD len; UINT_PTR retval = SE_ERR_NOASSOC; diff --git a/win32ss/user/ntuser/hotkey.c b/win32ss/user/ntuser/hotkey.c index 9a9e41869d4..54d0a923f4b 100644 --- a/win32ss/user/ntuser/hotkey.c +++ b/win32ss/user/ntuser/hotkey.c @@ -40,6 +40,8 @@ UINT gfsModOnlyCandidate; /* FUNCTIONS *****************************************************************/ +#define IsWindowHotKey(pHK) ( (pHK)->pti == NULL && (pHK)->id == IDHK_WNDKEY ) + VOID FASTCALL StartDebugHotKeys(VOID) { @@ -82,6 +84,18 @@ IntGetModifiers(PBYTE pKeyState) return fModifiers; } +/* + * IntSwapModHKF + * + * Maps to/from MOD_/HOTKEYF_ (swaps the SHIFT and ALT bits) + */ +static inline +UCHAR +IntSwapModHKF(UINT Input) +{ + return (Input & 2) | ((Input & 1) << 2) | ((Input >> 2) & 1); +} + /* * UnregisterWindowHotKeys * @@ -290,6 +304,17 @@ co_UserProcessHotKeys(WORD wVk, BOOL bIsDown) UserPostMessage(UserHMGetHandle(pWnd), WM_SYSCOMMAND, SC_TASKLIST, 0); co_IntShellHookNotify(HSHELL_TASKMAN, 0, 0); } + else if (IsWindowHotKey(pHotKey)) + { + /* WM_SETHOTKEY notifies with WM_SYSCOMMAND, not WM_HOTKEY */ + if (bIsDown) + { + if (gpqForeground && gpqForeground->spwndActive) + pWnd = gpqForeground->spwndActive; + UserPostMessage(UserHMGetHandle(pWnd), WM_SYSCOMMAND, + SC_HOTKEY, (LPARAM)UserHMGetHandle(pHotKey->pWnd)); + } + } else { TRACE("UPM Hot key Id %d Key %u\n", pHotKey->id, wVk ); @@ -318,10 +343,10 @@ DefWndGetHotKey(PWND pWnd) while (pHotKey) { - if (pHotKey->pWnd == pWnd && pHotKey->id == IDHK_REACTOS) + if (pHotKey->pWnd == pWnd && IsWindowHotKey(pHotKey)) { /* We have found it */ - return MAKELONG(pHotKey->vk, pHotKey->fsModifiers); + return MAKEWORD(pHotKey->vk, IntSwapModHKF(pHotKey->fsModifiers)); } /* Move to the next entry */ @@ -339,7 +364,8 @@ DefWndGetHotKey(PWND pWnd) INT FASTCALL DefWndSetHotKey(PWND pWnd, WPARAM wParam) { - UINT fsModifiers, vk; + const UINT fsModifiers = IntSwapModHKF(HIBYTE(wParam)); + const UINT vk = LOBYTE(wParam); PHOT_KEY pHotKey, *pLink; INT iRet = 1; @@ -349,17 +375,12 @@ DefWndSetHotKey(PWND pWnd, WPARAM wParam) if (pWnd->style & WS_CHILD) return 0; - // VK_ESCAPE, VK_SPACE, and VK_TAB are invalid hot keys. - if (LOWORD(wParam) == VK_ESCAPE || - LOWORD(wParam) == VK_SPACE || - LOWORD(wParam) == VK_TAB) + // VK_ESCAPE, VK_SPACE, VK_TAB and VK_PACKET are invalid hot keys. + if (vk == VK_ESCAPE || vk == VK_SPACE || vk == VK_TAB || vk == VK_PACKET) { return -1; } - vk = LOWORD(wParam); - fsModifiers = HIWORD(wParam); - if (wParam) { pHotKey = gphkFirst; @@ -367,7 +388,7 @@ DefWndSetHotKey(PWND pWnd, WPARAM wParam) { if (pHotKey->fsModifiers == fsModifiers && pHotKey->vk == vk && - pHotKey->id == IDHK_REACTOS) + IsWindowHotKey(pHotKey)) { if (pHotKey->pWnd != pWnd) iRet = 2; // Another window already has the same hot key. @@ -383,8 +404,7 @@ DefWndSetHotKey(PWND pWnd, WPARAM wParam) pLink = &gphkFirst; while (pHotKey) { - if (pHotKey->pWnd == pWnd && - pHotKey->id == IDHK_REACTOS) + if (pHotKey->pWnd == pWnd && IsWindowHotKey(pHotKey)) { /* This window has already hotkey registered */ break; @@ -405,14 +425,14 @@ DefWndSetHotKey(PWND pWnd, WPARAM wParam) return 0; pHotKey->pWnd = pWnd; - pHotKey->id = IDHK_REACTOS; // Don't care, these hot keys are unrelated to the hot keys set by RegisterHotKey + pHotKey->id = IDHK_WNDKEY; // Don't care, these hot keys are unrelated to the hot keys set by RegisterHotKey pHotKey->pNext = gphkFirst; gphkFirst = pHotKey; } /* A window can only have one hot key. If the window already has a hot key associated with it, the new hot key replaces the old one. */ - pHotKey->pti = NULL; + pHotKey->pti = NULL; /* IsWindowHotKey */ pHotKey->fsModifiers = fsModifiers; pHotKey->vk = vk; } diff --git a/win32ss/user/ntuser/hotkey.h b/win32ss/user/ntuser/hotkey.h index e86452204c6..cc9da64f5ee 100644 --- a/win32ss/user/ntuser/hotkey.h +++ b/win32ss/user/ntuser/hotkey.h @@ -14,7 +14,7 @@ typedef struct _HOT_KEY #define IDHK_F12 -5 #define IDHK_SHIFTF12 -6 #define IDHK_WINKEY -7 -#define IDHK_REACTOS -8 +#define IDHK_WNDKEY -8 /* WM_SETHOTKEY */ /* Window Snap Hot Keys */ #define IDHK_SNAP_LEFT -10 diff --git a/win32ss/user/ntuser/main.c b/win32ss/user/ntuser/main.c index 1e817ddc1c7..675008be13a 100644 --- a/win32ss/user/ntuser/main.c +++ b/win32ss/user/ntuser/main.c @@ -13,6 +13,13 @@ #include #include +#ifndef STARTF_USEHOTKEY +#define STARTF_USEHOTKEY 0x0200 +#endif +#ifndef STARTF_SHELLPRIVATE +#define STARTF_SHELLPRIVATE 0x0400 +#endif + HANDLE hModuleWin; NTSTATUS ExitProcessCallback(PEPROCESS Process); @@ -462,6 +469,7 @@ InitThreadCallback(PETHREAD Thread) PTEB pTeb; PRTL_USER_PROCESS_PARAMETERS ProcessParams; PKL pDefKL; + BOOLEAN bFirstThread; Process = Thread->ThreadsProcess; @@ -486,6 +494,7 @@ InitThreadCallback(PETHREAD Thread) pTeb->Win32ThreadInfo = ptiCurrent; ptiCurrent->pClientInfo = (PCLIENTINFO)pTeb->Win32ClientInfo; ptiCurrent->pcti = &ptiCurrent->cti; + bFirstThread = !(ptiCurrent->ppi->W32PF_flags & W32PF_THREADCONNECTED); /* Mark the process as having threads */ ptiCurrent->ppi->W32PF_flags |= W32PF_THREADCONNECTED; @@ -578,6 +587,25 @@ InitThreadCallback(PETHREAD Thread) ptiCurrent->ppi->usi.wShowWindow = (WORD)ProcessParams->ShowWindowFlags; } } + + if (bFirstThread) + { + /* Note: Only initialize once so it can be set back to 0 after being used */ + if (ProcessParams->WindowFlags & STARTF_USEHOTKEY) + ptiCurrent->ppi->dwHotkey = HandleToUlong(ProcessParams->StandardInput); + /* TODO: + else if (ProcessParams->ShellInfo.Buffer) + ..->dwHotkey = ParseShellInfo(ProcessParams->ShellInfo.Buffer, L"hotkey."); + */ + + if (ProcessParams->WindowFlags & STARTF_SHELLPRIVATE) + { + /* We need to validate this handle because it can also be a HICON */ + HMONITOR hMonitor = (HMONITOR)ProcessParams->StandardOutput; + if (hMonitor && UserGetMonitorObject(hMonitor)) + ptiCurrent->ppi->hMonitor = hMonitor; + } + } } /* diff --git a/win32ss/user/ntuser/window.c b/win32ss/user/ntuser/window.c index 9f8d669e3e8..fe9c8fc2a61 100644 --- a/win32ss/user/ntuser/window.c +++ b/win32ss/user/ntuser/window.c @@ -1718,13 +1718,17 @@ IntFixWindowCoordinates(CREATESTRUCTW* Cs, PWND ParentWindow, DWORD* dwShowMode) /* default positioning for overlapped windows */ if(!(Cs->style & (WS_POPUP | WS_CHILD))) { - PMONITOR pMonitor; + PMONITOR pMonitor = NULL; PRTL_USER_PROCESS_PARAMETERS ProcessParams; + PPROCESSINFO ppi = PsGetCurrentProcessWin32Process(); - pMonitor = UserGetPrimaryMonitor(); + if (ppi && ppi->hMonitor) + pMonitor = UserGetMonitorObject(ppi->hMonitor); + if (!pMonitor) + pMonitor = UserGetPrimaryMonitor(); /* Check if we don't have a monitor attached yet */ - if(pMonitor == NULL) + if (pMonitor == NULL) { Cs->x = Cs->y = 0; Cs->cx = 800; @@ -2575,6 +2579,16 @@ co_UserCreateWindowEx(CREATESTRUCTW* Cs, co_IntUserManualGuiCheck(TRUE); } + /* Set the hotkey */ + if (!(Window->style & (WS_POPUP | WS_CHILD)) || (Window->ExStyle & WS_EX_APPWINDOW)) + { + if (pti->ppi->dwHotkey) + { + co_IntSendMessage(UserHMGetHandle(Window), WM_SETHOTKEY, pti->ppi->dwHotkey, 0); + pti->ppi->dwHotkey = 0; /* Only the first suitable window gets the hotkey */ + } + } + TRACE("co_UserCreateWindowEx(%wZ): Created window %p\n", ClassName, hWnd); ret = Window;