From: Zhiyi Zhang Subject: [PATCH 1/4] winex11.drv: Store monitor information in the wineserver for EnumDisplayMonitors(). Message-Id: <76b17e19-9d54-1434-336b-6fefde2d7a28@codeweavers.com> Date: Wed, 28 Oct 2020 14:40:57 +0800 Fix Office 2016/365 having a 640x480 main window. Office 2016/365 hooks NtOpenKeyEx() and prevents access to SetupAPI device properties. After failed to query monitor information from SetupAPI, EnumDisplayMonitors() reports a fallback monitor of size 640x480. As to why store the monitor information in wineserver, it seems the EnumDisplayMonitors() reports monitors connected to current user logon session. For instance, EnumDisplayMonitors() always report one monitor when called by services. Signed-off-by: Zhiyi Zhang --- dlls/user32/sysparams.c | 53 ++++++++++ dlls/winex11.drv/display.c | 54 +++++++++-- server/Makefile.in | 1 + server/display.c | 192 +++++++++++++++++++++++++++++++++++++ server/protocol.def | 35 +++++++ server/user.h | 11 +++ 6 files changed, 340 insertions(+), 6 deletions(-) create mode 100644 server/display.c diff --git a/dlls/user32/sysparams.c b/dlls/user32/sysparams.c index 1381f387e03..227d57b0e5c 100644 --- a/dlls/user32/sysparams.c +++ b/dlls/user32/sysparams.c @@ -46,6 +46,7 @@ #include "win.h" #include "user_private.h" #include "wine/gdi_driver.h" +#include "wine/server.h" #include "wine/asm.h" #include "wine/debug.h" @@ -3858,9 +3859,31 @@ fail: BOOL CDECL nulldrv_GetMonitorInfo( HMONITOR handle, MONITORINFO *info ) { UINT index = (UINT_PTR)handle - 1; + WCHAR adapter_name[CCHDEVICENAME]; TRACE("(%p, %p)\n", handle, info); + SERVER_START_REQ( get_monitor_info ) + { + req->handle = wine_server_obj_handle( handle ); + wine_server_set_reply( req, adapter_name, sizeof(adapter_name) ); + if (!wine_server_call( req )) + { + SetRect( &info->rcMonitor, reply->monitor_rect.left, reply->monitor_rect.top, + reply->monitor_rect.right, reply->monitor_rect.bottom ); + SetRect( &info->rcWork, reply->work_rect.left, reply->work_rect.top, + reply->work_rect.right, reply->work_rect.bottom ); + if (!IsRectEmpty( &info->rcMonitor ) && !info->rcMonitor.top && !info->rcMonitor.left) + info->dwFlags = MONITORINFOF_PRIMARY; + else + info->dwFlags = 0; + if (info->cbSize >= sizeof(MONITORINFOEXW)) + lstrcpyW( ((MONITORINFOEXW *)info)->szDevice, adapter_name ); + return TRUE; + } + } + SERVER_END_REQ; + /* Fallback to report one monitor */ if (handle == NULLDRV_DEFAULT_HMONITOR) { @@ -3874,7 +3897,10 @@ BOOL CDECL nulldrv_GetMonitorInfo( HMONITOR handle, MONITORINFO *info ) } if (!update_monitor_cache()) + { + SetLastError( ERROR_INVALID_MONITOR_HANDLE ); return FALSE; + } EnterCriticalSection( &monitors_section ); if (index < monitor_count) @@ -3995,11 +4021,38 @@ static BOOL CALLBACK enum_mon_callback( HMONITOR monitor, HDC hdc, LPRECT rect, BOOL CDECL nulldrv_EnumDisplayMonitors( HDC hdc, RECT *rect, MONITORENUMPROC proc, LPARAM lp ) { + HMONITOR monitor = NULL; RECT monitor_rect; + NTSTATUS status; DWORD i = 0; TRACE("(%p, %p, %p, 0x%lx)\n", hdc, rect, proc, lp); + while (TRUE) + { + SERVER_START_REQ( enum_monitor ) + { + req->index = i; + if (!(status = wine_server_call( req ))) + { + SetRect( &monitor_rect, reply->monitor_rect.left, reply->monitor_rect.top, + reply->monitor_rect.right, reply->monitor_rect.bottom ); + monitor = wine_server_ptr_handle( reply->handle ); + } + } + SERVER_END_REQ; + + if (status) + break; + + ++i; + if (!proc( monitor, hdc, &monitor_rect, lp )) + return FALSE; + } + + if (i) + return TRUE; + if (update_monitor_cache()) { while (TRUE) diff --git a/dlls/winex11.drv/display.c b/dlls/winex11.drv/display.c index 0f61f9f7b2e..abc4f2a07cc 100644 --- a/dlls/winex11.drv/display.c +++ b/dlls/winex11.drv/display.c @@ -34,6 +34,7 @@ #define WIN32_NO_STATUS #include "winternl.h" #include "wine/debug.h" +#include "wine/server.h" #include "wine/unicode.h" #include "x11drv.h" @@ -47,7 +48,6 @@ DEFINE_DEVPROPKEY(DEVPROPKEY_MONITOR_OUTPUT_ID, 0xca085853, 0x16ce, 0x48aa, 0xb1 DEFINE_DEVPROPKEY(WINE_DEVPROPKEY_GPU_VULKAN_UUID, 0x233a9ef3, 0xafc4, 0x4abd, 0xb5, 0x64, 0xc3, 0x2f, 0x21, 0xf1, 0x53, 0x5c, 2); DEFINE_DEVPROPKEY(WINE_DEVPROPKEY_MONITOR_STATEFLAGS, 0x233a9ef3, 0xafc4, 0x4abd, 0xb5, 0x64, 0xc3, 0x2f, 0x21, 0xf1, 0x53, 0x5b, 2); DEFINE_DEVPROPKEY(WINE_DEVPROPKEY_MONITOR_RCMONITOR, 0x233a9ef3, 0xafc4, 0x4abd, 0xb5, 0x64, 0xc3, 0x2f, 0x21, 0xf1, 0x53, 0x5b, 3); -DEFINE_DEVPROPKEY(WINE_DEVPROPKEY_MONITOR_RCWORK, 0x233a9ef3, 0xafc4, 0x4abd, 0xb5, 0x64, 0xc3, 0x2f, 0x21, 0xf1, 0x53, 0x5b, 4); DEFINE_DEVPROPKEY(WINE_DEVPROPKEY_MONITOR_ADAPTERNAME, 0x233a9ef3, 0xafc4, 0x4abd, 0xb5, 0x64, 0xc3, 0x2f, 0x21, 0xf1, 0x53, 0x5b, 5); static const WCHAR driver_date_dataW[] = {'D','r','i','v','e','r','D','a','t','e','D','a','t','a',0}; @@ -586,6 +586,7 @@ static BOOL X11DRV_InitMonitor(HDEVINFO devinfo, const struct x11drv_monitor *mo { SP_DEVINFO_DATA device_data = {sizeof(SP_DEVINFO_DATA)}; WCHAR bufferW[MAX_PATH]; + DWORD size; HKEY hkey; BOOL ret = FALSE; @@ -624,16 +625,33 @@ static BOOL X11DRV_InitMonitor(HDEVINFO devinfo, const struct x11drv_monitor *mo if (!SetupDiSetDevicePropertyW(devinfo, &device_data, &WINE_DEVPROPKEY_MONITOR_RCMONITOR, DEVPROP_TYPE_BINARY, (const BYTE *)&monitor->rc_monitor, sizeof(monitor->rc_monitor), 0)) goto done; - /* RcWork */ - if (!SetupDiSetDevicePropertyW(devinfo, &device_data, &WINE_DEVPROPKEY_MONITOR_RCWORK, DEVPROP_TYPE_BINARY, - (const BYTE *)&monitor->rc_work, sizeof(monitor->rc_work), 0)) - goto done; /* Adapter name */ sprintfW(bufferW, adapter_name_fmtW, video_index + 1); + size = (strlenW(bufferW) + 1) * sizeof(WCHAR); if (!SetupDiSetDevicePropertyW(devinfo, &device_data, &WINE_DEVPROPKEY_MONITOR_ADAPTERNAME, DEVPROP_TYPE_STRING, - (const BYTE *)bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR), 0)) + (const BYTE *)bufferW, size, 0)) goto done; + /* EnumDisplayMonitors() doesn't enumerate mirrored replicas and inactive monitors */ + if (monitor_index == 0 && monitor->state_flags & DISPLAY_DEVICE_ACTIVE) + { + SERVER_START_REQ(create_monitor) + { + req->monitor_rect.top = monitor->rc_monitor.top; + req->monitor_rect.left = monitor->rc_monitor.left; + req->monitor_rect.right = monitor->rc_monitor.right; + req->monitor_rect.bottom = monitor->rc_monitor.bottom; + req->work_rect.top = monitor->rc_work.top; + req->work_rect.left = monitor->rc_work.left; + req->work_rect.right = monitor->rc_work.right; + req->work_rect.bottom = monitor->rc_work.bottom; + wine_server_add_data(req, bufferW, size); + if (wine_server_call(req)) + goto done; + } + SERVER_END_REQ; + } + ret = TRUE; done: if (!ret) @@ -645,7 +663,9 @@ static void prepare_devices(HKEY video_hkey) { static const BOOL not_present = FALSE; SP_DEVINFO_DATA device_data = {sizeof(device_data)}; + HMONITOR monitor = NULL; HDEVINFO devinfo; + NTSTATUS status; DWORD i = 0; /* Remove all monitors */ @@ -657,6 +677,28 @@ static void prepare_devices(HKEY video_hkey) } SetupDiDestroyDeviceInfoList(devinfo); + while (TRUE) + { + SERVER_START_REQ(enum_monitor) + { + req->index = 0; + if (!(status = wine_server_call(req))) + monitor = wine_server_ptr_handle(reply->handle); + } + SERVER_END_REQ; + + if (status) + break; + + SERVER_START_REQ(destroy_monitor) + { + req->handle = wine_server_obj_handle(monitor); + if (wine_server_call(req)) + ERR("Failed to remove monitor.\n"); + } + SERVER_END_REQ; + } + /* Clean up old adapter keys for reinitialization */ RegDeleteTreeW(video_hkey, NULL); diff --git a/server/Makefile.in b/server/Makefile.in index e90c5d1336c..af6cbc2b6f8 100644 --- a/server/Makefile.in +++ b/server/Makefile.in @@ -10,6 +10,7 @@ C_SRCS = \ console.c \ debugger.c \ device.c \ + display.c \ directory.c \ event.c \ fd.c \ diff --git a/server/display.c b/server/display.c new file mode 100644 index 00000000000..ebaa9925253 --- /dev/null +++ b/server/display.c @@ -0,0 +1,192 @@ +/* + * Server-side display device management + * + * Copyright (C) 2020 Zhiyi Zhang for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ +#include "config.h" +#include "wine/port.h" + +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "winternl.h" + +#include "object.h" +#include "handle.h" +#include "request.h" +#include "unicode.h" +#include "user.h" + +static struct list monitor_list = LIST_INIT(monitor_list); + +static void monitor_dump( struct object *obj, int verbose ); +static void monitor_destroy( struct object *obj ); + +static const struct object_ops monitor_ops = +{ + sizeof(struct monitor), /* size */ + monitor_dump, /* dump */ + no_get_type, /* get_type */ + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ + no_map_access, /* map_access */ + default_get_sd, /* get_sd */ + default_set_sd, /* set_sd */ + no_get_full_name, /* get_full_name */ + no_lookup_name, /* lookup_name */ + no_link_name, /* link_name */ + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ + no_close_handle, /* close_handle */ + monitor_destroy /* destroy */ +}; + +/* create a monitor */ +static struct monitor *create_monitor( const rectangle_t *monitor_rect, + const rectangle_t *work_rect, + const struct unicode_str *adapter_name ) +{ + struct monitor *monitor; + obj_handle_t handle; + + if ((monitor = alloc_object( &monitor_ops ))) + { + if (!(monitor->adapter_name = memdup( adapter_name->str, adapter_name->len ))) + { + release_object( monitor ); + return NULL; + } + monitor->adapter_name_len = adapter_name->len; + + if (!(handle = alloc_handle( current->process, monitor, 0, 0 ))) + { + release_object( monitor ); + return NULL; + } + + if (!(monitor->handle = duplicate_handle( current->process, handle, NULL, 0, 0, + DUP_HANDLE_MAKE_GLOBAL ))) + { + close_handle( current->process, handle ); + release_object( monitor ); + return NULL; + } + + monitor->monitor_rect = *monitor_rect; + monitor->work_rect = *work_rect; + release_object( monitor ); + close_handle( current->process, handle ); + list_add_tail( &monitor_list, &monitor->entry ); + } + return monitor; +} + +static void monitor_dump( struct object *obj, int verbose ) +{ + struct monitor *monitor = (struct monitor *)obj; + assert( obj->ops == &monitor_ops ); + fprintf( stderr, "Monitor handle=%#x ", monitor->handle); + fprintf( stderr, "monitor_rect=(%d, %d, %d, %d) ", monitor->monitor_rect.left, + monitor->monitor_rect.top, monitor->monitor_rect.right, monitor->monitor_rect.bottom ); + fprintf( stderr, "work_rect=(%d, %d, %d, %d) ", monitor->work_rect.left, monitor->work_rect.top, + monitor->work_rect.right, monitor->work_rect.bottom ); + fprintf( stderr, "adapter_name=\""); + dump_strW( monitor->adapter_name, monitor->adapter_name_len, stderr, "\"\"" ); + fprintf( stderr, "\"\n" ); +} + +static void monitor_destroy( struct object *obj ) +{ + struct monitor *monitor = (struct monitor *)obj; + + if (monitor->adapter_name) + free( monitor->adapter_name ); + list_remove( &monitor->entry ); +} + +/* create a monitor */ +DECL_HANDLER(create_monitor) +{ + struct unicode_str adapter_name; + struct monitor *monitor; + + adapter_name = get_req_unicode_str(); + if ((monitor = create_monitor( &req->monitor_rect, &req->work_rect, &adapter_name ))) + { + reply->handle = monitor->handle; + clear_error(); + } +} + +/* get information about a monitor */ +DECL_HANDLER(get_monitor_info) +{ + struct monitor *monitor; + struct object *obj; + + if (!(obj = get_handle_obj( current->process, req->handle, 0, &monitor_ops ))) + return; + + monitor = (struct monitor *)obj; + reply->monitor_rect = monitor->monitor_rect; + reply->work_rect = monitor->work_rect; + set_reply_data( monitor->adapter_name, min(monitor->adapter_name_len, get_reply_max_size()) ); + release_object( monitor ); + clear_error(); + return; +} + +/* enumerate monitors */ +DECL_HANDLER(enum_monitor) +{ + struct monitor *monitor; + unsigned int index = 0; + + LIST_FOR_EACH_ENTRY( monitor, &monitor_list, struct monitor, entry ) + { + if (req->index > index++) + continue; + + reply->handle = monitor->handle; + reply->monitor_rect = monitor->monitor_rect; + clear_error(); + return; + } + set_error( STATUS_NO_MORE_ENTRIES ); +} + +/* destroy a monitor */ +DECL_HANDLER(destroy_monitor) +{ + struct object *obj; + + if ((obj = get_handle_obj( current->process, req->handle, 0, &monitor_ops ))) + { + close_handle( current->process, req->handle ); + release_object( obj ); + clear_error(); + } +} diff --git a/server/protocol.def b/server/protocol.def index 846d2e15602..93b8e783f85 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -2667,6 +2667,41 @@ enum coords_relative #define SET_USER_OBJECT_GET_FULL_NAME 2 +/* Create a monitor */ +@REQ(create_monitor) + rectangle_t monitor_rect; /* monitor rectangle */ + rectangle_t work_rect; /* monitor work area rectangle */ + VARARG(adapter,unicode_str); /* adapter name */ +@REPLY + obj_handle_t handle; /* handle to the object */ +@END + + +/* Get monitor information */ +@REQ(get_monitor_info) + obj_handle_t handle; /* handle to the object */ +@REPLY + rectangle_t monitor_rect; /* monitor rectangle */ + rectangle_t work_rect; /* monitor work area rectangle */ + VARARG(adapter,unicode_str); /* adapter name */ +@END + + +/* Enumerate monitors */ +@REQ(enum_monitor) + unsigned int index; /* current index */ +@REPLY + obj_handle_t handle; /* handle to the object */ + rectangle_t monitor_rect; /* monitor rectangle */ +@END + + +/* Destroy a monitor */ +@REQ(destroy_monitor) + obj_handle_t handle; /* handle to the monitor */ +@END + + /* Register a hotkey */ @REQ(register_hotkey) user_handle_t window; /* handle to the window */ diff --git a/server/user.h b/server/user.h index 6267f3e2881..f986dcc3d58 100644 --- a/server/user.h +++ b/server/user.h @@ -79,6 +79,17 @@ struct desktop unsigned char keystate[256]; /* asynchronous key state */ }; +struct monitor +{ + struct object obj; /* object header */ + obj_handle_t handle; /* object handle */ + struct list entry; /* entry in global monitor list */ + rectangle_t monitor_rect; /* monitor rectangle */ + rectangle_t work_rect; /* monitor work area rectangle */ + WCHAR *adapter_name; /* adapter name */ + data_size_t adapter_name_len; /* adapter name length */ +}; + /* user handles functions */ extern user_handle_t alloc_user_handle( void *ptr, enum user_object type ); -- 2.27.0