From 3783adb840b8e9628b2b0baf590f3d5a2759c587 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Thu, 6 Mar 2008 10:50:19 -0800 Subject: [PATCH] user32: Implement a better stub of BroadcastSystemMessage --- dlls/user32/message.c | 195 +++++++++++++++++++-- dlls/user32/tests/Makefile.in | 1 + dlls/user32/tests/broadcast.c | 370 ++++++++++++++++++++++++++++++++++++++++ dlls/user32/user32.spec | 4 +- include/wine/server_protocol.h | 20 ++- include/winuser.h | 10 + server/protocol.def | 10 + server/request.h | 2 + server/trace.c | 15 ++ server/user.h | 1 + server/window.c | 5 + server/winstation.c | 34 ++++ 12 files changed, 646 insertions(+), 21 deletions(-) create mode 100644 dlls/user32/tests/broadcast.c diff --git a/dlls/user32/message.c b/dlls/user32/message.c index eb0168f..da18fc4 100644 --- a/dlls/user32/message.c +++ b/dlls/user32/message.c @@ -2,6 +2,7 @@ * Window messaging support * * Copyright 2001 Alexandre Julliard + * Copyright 2008 Maarten Lankhorst * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -3336,6 +3337,116 @@ UINT WINAPI RegisterWindowMessageW( LPCWSTR str ) return ret; } +typedef struct BroadCastParameters +{ + DWORD flags; + LPDWORD recipients; + UINT msg; + WPARAM wp; + LPARAM lp; + DWORD success; +} BroadcastParm; + +static BOOL CALLBACK bcast_childwindow( HWND hw, LPARAM lp ) +{ + BroadcastParm *parm = (BroadcastParm*)lp; + DWORD timeoutflags; + DWORD_PTR retval = 0; + LONG lresult; + + TRACE("Telling window %p\n", hw); + + if (parm->flags & BSF_IGNORECURRENTTASK && WIN_IsCurrentProcess(hw)) + return TRUE; + + switch (parm->flags & (BSF_QUERY|BSF_POSTMESSAGE|BSF_SENDNOTIFYMESSAGE)) + { + case BSF_POSTMESSAGE: + PostMessageW( hw, parm->msg, parm->wp, parm->lp ); + break; + + case BSF_SENDNOTIFYMESSAGE: + SendNotifyMessageW( hw, parm->msg, parm->wp, parm->lp ); + break; + + case BSF_QUERY: + switch (parm->flags & (BSF_FORCEIFHUNG|BSF_NOHANG|BSF_NOTIMEOUTIFNOTHUNG)) + { + case BSF_NOHANG: + case BSF_FORCEIFHUNG: + timeoutflags = SMTO_ABORTIFHUNG; + break; + case BSF_NOTIMEOUTIFNOTHUNG: + timeoutflags = SMTO_NOTIMEOUTIFNOTHUNG; + break; + default: + timeoutflags = SMTO_NORMAL; + break; + } + lresult = SendMessageTimeoutW( hw, parm->msg, parm->wp, parm->lp, timeoutflags, 2000, &retval ); + if (!lresult && GetLastError() == ERROR_TIMEOUT) + { + WARN("Timed out!\n"); + return parm->flags & BSF_FORCEIFHUNG; + } + return retval != BROADCAST_QUERY_DENY; + } + + return TRUE; +} + +static BOOL CALLBACK bcast_topwindow(HWND top_window, LPARAM lp) +{ + BOOL ret; + + TRACE("top_window: %p\n", top_window); + ret = EnumChildWindows( top_window, bcast_childwindow, lp ); + TRACE("-->%d\n", ret); + return ret; +} + +static BOOL WINAPI WIN_EnumTopWindows( HWINSTA winsta, LPARAM lp ) +{ + unsigned int index = 0; + HWND top_window; + BOOL ret = TRUE, sret; + + while (ret) + { + SERVER_START_REQ( enum_top_windows ) + { + req->winstation = winsta; + req->index = index; + sret = wine_server_call( req ); + index = reply->next; + top_window = reply->top_window; + } + SERVER_END_REQ; + if (sret == STATUS_NO_MORE_ENTRIES) + break; + else if (sret) + { + WARN("Error %08x occured\n", sret); + SetLastError( RtlNtStatusToDosError( sret ) ); + return FALSE; + } + ret = bcast_topwindow( top_window, lp ); + } + return ret; +} + +static BOOL CALLBACK bcast_winsta( LPWSTR winsta, LPARAM lp ) +{ + BOOL ret; + HWINSTA hwinsta = OpenWindowStationW( winsta, FALSE, WINSTA_ENUMDESKTOPS ); + TRACE("hwinsta: %p/%s/%08x\n", hwinsta, debugstr_w( winsta ), GetLastError()); + if (!hwinsta) + return TRUE; + ret = WIN_EnumTopWindows( hwinsta, lp ); + CloseWindowStation( hwinsta ); + TRACE("-->%d\n", ret); + return ret; +} /*********************************************************************** * BroadcastSystemMessageA (USER32.@) @@ -3343,17 +3454,7 @@ UINT WINAPI RegisterWindowMessageW( LPCWSTR str ) */ LONG WINAPI BroadcastSystemMessageA( DWORD flags, LPDWORD recipients, UINT msg, WPARAM wp, LPARAM lp ) { - if ((*recipients & BSM_APPLICATIONS) || (*recipients == BSM_ALLCOMPONENTS)) - { - FIXME( "(%08x,%08x,%08x,%08lx,%08lx): semi-stub!\n", flags, *recipients, msg, wp, lp ); - PostMessageA( HWND_BROADCAST, msg, wp, lp ); - return 1; - } - else - { - FIXME( "(%08x,%08x,%08x,%08lx,%08lx): stub!\n", flags, *recipients, msg, wp, lp); - return -1; - } + return BroadcastSystemMessageExA( flags, recipients, msg, wp, lp, NULL ); } @@ -3362,19 +3463,77 @@ LONG WINAPI BroadcastSystemMessageA( DWORD flags, LPDWORD recipients, UINT msg, */ LONG WINAPI BroadcastSystemMessageW( DWORD flags, LPDWORD recipients, UINT msg, WPARAM wp, LPARAM lp ) { - if ((*recipients & BSM_APPLICATIONS) || (*recipients == BSM_ALLCOMPONENTS)) + return BroadcastSystemMessageExW( flags, recipients, msg, wp, lp, NULL ); +} + +/*********************************************************************** + * BroadcastSystemMessageExA (USER32.@) + */ +LONG WINAPI BroadcastSystemMessageExA( DWORD flags, LPDWORD recipients, UINT msg, WPARAM wp, LPARAM lp, PBSMINFO pinfo ) +{ + map_wparam_AtoW( msg, &wp, WMCHAR_MAP_NOMAPPING ); + return BroadcastSystemMessageExW( flags, recipients, msg, wp, lp, NULL ); +} + + +/*********************************************************************** + * BroadcastSystemMessageExW (USER32.@) + */ +LONG WINAPI BroadcastSystemMessageExW( DWORD flags, LPDWORD recipients, UINT msg, WPARAM wp, LPARAM lp, PBSMINFO pinfo ) +{ + BroadcastParm parm; + DWORD recips = BSM_ALLCOMPONENTS; + BOOL ret = TRUE; + DWORD lasterror; + + TRACE("Flags: %08x, recipients: %p(0x%x), msg: %04x, wparam: %08lx, lparam: %08lx\n", flags, recipients, + (recipients ? *recipients : recips), msg, wp, lp); + + if (flags > BSF_LUID) { - FIXME( "(%08x,%08x,%08x,%08lx,%08lx): semi-stub!\n", flags, *recipients, msg, wp, lp ); - PostMessageW( HWND_BROADCAST, msg, wp, lp ); - return 1; + SetLastError(ERROR_INVALID_PARAMETER); + return 0; } + + /* I don't know 100% for sure if this is what the windows implementation does, but it fits the tests */ + if (flags & BSF_QUERY) + flags &= ~(BSF_POSTMESSAGE|BSF_SENDNOTIFYMESSAGE); + else if (flags & BSF_POSTMESSAGE) + flags &= ~BSF_SENDNOTIFYMESSAGE; else + flags |= BSF_SENDNOTIFYMESSAGE; + + if (!!(flags & BSF_FORCEIFHUNG) + !!(flags & BSF_NOHANG) + !!(flags & BSF_NOTIMEOUTIFNOTHUNG) > 1) { - FIXME( "(%08x,%08x,%08x,%08lx,%08lx): stub!\n", flags, *recipients, msg, wp, lp ); - return -1; + FIXME("Not handling conflicting flags yet!\n"); + flags &= ~(BSF_FORCEIFHUNG|BSF_NOHANG|BSF_NOTIMEOUTIFNOTHUNG); + flags |= BSF_FORCEIFHUNG; } -} + if (!recipients) + recipients = &recips; + + if ( pinfo && flags & BSF_QUERY ) + FIXME("Not returning PBSMINFO information yet\n"); + + parm.flags = flags; + parm.recipients = recipients; + parm.msg = msg; + parm.wp = wp; + parm.lp = lp; + + lasterror = GetLastError(); + if (*recipients & BSM_ALLDESKTOPS || !(GetVersion() & 0x80000000) || *recipients == BSM_ALLCOMPONENTS) + ret = EnumWindowStationsW(bcast_winsta, (LONG_PTR)&parm); + else if (*recipients & BSM_APPLICATIONS) + ret = EnumWindows(bcast_childwindow, (LONG_PTR)&parm); + else + FIXME("Recipients %08x not supported!\n", *recipients); + if (ret > 0 && !GetLastError()) + SetLastError(lasterror); + + return ret; +} /*********************************************************************** * SetMessageQueue (USER32.@) diff --git a/dlls/user32/tests/Makefile.in b/dlls/user32/tests/Makefile.in index c3794cc..102653f 100644 --- a/dlls/user32/tests/Makefile.in +++ b/dlls/user32/tests/Makefile.in @@ -6,6 +6,7 @@ TESTDLL = user32.dll IMPORTS = user32 gdi32 advapi32 kernel32 CTESTS = \ + broadcast.c \ class.c \ clipboard.c \ combo.c \ diff --git a/dlls/user32/tests/broadcast.c b/dlls/user32/tests/broadcast.c new file mode 100644 index 0000000..d6fd838 --- /dev/null +++ b/dlls/user32/tests/broadcast.c @@ -0,0 +1,370 @@ +/* + * Unit tests for broadcastwindowmessage + * + * Copyright 2008 Maarten Lankhorst + * + * 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 + */ + +#include +#include +#include + +#define _WIN32_WINNT 0x0501 + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" + +#include "wine/test.h" + +typedef LONG WINAPI (*PBROADCAST)( DWORD,LPDWORD,UINT,WPARAM,LPARAM ); +typedef LONG WINAPI (*PBROADCASTEX)( DWORD,LPDWORD,UINT,WPARAM,LPARAM,PBSMINFO ); +static PBROADCAST pBroadcastA; +static PBROADCAST pBroadcastW; +static PBROADCASTEX pBroadcastExA; +static PBROADCASTEX pBroadcastExW; +static HANDLE hevent; + +static LRESULT WINAPI main_window_procA(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + if (msg == WM_NULL) + { + trace("main_window_procA: Sleeping for %lu ms\n", wparam); + if (wparam) + { + if (WaitForSingleObject(hevent, wparam) == WAIT_TIMEOUT) + SetEvent(hevent); + } + trace("main_window_procA: Returning WM_NULL with parameter %08lx\n", lparam); + return lparam; + } + + return DefWindowProcA(hwnd, msg, wparam, lparam); +} + +static BOOL init_procs(void) +{ + WNDCLASSA cls; + HANDLE user32 = GetModuleHandle("user32"); + pBroadcastA = (PBROADCAST)GetProcAddress(user32, "BroadcastSystemMessageA"); + if (!pBroadcastA) + pBroadcastA = (PBROADCAST)GetProcAddress(user32, "BroadcastSystemMessage"); + ok(pBroadcastA != NULL, "No BroadcastSystemMessage found\n"); + if (!pBroadcastA) + return FALSE; + + pBroadcastW = (PBROADCAST)GetProcAddress(user32, "BroadcastSystemMessageW"); + pBroadcastExA = (PBROADCASTEX)GetProcAddress(user32, "BroadcastSystemMessageExA"); + pBroadcastExW = (PBROADCASTEX)GetProcAddress(user32, "BroadcastSystemMessageExW"); + + hevent = CreateEventA(NULL, TRUE, FALSE, "Asynchronous checking event"); + + cls.style = CS_DBLCLKS; + cls.lpfnWndProc = main_window_procA; + cls.cbClsExtra = 0; + cls.cbWndExtra = 0; + cls.hInstance = GetModuleHandleA(0); + cls.hIcon = 0; + cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW); + cls.hbrBackground = GetStockObject(WHITE_BRUSH); + cls.lpszMenuName = NULL; + cls.lpszClassName = "MainWindowClass"; + + if (!RegisterClassA(&cls)) + return 0; + + if (!CreateWindowExA(0, "MainWindowClass", "Main window", WS_CAPTION | WS_SYSMENU | + WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP, 100, 100, 200, + 200, 0, 0, GetModuleHandle(0), NULL)) + return FALSE; + return TRUE; +} + +static void test_parameters(PBROADCAST broadcast) +{ + LONG ret; + DWORD recips; + + SetLastError(0xcafebabe); + recips = BSM_APPLICATIONS; + ret = broadcast( 0x80000000, &recips, WM_NULL, 0, 0 ); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "Last error: %08x\n", GetLastError()); + ok(!ret, "Returned: %d\n", ret); + + SetLastError(0xcafebabe); + recips = BSM_APPLICATIONS; + ret = broadcast( 0x80000000, &recips, WM_NULL, 0, 0 ); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "Last error: %08x\n", GetLastError()); + ok(!ret, "Returned: %d\n", ret); + +#if 0 /* TODO: Check the hang flags */ + SetLastError(0xcafebabe); + recips = BSM_APPLICATIONS; + ret = broadcast( BSF_QUERY|(BSF_NOHANG|BSF_FORCEIFHUNG), &recips, WM_NULL, 30000, 0 ); + ok(0, "Last error: %08x\n", GetLastError()); + ok(0, "Returned: %d\n", ret); + + SetLastError(0xcafebabe); + recips = BSM_APPLICATIONS; + ret = broadcast( BSF_QUERY|(BSF_NOHANG|BSF_NOTIMEOUTIFNOTHUNG), &recips, WM_NULL, 30000, 0 ); + ok(0, "Last error: %08x\n", GetLastError()); + ok(0, "Returned: %d\n", ret); + + SetLastError(0xcafebabe); + recips = BSM_APPLICATIONS; + ret = broadcast( BSF_QUERY|(BSF_NOTIMEOUTIFNOTHUNG|BSF_FORCEIFHUNG), &recips, WM_NULL, 30000, 0 ); + ok(0, "Last error: %08x\n", GetLastError()); + ok(0, "Returned: %d\n", ret); + + SetLastError(0xcafebabe); + recips = BSM_APPLICATIONS; + ret = broadcast( BSF_POSTMESSAGE|(BSF_NOTIMEOUTIFNOTHUNG|BSF_FORCEIFHUNG), &recips, WM_NULL, 30000, 0 ); + ok(0, "Last error: %08x\n", GetLastError()); + ok(0, "Returned: %d\n", ret); +#endif + + SetLastError(0xcafebabe); + recips = BSM_APPLICATIONS; + ResetEvent(hevent); + ret = broadcast( BSF_POSTMESSAGE|BSF_QUERY, &recips, WM_NULL, 100, 0 ); + ok(GetLastError() == 0xcafebabe, "Last error: %08x\n", GetLastError()); + ok(ret==1, "Returned: %d\n", ret); + ok(WaitForSingleObject(hevent, 0) != WAIT_TIMEOUT, "Asynchronous message sent instead\n"); + PulseEvent(hevent); + + SetLastError(0xcafebabe); + recips = BSM_APPLICATIONS; + ret = broadcast( BSF_POSTMESSAGE|BSF_SENDNOTIFYMESSAGE, &recips, WM_NULL, 100, 0 ); + ok(GetLastError() == 0xcafebabe, "Last error: %08x\n", GetLastError()); + ok(ret==1, "Returned: %d\n", ret); + ok(WaitForSingleObject(hevent, 0) != WAIT_OBJECT_0, "Synchronous message sent instead\n"); + PulseEvent(hevent); + + SetLastError(0xcafebabe); + recips = BSM_APPLICATIONS; + ret = broadcast( BSF_SENDNOTIFYMESSAGE, &recips, WM_NULL, 100, BROADCAST_QUERY_DENY ); + ok(GetLastError() == 0xcafebabe, "Last error: %08x\n", GetLastError()); + ok(ret==1, "Returned: %d\n", ret); + ok(WaitForSingleObject(hevent, 0) != WAIT_TIMEOUT, "Asynchronous message sent instead\n"); + PulseEvent(hevent); + + SetLastError(0xcafebabe); + recips = BSM_APPLICATIONS; + ret = broadcast( BSF_SENDNOTIFYMESSAGE|BSF_QUERY, &recips, WM_NULL, 100, BROADCAST_QUERY_DENY ); + ok(GetLastError() == 0xcafebabe, "Last error: %08x\n", GetLastError()); + ok(!ret, "Returned: %d\n", ret); + ok(WaitForSingleObject(hevent, 0) != WAIT_TIMEOUT, "Asynchronous message sent instead\n"); + PulseEvent(hevent); + + SetLastError(0xcafebabe); + recips = BSM_APPLICATIONS; + ret = broadcast( 0, &recips, WM_NULL, 100, 0 ); + ok(GetLastError() == 0xcafebabe, "Last error: %08x\n", GetLastError()); + ok(ret==1, "Returned: %d\n", ret); + ok(WaitForSingleObject(hevent, 0) != WAIT_TIMEOUT, "Asynchronous message sent instead\n"); + PulseEvent(hevent); +} + +/* BSF_SENDNOTIFYMESSAGE and BSF_QUERY are both synchronous within the same process + * However you should be able to distinguish them by sending the BROADCAST_QUERY_DENY flag + */ + +static void test_parametersEx(PBROADCASTEX broadcastex) +{ + LONG ret; + DWORD recips; + + SetLastError(0xcafebabe); + recips = BSM_APPLICATIONS; + ret = broadcastex( 0x80000000, &recips, WM_NULL, 0, 0, NULL ); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "Last error: %08x\n", GetLastError()); + ok(!ret, "Returned: %d\n", ret); + + SetLastError(0xcafebabe); + recips = BSM_APPLICATIONS; + ret = broadcastex( 0x80000000, &recips, WM_NULL, 0, 0, NULL ); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "Last error: %08x\n", GetLastError()); + ok(!ret, "Returned: %d\n", ret); + +#if 0 /* TODO: Check the hang flags */ + SetLastError(0xcafebabe); + recips = BSM_APPLICATIONS; + ret = broadcast( BSF_QUERY|(BSF_NOHANG|BSF_FORCEIFHUNG), &recips, WM_NULL, 30000, 0, NULL ); + ok(0, "Last error: %08x\n", GetLastError()); + ok(0, "Returned: %d\n", ret); + + SetLastError(0xcafebabe); + recips = BSM_APPLICATIONS; + ret = broadcast( BSF_QUERY|(BSF_NOHANG|BSF_NOTIMEOUTIFNOTHUNG), &recips, WM_NULL, 30000, 0, NULL ); + ok(0, "Last error: %08x\n", GetLastError()); + ok(0, "Returned: %d\n", ret); + + SetLastError(0xcafebabe); + recips = BSM_APPLICATIONS; + ret = broadcast( BSF_QUERY|(BSF_NOTIMEOUTIFNOTHUNG|BSF_FORCEIFHUNG), &recips, WM_NULL, 30000, 0, NULL ); + ok(0, "Last error: %08x\n", GetLastError()); + ok(0, "Returned: %d\n", ret); + + SetLastError(0xcafebabe); + recips = BSM_APPLICATIONS; + ret = broadcast( BSF_POSTMESSAGE|(BSF_NOTIMEOUTIFNOTHUNG|BSF_FORCEIFHUNG), &recips, WM_NULL, 30000, 0, NULL ); + ok(0, "Last error: %08x\n", GetLastError()); + ok(0, "Returned: %d\n", ret); +#endif + + SetLastError(0xcafebabe); + recips = BSM_APPLICATIONS; + ResetEvent(hevent); + ret = broadcastex( BSF_POSTMESSAGE|BSF_QUERY, &recips, WM_NULL, 100, 0, NULL ); + ok(GetLastError() == 0xcafebabe, "Last error: %08x\n", GetLastError()); + ok(ret==1, "Returned: %d\n", ret); + ok(WaitForSingleObject(hevent, 0) != WAIT_TIMEOUT, "Asynchronous message sent instead\n"); + PulseEvent(hevent); + + SetLastError(0xcafebabe); + recips = BSM_APPLICATIONS; + ret = broadcastex( BSF_POSTMESSAGE|BSF_SENDNOTIFYMESSAGE, &recips, WM_NULL, 100, 0, NULL ); + ok(GetLastError() == 0xcafebabe, "Last error: %08x\n", GetLastError()); + ok(ret==1, "Returned: %d\n", ret); + ok(WaitForSingleObject(hevent, 0) != WAIT_OBJECT_0, "Synchronous message sent instead\n"); + PulseEvent(hevent); + + SetLastError(0xcafebabe); + recips = BSM_APPLICATIONS; + ret = broadcastex( BSF_SENDNOTIFYMESSAGE, &recips, WM_NULL, 100, BROADCAST_QUERY_DENY, NULL ); + ok(GetLastError() == 0xcafebabe, "Last error: %08x\n", GetLastError()); + ok(ret==1, "Returned: %d\n", ret); + ok(WaitForSingleObject(hevent, 0) != WAIT_TIMEOUT, "Asynchronous message sent instead\n"); + PulseEvent(hevent); + + SetLastError(0xcafebabe); + recips = BSM_APPLICATIONS; + ret = broadcastex( BSF_SENDNOTIFYMESSAGE|BSF_QUERY, &recips, WM_NULL, 100, BROADCAST_QUERY_DENY, NULL ); + ok(GetLastError() == 0xcafebabe, "Last error: %08x\n", GetLastError()); + ok(!ret, "Returned: %d\n", ret); + ok(WaitForSingleObject(hevent, 0) != WAIT_TIMEOUT, "Asynchronous message sent instead\n"); + PulseEvent(hevent); + + SetLastError(0xcafebabe); + recips = BSM_APPLICATIONS; + ret = broadcastex( 0, &recips, WM_NULL, 100, 0, NULL ); + ok(GetLastError() == 0xcafebabe, "Last error: %08x\n", GetLastError()); + ok(ret==1, "Returned: %d\n", ret); + ok(WaitForSingleObject(hevent, 0) != WAIT_TIMEOUT, "Asynchronous message sent instead\n"); + PulseEvent(hevent); +} + +typedef BOOL WINAPI (*pOpenProcessToken)(HANDLE, DWORD, HANDLE*); +pOpenProcessToken pOpen; + +typedef BOOL WINAPI (*pAdjustTokenPrivileges)(HANDLE, BOOL, PTOKEN_PRIVILEGES, DWORD, PTOKEN_PRIVILEGES, PDWORD); +pAdjustTokenPrivileges pAdjust; + +static void test_noprivileges() +{ + HANDLE advapi32 = GetModuleHandleA("advapi32"); + HANDLE token; + DWORD recips; + BOOL ret; + + pOpen = (pOpenProcessToken)GetProcAddress(advapi32, "OpenProcessToken"); + pAdjust = (pAdjustTokenPrivileges)GetProcAddress(advapi32, "AdjustTokenPrivileges"); + if (!pOpen || !pAdjust || !pOpen(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token)) + { + skip("Can't open security token for process\n"); + return; + } + if (!pAdjust(token, TRUE, NULL, 0, NULL, NULL)) + { + skip("Can't adjust security token for process\n"); + return; + } + + trace("Trying privileged edition!\n"); + SetLastError(0xcafebabe); + recips = BSM_ALLDESKTOPS; + ResetEvent(hevent); + ret = pBroadcastExW( BSF_QUERY, &recips, WM_NULL, 100, 0, NULL ); + todo_wine ok(GetLastError() == ERROR_PRIVILEGE_NOT_HELD, "Last error: %08x\n", GetLastError()); + ok(ret==1, "Returned: %d\n", ret); + ok(WaitForSingleObject(hevent, 0) != WAIT_TIMEOUT, "Asynchronous message sent instead\n"); + ok(recips == BSM_ALLDESKTOPS, "Received by: %08x\n", recips); + PulseEvent(hevent); + + SetLastError(0xcafebabe); + recips = BSM_ALLCOMPONENTS; + ResetEvent(hevent); + ret = pBroadcastExW( BSF_QUERY, &recips, WM_NULL, 100, 0, NULL ); + ok(GetLastError() == 0xcafebabe, "Last error: %08x\n", GetLastError()); + ok(ret==1, "Returned: %d\n", ret); + ok(WaitForSingleObject(hevent, 0) != WAIT_TIMEOUT, "Asynchronous message sent instead\n"); + ok(recips == BSM_ALLCOMPONENTS, "Received by: %08x\n", recips); + PulseEvent(hevent); + + SetLastError(0xcafebabe); + recips = BSM_ALLDESKTOPS|BSM_APPLICATIONS; + ResetEvent(hevent); + ret = pBroadcastExW( BSF_QUERY, &recips, WM_NULL, 100, 0, NULL ); + todo_wine ok(GetLastError() == ERROR_PRIVILEGE_NOT_HELD, "Last error: %08x\n", GetLastError()); + ok(ret==1, "Returned: %d\n", ret); + ok(WaitForSingleObject(hevent, 0) != WAIT_TIMEOUT, "Asynchronous message sent instead\n"); + ok(recips == (BSM_ALLDESKTOPS|BSM_APPLICATIONS), "Received by: %08x\n", recips); + PulseEvent(hevent); + + SetLastError(0xcafebabe); + recips = BSM_ALLDESKTOPS|BSM_APPLICATIONS; + ResetEvent(hevent); + ret = pBroadcastExW( BSF_QUERY, &recips, WM_NULL, 100, BROADCAST_QUERY_DENY, NULL ); + todo_wine ok(GetLastError() == ERROR_PRIVILEGE_NOT_HELD, "Last error: %08x\n", GetLastError()); + ok(!ret, "Returned: %d\n", ret); + ok(WaitForSingleObject(hevent, 0) != WAIT_TIMEOUT, "Asynchronous message sent instead\n"); + ok(recips == (BSM_ALLDESKTOPS|BSM_APPLICATIONS), "Received by: %08x\n", recips); + PulseEvent(hevent); +} + +START_TEST(broadcast) +{ + if (!init_procs()) + return; + + trace("Running BroadcastSystemMessageA tests\n"); + test_parameters(pBroadcastA); + if (pBroadcastW) + { + trace("Running BroadcastSystemMessageW tests\n"); + test_parameters(pBroadcastW); + } + else + skip("No BroadcastSystemMessageW, skipping\n"); + if (pBroadcastExA) + { + trace("Running BroadcastSystemMessageExA tests\n"); + test_parametersEx(pBroadcastExA); + } + else + skip("No BroadcastSystemMessageExA, skipping\n"); + if (pBroadcastExW) + { + trace("Running BroadcastSystemMessageExW tests\n"); + test_parametersEx(pBroadcastExW); + trace("Attempting privileges checking tests\n"); + test_noprivileges(); + } + else + skip("No BroadcastSystemMessageExW, skipping\n"); +} diff --git a/dlls/user32/user32.spec b/dlls/user32/user32.spec index 1ef9732..30c3f42 100644 --- a/dlls/user32/user32.spec +++ b/dlls/user32/user32.spec @@ -16,8 +16,8 @@ @ stdcall BringWindowToTop(long) @ stdcall BroadcastSystemMessage(long ptr long long long) BroadcastSystemMessageA @ stdcall BroadcastSystemMessageA(long ptr long long long) -# @ stub BroadcastSystemMessageExA -# @ stub BroadcastSystemMessageExW +@ stdcall BroadcastSystemMessageExA (long ptr long long long ptr) +@ stdcall BroadcastSystemMessageExW (long ptr long long long ptr) @ stdcall BroadcastSystemMessageW(long ptr long long long) # @ stub BuildReasonArray @ stdcall CalcChildScroll(long long) diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index a659d82..7852eee 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -3279,6 +3279,21 @@ struct enum_winstation_reply +struct enum_top_windows_request +{ + struct request_header __header; + obj_handle_t winstation; + unsigned int index; +}; +struct enum_top_windows_reply +{ + struct reply_header __header; + unsigned int next; + obj_handle_t top_window; +}; + + + struct create_desktop_request { struct request_header __header; @@ -4452,6 +4467,7 @@ enum request REQ_get_process_winstation, REQ_set_process_winstation, REQ_enum_winstation, + REQ_enum_top_windows, REQ_create_desktop, REQ_open_desktop, REQ_close_desktop, @@ -4692,6 +4708,7 @@ union generic_request struct get_process_winstation_request get_process_winstation_request; struct set_process_winstation_request set_process_winstation_request; struct enum_winstation_request enum_winstation_request; + struct enum_top_windows_request enum_top_windows_request; struct create_desktop_request create_desktop_request; struct open_desktop_request open_desktop_request; struct close_desktop_request close_desktop_request; @@ -4930,6 +4947,7 @@ union generic_reply struct get_process_winstation_reply get_process_winstation_reply; struct set_process_winstation_reply set_process_winstation_reply; struct enum_winstation_reply enum_winstation_reply; + struct enum_top_windows_reply enum_top_windows_reply; struct create_desktop_reply create_desktop_reply; struct open_desktop_reply open_desktop_reply; struct close_desktop_reply close_desktop_reply; @@ -4994,6 +5012,6 @@ union generic_reply struct add_fd_completion_reply add_fd_completion_reply; }; -#define SERVER_PROTOCOL_VERSION 337 +#define SERVER_PROTOCOL_VERSION 338 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */ diff --git a/include/winuser.h b/include/winuser.h index d5d0086..a865814 100644 --- a/include/winuser.h +++ b/include/winuser.h @@ -104,6 +104,13 @@ typedef struct tagUSEROBJECTFLAGS { DWORD dwFlags; } USEROBJECTFLAGS, *PUSEROBJECTFLAGS; +typedef struct tagBSMINFO { + UINT cbSize; + HDESK hdesk; + HWND hwnd; + LUID luid; +} BSMINFO, *PBSMINFO; + /* Window stations */ #define WINSTA_ENUMDESKTOPS 0x0001 #define WINSTA_READATTRIBUTES 0x0002 @@ -4323,6 +4330,9 @@ WINUSERAPI BOOL WINAPI BringWindowToTop(HWND); WINUSERAPI LONG WINAPI BroadcastSystemMessageA(DWORD,LPDWORD,UINT,WPARAM,LPARAM); WINUSERAPI LONG WINAPI BroadcastSystemMessageW(DWORD,LPDWORD,UINT,WPARAM,LPARAM); #define BroadcastSystemMessage WINELIB_NAME_AW(BroadcastSystemMessage) +WINUSERAPI LONG WINAPI BroadcastSystemMessageExA(DWORD,LPDWORD,UINT,WPARAM,LPARAM,PBSMINFO); +WINUSERAPI LONG WINAPI BroadcastSystemMessageExW(DWORD,LPDWORD,UINT,WPARAM,LPARAM,PBSMINFO); +#define BroadcastSystemMessageEx WINELIB_NAME_AW(BroadcastSystemMessageEx) WINUSERAPI void WINAPI CalcChildScroll(HWND, INT); WINUSERAPI BOOL WINAPI CallMsgFilterA(LPMSG,INT); WINUSERAPI BOOL WINAPI CallMsgFilterW(LPMSG,INT); diff --git a/server/protocol.def b/server/protocol.def index ff8bf52..2d11002 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -2385,6 +2385,16 @@ enum message_type @END +/* Enumerate all top window handles of a specified window station */ +@REQ(enum_top_windows) + obj_handle_t winstation; /* handle to the window station */ + unsigned int index; /* current index */ +@REPLY + unsigned int next; /* next index */ + obj_handle_t top_window; /* window station name */ +@END + + /* Create a desktop */ @REQ(create_desktop) unsigned int flags; /* desktop flags */ diff --git a/server/request.h b/server/request.h index 283ad97..0f75cf8 100644 --- a/server/request.h +++ b/server/request.h @@ -281,6 +281,7 @@ DECL_HANDLER(close_winstation); DECL_HANDLER(get_process_winstation); DECL_HANDLER(set_process_winstation); DECL_HANDLER(enum_winstation); +DECL_HANDLER(enum_top_windows); DECL_HANDLER(create_desktop); DECL_HANDLER(open_desktop); DECL_HANDLER(close_desktop); @@ -520,6 +521,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = (req_handler)req_get_process_winstation, (req_handler)req_set_process_winstation, (req_handler)req_enum_winstation, + (req_handler)req_enum_top_windows, (req_handler)req_create_desktop, (req_handler)req_open_desktop, (req_handler)req_close_desktop, diff --git a/server/trace.c b/server/trace.c index 61d0238..2f87b9f 100644 --- a/server/trace.c +++ b/server/trace.c @@ -2955,6 +2955,18 @@ static void dump_enum_winstation_reply( const struct enum_winstation_reply *req dump_varargs_unicode_str( cur_size ); } +static void dump_enum_top_windows_request( const struct enum_top_windows_request *req ) +{ + fprintf( stderr, " winstation=%p,", req->winstation ); + fprintf( stderr, " index=%08x", req->index ); +} + +static void dump_enum_top_windows_reply( const struct enum_top_windows_reply *req ) +{ + fprintf( stderr, " next=%08x,", req->next ); + fprintf( stderr, " top_window=%p", req->top_window ); +} + static void dump_create_desktop_request( const struct create_desktop_request *req ) { fprintf( stderr, " flags=%08x,", req->flags ); @@ -3952,6 +3964,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_get_process_winstation_request, (dump_func)dump_set_process_winstation_request, (dump_func)dump_enum_winstation_request, + (dump_func)dump_enum_top_windows_request, (dump_func)dump_create_desktop_request, (dump_func)dump_open_desktop_request, (dump_func)dump_close_desktop_request, @@ -4188,6 +4201,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_get_process_winstation_reply, (dump_func)0, (dump_func)dump_enum_winstation_reply, + (dump_func)dump_enum_top_windows_reply, (dump_func)dump_create_desktop_reply, (dump_func)dump_open_desktop_reply, (dump_func)0, @@ -4424,6 +4438,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = { "get_process_winstation", "set_process_winstation", "enum_winstation", + "enum_top_windows", "create_desktop", "open_desktop", "close_desktop", diff --git a/server/user.h b/server/user.h index 946b578..49198b9 100644 --- a/server/user.h +++ b/server/user.h @@ -137,6 +137,7 @@ extern struct thread *get_window_thread( user_handle_t handle ); extern user_handle_t window_from_point( struct desktop *desktop, int x, int y ); extern user_handle_t find_window_to_repaint( user_handle_t parent, struct thread *thread ); extern struct window_class *get_window_class( user_handle_t window ); +extern obj_handle_t get_window_handle( struct window *window ); /* window class functions */ diff --git a/server/window.c b/server/window.c index 2633cd2..b3707b8 100644 --- a/server/window.c +++ b/server/window.c @@ -122,6 +122,11 @@ static inline struct window *get_window( user_handle_t handle ) return ret; } +obj_handle_t get_window_handle( struct window *window ) +{ + return window->handle; +} + /* check if window is the desktop */ static inline int is_desktop_window( const struct window *win ) { diff --git a/server/winstation.c b/server/winstation.c index 9ad65f0..bd79db6 100644 --- a/server/winstation.c +++ b/server/winstation.c @@ -680,3 +680,37 @@ DECL_HANDLER(enum_desktop) release_object( winstation ); set_error( STATUS_NO_MORE_ENTRIES ); } + + +/* enumerate top windows of desktops */ +DECL_HANDLER(enum_top_windows) +{ + struct winstation *winstation; + struct desktop *desktop; + unsigned int index = req->index; + obj_handle_t handle; + + if (!(winstation = (struct winstation *)get_handle_obj( current->process, req->winstation, + WINSTA_ENUMDESKTOPS, &winstation_ops ))) + return; + + while ((handle = enumerate_handles( current->process, &desktop_ops, &index ))) + { + if (!(desktop = get_desktop_obj( current->process, handle, DESKTOP_ENUMERATE ))) + continue; + + if (desktop->winstation == winstation && desktop->top_window) + { + release_object( desktop ); + release_object( winstation ); + clear_error(); + reply->next = index; + reply->top_window = get_window_handle(desktop->top_window); + return; + } + release_object( desktop ); + } + + release_object( winstation ); + set_error( STATUS_NO_MORE_ENTRIES ); +} -- 1.5.4.1