/* * PROJECT: ReactOS api tests * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later) * PURPOSE: Test for SHAppBarMessage * COPYRIGHT: Copyright 2020 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) */ #include "shelltest.h" #include #include #include /* Based on https://github.com/katahiromz/AppBarSample */ //#define VERBOSE #define IDT_AUTOHIDE 1 #define IDT_AUTOUNHIDE 2 #define ID_ACTION 100 #define APPBAR_CALLBACK (WM_USER + 100) #define LEFT_DOWN() mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0); #define LEFT_UP() mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0); #define MOVE(x, y) SetCursorPos((x), (y)) static const TCHAR s_szName[] = TEXT("AppBarSample"); static RECT s_rcWorkArea; static HWND s_hwnd1 = NULL; static HWND s_hwnd2 = NULL; #ifdef VERBOSE static LPCSTR MessageOfAppBar(DWORD dwMessage) { static char s_buf[32]; switch (dwMessage) { case ABM_NEW: return "ABM_NEW"; case ABM_REMOVE: return "ABM_REMOVE"; case ABM_QUERYPOS: return "ABM_QUERYPOS"; case ABM_SETPOS: return "ABM_SETPOS"; case ABM_GETSTATE: return "ABM_GETSTATE"; case ABM_GETTASKBARPOS: return "ABM_GETTASKBARPOS"; case ABM_ACTIVATE: return "ABM_ACTIVATE"; case ABM_GETAUTOHIDEBAR: return "ABM_GETAUTOHIDEBAR"; case ABM_SETAUTOHIDEBAR: return "ABM_SETAUTOHIDEBAR"; case ABM_WINDOWPOSCHANGED: return "ABM_WINDOWPOSCHANGED"; } wsprintfA(s_buf, "%lu", dwMessage); return s_buf; } static UINT WINAPI SHAppBarMessageWrap(DWORD dwMessage, PAPPBARDATA pData) { trace("SHAppBarMessage entered (dwMessage=%s, rc=(%ld, %ld, %ld, %ld))\n", MessageOfAppBar(dwMessage), pData->rc.left, pData->rc.top, pData->rc.right, pData->rc.bottom); UINT ret = SHAppBarMessage(dwMessage, pData); trace("SHAppBarMessage leaved (dwMessage=%s, rc=(%ld, %ld, %ld, %ld))\n", MessageOfAppBar(dwMessage), pData->rc.left, pData->rc.top, pData->rc.right, pData->rc.bottom); return ret; } #define SHAppBarMessage SHAppBarMessageWrap #undef ARRAYSIZE #define ARRAYSIZE _countof void appbar_tprintf(const TCHAR *fmt, ...) { TCHAR szText[512]; va_list va; va_start(va, fmt); wvsprintf(szText, fmt, va); #ifdef UNICODE printf("%ls", szText); #else printf("%s", szText); #endif va_end(va); } #define MSGDUMP_TPRINTF appbar_tprintf #include "msgdump.h" #endif // def VERBOSE void SlideWindow(HWND hwnd, LPRECT prc) { #define SLIDE_HIDE 400 #define SLIDE_SHOW 150 RECT rcOld, rcNew = *prc; GetWindowRect(hwnd, &rcOld); BOOL fShow = (rcNew.bottom - rcNew.top > rcOld.bottom - rcOld.top) || (rcNew.right - rcNew.left > rcOld.right - rcOld.left); INT dx = (rcNew.right - rcOld.right) + (rcNew.left - rcOld.left); INT dy = (rcNew.bottom - rcOld.bottom) + (rcNew.top - rcOld.top); LONG dt = SLIDE_HIDE; if (fShow) { dt = SLIDE_SHOW; rcOld = rcNew; OffsetRect(&rcOld, -dx, -dy); SetWindowPos(hwnd, NULL, rcOld.left, rcOld.top, rcOld.right - rcOld.left, rcOld.bottom - rcOld.top, SWP_NOZORDER | SWP_NOACTIVATE | SWP_DRAWFRAME); } HANDLE hThread = GetCurrentThread(); INT priority = GetThreadPriority(hThread); SetThreadPriority(hThread, THREAD_PRIORITY_HIGHEST); LONG t, t0 = GetTickCount(); while ((t = GetTickCount()) < t0 + dt) { INT x = rcOld.left + dx * (t - t0) / dt; INT y = rcOld.top + dy * (t - t0) / dt; SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); UpdateWindow(hwnd); UpdateWindow(GetDesktopWindow()); } SetThreadPriority(hThread, priority); SetWindowPos(hwnd, NULL, rcNew.left, rcNew.top, rcNew.right - rcNew.left, rcNew.bottom - rcNew.top, SWP_NOZORDER | SWP_NOACTIVATE | SWP_DRAWFRAME); #undef SLIDE_HIDE #undef SLIDE_SHOW } class Window { public: Window(INT cx, INT cy, BOOL fAutoHide = FALSE) : m_hwnd(NULL) , m_fAutoHide(fAutoHide) , m_cxWidth(cx) , m_cyHeight(cy) { } virtual ~Window() { } static BOOL DoRegisterClass(HINSTANCE hInstance) { WNDCLASS wc; ZeroMemory(&wc, sizeof(wc)); wc.lpfnWndProc = Window::WindowProc; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); wc.lpszClassName = s_szName; return !!RegisterClass(&wc); } static HWND DoCreateMainWnd(HINSTANCE hInstance, LPCTSTR pszText, INT cx, INT cy, DWORD style = WS_POPUP | WS_THICKFRAME | WS_CLIPCHILDREN, DWORD exstyle = WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST, BOOL fAutoHide = FALSE) { Window *this_ = new Window(cx, cy, fAutoHide); HWND hwnd = CreateWindowEx(exstyle, s_szName, pszText, style, CW_USEDEFAULT, CW_USEDEFAULT, 50, 50, NULL, NULL, hInstance, this_); ShowWindow(hwnd, SW_SHOWNORMAL); UpdateWindow(hwnd); return hwnd; } static INT DoMainLoop() { MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (INT)msg.wParam; } static Window *GetAppbarData(HWND hwnd) { return (Window *)GetWindowLongPtr(hwnd, GWLP_USERDATA); } virtual LRESULT CALLBACK WindowProcDx(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { #ifdef VERBOSE MD_msgdump(hwnd, uMsg, wParam, lParam); #endif switch (uMsg) { HANDLE_MSG(hwnd, WM_CREATE, OnCreate); HANDLE_MSG(hwnd, WM_COMMAND, OnCommand); HANDLE_MSG(hwnd, WM_ACTIVATE, OnActivate); HANDLE_MSG(hwnd, WM_WINDOWPOSCHANGED, OnWindowPosChanged); HANDLE_MSG(hwnd, WM_SIZE, OnSize); HANDLE_MSG(hwnd, WM_MOVE, OnMove); HANDLE_MSG(hwnd, WM_NCDESTROY, OnNCDestroy); HANDLE_MSG(hwnd, WM_TIMER, OnTimer); HANDLE_MSG(hwnd, WM_NCHITTEST, OnNCHitTest); HANDLE_MSG(hwnd, WM_LBUTTONDOWN, OnLButtonDown); HANDLE_MSG(hwnd, WM_MOUSEMOVE, OnMouseMove); HANDLE_MSG(hwnd, WM_LBUTTONUP, OnLButtonUp); HANDLE_MSG(hwnd, WM_RBUTTONDOWN, OnRButtonDown); HANDLE_MSG(hwnd, WM_KEYDOWN, OnKey); HANDLE_MSG(hwnd, WM_PAINT, OnPaint); case APPBAR_CALLBACK: OnAppBarCallback(hwnd, uMsg, wParam, lParam); break; default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } return 0; } static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { Window *this_ = GetAppbarData(hwnd); if (uMsg == WM_CREATE) { LPCREATESTRUCT pCS = (LPCREATESTRUCT)lParam; this_ = (Window *)pCS->lpCreateParams; SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)this_); } if (this_) return this_->WindowProcDx(hwnd, uMsg, wParam, lParam); return DefWindowProc(hwnd, uMsg, wParam, lParam); } protected: HWND m_hwnd; BOOL m_fAutoHide; BOOL m_fOnTop; BOOL m_fHiding; UINT m_uSide; LONG m_cxWidth; LONG m_cyHeight; LONG m_cxSave; LONG m_cySave; BOOL m_fAppBarRegd; BOOL m_fMoving; BOOL m_bDragged; POINT m_ptDragOn; RECT m_rcAppBar; RECT m_rcDrag; void OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) { HANDLE hThread; switch (id) { case ID_ACTION: PostMessage(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); return; } CloseHandle(hThread); } } void OnPaint(HWND hwnd) { PAINTSTRUCT ps; TCHAR szText[64]; GetWindowText(hwnd, szText, 64); RECT rc; GetClientRect(hwnd, &rc); if (HDC hdc = BeginPaint(hwnd, &ps)) { DrawText(hdc, szText, -1, &rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER); EndPaint(hwnd, &ps); } } void OnRButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags) { m_fAutoHide = !m_fAutoHide; AppBar_SetAutoHide(hwnd, m_fAutoHide); } void OnKey(HWND hwnd, UINT vk, BOOL fDown, int cRepeat, UINT flags) { if (vk == VK_ESCAPE) DestroyWindow(hwnd); } void OnAppBarCallback(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { static HWND s_hwndZOrder = NULL; switch (wParam) { case ABN_STATECHANGE: break; case ABN_FULLSCREENAPP: if (lParam) { s_hwndZOrder = GetWindow(hwnd, GW_HWNDPREV); SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); } else { SetWindowPos(hwnd, m_fOnTop ? HWND_TOPMOST : s_hwndZOrder, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); s_hwndZOrder = NULL; } break; case ABN_POSCHANGED: { APPBARDATA abd = { sizeof(abd) }; abd.hWnd = hwnd; AppBar_PosChanged(&abd); } break; } } BOOL AppBar_Register(HWND hwnd) { APPBARDATA abd = { sizeof(abd) }; abd.hWnd = hwnd; abd.uCallbackMessage = APPBAR_CALLBACK; m_fAppBarRegd = (BOOL)SHAppBarMessage(ABM_NEW, &abd); return m_fAppBarRegd; } BOOL AppBar_UnRegister(HWND hwnd) { APPBARDATA abd = { sizeof(abd) }; abd.hWnd = hwnd; m_fAppBarRegd = !SHAppBarMessage(ABM_REMOVE, &abd); return !m_fAppBarRegd; } BOOL AppBar_SetAutoHide(HWND hwnd, BOOL fHide) { if (fHide) return AppBar_AutoHide(hwnd); else return AppBar_NoAutoHide(hwnd); } BOOL AppBar_AutoHide(HWND hwnd) { APPBARDATA abd = { sizeof(abd) }; abd.hWnd = hwnd; abd.uEdge = m_uSide; HWND hwndAutoHide = (HWND)SHAppBarMessage(ABM_GETAUTOHIDEBAR, &abd); if (hwndAutoHide) return FALSE; abd.lParam = TRUE; if (!(BOOL)SHAppBarMessage(ABM_SETAUTOHIDEBAR, &abd)) return FALSE; m_fAutoHide = TRUE; m_cxSave = m_cxWidth; m_cySave = m_cyHeight; RECT rc = m_rcAppBar; switch (m_uSide) { case ABE_TOP: rc.bottom = rc.top + 2; break; case ABE_BOTTOM: rc.top = rc.bottom - 2; break; case ABE_LEFT: rc.right = rc.left + 2; break; case ABE_RIGHT: rc.left = rc.right - 2; break; } AppBar_QueryPos(hwnd, &rc); abd.rc = rc; SHAppBarMessage(ABM_SETPOS, &abd); rc = abd.rc; m_fHiding = TRUE; SlideWindow(hwnd, &rc); AppBar_SetAutoHideTimer(hwnd); return TRUE; } BOOL AppBar_NoAutoHide(HWND hwnd) { APPBARDATA abd = { sizeof(abd) }; abd.hWnd = hwnd; abd.uEdge = m_uSide; HWND hwndAutoHide = (HWND)SHAppBarMessage(ABM_GETAUTOHIDEBAR, &abd); if (hwndAutoHide != hwnd) return FALSE; abd.lParam = FALSE; if (!(BOOL)SHAppBarMessage(ABM_SETAUTOHIDEBAR, &abd)) return FALSE; m_fAutoHide = FALSE; m_cxWidth = m_cxSave; m_cyHeight = m_cySave; AppBar_SetSide(hwnd, m_uSide); return TRUE; } BOOL AppBar_SetSide(HWND hwnd, UINT uSide) { RECT rc; SetRect(&rc, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN)); BOOL fAutoHide = FALSE; if (m_fAutoHide) { fAutoHide = m_fAutoHide; SetWindowRedraw(GetDesktopWindow(), FALSE); AppBar_SetAutoHide(hwnd, FALSE); m_fHiding = FALSE; } 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; } APPBARDATA abd = { sizeof(abd) }; abd.hWnd = hwnd; AppBar_QuerySetPos(uSide, &rc, &abd, TRUE); if (fAutoHide) { AppBar_SetAutoHide(hwnd, TRUE); m_fHiding = TRUE; SetWindowRedraw(GetDesktopWindow(), TRUE); RedrawWindow(GetDesktopWindow(), NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN); } return TRUE; } void AppBar_SetAlwaysOnTop(HWND hwnd, BOOL fOnTop) { SetWindowPos(hwnd, (fOnTop ? HWND_TOPMOST : HWND_NOTOPMOST), 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); m_fOnTop = fOnTop; } void AppBar_Hide(HWND hwnd) { if (!m_fAutoHide) return; RECT rc = m_rcAppBar; switch (m_uSide) { case ABE_TOP: rc.bottom = rc.top + 2; break; case ABE_BOTTOM: rc.top = rc.bottom - 2; break; case ABE_LEFT: rc.right = rc.left + 2; break; case ABE_RIGHT: rc.left = rc.right - 2; break; } m_fHiding = TRUE; SlideWindow(hwnd, &rc); } void AppBar_UnHide(HWND hwnd) { SlideWindow(hwnd, &m_rcAppBar); m_fHiding = FALSE; AppBar_SetAutoHideTimer(hwnd); } void AppBar_SetAutoHideTimer(HWND hwnd) { if (m_fAutoHide) { SetTimer(hwnd, IDT_AUTOHIDE, 500, NULL); } } void AppBar_SetAutoUnhideTimer(HWND hwnd) { if (m_fAutoHide && m_fHiding) { SetTimer(hwnd, IDT_AUTOUNHIDE, 50, NULL); } } void AppBar_Size(HWND hwnd) { if (m_fAppBarRegd) { APPBARDATA abd = { sizeof(abd) }; abd.hWnd = hwnd; RECT rc; GetWindowRect(hwnd, &rc); AppBar_QuerySetPos(m_uSide, &rc, &abd, TRUE); } } void AppBar_QueryPos(HWND hwnd, LPRECT lprc) { APPBARDATA abd = { sizeof(abd) }; abd.hWnd = hwnd; abd.rc = *lprc; abd.uEdge = m_uSide; INT cx = 0, cy = 0; if (ABE_LEFT == abd.uEdge || ABE_RIGHT == abd.uEdge) { cx = abd.rc.right - abd.rc.left; abd.rc.top = 0; abd.rc.bottom = GetSystemMetrics(SM_CYSCREEN); } else { cy = abd.rc.bottom - abd.rc.top; abd.rc.left = 0; abd.rc.right = GetSystemMetrics(SM_CXSCREEN); } SHAppBarMessage(ABM_QUERYPOS, &abd); switch (abd.uEdge) { case ABE_LEFT: abd.rc.right = abd.rc.left + cx; break; case ABE_RIGHT: abd.rc.left = abd.rc.right - cx; break; case ABE_TOP: abd.rc.bottom = abd.rc.top + cy; break; case ABE_BOTTOM: abd.rc.top = abd.rc.bottom - cy; break; } *lprc = abd.rc; } void AppBar_QuerySetPos(UINT uEdge, LPRECT lprc, PAPPBARDATA pabd, BOOL fMove) { pabd->rc = *lprc; pabd->uEdge = uEdge; m_uSide = uEdge; AppBar_QueryPos(pabd->hWnd, &pabd->rc); SHAppBarMessage(ABM_SETPOS, pabd); if (fMove) { RECT rc = pabd->rc; MoveWindow(pabd->hWnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE); } if (!m_fAutoHide) { m_rcAppBar = pabd->rc; } } void AppBar_PosChanged(PAPPBARDATA pabd) { RECT rc; SetRect(&rc, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN)); if (m_fAutoHide) { m_rcAppBar = rc; switch (m_uSide) { case ABE_TOP: m_rcAppBar.bottom = m_rcAppBar.top + m_cySave; break; case ABE_BOTTOM: m_rcAppBar.top = m_rcAppBar.bottom - m_cySave; break; case ABE_LEFT: m_rcAppBar.right = m_rcAppBar.left + m_cxSave; break; case ABE_RIGHT: m_rcAppBar.left = m_rcAppBar.right - m_cxSave; break; } } RECT rcWindow; GetWindowRect(pabd->hWnd, &rcWindow); INT cx = rcWindow.right - rcWindow.left; INT cy = rcWindow.bottom - rcWindow.top; switch (m_uSide) { case ABE_TOP: rc.bottom = rc.top + cy; break; case ABE_BOTTOM: rc.top = rc.bottom - cy; break; case ABE_LEFT: rc.right = rc.left + cx; break; case ABE_RIGHT: rc.left = rc.right - cx; break; } AppBar_QuerySetPos(m_uSide, &rc, pabd, TRUE); } BOOL OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct) { m_hwnd = hwnd; m_fOnTop = TRUE; m_uSide = ABE_TOP; m_fAppBarRegd = FALSE; m_fMoving = FALSE; m_cxSave = m_cxWidth; m_cySave = m_cyHeight; m_bDragged = FALSE; AppBar_Register(hwnd); AppBar_SetSide(hwnd, ABE_TOP); return TRUE; } void OnActivate(HWND hwnd, UINT state, HWND hwndActDeact, BOOL fMinimized) { APPBARDATA abd = { sizeof(abd) }; abd.hWnd = hwnd; SHAppBarMessage(ABM_ACTIVATE, &abd); switch (state) { case WA_ACTIVE: case WA_CLICKACTIVE: AppBar_UnHide(hwnd); KillTimer(hwnd, IDT_AUTOHIDE); break; case WA_INACTIVE: AppBar_Hide(hwnd); break; } } void OnWindowPosChanged(HWND hwnd, const LPWINDOWPOS lpwpos) { APPBARDATA abd = { sizeof(abd) }; abd.hWnd = hwnd; SHAppBarMessage(ABM_WINDOWPOSCHANGED, &abd); FORWARD_WM_WINDOWPOSCHANGED(hwnd, lpwpos, DefWindowProc); } void OnSize(HWND hwnd, UINT state, int cx, int cy) { RECT rcWindow; if (m_fMoving || (m_fAutoHide && m_fHiding)) return; if (!m_fHiding) { if (!m_fAutoHide) AppBar_Size(hwnd); GetWindowRect(hwnd, &rcWindow); m_rcAppBar = rcWindow; if (m_uSide == ABE_TOP || m_uSide == ABE_BOTTOM) { m_cyHeight = m_cySave = rcWindow.bottom - rcWindow.top; } else { m_cxWidth = m_cxSave = rcWindow.right - rcWindow.left; } } InvalidateRect(hwnd, NULL, TRUE); } void OnMove(HWND hwnd, int x, int y) { if (m_fMoving || m_fAutoHide) return; if (!m_fHiding) AppBar_Size(hwnd); } void OnNCDestroy(HWND hwnd) { AppBar_UnRegister(hwnd); m_hwnd = NULL; SetWindowLongPtr(hwnd, GWLP_USERDATA, 0); delete this; } void OnTimer(HWND hwnd, UINT id) { POINT pt; RECT rc; HWND hwndActive; switch (id) { case IDT_AUTOHIDE: if (m_fAutoHide && !m_fHiding && !m_fMoving) { GetCursorPos(&pt); GetWindowRect(hwnd, &rc); hwndActive = GetForegroundWindow(); if (!PtInRect(&rc, pt) && hwndActive != hwnd && hwndActive != NULL && GetWindowOwner(hwndActive) != hwnd) { KillTimer(hwnd, id); AppBar_Hide(hwnd); } } break; case IDT_AUTOUNHIDE: KillTimer(hwnd, id); if (m_fAutoHide && m_fHiding) { GetCursorPos(&pt); GetWindowRect(hwnd, &rc); if (PtInRect(&rc, pt)) { AppBar_UnHide(hwnd); } } break; } } UINT OnNCHitTest(HWND hwnd, int x, int y) { AppBar_SetAutoUnhideTimer(hwnd); UINT uHitTest = FORWARD_WM_NCHITTEST(hwnd, x, y, DefWindowProc); if (m_uSide == ABE_TOP && uHitTest == HTBOTTOM) return HTBOTTOM; if (m_uSide == ABE_BOTTOM && uHitTest == HTTOP) return HTTOP; if (m_uSide == ABE_LEFT && uHitTest == HTRIGHT) return HTRIGHT; if (m_uSide == ABE_RIGHT && uHitTest == HTLEFT) return HTLEFT; return HTCLIENT; } void OnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags) { m_fMoving = TRUE; m_bDragged = FALSE; SetCapture(hwnd); GetCursorPos(&m_ptDragOn); } void OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags) { if (!m_fMoving) return; POINT pt; GetCursorPos(&pt); if (labs(pt.x - m_ptDragOn.x) > GetSystemMetrics(SM_CXDRAG) || labs(pt.y - m_ptDragOn.y) > GetSystemMetrics(SM_CYDRAG)) { m_bDragged = TRUE; } INT cxScreen = GetSystemMetrics(SM_CXSCREEN); INT cyScreen = GetSystemMetrics(SM_CYSCREEN); DWORD dx, dy; UINT ix, iy; if (pt.x < cxScreen / 2) { dx = pt.x; ix = ABE_LEFT; } else { dx = cxScreen - pt.x; ix = ABE_RIGHT; } if (pt.y < cyScreen / 2) { dy = pt.y; iy = ABE_TOP; } else { dy = cyScreen - pt.y; iy = ABE_BOTTOM; } if (cxScreen * dy > cyScreen * dx) { m_rcDrag.top = 0; m_rcDrag.bottom = cyScreen; if (ix == ABE_LEFT) { m_uSide = ABE_LEFT; m_rcDrag.left = 0; m_rcDrag.right = m_rcDrag.left + m_cxWidth; } else { m_uSide = ABE_RIGHT; m_rcDrag.right = cxScreen; m_rcDrag.left = m_rcDrag.right - m_cxWidth; } } else { m_rcDrag.left = 0; m_rcDrag.right = cxScreen; if (iy == ABE_TOP) { m_uSide = ABE_TOP; m_rcDrag.top = 0; m_rcDrag.bottom = m_rcDrag.top + m_cyHeight; } else { m_uSide = ABE_BOTTOM; m_rcDrag.bottom = cyScreen; m_rcDrag.top = m_rcDrag.bottom - m_cyHeight; } } AppBar_QueryPos(hwnd, &m_rcDrag); if (m_bDragged) { MoveWindow(hwnd, m_rcDrag.left, m_rcDrag.top, m_rcDrag.right - m_rcDrag.left, m_rcDrag.bottom - m_rcDrag.top, TRUE); } } void OnLButtonUp(HWND hwnd, int x, int y, UINT keyFlags) { if (!m_fMoving) return; OnMouseMove(hwnd, x, y, keyFlags); m_rcAppBar = m_rcDrag; ReleaseCapture(); if (m_fAutoHide) { switch (m_uSide) { case ABE_TOP: m_rcDrag.bottom = m_rcDrag.top + 2; break; case ABE_BOTTOM: m_rcDrag.top = m_rcDrag.bottom - 2; break; case ABE_LEFT: m_rcDrag.right = m_rcDrag.left + 2; break; case ABE_RIGHT: m_rcDrag.left = m_rcDrag.right - 2; break; } } if (m_bDragged) { if (m_fAutoHide) { AppBar_AutoHide(hwnd); } else { APPBARDATA abd = { sizeof(abd) }; abd.hWnd = hwnd; AppBar_QuerySetPos(m_uSide, &m_rcDrag, &abd, FALSE); } } m_fMoving = FALSE; } void GetWorkArea(LPRECT prc) const { SystemParametersInfoW(SPI_GETWORKAREA, 0, prc, 0); } public: void DoAction() { #define INTERVAL 250 POINT pt; RECT rc1, rc2, rcWork; DWORD dwTID = GetWindowThreadProcessId(s_hwnd1, NULL); GetWindowRect(s_hwnd1, &rc1); GetWindowRect(s_hwnd2, &rc2); GetWorkArea(&rcWork); ok_long(rc1.left, s_rcWorkArea.left); ok_long(rc1.top, s_rcWorkArea.top); ok_long(rc1.right, s_rcWorkArea.right); ok_long(rc1.bottom, s_rcWorkArea.top + 80); ok_long(rc2.left, s_rcWorkArea.left); ok_long(rc2.top, s_rcWorkArea.top + 80); ok_long(rc2.right, s_rcWorkArea.right); ok_long(rc2.bottom, s_rcWorkArea.top + 110); ok_long(rcWork.left, s_rcWorkArea.left); 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); Sleep(INTERVAL); GetWindowRect(s_hwnd2, &rc2); GetWorkArea(&rcWork); ok_long(rc2.left, s_rcWorkArea.left); ok_long(rc2.top, s_rcWorkArea.top); ok_long(rc2.right, s_rcWorkArea.right); ok_long(rc2.bottom, s_rcWorkArea.top + 30); ok_long(rcWork.left, s_rcWorkArea.left); ok_long(rcWork.top, s_rcWorkArea.top + 30); ok_long(rcWork.right, s_rcWorkArea.right); ok_long(rcWork.bottom, s_rcWorkArea.bottom); AppBar_SetSide(s_hwnd2, ABE_LEFT); Sleep(INTERVAL); GetWindowRect(s_hwnd2, &rc2); GetWorkArea(&rcWork); ok_long(rc2.left, s_rcWorkArea.left); ok_long(rc2.top, s_rcWorkArea.top); ok_long(rc2.right, s_rcWorkArea.left + 30); ok_long(rcWork.left, s_rcWorkArea.left + 30); ok_long(rcWork.top, s_rcWorkArea.top); ok_long(rcWork.right, s_rcWorkArea.right); ok_long(rcWork.bottom, s_rcWorkArea.bottom); AppBar_SetSide(s_hwnd2, ABE_TOP); Sleep(INTERVAL); GetWindowRect(s_hwnd2, &rc2); GetWorkArea(&rcWork); ok_long(rc2.left, s_rcWorkArea.left); ok_long(rc2.top, s_rcWorkArea.top); ok_long(rc2.right, s_rcWorkArea.right); ok_long(rc2.bottom, s_rcWorkArea.top + 30); ok_long(rcWork.left, s_rcWorkArea.left); ok_long(rcWork.top, s_rcWorkArea.top + 30); ok_long(rcWork.right, s_rcWorkArea.right); ok_long(rcWork.bottom, s_rcWorkArea.bottom); AppBar_SetSide(s_hwnd2, ABE_RIGHT); Sleep(INTERVAL); GetWindowRect(s_hwnd2, &rc2); GetWorkArea(&rcWork); ok_long(rc2.left, s_rcWorkArea.right - 30); ok_long(rc2.top, s_rcWorkArea.top); ok_long(rc2.right, s_rcWorkArea.right); ok_long(rcWork.left, s_rcWorkArea.left); ok_long(rcWork.top, s_rcWorkArea.top); ok_long(rcWork.right, s_rcWorkArea.right - 30); ok_long(rcWork.bottom, s_rcWorkArea.bottom); Sleep(INTERVAL); GetWindowRect(s_hwnd2, &rc2); pt.x = (rc2.left + rc2.right) / 2; pt.y = (rc2.top + rc2.bottom) / 2; MOVE(pt.x, pt.y); LEFT_DOWN(); MOVE(pt.x + 64, pt.y + 64); Sleep(INTERVAL); pt.x = s_rcWorkArea.left + 80; pt.y = (s_rcWorkArea.top + s_rcWorkArea.bottom) / 2; MOVE(pt.x, pt.y); LEFT_UP(); Sleep(INTERVAL); GetWindowRect(s_hwnd2, &rc2); GetWorkArea(&rcWork); ok_long(rc2.left, s_rcWorkArea.left); ok_long(rc2.top, s_rcWorkArea.top); ok_long(rc2.right, s_rcWorkArea.left + 30); ok_long(rcWork.left, s_rcWorkArea.left + 30); ok_long(rcWork.top, s_rcWorkArea.top); ok_long(rcWork.right, s_rcWorkArea.right); ok_long(rcWork.bottom, s_rcWorkArea.bottom); Sleep(INTERVAL); GetWindowRect(s_hwnd2, &rc2); pt.x = (rc2.left + rc2.right) / 2; pt.y = (rc2.top + rc2.bottom) / 2; MOVE(pt.x, pt.y); LEFT_DOWN(); MOVE(pt.x + 64, pt.y + 64); Sleep(INTERVAL); pt.x = s_rcWorkArea.right - 80; pt.y = (s_rcWorkArea.top + s_rcWorkArea.bottom) / 2; MOVE(pt.x, pt.y); LEFT_UP(); Sleep(INTERVAL); GetWindowRect(s_hwnd2, &rc2); GetWorkArea(&rcWork); ok_long(rc2.left, s_rcWorkArea.right - 30); ok_long(rc2.top, s_rcWorkArea.top); ok_long(rc2.right, s_rcWorkArea.right); ok_long(rcWork.left, s_rcWorkArea.left); ok_long(rcWork.top, s_rcWorkArea.top); ok_long(rcWork.right, s_rcWorkArea.right - 30); ok_long(rcWork.bottom, s_rcWorkArea.bottom); Sleep(INTERVAL); SendMessage(s_hwnd2, WM_CLOSE, 0, 0); Sleep(INTERVAL); GetWorkArea(&rcWork); ok_long(rcWork.left, s_rcWorkArea.left); ok_long(rcWork.top, s_rcWorkArea.top); ok_long(rcWork.right, s_rcWorkArea.right); ok_long(rcWork.bottom, s_rcWorkArea.bottom); PostMessage(s_hwnd2, WM_QUIT, 0, 0); PostThreadMessage(dwTID, WM_QUIT, 0, 0); #undef INTERVAL } static DWORD WINAPI ActionThreadFunc(LPVOID args) { Window *this_ = (Window *)args; this_->DoAction(); return 0; } }; START_TEST(SHAppBarMessage) { HINSTANCE hInstance = GetModuleHandle(NULL); if (!Window::DoRegisterClass(hInstance)) { skip("Window::DoRegisterClass failed\n"); return; } SystemParametersInfo(SPI_GETWORKAREA, 0, &s_rcWorkArea, FALSE); HWND hwnd1 = Window::DoCreateMainWnd(hInstance, TEXT("Test1"), 80, 80, WS_POPUP | WS_THICKFRAME | WS_CLIPCHILDREN); if (!hwnd1) { skip("CreateWindowExW failed\n"); return; } HWND hwnd2 = Window::DoCreateMainWnd(hInstance, TEXT("Test2"), 30, 30, WS_POPUP | WS_BORDER | WS_CLIPCHILDREN); if (!hwnd2) { skip("CreateWindowExW failed\n"); return; } s_hwnd1 = hwnd1; s_hwnd2 = hwnd2; PostMessage(hwnd1, WM_COMMAND, ID_ACTION, 0); Window::DoMainLoop(); }