[PATCH 1/6] user32: Implement WCA_CLOAK in SetWindowCompositionAttribute.

Gabriel Ivăncescu gabrielopcode at gmail.com
Fri Nov 6 10:03:30 CST 2020


This implements the user32 and server-side of keeping track of cloaking,
without actually doing the cloaking, which is done by the driver. The
driver function SetWindowCompositionAttribute must return whether it
supports cloaking, and whether it's a manual cloaking done by the app,
or the shell cloaking a window.

Manually cloaked windows can be activated and set to foreground. They also
remain on the taskbar and can be activated from the taskbar too (and function
like uncloaked windows), but are otherwise visibly hidden. WindowFromPoint
doesn't work to retrieve them, though.

Signed-off-by: Gabriel Ivăncescu <gabrielopcode at gmail.com>
---

This patchset will attempt to implement both manual cloaking done by
applications, and shell cloaking using the WM (for virtual desktops/workspaces
for example).

It uses the undocumented user32 API SetWindowCompositionAttribute to do
so. Of course, the documented DwmSetWindowAttribute will just be a wrapper
around this one.

 dlls/user32/driver.c       |   9 +++
 dlls/user32/message.c      |   2 +
 dlls/user32/user_private.h |  41 +++++++++++++
 dlls/user32/win.c          | 121 ++++++++++++++++++++++++++++++++++---
 server/protocol.def        |  13 ++++
 server/window.c            |  80 +++++++++++++++++++++---
 6 files changed, 250 insertions(+), 16 deletions(-)

diff --git a/dlls/user32/driver.c b/dlls/user32/driver.c
index 7ac7714..6dad5dc 100644
--- a/dlls/user32/driver.c
+++ b/dlls/user32/driver.c
@@ -140,6 +140,7 @@ static const USER_DRIVER *load_driver(void)
         GET_USER_FUNC(SetFocus);
         GET_USER_FUNC(SetLayeredWindowAttributes);
         GET_USER_FUNC(SetParent);
+        GET_USER_FUNC(SetWindowCompositionAttribute);
         GET_USER_FUNC(SetWindowRgn);
         GET_USER_FUNC(SetWindowIcon);
         GET_USER_FUNC(SetWindowStyle);
@@ -421,6 +422,12 @@ static void CDECL nulldrv_SetParent( HWND hwnd, HWND parent, HWND old_parent )
 {
 }
 
+static DWORD CDECL nulldrv_SetWindowCompositionAttribute( HWND hwnd, DWORD attribute, void *data )
+{
+    SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
+    return ~0;
+}
+
 static void CDECL nulldrv_SetWindowRgn( HWND hwnd, HRGN hrgn, BOOL redraw )
 {
 }
@@ -522,6 +529,7 @@ static USER_DRIVER null_driver =
     nulldrv_SetFocus,
     nulldrv_SetLayeredWindowAttributes,
     nulldrv_SetParent,
+    nulldrv_SetWindowCompositionAttribute,
     nulldrv_SetWindowRgn,
     nulldrv_SetWindowIcon,
     nulldrv_SetWindowStyle,
@@ -737,6 +745,7 @@ static USER_DRIVER lazy_load_driver =
     nulldrv_SetFocus,
     loaderdrv_SetLayeredWindowAttributes,
     nulldrv_SetParent,
+    nulldrv_SetWindowCompositionAttribute,
     loaderdrv_SetWindowRgn,
     nulldrv_SetWindowIcon,
     nulldrv_SetWindowStyle,
diff --git a/dlls/user32/message.c b/dlls/user32/message.c
index 4434f4b..67f89bb 100644
--- a/dlls/user32/message.c
+++ b/dlls/user32/message.c
@@ -1887,6 +1887,8 @@ static LRESULT handle_internal_message( HWND hwnd, UINT msg, WPARAM wparam, LPAR
     case WM_WINE_UPDATEWINDOWSTATE:
         update_window_state( hwnd );
         return 0;
+    case WM_WINE_SETWINDOWCLOAKED:
+        return USER_Driver->pSetWindowCompositionAttribute( hwnd, WCA_CLOAK, &wparam );
     default:
         if (msg >= WM_WINE_FIRST_DRIVER_MSG && msg <= WM_WINE_LAST_DRIVER_MSG)
             return USER_Driver->pWindowMessage( hwnd, msg, wparam, lparam );
diff --git a/dlls/user32/user_private.h b/dlls/user32/user_private.h
index 7761a1c..3baf39c 100644
--- a/dlls/user32/user_private.h
+++ b/dlls/user32/user_private.h
@@ -56,6 +56,7 @@ enum wine_internal_message
     WM_WINE_MOUSE_LL_HOOK,
     WM_WINE_CLIPCURSOR,
     WM_WINE_UPDATEWINDOWSTATE,
+    WM_WINE_SETWINDOWCLOAKED,
     WM_WINE_FIRST_DRIVER_MSG = 0x80001000,  /* range of messages reserved for the USER driver */
     WM_WINE_LAST_DRIVER_MSG = 0x80001fff
 };
