[PATCH v2 12/15] winex11.drv, user32, shell32 Use AppUserModelID to populate WM_CLASS

Jonas Kümmerlin rgcjonas at gmail.com
Sat Jul 18 11:26:51 CDT 2015


If the window has an AppUserModelID set, it will be exported as WM_CLASS.
As a fallback, the process-wide explicit AppUserModelID is used, and if
that isn't present either, the full path of the executable will be utilized.

This is fundamentally incompatible with the previous approach
of using the basename only, but ultimately more similar to
how Windows 7 and later work.
---
 dlls/shell32/winpropstore.c |  12 ++++
 dlls/user32/message.c       |   2 +-
 dlls/user32/user_private.h  |   5 +-
 dlls/winex11.drv/window.c   | 160 +++++++++++++++++++++++++++++++++++---------
 4 files changed, 144 insertions(+), 35 deletions(-)

diff --git a/dlls/shell32/winpropstore.c b/dlls/shell32/winpropstore.c
index df4bd74..6aea596 100644
--- a/dlls/shell32/winpropstore.c
+++ b/dlls/shell32/winpropstore.c
@@ -33,8 +33,14 @@
 #include "wine/server.h"
 #include "mimeole.h"
 
+#include "initguid.h"
+#include "propkey.h"
+
 WINE_DEFAULT_DEBUG_CHANNEL(shell);
 
