From f19c62c80ea2fa5990ed9f0cf1688c5a3d3f39fc Mon Sep 17 00:00:00 2001 From: Katayama Hirofumi MZ Date: Mon, 28 Apr 2025 19:08:13 +0900 Subject: [PATCH] [EXPLORER][SHELL32][SHELL32_APITEST][SDK] AppBar: Initial support (#7778) Supporting AppBars. JIRA issue: CORE-7237 - Rewrite shell32!SHAppBarMessage function. - Introduce CAppBarManager class in base/shell/explorer/appbar.cpp. - Add support of ABM_NEW, ABM_REMOVE, ABM_QUERYPOS, and ABM_SETPOS messages for AppBar in Tray Window. --- base/shell/explorer/appbar.cpp | 688 +++++++++++------- base/shell/explorer/appbar.h | 96 +++ base/shell/explorer/traywnd.cpp | 70 +- dll/win32/shell32/CMakeLists.txt | 2 +- dll/win32/shell32/appbar.c | 129 ++++ dll/win32/shell32/wine/appbar.c | 168 ----- .../apitests/shell32/SHAppBarMessage.cpp | 61 +- sdk/include/psdk/shellapi.h | 7 +- sdk/include/reactos/undocshell.h | 54 +- 9 files changed, 780 insertions(+), 495 deletions(-) create mode 100644 base/shell/explorer/appbar.h create mode 100644 dll/win32/shell32/appbar.c delete mode 100644 dll/win32/shell32/wine/appbar.c diff --git a/base/shell/explorer/appbar.cpp b/base/shell/explorer/appbar.cpp index 831231bb190..964bd60ec2b 100644 --- a/base/shell/explorer/appbar.cpp +++ b/base/shell/explorer/appbar.cpp @@ -1,290 +1,470 @@ /* - * SHAppBarMessage implementation - * - * Copyright 2008 Vincent Povirk for CodeWeavers - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - * - * TODO: freedesktop _NET_WM_STRUT integration - * - * TODO: find when a fullscreen app is in the foreground and send FULLSCREENAPP - * notifications - * - * TODO: detect changes in the screen size and send ABN_POSCHANGED ? - * - * TODO: multiple monitor support + * PROJECT: ReactOS Explorer + * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) + * PURPOSE: AppBar implementation + * COPYRIGHT: Copyright 2008 Vincent Povirk for CodeWeavers + * Copyright 2025 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) */ -// -// Adapted from Wine appbar.c . -// - #include "precomp.h" +#include "appbar.h" -#include - -#define GetPrimaryTaskbar() FindWindowW(L"Shell_TrayWnd", NULL) - -struct appbar_cmd +CAppBarManager::CAppBarManager() + : m_hAppBarDPA(NULL) { - DWORD dwMsg; - ULONG return_map; - DWORD return_process; - struct _AppBarData abd; -}; +} -struct appbar_response +CAppBarManager::~CAppBarManager() { - ULONGLONG result; - struct _AppBarData abd; -}; + DestroyAppBarDPA(); +} -struct appbar_data +PAPPBAR CAppBarManager::FindAppBar(_In_ HWND hwndAppBar) const { - struct list entry; - HWND hwnd; - UINT callback_msg; - UINT edge; - RECT rc; - BOOL space_reserved; - /* BOOL autohide; */ -}; + if (!m_hAppBarDPA) + return NULL; -static struct list appbars = LIST_INIT(appbars); - -static struct appbar_data* get_appbar(HWND hwnd) -{ - struct appbar_data* data; - - LIST_FOR_EACH_ENTRY(data, &appbars, struct appbar_data, entry) + INT nItems = DPA_GetPtrCount(m_hAppBarDPA); + while (--nItems >= 0) { - if (data->hwnd == hwnd) - return data; + PAPPBAR pAppBar = (PAPPBAR)DPA_GetPtr(m_hAppBarDPA, nItems); + if (pAppBar && hwndAppBar == pAppBar->hWnd) + return pAppBar; } return NULL; } -void appbar_notify_all(HMONITOR hMon, UINT uMsg, HWND hwndExclude, LPARAM lParam) +void CAppBarManager::EliminateAppBar(_In_ INT iItem) { - struct appbar_data* data; + LocalFree(DPA_GetPtr(m_hAppBarDPA, iItem)); + DPA_DeletePtr(m_hAppBarDPA, iItem); +} - LIST_FOR_EACH_ENTRY(data, &appbars, struct appbar_data, entry) +void CAppBarManager::DestroyAppBarDPA() +{ + if (!m_hAppBarDPA) + return; + + INT nItems = DPA_GetPtrCount(m_hAppBarDPA); + while (--nItems >= 0) { - if (data->hwnd == hwndExclude) - continue; - - if (hMon && hMon != MonitorFromWindow(data->hwnd, MONITOR_DEFAULTTONULL)) - continue; - - SendMessageW(data->hwnd, data->callback_msg, uMsg, lParam); + ::LocalFree(DPA_GetPtr(m_hAppBarDPA, nItems)); } + + DPA_Destroy(m_hAppBarDPA); + m_hAppBarDPA = NULL; } -/* send_poschanged: send ABN_POSCHANGED to every appbar except one */ -static void send_poschanged(HWND hwnd) +// ABM_NEW +BOOL CAppBarManager::OnAppBarNew(_In_ const APPBAR_COMMAND *pData) { - appbar_notify_all(NULL, ABN_POSCHANGED, hwnd, 0); -} + HWND hWnd = (HWND)UlongToHandle(pData->abd.hWnd32); -/* appbar_cliprect: cut out parts of the rectangle that interfere with existing appbars */ -static void appbar_cliprect( HWND hwnd, RECT *rect ) -{ - struct appbar_data* data; - LIST_FOR_EACH_ENTRY(data, &appbars, struct appbar_data, entry) + if (m_hAppBarDPA) { - if (data->hwnd == hwnd) + if (FindAppBar(hWnd)) { - /* we only care about appbars that were added before this one */ - return; - } - if (data->space_reserved) - { - /* move in the side that corresponds to the other appbar's edge */ - switch (data->edge) - { - case ABE_BOTTOM: - rect->bottom = min(rect->bottom, data->rc.top); - break; - case ABE_LEFT: - rect->left = max(rect->left, data->rc.right); - break; - case ABE_RIGHT: - rect->right = min(rect->right, data->rc.left); - break; - case ABE_TOP: - rect->top = max(rect->top, data->rc.bottom); - break; - } - } - } -} - -static UINT_PTR handle_appbarmessage(DWORD msg, _AppBarData *abd) -{ - struct appbar_data* data; - HWND hwnd = abd->hWnd; - - switch (msg) - { - case ABM_NEW: - if (get_appbar(hwnd)) - { - /* fail when adding an hwnd the second time */ + ERR("Already exists: %p\n", hWnd); return FALSE; } - - data = (struct appbar_data*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct appbar_data)); - if (!data) - { - ERR("out of memory\n"); - return FALSE; - } - data->hwnd = hwnd; - data->callback_msg = abd->uCallbackMessage; - - list_add_tail(&appbars, &data->entry); - - return TRUE; - case ABM_REMOVE: - if ((data = get_appbar(hwnd))) - { - list_remove(&data->entry); - - send_poschanged(hwnd); - - HeapFree(GetProcessHeap(), 0, data); - } - else - WARN("removing hwnd %p not on the list\n", hwnd); - return TRUE; - case ABM_QUERYPOS: - if (abd->uEdge > ABE_BOTTOM) - WARN("invalid edge %i for %p\n", abd->uEdge, hwnd); - appbar_cliprect( hwnd, &abd->rc ); - return TRUE; - case ABM_SETPOS: - if (abd->uEdge > ABE_BOTTOM) - { - WARN("invalid edge %i for %p\n", abd->uEdge, hwnd); - return TRUE; - } - if ((data = get_appbar(hwnd))) - { - /* calculate acceptable space */ - appbar_cliprect( hwnd, &abd->rc ); - - if (!EqualRect(&abd->rc, &data->rc)) - send_poschanged(hwnd); - - /* reserve that space for this appbar */ - data->edge = abd->uEdge; - data->rc = abd->rc; - data->space_reserved = TRUE; - } - else - { - WARN("app sent ABM_SETPOS message for %p without ABM_ADD\n", hwnd); - } - return TRUE; - case ABM_GETSTATE: - TRACE("SHAppBarMessage(ABM_GETSTATE)\n"); - return (g_TaskbarSettings.sr.AutoHide ? ABS_AUTOHIDE : 0) | - (g_TaskbarSettings.sr.AlwaysOnTop ? ABS_ALWAYSONTOP : 0); - case ABM_SETSTATE: - TRACE("SHAppBarMessage(ABM_SETSTATE lparam=%s)\n", wine_dbgstr_longlong(abd->lParam)); - hwnd = GetPrimaryTaskbar(); - if (hwnd) - { - TaskbarSettings settings = g_TaskbarSettings; - settings.sr.AutoHide = (abd->lParam & ABS_AUTOHIDE) != 0; - settings.sr.AlwaysOnTop = (abd->lParam & ABS_ALWAYSONTOP) != 0; - SendMessageW(hwnd, TWM_SETTINGSCHANGED, 0, (LPARAM)&settings); - return TRUE; - } - return FALSE; - case ABM_GETTASKBARPOS: - TRACE("SHAppBarMessage(ABM_GETTASKBARPOS, hwnd=%p)\n", hwnd); - abd->uEdge = g_TaskbarSettings.sr.Position; - abd->hWnd = GetPrimaryTaskbar(); - return abd->hWnd && GetWindowRect(abd->hWnd, &abd->rc); - case ABM_ACTIVATE: - return TRUE; - case ABM_GETAUTOHIDEBAR: - FIXME("SHAppBarMessage(ABM_GETAUTOHIDEBAR, hwnd=%p, edge=%x): stub\n", hwnd, abd->uEdge); - if (abd->uEdge == g_TaskbarSettings.sr.Position && g_TaskbarSettings.sr.AutoHide) - return (SIZE_T)GetPrimaryTaskbar(); - return NULL; - case ABM_SETAUTOHIDEBAR: - FIXME("SHAppBarMessage(ABM_SETAUTOHIDEBAR, hwnd=%p, edge=%x, lparam=%s): stub\n", - hwnd, abd->uEdge, wine_dbgstr_longlong(abd->lParam)); - return TRUE; - case ABM_WINDOWPOSCHANGED: - return TRUE; - default: - FIXME("SHAppBarMessage(%x) unimplemented\n", msg); - return FALSE; - } -} - -LRESULT appbar_message ( COPYDATASTRUCT* cds ) -{ - struct appbar_cmd cmd; - UINT_PTR result; - HANDLE return_hproc; - HANDLE return_map; - LPVOID return_view; - struct appbar_response* response; - - if (cds->cbData != sizeof(struct appbar_cmd)) - return TRUE; - - RtlCopyMemory(&cmd, cds->lpData, cds->cbData); - - result = handle_appbarmessage(cmd.dwMsg, &cmd.abd); - - return_hproc = OpenProcess(PROCESS_DUP_HANDLE, FALSE, cmd.return_process); - if (return_hproc == NULL) - { - ERR("couldn't open calling process\n"); - return TRUE; - } - - if (!DuplicateHandle(return_hproc, UlongToHandle(cmd.return_map), GetCurrentProcess(), &return_map, 0, FALSE, DUPLICATE_SAME_ACCESS)) - { - ERR("couldn't duplicate handle\n"); - CloseHandle(return_hproc); - return TRUE; - } - CloseHandle(return_hproc); - - return_view = MapViewOfFile(return_map, FILE_MAP_WRITE, 0, 0, sizeof(struct appbar_response)); - - if (return_view) - { - response = (struct appbar_response*)return_view; - response->result = result; - response->abd = cmd.abd; - - UnmapViewOfFile(return_view); } else { - ERR("couldn't map view of file\n"); + const UINT c_nGrow = 4; + m_hAppBarDPA = DPA_Create(c_nGrow); + if (!m_hAppBarDPA) + { + ERR("Out of memory\n"); + return FALSE; + } } - CloseHandle(return_map); + PAPPBAR pAppBar = (PAPPBAR)::LocalAlloc(LPTR, sizeof(*pAppBar)); + if (pAppBar) + { + pAppBar->hWnd = hWnd; + pAppBar->uEdge = UINT_MAX; + pAppBar->uCallbackMessage = pData->abd.uCallbackMessage; + if (DPA_InsertPtr(m_hAppBarDPA, INT_MAX, pAppBar) >= 0) + return TRUE; // Success! + + ::LocalFree(pAppBar); + } + + ERR("Out of memory\n"); + return FALSE; +} + +// ABM_REMOVE +void CAppBarManager::OnAppBarRemove(_In_ const APPBAR_COMMAND *pData) +{ + if (!m_hAppBarDPA) + return; + + HWND hWnd = (HWND)UlongToHandle(pData->abd.hWnd32); + INT nItems = DPA_GetPtrCount(m_hAppBarDPA); + while (--nItems >= 0) + { + PAPPBAR pAppBar = (PAPPBAR)DPA_GetPtr(m_hAppBarDPA, nItems); + if (!pAppBar) + continue; + + if (pAppBar->hWnd == hWnd) + { + RECT rcOld = pAppBar->rc; + EliminateAppBar(nItems); + StuckAppChange(hWnd, &rcOld, NULL, FALSE); + } + } +} + +// ABM_QUERYPOS +void CAppBarManager::OnAppBarQueryPos(_Inout_ PAPPBAR_COMMAND pData) +{ + HWND hWnd = (HWND)UlongToHandle(pData->abd.hWnd32); + PAPPBAR pAppBar1 = FindAppBar(hWnd); + if (!pAppBar1) + { + ERR("Not found: %p\n", hWnd); + return; + } + + PAPPBARDATA3264 pOutput = AppBar_LockOutput(pData); + if (!pOutput) + { + ERR("!pOutput: %d\n", pData->dwProcessId); + return; + } + pOutput->rc = pData->abd.rc; + + if (::IsRectEmpty(&pOutput->rc)) + ERR("IsRectEmpty\n"); + + HMONITOR hMon1 = ::MonitorFromRect(&pOutput->rc, MONITOR_DEFAULTTOPRIMARY); + ASSERT(hMon1 != NULL); + + // Subtract tray rectangle from pOutput->rc if necessary + if (hMon1 == GetMonitor() && !IsAutoHideState()) + { + APPBAR dummyAppBar; + dummyAppBar.uEdge = GetPosition(); + GetDockedRect(&dummyAppBar.rc); + AppBarSubtractRect(&dummyAppBar, &pOutput->rc); + } + + // Subtract area from pOutput->rc + UINT uEdge = pData->abd.uEdge; + INT nItems = DPA_GetPtrCount(m_hAppBarDPA); + while (--nItems >= 0) + { + PAPPBAR pAppBar2 = (PAPPBAR)DPA_GetPtr(m_hAppBarDPA, nItems); + if (!pAppBar2 || pAppBar1->hWnd == pAppBar2->hWnd) + continue; + + if ((Edge_IsVertical(uEdge) || !Edge_IsVertical(pAppBar2->uEdge)) && + (pAppBar1->uEdge != uEdge || !AppBarOutsideOf(pAppBar1, pAppBar2))) + { + if (pAppBar1->uEdge == uEdge || pAppBar2->uEdge != uEdge) + continue; + } + + HMONITOR hMon2 = ::MonitorFromRect(&pAppBar2->rc, MONITOR_DEFAULTTONULL); + if (hMon1 == hMon2) + AppBarSubtractRect(pAppBar2, &pOutput->rc); + } + + AppBar_UnLockOutput(pOutput); +} + +// ABM_SETPOS +void CAppBarManager::OnAppBarSetPos(_Inout_ PAPPBAR_COMMAND pData) +{ + HWND hWnd = (HWND)UlongToHandle(pData->abd.hWnd32); + PAPPBAR pAppBar = FindAppBar(hWnd); + if (!pAppBar) + return; + + OnAppBarQueryPos(pData); + + PAPPBARDATA3264 pOutput = AppBar_LockOutput(pData); + if (!pOutput) + return; + + RECT rcOld = pAppBar->rc, rcNew = pData->abd.rc; + BOOL bChanged = !::EqualRect(&rcOld, &rcNew); + + pAppBar->rc = rcNew; + pAppBar->uEdge = pData->abd.uEdge; + + AppBar_UnLockOutput(pOutput); + + if (bChanged) + StuckAppChange(hWnd, &rcOld, &rcNew, FALSE); +} + +void CAppBarManager::OnAppBarNotifyAll( + _In_opt_ HMONITOR hMon, + _In_opt_ HWND hwndIgnore, + _In_ DWORD dwNotify, + _In_opt_ LPARAM lParam) +{ + TRACE("%p, %p, 0x%X, %p\n", hMon, hwndIgnore, dwNotify, lParam); + + if (!m_hAppBarDPA) + return; + + INT nItems = DPA_GetPtrCount(m_hAppBarDPA); + while (--nItems >= 0) + { + PAPPBAR pAppBar = (PAPPBAR)DPA_GetPtr(m_hAppBarDPA, nItems); + if (!pAppBar || pAppBar->hWnd == hwndIgnore) + continue; + + HWND hwndAppBar = pAppBar->hWnd; + if (!::IsWindow(hwndAppBar)) + { + EliminateAppBar(nItems); + continue; + } + + if (!hMon || hMon == ::MonitorFromWindow(hwndAppBar, MONITOR_DEFAULTTONULL)) + ::PostMessageW(hwndAppBar, pAppBar->uCallbackMessage, dwNotify, lParam); + } +} + +/// @param pAppBar The target AppBar to subtract. +/// @param prc The rectangle to be subtracted. +void CAppBarManager::AppBarSubtractRect(_In_ PAPPBAR pAppBar, _Inout_ PRECT prc) +{ + switch (pAppBar->uEdge) + { + case ABE_LEFT: prc->left = max(prc->left, pAppBar->rc.right); break; + case ABE_TOP: prc->top = max(prc->top, pAppBar->rc.bottom); break; + case ABE_RIGHT: prc->right = min(prc->right, pAppBar->rc.left); break; + case ABE_BOTTOM: prc->bottom = min(prc->bottom, pAppBar->rc.top); break; + default: + ASSERT(FALSE); + break; + } +} + +// Is pAppBar1 outside of pAppBar2? +BOOL CAppBarManager::AppBarOutsideOf( + _In_ const APPBAR *pAppBar1, + _In_ const APPBAR *pAppBar2) +{ + if (pAppBar1->uEdge != pAppBar2->uEdge) + return FALSE; + + switch (pAppBar2->uEdge) + { + case ABE_LEFT: return pAppBar1->rc.left >= pAppBar2->rc.left; + case ABE_TOP: return pAppBar1->rc.top >= pAppBar2->rc.top; + case ABE_RIGHT: return pAppBar1->rc.right <= pAppBar2->rc.right; + case ABE_BOTTOM: return pAppBar1->rc.bottom <= pAppBar2->rc.bottom; + default: + ASSERT(FALSE); + return FALSE; + } +} + +/// Get rectangle of the tray window. +/// @param prcDocked The pointer to the rectangle to be received. +void CAppBarManager::GetDockedRect(_Out_ PRECT prcDocked) +{ + *prcDocked = *GetTrayRect(); + if (IsAutoHideState() && IsHidingState()) + ComputeHiddenRect(prcDocked, GetPosition()); +} + +/// Compute the position and size of the hidden TaskBar. +/// @param prc The rectangle before hiding TaskBar. +/// @param uSide The side of TaskBar (ABE_...). +void CAppBarManager::ComputeHiddenRect(_Inout_ PRECT prc, _In_ UINT uSide) +{ + MONITORINFO mi = { sizeof(mi) }; + HMONITOR hMonitor = ::MonitorFromRect(prc, MONITOR_DEFAULTTONULL); + if (!::GetMonitorInfoW(hMonitor, &mi)) + return; + RECT rcMon = mi.rcMonitor; + + INT cxy = Edge_IsVertical(uSide) ? (prc->bottom - prc->top) : (prc->right - prc->left); + switch (uSide) + { + case ABE_LEFT: + prc->right = rcMon.left + GetSystemMetrics(SM_CXFRAME) / 2; + prc->left = prc->right - cxy; + break; + case ABE_TOP: + prc->bottom = rcMon.top + GetSystemMetrics(SM_CYFRAME) / 2; + prc->top = prc->bottom - cxy; + break; + case ABE_RIGHT: + prc->left = rcMon.right - GetSystemMetrics(SM_CXFRAME) / 2; + prc->right = prc->left + cxy; + break; + case ABE_BOTTOM: + prc->top = rcMon.bottom - GetSystemMetrics(SM_CYFRAME) / 2; + prc->bottom = prc->top + cxy; + break; + default: + ASSERT(FALSE); + break; + } +} + +/// This function is called when AppBar and/or TaskBar is being moved, removed, and/or updated. +/// @param hwndTarget The target window. Optional. +/// @param prcOld The old position and size. Optional. +/// @param prcNew The new position and size. Optional. +/// @param bTray TRUE if the tray is being moved. +void +CAppBarManager::StuckAppChange( + _In_opt_ HWND hwndTarget, + _In_opt_ const RECT *prcOld, + _In_opt_ const RECT *prcNew, + _In_ BOOL bTray) +{ + RECT rcWorkArea1, rcWorkArea2; + HMONITOR hMon1 = NULL; + UINT flags = 0; + enum { SET_WORKAREA_1 = 1, SET_WORKAREA_2 = 2, NEED_SIZING = 4 }; // for flags + + if (prcOld) + { + hMon1 = (bTray ? GetPreviousMonitor() : ::MonitorFromRect(prcOld, MONITOR_DEFAULTTONEAREST)); + if (hMon1) + { + WORKAREA_TYPE type1 = RecomputeWorkArea(GetTrayRect(), hMon1, &rcWorkArea1); + if (type1 == WORKAREA_IS_NOT_MONITOR) + flags = SET_WORKAREA_1; + if (type1 == WORKAREA_SAME_AS_MONITOR) + flags = NEED_SIZING; + } + } + + if (prcNew) + { + HMONITOR hMon2 = ::MonitorFromRect(prcNew, MONITOR_DEFAULTTONULL); + if (hMon2 && hMon2 != hMon1) + { + WORKAREA_TYPE type2 = RecomputeWorkArea(GetTrayRect(), hMon2, &rcWorkArea2); + if (type2 == WORKAREA_IS_NOT_MONITOR) + flags |= SET_WORKAREA_2; + else if (type2 == WORKAREA_SAME_AS_MONITOR && !flags) + flags = NEED_SIZING; + } + } + + if (flags & SET_WORKAREA_1) + { + UINT fWinIni = ((flags == SET_WORKAREA_1 && GetDesktopWnd()) ? SPIF_SENDCHANGE : 0); + ::SystemParametersInfoW(SPI_SETWORKAREA, TRUE, &rcWorkArea1, fWinIni); + RedrawDesktop(GetDesktopWnd(), &rcWorkArea1); + } + + if (flags & SET_WORKAREA_2) + { + UINT fWinIni = (GetDesktopWnd() ? SPIF_SENDCHANGE : 0); + ::SystemParametersInfoW(SPI_SETWORKAREA, TRUE, &rcWorkArea2, fWinIni); + RedrawDesktop(GetDesktopWnd(), &rcWorkArea2); + } + + if (bTray || flags == NEED_SIZING) + ::SendMessageW(GetDesktopWnd(), WM_SIZE, 0, 0); + + // Post ABN_POSCHANGED messages to AppBar windows + OnAppBarNotifyAll(NULL, hwndTarget, ABN_POSCHANGED, TRUE); +} + +void CAppBarManager::RedrawDesktop(_In_ HWND hwndDesktop, _Inout_ PRECT prc) +{ + if (!hwndDesktop) + return; + ::MapWindowPoints(NULL, hwndDesktop, (POINT*)prc, sizeof(*prc) / sizeof(POINT)); + ::RedrawWindow(hwndDesktop, prc, 0, RDW_ALLCHILDREN | RDW_ERASE | RDW_INVALIDATE); +} + +/// Re-compute the work area. +/// @param prcTray The position and size of the tray window +/// @param hMonitor The monitor of the work area to re-compute. +/// @param prcWorkArea The work area to be re-computed. +WORKAREA_TYPE +CAppBarManager::RecomputeWorkArea( + _In_ const RECT *prcTray, + _In_ HMONITOR hMonitor, + _Out_ PRECT prcWorkArea) +{ + MONITORINFO mi = { sizeof(mi) }; + if (!::GetMonitorInfoW(hMonitor, &mi)) + return WORKAREA_NO_TRAY_AREA; + + if (IsAutoHideState()) + *prcWorkArea = mi.rcMonitor; + else + ::SubtractRect(prcWorkArea, &mi.rcMonitor, prcTray); + + if (m_hAppBarDPA) + { + INT nItems = DPA_GetPtrCount(m_hAppBarDPA); + while (--nItems >= 0) + { + PAPPBAR pAppBar = (PAPPBAR)DPA_GetPtr(m_hAppBarDPA, nItems); + if (pAppBar && hMonitor == ::MonitorFromRect(&pAppBar->rc, MONITOR_DEFAULTTONULL)) + AppBarSubtractRect(pAppBar, prcWorkArea); + } + } + + if (!::EqualRect(prcWorkArea, &mi.rcWork)) + return WORKAREA_IS_NOT_MONITOR; + + if (IsAutoHideState() || ::IsRectEmpty(prcTray)) + return WORKAREA_NO_TRAY_AREA; + + return WORKAREA_SAME_AS_MONITOR; +} + +PAPPBAR_COMMAND +CAppBarManager::GetAppBarMessage(_Inout_ PCOPYDATASTRUCT pCopyData) +{ + PAPPBAR_COMMAND pData = (PAPPBAR_COMMAND)pCopyData->lpData; + + if (pCopyData->cbData != sizeof(*pData) || + pData->abd.cbSize != sizeof(pData->abd)) + { + ERR("Invalid AppBar message\n"); + return NULL; + } + + return pData; +} + +// WM_COPYDATA TABDMC_APPBAR +LRESULT CAppBarManager::OnAppBarMessage(_Inout_ PCOPYDATASTRUCT pCopyData) +{ + PAPPBAR_COMMAND pData = GetAppBarMessage(pCopyData); + if (!pData) + return 0; + + switch (pData->dwMessage) + { + case ABM_NEW: + return OnAppBarNew(pData); + case ABM_REMOVE: + OnAppBarRemove(pData); + break; + case ABM_QUERYPOS: + OnAppBarQueryPos(pData); + break; + case ABM_SETPOS: + OnAppBarSetPos(pData); + break; + default: + { + FIXME("0x%X\n", pData->dwMessage); + return FALSE; + } + } return TRUE; } diff --git a/base/shell/explorer/appbar.h b/base/shell/explorer/appbar.h new file mode 100644 index 00000000000..c5b873365de --- /dev/null +++ b/base/shell/explorer/appbar.h @@ -0,0 +1,96 @@ +/* + * PROJECT: ReactOS Explorer + * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) + * PURPOSE: AppBar implementation + * COPYRIGHT: Copyright 2025 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) + */ + +#pragma once + +typedef struct tagAPPBAR +{ + HWND hWnd; + UINT uCallbackMessage; + UINT uEdge; + RECT rc; +} APPBAR, *PAPPBAR; + +static inline PAPPBARDATA3264 +AppBar_LockOutput(_In_ PAPPBAR_COMMAND pData) +{ + return (PAPPBARDATA3264)SHLockShared(UlongToHandle(pData->hOutput32), pData->dwProcessId); +} + +static inline VOID +AppBar_UnLockOutput(_Out_ PAPPBARDATA3264 pOutput) +{ + SHUnlockShared(pOutput); +} + +static inline BOOL +Edge_IsVertical(_In_ UINT uEdge) +{ + return uEdge == ABE_TOP || uEdge == ABE_BOTTOM; +} + +// Return value of CAppBarManager::RecomputeWorkArea +enum WORKAREA_TYPE +{ + WORKAREA_NO_TRAY_AREA = 0, + WORKAREA_IS_NOT_MONITOR = 1, + WORKAREA_SAME_AS_MONITOR = 2, +}; + +class CAppBarManager +{ +public: + CAppBarManager(); + virtual ~CAppBarManager(); + + LRESULT OnAppBarMessage(_Inout_ PCOPYDATASTRUCT pCopyData); + +protected: + HDPA m_hAppBarDPA; // DPA (Dynamic Pointer Array) + + PAPPBAR FindAppBar(_In_ HWND hwndAppBar) const; + void EliminateAppBar(_In_ INT iItem); + void DestroyAppBarDPA(); + void AppBarSubtractRect(_In_ PAPPBAR pAppBar, _Inout_ PRECT prc); + BOOL AppBarOutsideOf(_In_ const APPBAR *pAppBar1, _In_ const APPBAR *pAppBar2); + void ComputeHiddenRect(_Inout_ PRECT prc, _In_ UINT uSide); + PAPPBAR_COMMAND GetAppBarMessage(_Inout_ PCOPYDATASTRUCT pCopyData); + void GetDockedRect(_Out_ PRECT prcDocked); + + BOOL OnAppBarNew(_In_ const APPBAR_COMMAND *pData); + void OnAppBarRemove(_In_ const APPBAR_COMMAND *pData); + void OnAppBarQueryPos(_Inout_ PAPPBAR_COMMAND pData); + void OnAppBarSetPos(_Inout_ PAPPBAR_COMMAND pData); + + void OnAppBarNotifyAll( + _In_opt_ HMONITOR hMon, + _In_opt_ HWND hwndIgnore, + _In_ DWORD dwNotify, + _In_opt_ LPARAM lParam); + + WORKAREA_TYPE + RecomputeWorkArea( + _In_ const RECT *prcTray, + _In_ HMONITOR hMonitor, + _Out_ PRECT prcWorkArea); + + void StuckAppChange( + _In_opt_ HWND hwndTarget, + _In_opt_ const RECT *prcOld, + _In_opt_ const RECT *prcNew, + _In_ BOOL bTray); + + void RedrawDesktop(_In_ HWND hwndDesktop, _Inout_ PRECT prc); + + virtual BOOL IsAutoHideState() const = 0; + virtual BOOL IsHidingState() const = 0; + virtual HMONITOR GetMonitor() const = 0; + virtual HMONITOR GetPreviousMonitor() const = 0; + virtual INT GetPosition() const = 0; + virtual const RECT* GetTrayRect() = 0; + virtual HWND GetDesktopWnd() const = 0; +}; diff --git a/base/shell/explorer/traywnd.cpp b/base/shell/explorer/traywnd.cpp index 9b715b5eca2..85f7ae62816 100644 --- a/base/shell/explorer/traywnd.cpp +++ b/base/shell/explorer/traywnd.cpp @@ -1,30 +1,16 @@ /* - * ReactOS Explorer - * - * Copyright 2006 - 2007 Thomas Weidenmueller - * Copyright 2018-2022 Katayama Hirofumi MZ - * - * this library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * this library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * PROJECT: ReactOS Explorer + * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) + * PURPOSE: Tray window implementation + * COPYRIGHT: Copyright 2006-2007 Thomas Weidenmueller + * Copyright 2018-2025 Katayama Hirofumi MZ */ #include "precomp.h" #include +#include "appbar.h" HRESULT TrayWindowCtxMenuCreator(ITrayWindow * TrayWnd, IN HWND hWndOwner, IContextMenu ** ppCtxMenu); -LRESULT appbar_message(COPYDATASTRUCT* cds); -void appbar_notify_all(HMONITOR hMon, UINT uMsg, HWND hwndExclude, LPARAM lParam); #define WM_APP_TRAYDESTROY (WM_APP + 0x100) @@ -56,8 +42,6 @@ void appbar_notify_all(HMONITOR hMon, UINT uMsg, HWND hwndExclude, LPARAM lParam #define IDHK_DESKTOP 0x1fe #define IDHK_PAGER 0x1ff -static const WCHAR szTrayWndClass[] = L"Shell_TrayWnd"; - enum { NONE, TILED, CASCADED } g_Arrangement = NONE; struct WINDOWPOSBACKUPDATA @@ -323,6 +307,7 @@ class CTrayWindow : public CComCoClass, public CComObjectRootEx, public CWindowImpl < CTrayWindow, CWindow, CControlWinTraits >, + public CAppBarManager, public ITrayWindow, public IShellDesktopTray, public IOleWindow, @@ -684,35 +669,35 @@ public: break; case ID_SHELL_CMD_TILE_WND_H: - appbar_notify_all(NULL, ABN_WINDOWARRANGE, NULL, TRUE); + OnAppBarNotifyAll(NULL, NULL, ABN_WINDOWARRANGE, TRUE); if (g_Arrangement == NONE) { BackupWindowPos(); } TileWindows(NULL, MDITILE_HORIZONTAL, NULL, 0, NULL); - appbar_notify_all(NULL, ABN_WINDOWARRANGE, NULL, FALSE); + OnAppBarNotifyAll(NULL, NULL, ABN_WINDOWARRANGE, FALSE); g_Arrangement = TILED; break; case ID_SHELL_CMD_TILE_WND_V: - appbar_notify_all(NULL, ABN_WINDOWARRANGE, NULL, TRUE); + OnAppBarNotifyAll(NULL, NULL, ABN_WINDOWARRANGE, TRUE); if (g_Arrangement == NONE) { BackupWindowPos(); } TileWindows(NULL, MDITILE_VERTICAL, NULL, 0, NULL); - appbar_notify_all(NULL, ABN_WINDOWARRANGE, NULL, FALSE); + OnAppBarNotifyAll(NULL, NULL, ABN_WINDOWARRANGE, FALSE); g_Arrangement = TILED; break; case ID_SHELL_CMD_CASCADE_WND: - appbar_notify_all(NULL, ABN_WINDOWARRANGE, NULL, TRUE); + OnAppBarNotifyAll(NULL, NULL, ABN_WINDOWARRANGE, TRUE); if (g_Arrangement == NONE) { BackupWindowPos(); } CascadeWindows(NULL, MDITILE_SKIPDISABLED, NULL, 0, NULL); - appbar_notify_all(NULL, ABN_WINDOWARRANGE, NULL, FALSE); + OnAppBarNotifyAll(NULL, NULL, ABN_WINDOWARRANGE, FALSE); g_Arrangement = CASCADED; break; @@ -2518,11 +2503,14 @@ ChangePos: LRESULT OnCopyData(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { - COPYDATASTRUCT *pCopyData = (COPYDATASTRUCT *)lParam; + PCOPYDATASTRUCT pCopyData = (PCOPYDATASTRUCT)lParam; + if (!pCopyData) + return FALSE; + switch (pCopyData->dwData) { case TABDMC_APPBAR: - return appbar_message(pCopyData); + return OnAppBarMessage(pCopyData); case TABDMC_NOTIFY: case TABDMC_LOADINPROC: return ::SendMessageW(m_TrayNotify, uMsg, wParam, lParam); @@ -2951,7 +2939,7 @@ HandleTrayContextMenu: LRESULT Ret = FALSE; /* FIXME: We can't check with IsChild whether the hwnd is somewhere inside the rebar control! But we shouldn't forward messages that the band - site doesn't handle, such as other controls (start button, tray window */ + site doesn't handle, such as other controls (start button, tray window) */ HRESULT hr = E_FAIL; @@ -3386,7 +3374,7 @@ HandleTrayContextMenu: return 0; } - DECLARE_WND_CLASS_EX(szTrayWndClass, CS_DBLCLKS, COLOR_3DFACE) + DECLARE_WND_CLASS_EX(L"Shell_TrayWnd", CS_DBLCLKS, COLOR_3DFACE) BEGIN_MSG_MAP(CTrayWindow) if (m_StartMenuBand != NULL) @@ -3577,6 +3565,24 @@ HandleTrayContextMenu: COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow) COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu) END_COM_MAP() + +protected: + ////////////////////////////////////////////////////////////////////////////////////////////// + // AppBar section + // + // See also: appbar.cpp + // TODO: freedesktop _NET_WM_STRUT integration + // TODO: find when a fullscreen app is in the foreground and send FULLSCREENAPP notifications + // TODO: detect changes in the screen size and send ABN_POSCHANGED ? + // TODO: multiple monitor support + + BOOL IsAutoHideState() const override { return g_TaskbarSettings.sr.AutoHide; } + BOOL IsHidingState() const override { return m_AutoHideState == AUTOHIDE_HIDING; } + HMONITOR GetMonitor() const override { return m_Monitor; } + HMONITOR GetPreviousMonitor() const override { return m_PreviousMonitor; } + INT GetPosition() const override { return m_Position; } + const RECT* GetTrayRect() override { return &m_TrayRects[m_Position]; } + HWND GetDesktopWnd() const override { return m_DesktopWnd; } }; class CTrayWindowCtxMenu : diff --git a/dll/win32/shell32/CMakeLists.txt b/dll/win32/shell32/CMakeLists.txt index 3660b02f36e..934c40f7fec 100644 --- a/dll/win32/shell32/CMakeLists.txt +++ b/dll/win32/shell32/CMakeLists.txt @@ -85,7 +85,7 @@ list(APPEND SOURCE CFolderItemVerbs.cpp) list(APPEND PCH_SKIP_SOURCE - wine/appbar.c + appbar.c wine/classes.c wine/clipboard.c wine/control.c diff --git a/dll/win32/shell32/appbar.c b/dll/win32/shell32/appbar.c new file mode 100644 index 00000000000..eef28ded2bb --- /dev/null +++ b/dll/win32/shell32/appbar.c @@ -0,0 +1,129 @@ +/* + * PROJECT: ReactOS Shell32 + * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) + * PURPOSE: SHAppBarMessage implementation + * COPYRIGHT: Copyright 2008 Vincent Povirk for CodeWeavers + * Copyright 2025 Katayama Hirofumi MZ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +WINE_DEFAULT_DEBUG_CHANNEL(appbar); + +static UINT32 +AppBar_CopyIn( + _In_ const VOID *pvSrc, + _In_ SIZE_T dwSize, + _In_ DWORD dwProcessId) +{ + HANDLE hMem = SHAllocShared(NULL, dwSize, dwProcessId); + if (!hMem) + return 0; + + PVOID pvDest = SHLockShared(hMem, dwProcessId); + if (!pvDest) + { + SHFreeShared(hMem, dwProcessId); + return 0; + } + + CopyMemory(pvDest, pvSrc, dwSize); + SHUnlockShared(pvDest); + return HandleToUlong(hMem); +} + +static BOOL +AppBar_CopyOut( + _In_ UINT32 hOutput32, + _Out_ PVOID pvDest, + _In_ SIZE_T cbDest, + _In_ DWORD dwProcessId) +{ + HANDLE hOutput = UlongToHandle(hOutput32); + PVOID pvSrc = SHLockShared(hOutput, dwProcessId); + if (pvSrc) + { + CopyMemory(pvDest, pvSrc, cbDest); + SHUnlockShared(pvSrc); + } + + SHFreeShared(hOutput, dwProcessId); + return pvSrc != NULL; +} + +/************************************************************************* + * SHAppBarMessage [SHELL32.@] + */ +UINT_PTR +WINAPI +SHAppBarMessage( + _In_ DWORD dwMessage, + _Inout_ PAPPBARDATA pData) +{ + TRACE("dwMessage=%d, pData={cb=%d, hwnd=%p}\n", dwMessage, pData->cbSize, pData->hWnd); + + HWND hTrayWnd = FindWindowW(L"Shell_TrayWnd", NULL); + if (!hTrayWnd || pData->cbSize > sizeof(*pData)) + { + WARN("%p, %d\n", hTrayWnd, pData->cbSize); + return FALSE; + } + + APPBAR_COMMAND cmd; + cmd.abd.cbSize = sizeof(cmd.abd); + cmd.abd.hWnd32 = HandleToUlong(pData->hWnd); // Truncated on x64, as on Windows! + cmd.abd.uCallbackMessage = pData->uCallbackMessage; + cmd.abd.uEdge = pData->uEdge; + cmd.abd.rc = pData->rc; + cmd.abd.lParam64 = pData->lParam; + cmd.dwMessage = dwMessage; + cmd.hOutput32 = 0; + cmd.dwProcessId = GetCurrentProcessId(); + + /* Make output data if necessary */ + switch (dwMessage) + { + case ABM_QUERYPOS: + case ABM_SETPOS: + case ABM_GETTASKBARPOS: + cmd.hOutput32 = AppBar_CopyIn(&cmd.abd, sizeof(cmd.abd), cmd.dwProcessId); + if (!cmd.hOutput32) + { + ERR("AppBar_CopyIn: %d\n", dwMessage); + return FALSE; + } + break; + default: + break; + } + + /* Send WM_COPYDATA message */ + COPYDATASTRUCT copyData = { TABDMC_APPBAR, sizeof(cmd), &cmd }; + UINT_PTR ret = SendMessageW(hTrayWnd, WM_COPYDATA, (WPARAM)pData->hWnd, (LPARAM)©Data); + + /* Copy back output data */ + if (cmd.hOutput32) + { + if (!AppBar_CopyOut(cmd.hOutput32, &cmd.abd, sizeof(cmd.abd), cmd.dwProcessId)) + { + ERR("AppBar_CopyOut: %d\n", dwMessage); + return FALSE; + } + pData->hWnd = UlongToHandle(cmd.abd.hWnd32); + pData->uCallbackMessage = cmd.abd.uCallbackMessage; + pData->uEdge = cmd.abd.uEdge; + pData->rc = cmd.abd.rc; + pData->lParam = (LPARAM)cmd.abd.lParam64; + } + + return ret; +} diff --git a/dll/win32/shell32/wine/appbar.c b/dll/win32/shell32/wine/appbar.c deleted file mode 100644 index 3bcaf085cde..00000000000 --- a/dll/win32/shell32/wine/appbar.c +++ /dev/null @@ -1,168 +0,0 @@ -/* - * SHAppBarMessage implementation - * - * Copyright 2008 Vincent Povirk for CodeWeavers - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -// -// Adapted from Wine appbar.c . -// - -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "shell32_main.h" - -WINE_DEFAULT_DEBUG_CHANNEL(appbar); - -struct appbar_cmd -{ - DWORD dwMsg; - ULONG return_map; - DWORD return_process; - struct _AppBarData abd; -}; - -struct appbar_response -{ - ULONGLONG result; - struct _AppBarData abd; -}; - -/************************************************************************* - * SHAppBarMessage [SHELL32.@] - */ -UINT_PTR WINAPI SHAppBarMessage(DWORD msg, PAPPBARDATA data) -{ - struct appbar_cmd command; - struct appbar_response* response; - HANDLE return_map; - LPVOID return_view; - HWND appbarmsg_window; - COPYDATASTRUCT cds; - - UINT_PTR ret = 0; - - TRACE("msg=%d, data={cb=%d, hwnd=%p}\n", msg, data->cbSize, data->hWnd); - - /* These members are message dependent */ - switch(msg) - { - case ABM_NEW: - TRACE("callback: %x\n", data->uCallbackMessage); - break; - - case ABM_GETAUTOHIDEBAR: - TRACE("edge: %d\n", data->uEdge); - break; - - case ABM_QUERYPOS: - case ABM_SETPOS: - TRACE("edge: %d, rc: %s\n", data->uEdge, wine_dbgstr_rect(&data->rc)); - break; - - case ABM_GETTASKBARPOS: - TRACE("rc: %s\n", wine_dbgstr_rect(&data->rc)); - break; - - case ABM_SETAUTOHIDEBAR: - TRACE("edge: %d, lParam: %lx\n", data->uEdge, data->lParam); - break; - - default: - FIXME("unknown msg: %d\n", msg); - break; - } - - if (data->cbSize < sizeof(APPBARDATA)) - { - WARN("data at %p is too small\n", data); - return FALSE; - } - - command.dwMsg = msg; - command.abd.hWnd = data->hWnd; - command.abd.uCallbackMessage = data->uCallbackMessage; - command.abd.uEdge = data->uEdge; - command.abd.rc = data->rc; - command.abd.lParam = data->lParam; - - return_map = CreateFileMappingW(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, sizeof(struct appbar_response), NULL); - if (return_map == NULL) - { - ERR("couldn't create file mapping\n"); - return 0; - } - command.return_map = HandleToUlong( return_map ); - - command.return_process = GetCurrentProcessId(); - - appbarmsg_window = FindWindowW(L"Shell_TrayWnd", NULL); - if (appbarmsg_window == NULL) - { - ERR("couldn't find appbar window\n"); - CloseHandle(return_map); - return 0; - } - - cds.dwData = TABDMC_APPBAR; - cds.cbData = sizeof(command); - cds.lpData = &command; - - SendMessageW(appbarmsg_window, WM_COPYDATA, (WPARAM)data->hWnd, (LPARAM)&cds); - - return_view = MapViewOfFile(return_map, FILE_MAP_READ, 0, 0, sizeof(struct appbar_response)); - if (return_view == NULL) - { - ERR("MapViewOfFile failed\n"); - CloseHandle(return_map); - return 0; - } - - response = return_view; - - ret = response->result; - if (ret) - { - data->hWnd = response->abd.hWnd; - data->uCallbackMessage = response->abd.uCallbackMessage; - data->uEdge = response->abd.uEdge; - data->rc = response->abd.rc; - data->lParam = response->abd.lParam; - } - UnmapViewOfFile(return_view); - - CloseHandle(return_map); - - return ret; -} diff --git a/modules/rostests/apitests/shell32/SHAppBarMessage.cpp b/modules/rostests/apitests/shell32/SHAppBarMessage.cpp index 324eb585d62..26d7246ee65 100644 --- a/modules/rostests/apitests/shell32/SHAppBarMessage.cpp +++ b/modules/rostests/apitests/shell32/SHAppBarMessage.cpp @@ -10,6 +10,9 @@ #include #include +#define NDEBUG +#include + /* Based on https://github.com/katahiromz/AppBarSample */ //#define VERBOSE @@ -264,15 +267,15 @@ protected: switch (id) { case ID_ACTION: - PostMessage(s_hwnd2, WM_COMMAND, ID_ACTION + 1, 0); + PostMessageW(s_hwnd2, WM_COMMAND, ID_ACTION + 1, 0); break; case ID_ACTION + 1: hThread = CreateThread(NULL, 0, ActionThreadFunc, this, 0, NULL); if (!hThread) { skip("failed to create thread\n"); - PostMessage(s_hwnd1, WM_CLOSE, 0, 0); - PostMessage(s_hwnd2, WM_CLOSE, 0, 0); + PostMessageW(s_hwnd1, WM_CLOSE, 0, 0); + PostMessageW(s_hwnd2, WM_CLOSE, 0, 0); return; } CloseHandle(hThread); @@ -438,8 +441,10 @@ protected: BOOL AppBar_SetSide(HWND hwnd, UINT uSide) { - RECT rc; - SetRect(&rc, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN)); + HMONITOR hMon = ::MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY); + MONITORINFO mi = { sizeof(mi) }; + ::GetMonitorInfo(hMon, &mi); + RECT rc = mi.rcWork; BOOL fAutoHide = FALSE; if (m_fAutoHide) @@ -452,18 +457,21 @@ protected: switch (uSide) { - case ABE_TOP: - rc.bottom = rc.top + m_cyHeight; - break; - case ABE_BOTTOM: - rc.top = rc.bottom - m_cyHeight; - break; - case ABE_LEFT: - rc.right = rc.left + m_cxWidth; - break; - case ABE_RIGHT: - rc.left = rc.right - m_cxWidth; - break; + case ABE_TOP: + rc.bottom = rc.top + m_cyHeight; + break; + case ABE_BOTTOM: + rc.top = rc.bottom - m_cyHeight; + break; + case ABE_LEFT: + rc.right = rc.left + m_cxWidth; + break; + case ABE_RIGHT: + rc.left = rc.right - m_cxWidth; + break; + default: + ASSERT(FALSE); + break; } APPBARDATA abd = { sizeof(abd) }; @@ -680,6 +688,7 @@ protected: AppBar_Register(hwnd); AppBar_SetSide(hwnd, ABE_TOP); + DPRINT1("OnCreate(%p) done\n", hwnd); return TRUE; } @@ -975,6 +984,9 @@ public: RECT rc1, rc2, rcWork; DWORD dwTID = GetWindowThreadProcessId(s_hwnd1, NULL); + DPRINT1("DoAction\n"); + Sleep(INTERVAL); + GetWindowRect(s_hwnd1, &rc1); GetWindowRect(s_hwnd2, &rc2); GetWorkArea(&rcWork); @@ -990,7 +1002,7 @@ public: ok_long(rcWork.top, s_rcWorkArea.top + 110); ok_long(rcWork.right, s_rcWorkArea.right); ok_long(rcWork.bottom, s_rcWorkArea.bottom); - PostMessage(s_hwnd1, WM_CLOSE, 0, 0); + PostMessageW(s_hwnd1, WM_CLOSE, 0, 0); Sleep(INTERVAL); GetWindowRect(s_hwnd2, &rc2); @@ -1101,7 +1113,7 @@ public: ok_long(rcWork.right, s_rcWorkArea.right); ok_long(rcWork.bottom, s_rcWorkArea.bottom); - PostMessage(s_hwnd2, WM_QUIT, 0, 0); + PostMessageW(s_hwnd2, WM_QUIT, 0, 0); PostThreadMessage(dwTID, WM_QUIT, 0, 0); #undef INTERVAL } @@ -1124,7 +1136,16 @@ START_TEST(SHAppBarMessage) return; } + DPRINT1("SM_CMONITORS: %d\n", GetSystemMetrics(SM_CMONITORS)); + if (GetSystemMetrics(SM_CMONITORS) != 1) + { + skip("Multi-monitor not supported yet\n"); + return; + } + SystemParametersInfo(SPI_GETWORKAREA, 0, &s_rcWorkArea, FALSE); + DPRINT1("s_rcWorkArea: %d, %d, %d, %d\n", + s_rcWorkArea.left, s_rcWorkArea.top, s_rcWorkArea.right, s_rcWorkArea.bottom); HWND hwnd1 = Window::DoCreateMainWnd(hInstance, TEXT("Test1"), 80, 80, WS_POPUP | WS_THICKFRAME | WS_CLIPCHILDREN); @@ -1145,7 +1166,7 @@ START_TEST(SHAppBarMessage) s_hwnd1 = hwnd1; s_hwnd2 = hwnd2; - PostMessage(hwnd1, WM_COMMAND, ID_ACTION, 0); + PostMessageW(hwnd1, WM_COMMAND, ID_ACTION, 0); Window::DoMainLoop(); } diff --git a/sdk/include/psdk/shellapi.h b/sdk/include/psdk/shellapi.h index fa94826ade4..320343d7cba 100644 --- a/sdk/include/psdk/shellapi.h +++ b/sdk/include/psdk/shellapi.h @@ -494,7 +494,12 @@ FindExecutableW( _In_opt_ LPCWSTR lpDirectory, _Out_writes_(MAX_PATH) LPWSTR lpResult); -UINT_PTR WINAPI SHAppBarMessage(_In_ DWORD, _Inout_ PAPPBARDATA); +UINT_PTR +WINAPI +SHAppBarMessage( + _In_ DWORD dwMessage, + _Inout_ PAPPBARDATA pData); + BOOL WINAPI Shell_NotifyIconA(_In_ DWORD, _In_ PNOTIFYICONDATAA); BOOL WINAPI Shell_NotifyIconW(_In_ DWORD, _In_ PNOTIFYICONDATAW); diff --git a/sdk/include/reactos/undocshell.h b/sdk/include/reactos/undocshell.h index 52330f25123..e19f0705be5 100644 --- a/sdk/include/reactos/undocshell.h +++ b/sdk/include/reactos/undocshell.h @@ -1,23 +1,14 @@ /* - * Copyright 1999, 2000 Juergen Schmied - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * PROJECT: ReactOS Shell + * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) + * PURPOSE: Undocumented shell definitions + * COPYRIGHT: Copyright 1999, 2000 Juergen Schmied + * Copyright 2025 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) */ -#ifndef __WINE_UNDOCSHELL_H -#define __WINE_UNDOCSHELL_H +#pragma once + +#include #ifndef SHSTDAPI #if defined(_SHELL32_) /* DECLSPEC_IMPORT disabled because of CORE-6504: */ || TRUE @@ -1231,8 +1222,33 @@ typedef struct SFVM_CUSTOMVIEWINFO_DATA #include +/* + * Private structures for internal AppBar messaging. + * These structures can be sent from 32-bit shell32 to 64-bit Explorer. + * See also: https://learn.microsoft.com/en-us/windows/win32/winprog64/interprocess-communication + * > ... only the lower 32 bits are significant, so it is safe to truncate the handle + */ +#include +typedef struct tagAPPBARDATA3264 +{ + DWORD cbSize; /* == sizeof(APPBARDATA3264) */ + UINT32 hWnd32; + UINT uCallbackMessage; + UINT uEdge; + RECT rc; + LONGLONG lParam64; +} APPBARDATA3264, *PAPPBARDATA3264; +typedef struct tagAPPBAR_COMMAND +{ + APPBARDATA3264 abd; + DWORD dwMessage; + UINT32 hOutput32; /* For shlwapi!SHAllocShared */ + DWORD dwProcessId; +} APPBAR_COMMAND, *PAPPBAR_COMMAND; +#include + +C_ASSERT(sizeof(APPBAR_COMMAND) == 0x38); + #ifdef __cplusplus } /* extern "C" */ #endif /* defined(__cplusplus) */ - -#endif /* __WINE_UNDOCSHELL_H */