1 /* Unit tests for appbars 2 * 3 * Copyright 2008 Vincent Povirk for CodeWeavers 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Lesser General Public 7 * License as published by the Free Software Foundation; either 8 * version 2.1 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public 16 * License along with this library; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 18 */ 19 20 #include <stdarg.h> 21 22 #include <windows.h> 23 #include "shellapi.h" 24 25 #include "wine/test.h" 26 27 #define MSG_APPBAR WM_APP 28 29 static const CHAR testwindow_class[] = "testwindow"; 30 31 static HMONITOR (WINAPI *pMonitorFromWindow)(HWND, DWORD); 32 static HRESULT (WINAPI *pGetCurrentProcessExplicitAppUserModelID)(PWSTR*); 33 34 typedef BOOL (*boolean_function)(void); 35 36 struct testwindow_info 37 { 38 HWND hwnd; 39 BOOL registered; 40 BOOL to_be_deleted; 41 RECT desired_rect; 42 UINT edge; 43 RECT allocated_rect; 44 }; 45 46 static struct testwindow_info windows[3]; 47 48 static int expected_bottom; 49 50 static void testwindow_setpos(HWND hwnd) 51 { 52 struct testwindow_info* info = (struct testwindow_info*)GetWindowLongPtrA(hwnd, GWLP_USERDATA); 53 APPBARDATA abd; 54 BOOL ret; 55 56 ok(info != NULL, "got unexpected ABN_POSCHANGED notification\n"); 57 58 if (!info || !info->registered) 59 { 60 return; 61 } 62 63 if (info->to_be_deleted) 64 { 65 win_skip("Some Win95 and NT4 systems send messages to removed taskbars\n"); 66 return; 67 } 68 69 abd.cbSize = sizeof(abd); 70 abd.hWnd = hwnd; 71 abd.uEdge = info->edge; 72 abd.rc = info->desired_rect; 73 ret = SHAppBarMessage(ABM_QUERYPOS, &abd); 74 ok(ret == TRUE, "SHAppBarMessage returned %i\n", ret); 75 switch (info->edge) 76 { 77 case ABE_BOTTOM: 78 ok(info->desired_rect.top == abd.rc.top, "ABM_QUERYPOS changed top of rect from %i to %i\n", info->desired_rect.top, abd.rc.top); 79 abd.rc.top = abd.rc.bottom - (info->desired_rect.bottom - info->desired_rect.top); 80 break; 81 case ABE_LEFT: 82 ok(info->desired_rect.right == abd.rc.right, "ABM_QUERYPOS changed right of rect from %i to %i\n", info->desired_rect.right, abd.rc.right); 83 abd.rc.right = abd.rc.left + (info->desired_rect.right - info->desired_rect.left); 84 break; 85 case ABE_RIGHT: 86 ok(info->desired_rect.left == abd.rc.left, "ABM_QUERYPOS changed left of rect from %i to %i\n", info->desired_rect.left, abd.rc.left); 87 abd.rc.left = abd.rc.right - (info->desired_rect.right - info->desired_rect.left); 88 break; 89 case ABE_TOP: 90 ok(info->desired_rect.bottom == abd.rc.bottom, "ABM_QUERYPOS changed bottom of rect from %i to %i\n", info->desired_rect.bottom, abd.rc.bottom); 91 abd.rc.bottom = abd.rc.top + (info->desired_rect.bottom - info->desired_rect.top); 92 break; 93 } 94 95 ret = SHAppBarMessage(ABM_SETPOS, &abd); 96 ok(ret == TRUE, "SHAppBarMessage returned %i\n", ret); 97 98 info->allocated_rect = abd.rc; 99 MoveWindow(hwnd, abd.rc.left, abd.rc.top, abd.rc.right-abd.rc.left, abd.rc.bottom-abd.rc.top, TRUE); 100 } 101 102 static LRESULT CALLBACK testwindow_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) 103 { 104 switch(msg) 105 { 106 case MSG_APPBAR: 107 { 108 switch(wparam) 109 { 110 case ABN_POSCHANGED: 111 testwindow_setpos(hwnd); 112 break; 113 } 114 return 0; 115 } 116 } 117 118 return DefWindowProcA(hwnd, msg, wparam, lparam); 119 } 120 121 /* process pending messages until a condition is true or 3 seconds pass */ 122 static void do_events_until(boolean_function test) 123 { 124 MSG msg; 125 UINT_PTR timerid; 126 BOOL timedout=FALSE; 127 128 timerid = SetTimer(0, 0, 3000, NULL); 129 130 while (1) 131 { 132 while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) 133 { 134 if (msg.hwnd == 0 && msg.message == WM_TIMER && msg.wParam == timerid) 135 timedout = TRUE; 136 TranslateMessage(&msg); 137 DispatchMessageA(&msg); 138 } 139 if (timedout || test()) 140 break; 141 WaitMessage(); 142 } 143 144 KillTimer(0, timerid); 145 } 146 147 /* process any pending messages */ 148 static void do_events(void) 149 { 150 MSG msg; 151 152 while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) 153 { 154 TranslateMessage(&msg); 155 DispatchMessageA(&msg); 156 } 157 } 158 159 static BOOL no_appbars_intersect(void) 160 { 161 int i, j; 162 RECT rc; 163 164 for (i=0; i<2; i++) 165 { 166 for (j=i+1; j<3; j++) 167 { 168 if (windows[i].registered && windows[j].registered && 169 IntersectRect(&rc, &windows[i].allocated_rect, &windows[j].allocated_rect)) 170 return FALSE; 171 } 172 } 173 return TRUE; 174 } 175 176 static BOOL got_expected_bottom(void) 177 { 178 return (no_appbars_intersect() && windows[1].allocated_rect.bottom == expected_bottom); 179 } 180 181 static void register_testwindow_class(void) 182 { 183 WNDCLASSEXA cls; 184 185 ZeroMemory(&cls, sizeof(cls)); 186 cls.cbSize = sizeof(cls); 187 cls.style = 0; 188 cls.lpfnWndProc = testwindow_wndproc; 189 cls.hInstance = NULL; 190 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW); 191 cls.hbrBackground = (HBRUSH) COLOR_WINDOW; 192 cls.lpszClassName = testwindow_class; 193 194 RegisterClassExA(&cls); 195 } 196 197 #define test_window_rects(a, b) \ 198 ok(!IntersectRect(&rc, &windows[a].allocated_rect, &windows[b].allocated_rect), \ 199 "rectangles intersect %s / %s\n", wine_dbgstr_rect(&windows[a].allocated_rect), \ 200 wine_dbgstr_rect(&windows[b].allocated_rect)) 201 202 static void test_setpos(void) 203 { 204 APPBARDATA abd; 205 RECT rc; 206 int screen_width, screen_height; 207 BOOL ret; 208 int org_bottom1; 209 210 screen_width = GetSystemMetrics(SM_CXSCREEN); 211 screen_height = GetSystemMetrics(SM_CYSCREEN); 212 213 /* create and register windows[0] */ 214 windows[0].hwnd = CreateWindowExA(WS_EX_TOOLWINDOW|WS_EX_TOPMOST, 215 testwindow_class, testwindow_class, WS_POPUP|WS_VISIBLE, 0, 0, 0, 0, 216 NULL, NULL, NULL, NULL); 217 ok(windows[0].hwnd != NULL, "couldn't create window\n"); 218 do_events(); 219 abd.cbSize = sizeof(abd); 220 abd.hWnd = windows[0].hwnd; 221 abd.uCallbackMessage = MSG_APPBAR; 222 ret = SHAppBarMessage(ABM_NEW, &abd); 223 ok(ret == TRUE, "SHAppBarMessage returned %i\n", ret); 224 225 /* ABM_NEW should return FALSE if the window is already registered */ 226 ret = SHAppBarMessage(ABM_NEW, &abd); 227 ok(ret == FALSE, "SHAppBarMessage returned %i\n", ret); 228 do_events(); 229 230 /* dock windows[0] to the bottom of the screen */ 231 windows[0].registered = TRUE; 232 windows[0].to_be_deleted = FALSE; 233 windows[0].edge = ABE_BOTTOM; 234 SetRect(&windows[0].desired_rect, 0, screen_height - 15, screen_width, screen_height); 235 SetWindowLongPtrA(windows[0].hwnd, GWLP_USERDATA, (LONG_PTR)&windows[0]); 236 testwindow_setpos(windows[0].hwnd); 237 do_events(); 238 239 /* create and register windows[1] */ 240 windows[1].hwnd = CreateWindowExA(WS_EX_TOOLWINDOW|WS_EX_TOPMOST, 241 testwindow_class, testwindow_class, WS_POPUP|WS_VISIBLE, 0, 0, 0, 0, 242 NULL, NULL, NULL, NULL); 243 ok(windows[1].hwnd != NULL, "couldn't create window\n"); 244 abd.hWnd = windows[1].hwnd; 245 ret = SHAppBarMessage(ABM_NEW, &abd); 246 ok(ret == TRUE, "SHAppBarMessage returned %i\n", ret); 247 248 /* dock windows[1] to the bottom of the screen */ 249 windows[1].registered = TRUE; 250 windows[1].to_be_deleted = FALSE; 251 windows[1].edge = ABE_BOTTOM; 252 SetRect(&windows[1].desired_rect, 0, screen_height - 10, screen_width, screen_height); 253 SetWindowLongPtrA(windows[1].hwnd, GWLP_USERDATA, (LONG_PTR)&windows[1]); 254 testwindow_setpos(windows[1].hwnd); 255 256 /* the windows are adjusted to they don't overlap */ 257 do_events_until(no_appbars_intersect); 258 test_window_rects(0, 1); 259 260 /* make windows[0] larger, forcing windows[1] to move out of its way */ 261 windows[0].desired_rect.top = screen_height - 20; 262 testwindow_setpos(windows[0].hwnd); 263 do_events_until(no_appbars_intersect); 264 test_window_rects(0, 1); 265 266 /* create and register windows[2] */ 267 windows[2].hwnd = CreateWindowExA(WS_EX_TOOLWINDOW|WS_EX_TOPMOST, 268 testwindow_class, testwindow_class, WS_POPUP|WS_VISIBLE, 0, 0, 0, 0, 269 NULL, NULL, NULL, NULL); 270 ok(windows[2].hwnd != NULL, "couldn't create window\n"); 271 do_events(); 272 273 abd.hWnd = windows[2].hwnd; 274 ret = SHAppBarMessage(ABM_NEW, &abd); 275 ok(ret == TRUE, "SHAppBarMessage returned %i\n", ret); 276 277 /* dock windows[2] to the bottom of the screen */ 278 windows[2].registered = TRUE; 279 windows[2].to_be_deleted = FALSE; 280 windows[2].edge = ABE_BOTTOM; 281 SetRect(&windows[2].desired_rect, 0, screen_height - 10, screen_width, screen_height); 282 SetWindowLongPtrA(windows[2].hwnd, GWLP_USERDATA, (LONG_PTR)&windows[2]); 283 testwindow_setpos(windows[2].hwnd); 284 285 do_events_until(no_appbars_intersect); 286 test_window_rects(0, 1); 287 test_window_rects(0, 2); 288 test_window_rects(1, 2); 289 290 /* move windows[2] to the right side of the screen */ 291 windows[2].edge = ABE_RIGHT; 292 SetRect(&windows[2].desired_rect, screen_width - 15, 0, screen_width, screen_height); 293 testwindow_setpos(windows[2].hwnd); 294 295 do_events_until(no_appbars_intersect); 296 test_window_rects(0, 1); 297 test_window_rects(0, 2); 298 test_window_rects(1, 2); 299 300 /* move windows[1] to the top of the screen */ 301 windows[1].edge = ABE_TOP; 302 SetRect(&windows[1].desired_rect, 0, 0, screen_width, 15); 303 testwindow_setpos(windows[1].hwnd); 304 305 do_events_until(no_appbars_intersect); 306 test_window_rects(0, 1); 307 test_window_rects(0, 2); 308 test_window_rects(1, 2); 309 310 /* move windows[1] back to the bottom of the screen */ 311 windows[1].edge = ABE_BOTTOM; 312 SetRect(&windows[1].desired_rect, 0, screen_height - 10, screen_width, screen_height); 313 testwindow_setpos(windows[1].hwnd); 314 315 do_events_until(no_appbars_intersect); 316 test_window_rects(0, 1); 317 test_window_rects(0, 2); 318 test_window_rects(1, 2); 319 320 /* removing windows[0] will cause windows[1] to move down into its space */ 321 expected_bottom = max(windows[0].allocated_rect.bottom, windows[1].allocated_rect.bottom); 322 org_bottom1 = windows[1].allocated_rect.bottom; 323 ok(windows[0].allocated_rect.bottom > windows[1].allocated_rect.bottom, 324 "Expected windows[0] to be lower than windows[1]\n"); 325 326 abd.hWnd = windows[0].hwnd; 327 windows[0].to_be_deleted = TRUE; 328 ret = SHAppBarMessage(ABM_REMOVE, &abd); 329 ok(ret == TRUE, "SHAppBarMessage returned %i\n", ret); 330 windows[0].registered = FALSE; 331 DestroyWindow(windows[0].hwnd); 332 333 do_events_until(got_expected_bottom); 334 335 if (windows[1].allocated_rect.bottom == org_bottom1) 336 win_skip("Some broken Vista boxes don't move the higher appbar down\n"); 337 else 338 ok(windows[1].allocated_rect.bottom == expected_bottom, 339 "windows[1]'s bottom is %i, expected %i\n", 340 windows[1].allocated_rect.bottom, expected_bottom); 341 342 test_window_rects(1, 2); 343 344 /* remove the other windows */ 345 abd.hWnd = windows[1].hwnd; 346 windows[1].to_be_deleted = TRUE; 347 ret = SHAppBarMessage(ABM_REMOVE, &abd); 348 ok(ret == TRUE, "SHAppBarMessage returned %i\n", ret); 349 windows[1].registered = FALSE; 350 DestroyWindow(windows[1].hwnd); 351 352 abd.hWnd = windows[2].hwnd; 353 windows[2].to_be_deleted = TRUE; 354 ret = SHAppBarMessage(ABM_REMOVE, &abd); 355 ok(ret == TRUE, "SHAppBarMessage returned %i\n", ret); 356 windows[2].registered = FALSE; 357 DestroyWindow(windows[2].hwnd); 358 } 359 360 static void test_appbarget(void) 361 { 362 APPBARDATA abd; 363 HWND hwnd, foregnd, unset_hwnd; 364 UINT_PTR ret; 365 366 memset(&abd, 0xcc, sizeof(abd)); 367 memset(&unset_hwnd, 0xcc, sizeof(unset_hwnd)); 368 abd.cbSize = sizeof(abd); 369 abd.uEdge = ABE_BOTTOM; 370 371 hwnd = (HWND)SHAppBarMessage(ABM_GETAUTOHIDEBAR, &abd); 372 ok(hwnd == NULL || IsWindow(hwnd), "ret %p which is not a window\n", hwnd); 373 ok(abd.hWnd == unset_hwnd, "hWnd overwritten %p\n",abd.hWnd); 374 375 if (!pMonitorFromWindow) 376 { 377 win_skip("MonitorFromWindow is not available\n"); 378 } 379 else 380 { 381 /* Presumably one can pass a hwnd with ABM_GETAUTOHIDEBAR to specify a monitor. 382 Pass the foreground window and check */ 383 foregnd = GetForegroundWindow(); 384 if(foregnd) 385 { 386 abd.hWnd = foregnd; 387 hwnd = (HWND)SHAppBarMessage(ABM_GETAUTOHIDEBAR, &abd); 388 ok(hwnd == NULL || IsWindow(hwnd), "ret %p which is not a window\n", hwnd); 389 ok(abd.hWnd == foregnd, "hWnd overwritten\n"); 390 if(hwnd) 391 { 392 HMONITOR appbar_mon, foregnd_mon; 393 appbar_mon = pMonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); 394 foregnd_mon = pMonitorFromWindow(foregnd, MONITOR_DEFAULTTONEAREST); 395 ok(appbar_mon == foregnd_mon, "Windows on different monitors\n"); 396 } 397 } 398 } 399 400 memset(&abd, 0xcc, sizeof(abd)); 401 abd.cbSize = sizeof(abd); 402 ret = SHAppBarMessage(ABM_GETTASKBARPOS, &abd); 403 if(ret) 404 { 405 ok(abd.hWnd == (HWND)0xcccccccc, "hWnd overwritten\n"); 406 ok(abd.uEdge <= ABE_BOTTOM || 407 broken(abd.uEdge == 0xcccccccc), /* Some Win95 and NT4 */ 408 "uEdge not returned\n"); 409 ok(abd.rc.left != 0xcccccccc, "rc not updated\n"); 410 } 411 412 return; 413 } 414 415 static void test_GetCurrentProcessExplicitAppUserModelID(void) 416 { 417 WCHAR *appid; 418 HRESULT hr; 419 420 if (!pGetCurrentProcessExplicitAppUserModelID) 421 { 422 win_skip("GetCurrentProcessExplicitAppUserModelID() is not supported.\n"); 423 return; 424 } 425 426 if (0) /* crashes on native */ 427 hr = pGetCurrentProcessExplicitAppUserModelID(NULL); 428 429 appid = (void*)0xdeadbeef; 430 hr = pGetCurrentProcessExplicitAppUserModelID(&appid); 431 todo_wine 432 ok(hr == E_FAIL, "got 0x%08x\n", hr); 433 ok(appid == NULL, "got %p\n", appid); 434 } 435 436 START_TEST(appbar) 437 { 438 HMODULE huser32, hshell32; 439 440 huser32 = GetModuleHandleA("user32.dll"); 441 hshell32 = GetModuleHandleA("shell32.dll"); 442 pMonitorFromWindow = (void*)GetProcAddress(huser32, "MonitorFromWindow"); 443 pGetCurrentProcessExplicitAppUserModelID = (void*)GetProcAddress(hshell32, "GetCurrentProcessExplicitAppUserModelID"); 444 445 register_testwindow_class(); 446 447 test_setpos(); 448 test_appbarget(); 449 test_GetCurrentProcessExplicitAppUserModelID(); 450 } 451