+/* keep in sync with dlls/user32/user_private.h */
+#define WM_WINE_APPUSERMODEL_ID_CHANGED  0x80002000
+
 typedef struct {
     IPropertyStore IPropertyStore_iface;
     HWND window;
@@ -234,6 +240,12 @@ static HRESULT WINAPI WindowPropertyStore_SetValue(IPropertyStore *iface,
 
     CoTaskMemFree(buffer);
 
+    /* If we recognize the property, we notify the window */
+    if (IsEqualGUID(key, &PKEY_AppUserModel_ID))
+    {
+        PostMessageW(This->window, WM_WINE_APPUSERMODEL_ID_CHANGED, 0, 0);
+    }
+
     return hr;
 }
 
diff --git a/dlls/user32/message.c b/dlls/user32/message.c
index d122980..c29455f 100644
--- a/dlls/user32/message.c
+++ b/dlls/user32/message.c
@@ -1889,7 +1889,7 @@ static LRESULT handle_internal_message( HWND hwnd, UINT msg, WPARAM wparam, LPAR
         }
         return USER_Driver->pClipCursor( NULL );
     default:
-        if (msg >= WM_WINE_FIRST_DRIVER_MSG && msg <= WM_WINE_LAST_DRIVER_MSG)
+        if (msg >= WM_WINE_FIRST_DRIVER_MSG && msg <= WM_WINE_LAST_DRIVER_NOTIFY_MSG)
             return USER_Driver->pWindowMessage( hwnd, msg, wparam, lparam );
         FIXME( "unknown internal message %x\n", msg );
         return 0;
diff --git a/dlls/user32/user_private.h b/dlls/user32/user_private.h
index d3affb0..2226df6 100644
--- a/dlls/user32/user_private.h
+++ b/dlls/user32/user_private.h
@@ -54,7 +54,10 @@ enum wine_internal_message
     WM_WINE_MOUSE_LL_HOOK,
     WM_WINE_CLIPCURSOR,
     WM_WINE_FIRST_DRIVER_MSG = 0x80001000,  /* range of messages reserved for the USER driver */
-    WM_WINE_LAST_DRIVER_MSG = 0x80001fff
+    WM_WINE_LAST_DRIVER_MSG = 0x80001ff,
+    WM_WINE_FIRST_DRIVER_NOTIFY_MSG = 0x80002000, /* range of defined messages to notify the driver */
+    WM_WINE_APPUSERMODEL_ID_CHANGED = WM_WINE_FIRST_DRIVER_NOTIFY_MSG,
+    WM_WINE_LAST_DRIVER_NOTIFY_MSG = 0x80002fff
 };
 
 typedef struct tagUSER_DRIVER {
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c
index 50c3290..b181200 100644
--- a/dlls/winex11.drv/window.c
+++ b/dlls/winex11.drv/window.c
@@ -22,6 +22,9 @@
 
 #include "config.h"
 
+#define NONAMELESSUNION
+#define COBJMACROS
+
 #include <stdarg.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -42,11 +45,21 @@
 #include "winuser.h"
 #include "wine/unicode.h"
 
+/* rpcasync.h declares ULONG Status; Xlib.h does #define Status int */
+#undef Status
+#include "shobjidl.h"
+#include "propsys.h"
+#include "propvarutil.h"
+#include "shellapi.h"
+
 #include "x11drv.h"
 #include "wine/debug.h"
 #include "wine/server.h"
 #include "mwm.h"
 
+#include "initguid.h"
+#include "propkey.h"
+
 WINE_DEFAULT_DEBUG_CHANNEL(x11drv);
 
 #define _NET_WM_MOVERESIZE_SIZE_TOPLEFT      0
@@ -65,6 +78,9 @@ WINE_DEFAULT_DEBUG_CHANNEL(x11drv);
 #define _NET_WM_STATE_ADD     1
 #define _NET_WM_STATE_TOGGLE  2
 
+/* keep in sync with dlls/user32/user_private.h */
+#define WM_WINE_APPUSERMODEL_ID_CHANGED  0x80002000
+
 static const unsigned int net_wm_state_atoms[NB_NET_WM_STATES] =
 {
     XATOM__NET_WM_STATE_FULLSCREEN,
@@ -797,34 +813,105 @@ static void set_style_hints( struct x11drv_win_data *data, DWORD style, DWORD ex
 
 
 /***********************************************************************
- *              get_process_name
+ *              get_appusermodel_id
  *
- * get the name of the current process for setting class hints
+ * Get the id of the current process for setting class hints.
+ * Free with HeapFree(GetProcessHeap(), ...).
  */
-static char *get_process_name(void)
+static char *get_appusermodel_id(HWND hWindow)
 {
-    static char *name;
+    WCHAR *explicit_id = NULL;
+    WCHAR module[MAX_PATH];
+    DWORD len;
+    IPropertyStore *store = NULL;
+    HRESULT hr;
 
-    if (!name)
+    /* Get the window-specific AppUserModelID */
+    hr = SHGetPropertyStoreForWindow( hWindow, &IID_IPropertyStore, (void**)&store );
+    if (SUCCEEDED( hr ))
     {
-        WCHAR module[MAX_PATH];
-        DWORD len = GetModuleFileNameW( 0, module, MAX_PATH );
-        if (len && len < MAX_PATH)
-        {
-            char *ptr;
-            WCHAR *p, *appname = module;
+        PROPVARIANT v;
+        char *ptr = NULL;
+        DWORD len;
+
+        PropVariantInit(&v);
 
-            if ((p = strrchrW( appname, '/' ))) appname = p + 1;
-            if ((p = strrchrW( appname, '\\' ))) appname = p + 1;
-            len = WideCharToMultiByte( CP_UNIXCP, 0, appname, -1, NULL, 0, NULL, NULL );
+        hr = IPropertyStore_GetValue( store, &PKEY_AppUserModel_ID, &v );
+        if (SUCCEEDED( hr ) && v.vt == VT_LPWSTR)
+        {
+            len = WideCharToMultiByte( CP_UTF8, 0, v.u.pwszVal, -1, NULL, 0, NULL, NULL );
             if ((ptr = HeapAlloc( GetProcessHeap(), 0, len )))
             {
-                WideCharToMultiByte( CP_UNIXCP, 0, appname, -1, ptr, len, NULL, NULL );
-                name = ptr;
+                WideCharToMultiByte( CP_UTF8, 0, v.u.pwszVal, -1, ptr, len, NULL, NULL );
             }
         }
+
+        IPropertyStore_Release( store );
+        PropVariantClear( &v );
+
+        if ( ptr )
+            return ptr;
+    }
+
+    /* A process-wide explicit AppUserModelID might be available */
+    if (SUCCEEDED(GetCurrentProcessExplicitAppUserModelID( &explicit_id )))
+    {
+        char *ptr;
+        DWORD len;
+
+        len = WideCharToMultiByte( CP_UTF8, 0, explicit_id, -1, NULL, 0, NULL, NULL );
+        if ((ptr = HeapAlloc( GetProcessHeap(), 0, len )))
+        {
+            WideCharToMultiByte( CP_UTF8, 0, explicit_id, -1, ptr, len, NULL, NULL );
+
+            CoTaskMemFree( explicit_id );
+
+            return ptr;
+        }
+
+        CoTaskMemFree( explicit_id );
+        return NULL;
+    }
+
+    /* Employ a fallback algorithm */
+
+    /* The Windows taskbar displays the same exe in two different directories
+     * as two different icons => the full path is used */
+    len = GetModuleFileNameW( 0, module, MAX_PATH );
+    if (len && len < MAX_PATH)
+    {
+        char *ptr;
+
+        len = WideCharToMultiByte( CP_UTF8, 0, module, -1, NULL, 0, NULL, NULL );
+        if ((ptr = HeapAlloc( GetProcessHeap(), 0, len )))
+        {
+            WideCharToMultiByte( CP_UTF8, 0, module, -1,
+                                 ptr, len, NULL, NULL );
+
+            return ptr;
+        }
+    }
+
+    return NULL;
+}
+
+static void set_class_hints ( Display *display, Window window, HWND hwnd )
+{
+    XClassHint *class_hints;
+
+    /* class hints */
+    if ((class_hints = XAllocClassHint()))
+    {
+        static char wine[] = "Wine";
+        char *id = get_appusermodel_id( hwnd );
+
+        class_hints->res_name = wine;
+        class_hints->res_class = id;
+        XSetClassHint( display, window, class_hints );
+        XFree( class_hints );
+
+        HeapFree( GetProcessHeap(), 0, id );
     }
-    return name;
 }
 
 
@@ -833,13 +920,11 @@ static char *get_process_name(void)
  *
  * Set the window manager hints that don't change over the lifetime of a window.
  */
-static void set_initial_wm_hints( Display *display, Window window )
+static void set_initial_wm_hints( Display *display, Window window, HWND hWnd )
 {
     long i;
     Atom protocols[3];
     Atom dndVersion = WINE_XDND_VERSION;
-    XClassHint *class_hints;
-    char *process_name = get_process_name();
 
     /* wm protocols */
     i = 0;
@@ -849,16 +934,7 @@ static void set_initial_wm_hints( Display *display, Window window )
     XChangeProperty( display, window, x11drv_atom(WM_PROTOCOLS),
                      XA_ATOM, 32, PropModeReplace, (unsigned char *)protocols, i );
 
-    /* class hints */
-    if ((class_hints = XAllocClassHint()))
-    {
-        static char wine[] = "Wine";
-
-        class_hints->res_name = process_name;
-        class_hints->res_class = wine;
-        XSetClassHint( display, window, class_hints );
-        XFree( class_hints );
-    }
+    set_class_hints( display, window, hWnd );
 
     /* set the WM_CLIENT_MACHINE and WM_LOCALE_NAME properties */
     XSetWMProperties(display, window, NULL, NULL, NULL, 0, NULL, NULL, NULL);
@@ -1505,7 +1581,7 @@ static void create_whole_window( struct x11drv_win_data *data )
                                         data->vis.visual, mask, &attr );
     if (!data->whole_window) goto done;
 
-    set_initial_wm_hints( data->display, data->whole_window );
+    set_initial_wm_hints( data->display, data->whole_window, data->hwnd );
     set_wm_hints( data );
 
     XSaveContext( data->display, data->whole_window, winContext, (char *)data->hwnd );
@@ -1577,7 +1653,6 @@ static void destroy_whole_window( struct x11drv_win_data *data, BOOL already_des
     RemovePropA( data->hwnd, whole_window_prop );
 }
 
-
 /**********************************************************************
  *		set_window_visual
  *
@@ -1717,7 +1792,7 @@ BOOL create_desktop_win_data( Window win )
     data->whole_window = win;
     data->managed = TRUE;
     SetPropA( data->hwnd, whole_window_prop, (HANDLE)win );
-    set_initial_wm_hints( display, win );
+    set_initial_wm_hints( display, win, data->hwnd );
     release_win_data( data );
     if (thread_data->clip_window) XReparentWindow( display, thread_data->clip_window, win, 0, 0 );
     return TRUE;
@@ -2662,6 +2737,25 @@ LRESULT CDECL X11DRV_WindowMessage( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp )
         return 0;
     case WM_X11DRV_CLIP_CURSOR:
         return clip_cursor_notify( hwnd, (HWND)lp );
+    case WM_WINE_APPUSERMODEL_ID_CHANGED:
+    {
+        if ((data = get_win_data( hwnd )))
+        {
+            /* The ICCCM spec says we have to withdraw the window to change WM_CLASS.
+             * Gnome works fine without doing that, but KDE requires it. */
+
+            ShowWindow( hwnd, SW_HIDE );
+
+            if ( data->display && data->whole_window )
+                set_class_hints( data->display, data->whole_window, hwnd );
+
+            ShowWindow( hwnd, SW_SHOW );
+
+            release_win_data( data );
+        }
+
+        return 0;
+    }
     default:
         FIXME( "got window msg %x hwnd %p wp %lx lp %lx\n", msg, hwnd, wp, lp );
         return 0;
-- 
2.4.3




More information about the wine-devel mailing list