@@ -101,6 +102,7 @@ typedef struct tagUSER_DRIVER {
     void   (CDECL *pSetFocus)(HWND);
     void   (CDECL *pSetLayeredWindowAttributes)(HWND,COLORREF,BYTE,DWORD);
     void   (CDECL *pSetParent)(HWND,HWND,HWND);
+    DWORD  (CDECL *pSetWindowCompositionAttribute)(HWND,DWORD,void*);
     void   (CDECL *pSetWindowRgn)(HWND,HRGN,BOOL);
     void   (CDECL *pSetWindowIcon)(HWND,UINT,HICON);
     void   (CDECL *pSetWindowStyle)(HWND,INT,STYLESTRUCT*);
@@ -366,6 +368,45 @@ typedef struct
 
 #include "poppack.h"
 
+/* Undocumented structure for (Get|Set)WindowCompositionAttribute */
+struct WINCOMPATTRDATA
+{
+    DWORD attribute;
+    void *pData;
+    ULONG dataSize;
+};
+enum
+{
+    WCA_UNDEFINED = 0,
+    WCA_NCRENDERING_ENABLED = 1,
+    WCA_NCRENDERING_POLICY = 2,
+    WCA_TRANSITIONS_FORCEDISABLED = 3,
+    WCA_ALLOW_NCPAINT = 4,
+    WCA_CAPTION_BUTTON_BOUNDS = 5,
+    WCA_NONCLIENT_RTL_LAYOUT = 6,
+    WCA_FORCE_ICONIC_REPRESENTATION = 7,
+    WCA_EXTENDED_FRAME_BOUNDS = 8,
+    WCA_HAS_ICONIC_BITMAP = 9,
+    WCA_THEME_ATTRIBUTES = 10,
+    WCA_NCRENDERING_EXILED = 11,
+    WCA_NCADORNMENTINFO = 12,
+    WCA_EXCLUDED_FROM_LIVEPREVIEW = 13,
+    WCA_VIDEO_OVERLAY_ACTIVE = 14,
+    WCA_FORCE_ACTIVEWINDOW_APPEARANCE = 15,
+    WCA_DISALLOW_PEEK = 16,
+    WCA_CLOAK = 17,
+    WCA_CLOAKED = 18,
+    WCA_ACCENT_POLICY = 19,
+    WCA_FREEZE_REPRESENTATION = 20,
+    WCA_EVER_UNCLOAKED = 21,
+    WCA_VISUAL_OWNER = 22,
+    WCA_HOLOGRAPHIC = 23,
+    WCA_EXCLUDED_FROM_DDA = 24,
+    WCA_PASSIVEUPDATEMODE = 25,
+    WCA_USEDARKMODECOLORS = 26,
+    WCA_LAST
+};
+
 extern int bitmap_info_size( const BITMAPINFO * info, WORD coloruse ) DECLSPEC_HIDDEN;
 extern BOOL get_icon_size( HICON handle, SIZE *size ) DECLSPEC_HIDDEN;
 
diff --git a/dlls/user32/win.c b/dlls/user32/win.c
index 680defc..9840a76 100644
--- a/dlls/user32/win.c
+++ b/dlls/user32/win.c
@@ -27,6 +27,7 @@
 #include "winbase.h"
 #include "winnls.h"
 #include "winver.h"
+#include "dwmapi.h"
 #include "wine/server.h"
 #include "wine/asm.h"
 #include "win.h"
@@ -54,6 +55,8 @@ static CRITICAL_SECTION_DEBUG critsect_debug =
 };
 static CRITICAL_SECTION surfaces_section = { &critsect_debug, -1, 0, 0, 0, 0 };
 
+BOOL WINAPI SetWindowCompositionAttribute(HWND, const struct WINCOMPATTRDATA*);
+
 /**********************************************************************/
 
 /* helper for Get/SetWindowLong */
