From 328df2615b94c9d8ccb320730ab50db4d7c318a4 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 | 175 +++++++++++++++++-- dlls/user32/tests/Makefile.in | 1 + dlls/user32/tests/broadcast.c | 370 ++++++++++++++++++++++++++++++++++++++++ dlls/user32/user32.spec | 4 +- dlls/user32/win.h | 1 + dlls/user32/winstation.c | 66 +++++++- include/wine/server_protocol.h | 19 ++- include/winuser.h | 10 + server/protocol.def | 9 + server/request.h | 2 + server/trace.c | 15 ++ server/user.h | 1 + server/window.c | 5 + server/winstation.c | 32 ++++- 14 files changed, 682 insertions(+), 28 deletions(-) create mode 100644 dlls/user32/tests/broadcast.c diff --git a/dlls/user32/message.c b/dlls/user32/message.c index eb0168f..88e136b 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,96 @@ UINT WINAPI RegisterWindowMessageW( LPCWSTR str ) return ret; } +typedef struct BroadcastParm +{ + DWORD flags; + LPDWORD recipients; + UINT msg; + WPARAM wp; + LPARAM lp; + DWORD success; + HWINSTA winsta; +} 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_desktop( LPWSTR desktop, LPARAM lp ) +{ + BOOL ret; + HDESK hdesktop; + BroadcastParm *parm = (BroadcastParm*)lp; + + TRACE("desktop: %s\n", debugstr_w( desktop )); + + hdesktop = open_remote_desktop( parm->winsta, desktop, 0, FALSE, DESKTOP_ENUMERATE|DESKTOP_WRITEOBJECTS|STANDARD_RIGHTS_WRITE ); + if (!hdesktop) + return TRUE; + + ret = EnumDesktopWindows( hdesktop, bcast_childwindow, lp ); + CloseDesktop(hdesktop); + TRACE("-->%d\n", ret); + 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; + ((BroadcastParm *)lp)->winsta = hwinsta; + ret = EnumDesktopsW( hwinsta, bcast_desktop, lp ); + CloseWindowStation( hwinsta ); + TRACE("-->%d\n", ret); + return ret; +} /*********************************************************************** * BroadcastSystemMessageA (USER32.@) @@ -3343,17 +3434,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 +3443,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/dlls/user32/win.h b/dlls/user32/win.h index 581c520..a880eff 100644 --- a/dlls/user32/win.h +++ b/dlls/user32/win.h @@ -89,6 +89,7 @@ extern BOOL WIN_IsWindowDrawable( HWND hwnd, BOOL ) DECLSPEC_HIDDEN; extern HWND *WIN_ListChildren( HWND hwnd ) DECLSPEC_HIDDEN; extern LONG_PTR WIN_SetWindowLong( HWND hwnd, INT offset, UINT size, LONG_PTR newval, BOOL unicode ) DECLSPEC_HIDDEN; extern void MDI_CalcDefaultChildPos( HWND hwndClient, INT total, LPPOINT lpPos, INT delta, UINT *id ) DECLSPEC_HIDDEN; +extern HDESK WINAPI open_remote_desktop( HWINSTA hwinsta, LPCWSTR name, DWORD flags, BOOL inherit, ACCESS_MASK access ) DECLSPEC_HIDDEN; /* user lock */ extern void USER_Lock(void) DECLSPEC_HIDDEN; diff --git a/dlls/user32/winstation.c b/dlls/user32/winstation.c index 906f2ba..e0a5753 100644 --- a/dlls/user32/winstation.c +++ b/dlls/user32/winstation.c @@ -317,10 +317,7 @@ HDESK WINAPI OpenDesktopA( LPCSTR name, DWORD flags, BOOL inherit, ACCESS_MASK a } -/****************************************************************************** - * OpenDesktopW (USER32.@) - */ -HDESK WINAPI OpenDesktopW( LPCWSTR name, DWORD flags, BOOL inherit, ACCESS_MASK access ) +HDESK WINAPI open_remote_desktop( HWINSTA hwinsta, LPCWSTR name, DWORD flags, BOOL inherit, ACCESS_MASK access ) { HANDLE ret = 0; DWORD len = name ? strlenW(name) : 0; @@ -334,6 +331,7 @@ HDESK WINAPI OpenDesktopW( LPCWSTR name, DWORD flags, BOOL inherit, ACCESS_MASK req->flags = flags; req->access = access; req->attributes = OBJ_CASE_INSENSITIVE | (inherit ? OBJ_INHERIT : 0); + req->winsta = hwinsta; wine_server_add_data( req, name, len * sizeof(WCHAR) ); if (!wine_server_call( req )) ret = reply->handle; } @@ -342,6 +340,15 @@ HDESK WINAPI OpenDesktopW( LPCWSTR name, DWORD flags, BOOL inherit, ACCESS_MASK } +/****************************************************************************** + * OpenDesktopW (USER32.@) + */ +HDESK WINAPI OpenDesktopW( LPCWSTR name, DWORD flags, BOOL inherit, ACCESS_MASK access ) +{ + return open_remote_desktop( NULL, name, flags, inherit, access ); +} + + /*********************************************************************** * CloseDesktop (USER32.@) */ @@ -454,13 +461,60 @@ HDESK WINAPI OpenInputDesktop( DWORD flags, BOOL inherit, ACCESS_MASK access ) } +extern HWND *WIN_ListChildren( HWND hwnd ); + +static HWND get_desktop_hwnd( HDESK desktop ) +{ + NTSTATUS error; + HWND window; + + SERVER_START_REQ( get_top_window ) + { + req->handle = desktop; + error = wine_server_call_err( req ); + window = reply->window; + } + SERVER_END_REQ; + if (error) + { + SetLastError( RtlNtStatusToDosError( error ) ); + return NULL; + } + return window; +} + /*********************************************************************** * EnumDesktopWindows (USER32.@) */ BOOL WINAPI EnumDesktopWindows( HDESK desktop, WNDENUMPROC func, LPARAM lparam ) { - FIXME( "(%p,%p,0x%lx): stub!\n", desktop, func, lparam ); - return TRUE; + HWND *list; + BOOL ret = TRUE; + int i; + HWND desktop_window; + + USER_CheckNotLock(); + + desktop_window = get_desktop_hwnd( desktop ); + if (!desktop_window) + return FALSE; + + /* We have to build a list of all windows first, to avoid */ + /* unpleasant side-effects, for instance if the callback */ + /* function changes the Z-order of the windows. */ + + if (!(list = WIN_ListChildren( desktop_window ))) return TRUE; + + /* Now call the callback function for every window */ + + for (i = 0; list[i]; i++) + { + /* Make sure that the window still exists */ + if (!IsWindow( list[i] )) continue; + if (!(ret = func( list[i], lparam ))) break; + } + HeapFree( GetProcessHeap(), 0, list ); + return ret; } diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index a659d82..743335a 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -3302,6 +3302,7 @@ struct open_desktop_request unsigned int access; unsigned int attributes; /* VARARG(name,unicode_str); */ + obj_handle_t winsta; }; struct open_desktop_reply { @@ -3363,6 +3364,19 @@ struct enum_desktop_reply +struct get_top_window_request +{ + struct request_header __header; + obj_handle_t handle; +}; +struct get_top_window_reply +{ + struct reply_header __header; + obj_handle_t window; +}; + + + struct set_user_object_info_request { struct request_header __header; @@ -4458,6 +4472,7 @@ enum request REQ_get_thread_desktop, REQ_set_thread_desktop, REQ_enum_desktop, + REQ_get_top_window, REQ_set_user_object_info, REQ_attach_thread_input, REQ_get_thread_input, @@ -4698,6 +4713,7 @@ union generic_request struct get_thread_desktop_request get_thread_desktop_request; struct set_thread_desktop_request set_thread_desktop_request; struct enum_desktop_request enum_desktop_request; + struct get_top_window_request get_top_window_request; struct set_user_object_info_request set_user_object_info_request; struct attach_thread_input_request attach_thread_input_request; struct get_thread_input_request get_thread_input_request; @@ -4936,6 +4952,7 @@ union generic_reply struct get_thread_desktop_reply get_thread_desktop_reply; struct set_thread_desktop_reply set_thread_desktop_reply; struct enum_desktop_reply enum_desktop_reply; + struct get_top_window_reply get_top_window_reply; struct set_user_object_info_reply set_user_object_info_reply; struct attach_thread_input_reply attach_thread_input_reply; struct get_thread_input_reply get_thread_input_reply; @@ -4994,6 +5011,6 @@ union generic_reply struct add_fd_completion_reply add_fd_completion_reply; }; -#define SERVER_PROTOCOL_VERSION 337 +#define SERVER_PROTOCOL_VERSION 342 #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..72424fa 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -2402,6 +2402,7 @@ enum message_type unsigned int access; /* wanted access rights */ unsigned int attributes; /* object attributes */ VARARG(name,unicode_str); /* object name */ + obj_handle_t winsta; /* handle to window station (can be null) */ @REPLY obj_handle_t handle; /* handle to the desktop */ @END @@ -2437,6 +2438,14 @@ enum message_type @END +/* Open a desktop's top window */ +@REQ(get_top_window) + obj_handle_t handle; /* handle to the desktop */ +@REPLY + obj_handle_t window; /* top window */ +@END + + /* Get/set information about a user object (window station or desktop) */ @REQ(set_user_object_info) obj_handle_t handle; /* handle to the object */ diff --git a/server/request.h b/server/request.h index 283ad97..c6dabfa 100644 --- a/server/request.h +++ b/server/request.h @@ -287,6 +287,7 @@ DECL_HANDLER(close_desktop); DECL_HANDLER(get_thread_desktop); DECL_HANDLER(set_thread_desktop); DECL_HANDLER(enum_desktop); +DECL_HANDLER(get_top_window); DECL_HANDLER(set_user_object_info); DECL_HANDLER(attach_thread_input); DECL_HANDLER(get_thread_input); @@ -526,6 +527,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = (req_handler)req_get_thread_desktop, (req_handler)req_set_thread_desktop, (req_handler)req_enum_desktop, + (req_handler)req_get_top_window, (req_handler)req_set_user_object_info, (req_handler)req_attach_thread_input, (req_handler)req_get_thread_input, diff --git a/server/trace.c b/server/trace.c index 61d0238..756c4d9 100644 --- a/server/trace.c +++ b/server/trace.c @@ -2976,6 +2976,8 @@ static void dump_open_desktop_request( const struct open_desktop_request *req ) fprintf( stderr, " attributes=%08x,", req->attributes ); fprintf( stderr, " name=" ); dump_varargs_unicode_str( cur_size ); + fputc( ',', stderr ); + fprintf( stderr, " winsta=%p", req->winsta ); } static void dump_open_desktop_reply( const struct open_desktop_reply *req ) @@ -3016,6 +3018,16 @@ static void dump_enum_desktop_reply( const struct enum_desktop_reply *req ) dump_varargs_unicode_str( cur_size ); } +static void dump_get_top_window_request( const struct get_top_window_request *req ) +{ + fprintf( stderr, " handle=%p", req->handle ); +} + +static void dump_get_top_window_reply( const struct get_top_window_reply *req ) +{ + fprintf( stderr, " window=%p", req->window ); +} + static void dump_set_user_object_info_request( const struct set_user_object_info_request *req ) { fprintf( stderr, " handle=%p,", req->handle ); @@ -3958,6 +3970,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_get_thread_desktop_request, (dump_func)dump_set_thread_desktop_request, (dump_func)dump_enum_desktop_request, + (dump_func)dump_get_top_window_request, (dump_func)dump_set_user_object_info_request, (dump_func)dump_attach_thread_input_request, (dump_func)dump_get_thread_input_request, @@ -4194,6 +4207,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_get_thread_desktop_reply, (dump_func)0, (dump_func)dump_enum_desktop_reply, + (dump_func)dump_get_top_window_reply, (dump_func)dump_set_user_object_info_reply, (dump_func)0, (dump_func)dump_get_thread_input_reply, @@ -4430,6 +4444,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = { "get_thread_desktop", "set_thread_desktop", "enum_desktop", + "get_top_window", "set_user_object_info", "attach_thread_input", "get_thread_input", 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..7847f28 100644 --- a/server/winstation.c +++ b/server/winstation.c @@ -509,7 +509,14 @@ DECL_HANDLER(open_desktop) struct unicode_str name; get_req_unicode_str( &name ); - if ((winstation = get_process_winstation( current->process, 0 /* FIXME: access rights? */ ))) + + /* FIXME: check access rights */ + if (!req->winsta) + winstation = get_process_winstation( current->process, 0 ); + else + winstation = (struct winstation *)get_handle_obj( current->process, req->winsta, 0, &winstation_ops ); + + if (winstation) { struct unicode_str full_str; WCHAR *full_name; @@ -520,6 +527,7 @@ DECL_HANDLER(open_desktop) req->attributes ); free( full_name ); } + release_object( winstation ); } } @@ -680,3 +688,25 @@ DECL_HANDLER(enum_desktop) release_object( winstation ); set_error( STATUS_NO_MORE_ENTRIES ); } + + +/* enumerate top windows of desktops */ +DECL_HANDLER(get_top_window) +{ + struct desktop *desktop; + + if (!(desktop = (struct desktop *)get_handle_obj( current->process, req->handle, + DESKTOP_ENUMERATE, &desktop_ops ))) + return; + + if (!desktop->top_window) + { + set_error( STATUS_OBJECT_NAME_NOT_FOUND ); + release_object( desktop ); + return; + } + + reply->window = get_window_handle(desktop->top_window); + + release_object( desktop ); +} -- 1.5.4.1