@@ -191,7 +194,7 @@ void *free_user_handle( HANDLE handle, enum user_obj_type type )
  * Create a window handle with the server.
  */
 static WND *create_window_handle( HWND parent, HWND owner, LPCWSTR name,
-                                  HINSTANCE instance, BOOL unicode )
+                                  HINSTANCE instance, BOOL unicode, BOOL *needs_cloak )
 {
     WORD index;
     WND *win;
@@ -219,6 +222,7 @@ static WND *create_window_handle( HWND parent, HWND owner, LPCWSTR name,
             dpi         = reply->dpi;
             awareness   = reply->awareness;
             class       = wine_server_get_ptr( reply->class_ptr );
+            *needs_cloak = reply->needs_cloak;
         }
     }
     SERVER_END_REQ;
@@ -749,6 +753,7 @@ HWND WIN_GetFullHandle( HWND hwnd )
 static HWND WIN_SetOwner( HWND hwnd, HWND owner )
 {
     WND *win = WIN_GetPtr( hwnd );
+    BOOL needs_cloak = FALSE;
     HWND ret = 0;
 
     if (!win || win == WND_DESKTOP) return 0;
@@ -765,10 +770,19 @@ static HWND WIN_SetOwner( HWND hwnd, HWND owner )
         {
             win->owner = wine_server_ptr_handle( reply->full_owner );
             ret = wine_server_ptr_handle( reply->prev_owner );
+            needs_cloak = reply->needs_cloak;
         }
     }
     SERVER_END_REQ;
     WIN_ReleasePtr( win );
+    if (needs_cloak)
+    {
+        struct WINCOMPATTRDATA data;
+        data.attribute = WCA_CLOAK;
+        data.pData = &needs_cloak;
+        data.dataSize = sizeof(needs_cloak);
+        SetWindowCompositionAttribute( hwnd, &data );
+    }
     return ret;
 }
 
@@ -1349,6 +1363,7 @@ HWND WIN_CreateWindowEx( CREATESTRUCTW *cs, LPCWSTR className, HINSTANCE module,
     MDICREATESTRUCTW mdi_cs;
     CBT_CREATEWNDW cbtc;
     CREATESTRUCTW cbcs;
+    BOOL needs_cloak;
 
     className = CLASS_GetVersionedName(className, NULL, NULL, TRUE);
 
@@ -1476,13 +1491,13 @@ HWND WIN_CreateWindowEx( CREATESTRUCTW *cs, LPCWSTR className, HINSTANCE module,
 
     /* Create the window structure */
 
-    if (!(wndPtr = create_window_handle( parent, owner, className, module, unicode )))
+    if (!(wndPtr = create_window_handle( parent, owner, className, module, unicode, &needs_cloak )))
     {
         WNDCLASSW wc;
         /* if it's a comctl32 class, GetClassInfo will load it, then we can retry */
         if (GetLastError() != ERROR_INVALID_HANDLE ||
             !GetClassInfoW( 0, className, &wc ) ||
-            !(wndPtr = create_window_handle( parent, owner, className, module, unicode )))
+            !(wndPtr = create_window_handle( parent, owner, className, module, unicode, &needs_cloak )))
             return 0;
     }
     hwnd = wndPtr->obj.handle;
@@ -1663,6 +1678,8 @@ HWND WIN_CreateWindowEx( CREATESTRUCTW *cs, LPCWSTR className, HINSTANCE module,
     /* call the driver */
 
     if (!USER_Driver->pCreateWindow( hwnd )) goto failed;
+    if (needs_cloak)
+        USER_Driver->pSetWindowCompositionAttribute( hwnd, WCA_CLOAK, &needs_cloak );
 
     NotifyWinEvent(EVENT_OBJECT_CREATE, hwnd, OBJID_WINDOW, 0);
 
@@ -4233,9 +4250,99 @@ BOOL WINAPI SetWindowDisplayAffinity(HWND hwnd, DWORD affinity)
 /**********************************************************************
  *              SetWindowCompositionAttribute (USER32.@)
  */
-BOOL WINAPI SetWindowCompositionAttribute(HWND hwnd, void *data)
+BOOL WINAPI SetWindowCompositionAttribute(HWND hwnd, const struct WINCOMPATTRDATA *data)
 {
-    FIXME("(%p, %p): stub\n", hwnd, data);
-    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-    return FALSE;
+    user_handle_t *list;
+    unsigned i, size;
+    NTSTATUS status;
+    HWND parent;
+    DWORD ret;
+    WND *win;
+
+    TRACE("(%p, %p)\n", hwnd, data);
+
+    if (!data || !data->pData)
+    {
+        SetLastError( ERROR_NOACCESS );
+        return FALSE;
+    }
+    if (!hwnd || is_broadcast(hwnd) || !(win = WIN_GetPtr(hwnd)))
+    {
+        SetLastError( ERROR_INVALID_HANDLE );
+        return FALSE;
+    }
+    if (win == WND_DESKTOP || win == WND_OTHER_PROCESS)
+    {
+        SetLastError( ERROR_ACCESS_DENIED );
+        return FALSE;
+    }
+    parent = win->parent;
+    WIN_ReleasePtr(win);
+    if (parent && parent != GetDesktopWindow())
+    {
+        SetLastError( ERROR_INVALID_HANDLE );
+        return FALSE;
+    }
+
+    switch (data->attribute)
+    {
+    case WCA_CLOAK:
+        if (data->dataSize < sizeof(BOOL))
+        {
+            SetLastError( ERROR_INSUFFICIENT_BUFFER );
+            return FALSE;
+        }
+
+        ret = USER_Driver->pSetWindowCompositionAttribute( hwnd, WCA_CLOAK, data->pData );
+        if (ret == ~0) return FALSE;
+
+        size = 128;
+        for (;;)
+        {
+            unsigned count = 0;
+
+            if (!(list = HeapAlloc( GetProcessHeap(), 0, size * sizeof(user_handle_t) )))
+            {
+                SetLastError( ERROR_OUTOFMEMORY );
+                return FALSE;
+            }
+            SERVER_START_REQ( set_window_cloaked )
+            {
+                req->handle  = wine_server_user_handle( hwnd );
+                req->cloaked = ret;
+                wine_server_set_reply( req, list, size * sizeof(user_handle_t) );
+                if (!(status = wine_server_call( req ))) count = reply->count;
+            }
+            SERVER_END_REQ;
+            if (count < size)
+            {
+                /* Go through the list to cloak the windows that inherit it */
+                for (i = 0; i < count; i++)
+                {
+                    HWND full_handle, handle = wine_server_ptr_handle( list[i] );
+
+                    if ((full_handle = WIN_IsCurrentProcess( handle )))
+                        USER_Driver->pSetWindowCompositionAttribute( full_handle, WCA_CLOAK, data->pData );
+                    else
+                        SendMessageW( handle, WM_WINE_SETWINDOWCLOAKED, *(BOOL*)(data->pData), 0 );
+                }
+                count = 0;
+            }
+            HeapFree( GetProcessHeap(), 0, list );
+            if (!count) break;
+            size = count;  /* restart with a large enough buffer */
+        }
+        if (status)
+        {
+            SetLastError( RtlNtStatusToDosError( status ));
+            return FALSE;
+        }
+        return TRUE;
+
+    default:
+        FIXME("unimplemented attribute %d, size %u, for hwnd %p.\n", data->attribute, data->dataSize, hwnd);
+        SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
+        return FALSE;
+    }
+    return TRUE;
 }
diff --git a/server/protocol.def b/server/protocol.def
index 846d2e1..d96d2e7 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -2226,6 +2226,7 @@ enum message_type
     client_ptr_t   class_ptr;   /* pointer to class in client address space */
     int            dpi;         /* window DPI if not per-monitor aware */
     int            awareness;   /* window DPI awareness */
+    int            needs_cloak; /* the window needs to be cloaked by the driver */
 @END
 
 
@@ -2251,6 +2252,7 @@ enum message_type
 @REPLY
     user_handle_t  full_owner;  /* full handle of new owner */
     user_handle_t  prev_owner;  /* full handle of previous owner */
+    int            needs_cloak; /* the owned window needs to be cloaked */
 @END
 
 
@@ -2299,6 +2301,17 @@ enum message_type
 #define SET_WIN_UNICODE   0x40
 
 
+/* Set the window's cloaked attribute */
+ at REQ(set_window_cloaked)
+    user_handle_t  handle;        /* handle to the window */
+    unsigned int   cloaked;       /* cloaked attribute to set (see below) */
+ at REPLY
+    unsigned int   count;         /* total count of windows inheriting it */
+    VARARG(windows,user_handles); /* window handles that inherit it */
+ at END
+#define SET_WINDOW_CLOAKED_ON     0x01
+
+
 /* Set the parent of a window */
 @REQ(set_parent)
     user_handle_t  handle;      /* handle to the window */
diff --git a/server/window.c b/server/window.c
index 3a88b7f..e6fc90c 100644
--- a/server/window.c
+++ b/server/window.c
@@ -81,6 +81,7 @@ struct window
     unsigned int     is_unicode : 1;  /* ANSI or unicode */
     unsigned int     is_linked : 1;   /* is it linked into the parent z-order list? */
     unsigned int     is_layered : 1;  /* has layered info been set? */
+    unsigned int     is_cloaked : 1;  /* is the window cloaked by the app? */
     unsigned int     color_key;       /* color key for a layered window */
     unsigned int     alpha;           /* alpha value for a layered window */
     unsigned int     layered_flags;   /* flags for a layered window */
@@ -503,6 +504,7 @@ static struct window *create_window( struct window *parent, struct window *owner
     win->is_unicode     = 1;
     win->is_linked      = 0;
     win->is_layered     = 0;
+    win->is_cloaked     = 0;
     win->dpi_awareness  = DPI_AWARENESS_PER_MONITOR_AWARE;
     win->dpi            = 0;
     win->user_data      = 0;
@@ -784,6 +786,7 @@ static int get_window_children_from_point( struct window *parent, int x, int y,
     {
         int x_child = x, y_child = y;
 
+        if (is_desktop_window( parent ) && ptr->is_cloaked) continue;
         if (!is_point_in_window( ptr, &x_child, &y_child, parent->dpi )) continue;  /* skip it */
 
         /* if point is in client area, and window is not minimized or disabled, check children */
@@ -856,6 +859,21 @@ static int all_windows_from_point( struct window *top, int x, int y, unsigned in
     return 1;
 }
 
+/* fill an array with the handles of all the owned windows, recursively */
+static unsigned int get_owned_windows( struct window *win, user_handle_t *handles )
+{
+    struct window *ptr, *parent = win->parent;
+    unsigned int count = 0;
+
+    LIST_FOR_EACH_ENTRY( ptr, &parent->children, struct window, entry )
+    {
+        if (ptr->owner != win->handle) continue;
+        if (handles) handles[count] = ptr->handle;
+        count++;
+        count += get_owned_windows( ptr, handles ? handles + count : NULL );
+    }
+    return count;
+}
 
 /* return the thread owning a window */
 struct thread *get_window_thread( user_handle_t handle )
@@ -1965,14 +1983,16 @@ DECL_HANDLER(create_window)
         win->dpi_awareness = req->awareness;
         win->dpi = req->dpi;
     }
+    win->is_cloaked = owner ? owner->is_cloaked : 0;
 
-    reply->handle    = win->handle;
-    reply->parent    = win->parent ? win->parent->handle : 0;
-    reply->owner     = win->owner;
-    reply->extra     = win->nb_extra_bytes;
-    reply->dpi       = win->dpi;
-    reply->awareness = win->dpi_awareness;
-    reply->class_ptr = get_class_client_ptr( win->class );
+    reply->handle      = win->handle;
+    reply->parent      = win->parent ? win->parent->handle : 0;
+    reply->owner       = win->owner;
+    reply->extra       = win->nb_extra_bytes;
+    reply->dpi         = win->dpi;
+    reply->awareness   = win->dpi_awareness;
+    reply->class_ptr   = get_class_client_ptr( win->class );
+    reply->needs_cloak = win->is_cloaked;
 }
 
 
@@ -2072,8 +2092,9 @@ DECL_HANDLER(set_window_owner)
         }
     }
 
-    reply->prev_owner = win->owner;
-    reply->full_owner = win->owner = owner ? owner->handle : 0;
+    reply->prev_owner  = win->owner;
+    reply->full_owner  = win->owner = owner ? owner->handle : 0;
+    reply->needs_cloak = owner ? owner->is_cloaked : 0;
 }
 
 
@@ -2151,6 +2172,47 @@ DECL_HANDLER(set_window_info)
 }
 
 
+/* set the window's cloaked attribute */
+DECL_HANDLER(set_window_cloaked)
+{
+    struct window *win = get_window( req->handle );
+    unsigned int i, total;
+    user_handle_t *data;
+
+    if (!win) return;
+    if (is_desktop_window( win ))
+    {
+        set_error( STATUS_ACCESS_DENIED );
+        return;
+    }
+    if (!is_desktop_window( win->parent ))
+    {
+        set_error( STATUS_INVALID_HANDLE );
+        return;
+    }
+
+    reply->count = total = get_owned_windows( win, NULL );
+    if (get_reply_max_size() < total * sizeof(user_handle_t))
+        return;
+
+    if (total)
+    {
+        if (!(data = set_reply_data_size( total * sizeof(user_handle_t) )))
+        {
+            set_error( STATUS_NO_MEMORY );
+            return;
+        }
+        get_owned_windows( win, data );
+    }
+
+    win->is_cloaked = req->cloaked;
+
+    for (i = 0; i < total; i++)
+        if ((win = get_window( data[i] )))
+            win->is_cloaked = req->cloaked;
+}
+
+
 /* get a list of the window parents, up to the root of the tree */
 DECL_HANDLER(get_window_parents)
 {
-- 
2.21.0




More information about the wine-devel mailing list