[PATCH v2 11/15] shell32: Implement window property store (SHGetPropertyStoreForWindow)

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


This property store is underspecified, poorly documented by Microsoft
and the tests expose a fair bit of implementation WTF.
---
 dlls/shell32/Makefile.in          |   1 +
 dlls/shell32/shell32.spec         |   1 +
 dlls/shell32/tests/appusermodel.c | 153 +++++++++++++++++++-
 dlls/shell32/winpropstore.c       | 291 ++++++++++++++++++++++++++++++++++++++
 include/shellapi.h                |   1 +
 include/wine/server_protocol.h    |  59 +++++++-
 server/protocol.def               |  38 +++++
 server/request.h                  |  35 +++++
 server/trace.c                    |  47 ++++++
 server/window.c                   | 148 +++++++++++++++++++
 10 files changed, 772 insertions(+), 2 deletions(-)
 create mode 100644 dlls/shell32/winpropstore.c

diff --git a/dlls/shell32/Makefile.in b/dlls/shell32/Makefile.in
index 038db7f..234e700 100644
--- a/dlls/shell32/Makefile.in
+++ b/dlls/shell32/Makefile.in
@@ -53,6 +53,7 @@ C_SRCS = \
 	shpolicy.c \
 	systray.c \
 	trash.c \
+	winpropstore.c \
 	xdg.c
 
 RC_SRCS = shell32.rc
diff --git a/dlls/shell32/shell32.spec b/dlls/shell32/shell32.spec
index 1aa2c6b..358389f 100644
--- a/dlls/shell32/shell32.spec
+++ b/dlls/shell32/shell32.spec
@@ -390,6 +390,7 @@
 @ stdcall SHGetPathFromIDListA(ptr ptr)
 @ stdcall SHGetPathFromIDListW(ptr ptr)
 @ stdcall SHGetPropertyStoreFromParsingName(wstr ptr long ptr ptr)
+@ stdcall SHGetPropertyStoreForWindow(ptr ptr ptr)
 @ stdcall SHGetSettings(ptr long)
 @ stdcall SHGetSpecialFolderLocation(long long ptr)
 @ stdcall SHGetSpecialFolderPathA(long ptr long long)
diff --git a/dlls/shell32/tests/appusermodel.c b/dlls/shell32/tests/appusermodel.c
index dd83ef8..b6cdfd1 100644
--- a/dlls/shell32/tests/appusermodel.c
+++ b/dlls/shell32/tests/appusermodel.c
@@ -18,18 +18,25 @@
  */
 
 #define COBJMACROS
+#define NONAMELESSUNION
 
 #include "windows.h"
 #include "shlguid.h"
 #include "shobjidl.h"
+#include "propsys.h"
+#include "propkey.h"
 #include "shlobj.h"
 #include "shellapi.h"
 #include "wine/test.h"
 
 #include "shell32_test.h"
 
+
+static GUID PKEY_WineTest = {0x7b317433,0xdfa3,0x4c44,{0xad,0x3e,0x2f,0x80,0x4b,0x90,0xdb,0xf4}};
+
 static HRESULT (WINAPI *pSetCurrentProcessExplicitAppUserModelID)(const WCHAR *id);
 static HRESULT (WINAPI *pGetCurrentProcessExplicitAppUserModelID)(WCHAR **id);
+static HRESULT (WINAPI *pSHGetPropertyStoreForWindow)(HWND hwnd, REFIID iid, void **ppv);
 
 static void test_process_aum_id(void)
 {
@@ -104,7 +111,149 @@ static void test_process_aum_id(void)
     }
 }
 
-/* TODO: Test the window property store (SHGetPropertyStoreForWindow) */
+static LRESULT WINAPI wndprocA(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+    return DefWindowProcA(hwnd, msg, wparam, lparam);
+}
+
+
+static void test_window_propstore(void)
+{
+    IPropertyStore *store;
+    HRESULT hr;
+    PROPERTYKEY pkey;
+    PROPVARIANT propvar;
+    DWORD count;
+    WNDCLASSA cls;
+    HWND hwndMain;
+    WCHAR nums[] = { '1','2','3','4','5',0 };
+    WCHAR hello[] = { 'H','e','l','l','o',0 };
+
+    if (!pSHGetPropertyStoreForWindow)
+    {
+        win_skip("SHGetPropertyStoreForWindow is not available\n");
+        return;
+    }
+
+    /* create a small window to carry out our tests */
+    cls.style = CS_DBLCLKS;
+    cls.lpfnWndProc = wndprocA;
+    cls.cbClsExtra = 0;
+    cls.cbWndExtra = 0;
+    cls.hInstance = GetModuleHandleA(NULL);
+    cls.hIcon = 0;
+    cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
+    cls.hbrBackground = NULL;
+    cls.lpszMenuName = NULL;
+    cls.lpszClassName = "MainWindowClass";
+
+    ok(!!RegisterClassA(&cls), "WTF: registering class\n");
+
+    hwndMain = CreateWindowExA(0, "MainWindowClass", "Main window",
+                               WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX |
+                               WS_MAXIMIZEBOX | WS_POPUP | WS_VISIBLE,
+                               100, 100, 200, 200,
+                               0, 0, GetModuleHandleA(NULL), NULL);
+    ok(!!hwndMain, "WTF: CreateWindowEx\n");
+
+    hr = pSHGetPropertyStoreForWindow(hwndMain, &IID_IPropertyStore, (void**)&store);
+    ok(hr == S_OK, "SHGetPropertyStoreForWindow failed, hr=%x\n", hr);
+
+    /* crashes win7 */
+    if (0)
+    {
+        hr = IPropertyStore_GetCount(store, NULL);
+        ok(hr == E_POINTER, "GetCount unexpected hr=%x\n", hr);
+    }
+
+    hr = IPropertyStore_GetCount(store, &count);
+    ok(hr == S_OK /*Win7*/ || hr == E_FAIL/*Win8*/, "GetCount unexpected hr=%x\n", hr);
+    ok(count == 0, "GetCount returned count=%u\n", count);
+
+    hr = IPropertyStore_Commit(store);
+    ok(hr == S_OK, "Commit failed, hr=%x\n", hr);
+
+    hr = IPropertyStore_GetAt(store, 0, &pkey);
+    ok(hr == E_INVALIDARG /*Win7*/ || hr == E_FAIL /*Win8*/, "GetAt failed, hr=%x\n", hr);
+
+    pkey.fmtid = PKEY_WineTest;
+    pkey.pid = 4;
+
+    memset(&propvar, 0, sizeof(propvar));
+    propvar.vt = VT_I4;
+    propvar.u.lVal = 12345;
+
+    if (0)
+    {
+        /* Crashes on Windows 7 */
+        hr = IPropertyStore_SetValue(store, NULL, &propvar);
+        ok(hr == E_POINTER, "SetValue failed, hr=%x\n", hr);
+
+        hr = IPropertyStore_SetValue(store, &pkey, NULL);
+        ok(hr == E_POINTER, "SetValue failed, hr=%x\n", hr);
+    }
+
+    /* Set an integer value */
+    hr = IPropertyStore_SetValue(store, &pkey, &propvar);
+    ok(hr == S_OK, "SetValue failed, hr=%x\n", hr);
+
+    memset(&propvar, 0, sizeof(propvar));
+
+    /* This is a really weird error code */
+    hr = IPropertyStore_GetValue(store, NULL, &propvar);
+    todo_wine ok(hr == E_NOT_SUFFICIENT_BUFFER, "GetValue failed, hr=%x\n", hr);
+
+    hr = IPropertyStore_GetValue(store, &pkey, NULL);
+    ok(hr == E_POINTER, "GetValue failed, hr=%x\n", hr);
+
+    /* The windows implementation supports strings only and will coerce any other
+     * values to strings. This is undocumented, but we'll have to live with it */
+    hr = IPropertyStore_GetValue(store, &pkey, &propvar);
+    ok(hr == S_FALSE, "GetValue failed, hr=%x\n", hr);
+        /* WTF: INPLACE_S_TRUNCATED would be appropriate */
+    todo_wine ok(propvar.vt == VT_LPWSTR, "expected VT_LPWSTR, got %d\n", propvar.vt);
+    if (propvar.vt == VT_LPWSTR)
+        todo_wine ok(!lstrcmpW(propvar.u.pwszVal, nums),
+                     "expected L\"12345\", got %s\n", wine_dbgstr_w(propvar.u.pwszVal));
+    PropVariantClear(&propvar);
+
+    /* Set a string value */
+    propvar.vt = VT_LPWSTR;
+    propvar.u.pwszVal = hello;
+
+    hr = IPropertyStore_SetValue(store, &pkey, &propvar);
+    ok(hr == S_OK, "SetValue: hr=%x\n", hr);
+
+    /* and retrieve a string value */
+    hr = IPropertyStore_GetValue(store, &pkey, &propvar);
+    ok(hr == S_FALSE, "GetValue failed, hr=%x\n", hr);
+        /* WTF: The docs require S_OK here */
+    ok(propvar.vt == VT_LPWSTR, "expected VT_LPWSTR, got %d\n", propvar.vt);
+    ok(!lstrcmpW(propvar.u.pwszVal, hello),
+                 "expected L\"hello\", got %s\n", wine_dbgstr_w(propvar.u.pwszVal));
+    PropVariantClear(&propvar);
+
+    pkey.fmtid = PKEY_WineTest;
+    pkey.pid = 10;
+
+    /* Get information for field that isn't set yet */
+    propvar.vt = VT_I2;
+    hr = IPropertyStore_GetValue(store, &pkey, &propvar);
+    ok(hr == S_OK, "GetValue failed, hr=%x\n", hr);
+    ok(propvar.vt == VT_EMPTY, "expected VT_EMPTY, got %d\n", propvar.vt);
+
+    /* According to MSDN, we can leak memory if we don't delete our properties
+     * and because we're good citizens, we delete them. Note that wine doesn't
+     * care, it will clean everything up anyways, like you'd expect. */
+    propvar.vt = VT_EMPTY;
+    pkey.fmtid = PKEY_WineTest;
+    pkey.pid   = 4;
+    hr = IPropertyStore_SetValue(store, &pkey, &propvar);
+    ok(hr == S_OK, "SetValue failed, hr=%x\n", hr);
+
+    IPropertyStore_Release(store);
+    DestroyWindow(hwndMain);
+}
 
 #define RESOLVE(hDll, proc) p##proc = (void*)GetProcAddress(hDll, #proc)
 
@@ -116,6 +265,8 @@ START_TEST(appusermodel)
 
     RESOLVE(hShell32, SetCurrentProcessExplicitAppUserModelID);
     RESOLVE(hShell32, GetCurrentProcessExplicitAppUserModelID);
+    RESOLVE(hShell32, SHGetPropertyStoreForWindow);
 
     test_process_aum_id();
+    test_window_propstore();
 }
diff --git a/dlls/shell32/winpropstore.c b/dlls/shell32/winpropstore.c
new file mode 100644
index 0000000..df4bd74
--- /dev/null
+++ b/dlls/shell32/winpropstore.c
@@ -0,0 +1,291 @@
+/*
+ * IPropertyStore for window-attached properties
+ *
+ * Copyright 2015 Jonas Kümmerlin
+ *
+ * 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
+ */
+
+#define COBJMACROS
+#include "config.h"
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "objbase.h"
+#include "rpcproxy.h"
+#include "propsys.h"
+#include "propvarutil.h"
+#include "wine/debug.h"
+#include "wine/server.h"
+#include "mimeole.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(shell);
+
+typedef struct {
+    IPropertyStore IPropertyStore_iface;
+    HWND window;
+    LONG ref;
+    HMODULE hPropsys;
+    HRESULT (WINAPI *pStgSerializePropVariant)
+        (const PROPVARIANT *pVar, SERIALIZEDPROPERTYVALUE **ppProp, ULONG *pcb);
+    HRESULT (WINAPI *pStgDeserializePropVariant)
+        (const SERIALIZEDPROPERTYVALUE *pprop, ULONG cbMax, PROPVARIANT *pvar);
+} WindowPropertyStore;
+
+static inline WindowPropertyStore *impl_from_IPropertyStore(IPropertyStore *iface)
+{
+    return CONTAINING_RECORD(iface, WindowPropertyStore, IPropertyStore_iface);
+}
+
+static HRESULT WINAPI WindowPropertyStore_QueryInterface(IPropertyStore *iface, REFIID iid, void **ppv)
+{
+    WindowPropertyStore *This = impl_from_IPropertyStore(iface);
+
+    TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
+
+    if (!ppv) return E_INVALIDARG;
+
+    if (IsEqualIID(&IID_IUnknown, iid) || IsEqualIID(&IID_IPropertyStore, iid))
+    {
+        *ppv = &This->IPropertyStore_iface;
+    }
+    else
+    {
+        FIXME("No interface for %s\n", debugstr_guid(iid));
+        *ppv = NULL;
+        return E_NOINTERFACE;
+    }
+
+    IUnknown_AddRef((IUnknown*)*ppv);
+    return S_OK;
+}
+static ULONG WINAPI WindowPropertyStore_AddRef(IPropertyStore *iface)
+{
+    WindowPropertyStore *This = impl_from_IPropertyStore(iface);
+    ULONG ref = InterlockedIncrement(&This->ref);
+
+    TRACE("(%p) refcount=%u\n", iface, ref);
+
+    return ref;
+}
+
+static ULONG WINAPI WindowPropertyStore_Release(IPropertyStore *iface)
+{
+    WindowPropertyStore *This = impl_from_IPropertyStore(iface);
+    ULONG ref = InterlockedDecrement(&This->ref);
+
+    if (ref == 0)
+    {
+        FreeLibrary(This->hPropsys);
+        HeapFree(GetProcessHeap(), 0, This);
+    }
+
+    TRACE("(%p) refcount=%u\n", iface, ref);
+
+    return ref;
+}
+
+static HRESULT WINAPI WindowPropertyStore_GetCount(IPropertyStore *iface,
+    DWORD *cProps)
+{
+    TRACE("%p,%p\n", iface, cProps);
+
+    /* the window property store is not enumerable */
+    *cProps = 0;
+
+    return E_FAIL;
+}
+
+static HRESULT WINAPI WindowPropertyStore_GetAt(IPropertyStore *iface,
+    DWORD iProp, PROPERTYKEY *pkey)
+{
+    TRACE("%p,%d,%p\n", iface, iProp, pkey);
+
+    /* the window property store is not enumerable */
+    return E_FAIL;
+}
+
+static HRESULT WINAPI WindowPropertyStore_GetValue(IPropertyStore *iface,
+    REFPROPERTYKEY key, PROPVARIANT *pv)
+{
+    WindowPropertyStore *This = impl_from_IPropertyStore(iface);
+    void *buffer      = NULL;
+    DWORD buffer_size = 10; /*TODO increase*/
+    DWORD prop_size   = 0;
+    HRESULT hr        = S_OK;
+
+    TRACE("%p,%p,%p\n", iface, key, pv);
+
+    if (!pv || !key)
+        return E_POINTER;
+
+    PropVariantInit(pv);
+
+    /* try until we received a full property */
+    for (;;)
+    {
+        buffer = HeapAlloc(GetProcessHeap(), 0, buffer_size);
+
+        if (!buffer)
+        {
+            return E_OUTOFMEMORY;
+        }
+
+        SERVER_START_REQ(get_sh_window_property)
+        {
+            req->window = wine_server_user_handle(This->window);
+            req->fmtid_data1 = key->fmtid.Data1;
+            req->fmtid_data2 = key->fmtid.Data2;
+            req->fmtid_data3 = key->fmtid.Data3;
+            req->fmtid_data4_0 = key->fmtid.Data4[0];
+            req->fmtid_data4_1 = key->fmtid.Data4[1];
+            req->fmtid_data4_2 = key->fmtid.Data4[2];
+            req->fmtid_data4_3 = key->fmtid.Data4[3];
+            req->fmtid_data4_4 = key->fmtid.Data4[4];
+            req->fmtid_data4_5 = key->fmtid.Data4[5];
+            req->fmtid_data4_6 = key->fmtid.Data4[6];
+            req->fmtid_data4_7 = key->fmtid.Data4[7];
+            req->pid = key->pid;
+
+            wine_server_set_reply(req, buffer, buffer_size);
+
+            if (!wine_server_call(req))
+            {
+                prop_size = reply->property_size;
+            }
+        }
+        SERVER_END_REQ;
+
+        if (prop_size <= buffer_size)
+            break;
+
+        buffer_size = prop_size;
+        HeapFree(GetProcessHeap(), 0, buffer);
+    }
+
+    /* size zero == VT_EMPTY, no need to do work */
+    if (prop_size > 0)
+    {
+        hr = This->pStgDeserializePropVariant((void*)buffer, prop_size, pv);
+
+        /* HACK: Disregarding MSDN docs and common sense, windows always returns S_FALSE */
+        if (hr == S_OK)
+            hr = S_FALSE;
+    }
+
+    HeapFree(GetProcessHeap(), 0, buffer);
+
+    return hr;
+}
+
+static HRESULT WINAPI WindowPropertyStore_SetValue(IPropertyStore *iface,
+    REFPROPERTYKEY key, REFPROPVARIANT propvar)
+{
+    WindowPropertyStore *This = impl_from_IPropertyStore(iface);
+    HRESULT hr = S_OK;
+    SERIALIZEDPROPERTYVALUE *buffer = NULL;
+    DWORD size;
+
+    TRACE("%p,%p,%p\n", iface, key, propvar);
+
+    hr = This->pStgSerializePropVariant(propvar, &buffer, &size);
+    if (FAILED(hr))
+    {
+        WARN("Failed to serialize property of type %d: hr=%08x\n", propvar->vt, hr);
+        return hr;
+    }
+
+    /* send it to the server */
+    SERVER_START_REQ(set_sh_window_property)
+    {
+        req->window = wine_server_user_handle(This->window);
+        req->fmtid_data1 = key->fmtid.Data1;
+        req->fmtid_data2 = key->fmtid.Data2;
+        req->fmtid_data3 = key->fmtid.Data3;
+        req->fmtid_data4_0 = key->fmtid.Data4[0];
+        req->fmtid_data4_1 = key->fmtid.Data4[1];
+        req->fmtid_data4_2 = key->fmtid.Data4[2];
+        req->fmtid_data4_3 = key->fmtid.Data4[3];
+        req->fmtid_data4_4 = key->fmtid.Data4[4];
+        req->fmtid_data4_5 = key->fmtid.Data4[5];
+        req->fmtid_data4_6 = key->fmtid.Data4[6];
+        req->fmtid_data4_7 = key->fmtid.Data4[7];
+        req->pid = key->pid;
+
+        wine_server_add_data(req, buffer, size);
+
+        wine_server_call(req);
+    }
+    SERVER_END_REQ;
+
+    CoTaskMemFree(buffer);
+
+    return hr;
+}
+
+static HRESULT WINAPI WindowPropertyStore_Commit(IPropertyStore *iface)
+{
+    TRACE("%pn", iface);
+
+    /* MSDN claims it to be a no-op that always succeeds, so that's what we do */
+    return S_OK;
+}
+
+static const IPropertyStoreVtbl WindowPropertyStore_Vtbl = {
+    WindowPropertyStore_QueryInterface,
+    WindowPropertyStore_AddRef,
+    WindowPropertyStore_Release,
+    WindowPropertyStore_GetCount,
+    WindowPropertyStore_GetAt,
+    WindowPropertyStore_GetValue,
+    WindowPropertyStore_SetValue,
+    WindowPropertyStore_Commit,
+};
+
+HRESULT WINAPI SHGetPropertyStoreForWindow(HWND hwnd, REFIID iid, void** ppv)
+{
+    WindowPropertyStore *This;
+    HRESULT hr;
+
+    TRACE("(%p,%s,%p)\n", hwnd, debugstr_guid(iid), ppv);
+
+    *ppv = NULL;
+
+    This = HeapAlloc(GetProcessHeap(), 0, sizeof(WindowPropertyStore));
+    if (!This)
+        return E_OUTOFMEMORY;
+
+    This->hPropsys = LoadLibraryA("propsys.dll");
+    if (!This->hPropsys)
+    {
+        ERR("WTF: propsys.dll not available\n");
+        HeapFree(GetProcessHeap(), 0, This);
+        return E_FAIL;
+    }
+
+    This->pStgDeserializePropVariant = (void*)GetProcAddress(This->hPropsys, "StgDeserializePropVariant");
+    This->pStgSerializePropVariant = (void*)GetProcAddress(This->hPropsys, "StgSerializePropVariant");
+
+    This->IPropertyStore_iface.lpVtbl = &WindowPropertyStore_Vtbl;
+    This->window = hwnd;
+    This->ref = 1;
+
+    hr = IPropertyStore_QueryInterface(&This->IPropertyStore_iface, iid, ppv);
+    IPropertyStore_Release(&This->IPropertyStore_iface);
+
+    return hr;
+}
diff --git a/include/shellapi.h b/include/shellapi.h
index 8492155..f13cc6b 100644
--- a/include/shellapi.h
+++ b/include/shellapi.h
@@ -647,6 +647,7 @@ HRESULT     WINAPI SHEnumerateUnreadMailAccountsA(HKEY,DWORD,LPSTR,INT);
 HRESULT     WINAPI SHEnumerateUnreadMailAccountsW(HKEY,DWORD,LPWSTR,INT);
 #define     SHEnumerateUnreadMailAccounts WINELIB_NAME_AW(SHEnumerateUnreadMailAccounts)
 
+HRESULT     WINAPI SHGetPropertyStoreForWindow(HWND hwnd,REFIID riid,void **ppv);
 
 #ifdef __cplusplus
 } /* extern "C" */
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index 8b186fa..bb5fecc 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -3782,6 +3782,57 @@ struct get_window_properties_reply
 };
 
 
+struct set_sh_window_property_request
+{
+    struct request_header __header;
+    user_handle_t window;
+    unsigned int   fmtid_data1;
+    unsigned short fmtid_data2;
+    unsigned short fmtid_data3;
+    unsigned char  fmtid_data4_0;
+    unsigned char  fmtid_data4_1;
+    unsigned char  fmtid_data4_2;
+    unsigned char  fmtid_data4_3;
+    unsigned char  fmtid_data4_4;
+    unsigned char  fmtid_data4_5;
+    unsigned char  fmtid_data4_6;
+    unsigned char  fmtid_data4_7;
+    unsigned int   pid;
+    /* VARARG(property,bytes); */
+    char __pad_36[4];
+};
+struct set_sh_window_property_reply
+{
+    struct reply_header __header;
+};
+
+
+struct get_sh_window_property_request
+{
+    struct request_header __header;
+    user_handle_t window;
+    unsigned int   fmtid_data1;
+    unsigned short fmtid_data2;
+    unsigned short fmtid_data3;
+    unsigned char  fmtid_data4_0;
+    unsigned char  fmtid_data4_1;
+    unsigned char  fmtid_data4_2;
+    unsigned char  fmtid_data4_3;
+    unsigned char  fmtid_data4_4;
+    unsigned char  fmtid_data4_5;
+    unsigned char  fmtid_data4_6;
+    unsigned char  fmtid_data4_7;
+    unsigned int   pid;
+    char __pad_36[4];
+};
+struct get_sh_window_property_reply
+{
+    struct reply_header __header;
+    unsigned int  property_size;
+    /* VARARG(property,bytes); */
+    char __pad_12[4];
+};
+
 
 struct create_winstation_request
 {
@@ -5460,6 +5511,8 @@ enum request
     REQ_remove_window_property,
     REQ_get_window_property,
     REQ_get_window_properties,
+    REQ_set_sh_window_property,
+    REQ_get_sh_window_property,
     REQ_create_winstation,
     REQ_open_winstation,
     REQ_close_winstation,
@@ -5733,6 +5786,8 @@ union generic_request
     struct remove_window_property_request remove_window_property_request;
     struct get_window_property_request get_window_property_request;
     struct get_window_properties_request get_window_properties_request;
+    struct set_sh_window_property_request set_sh_window_property_request;
+    struct get_sh_window_property_request get_sh_window_property_request;
     struct create_winstation_request create_winstation_request;
     struct open_winstation_request open_winstation_request;
     struct close_winstation_request close_winstation_request;
@@ -6004,6 +6059,8 @@ union generic_reply
     struct remove_window_property_reply remove_window_property_reply;
     struct get_window_property_reply get_window_property_reply;
     struct get_window_properties_reply get_window_properties_reply;
+    struct set_sh_window_property_reply set_sh_window_property_reply;
+    struct get_sh_window_property_reply get_sh_window_property_reply;
     struct create_winstation_reply create_winstation_reply;
     struct open_winstation_reply open_winstation_reply;
     struct close_winstation_reply close_winstation_reply;
@@ -6096,6 +6153,6 @@ union generic_reply
     struct terminate_job_reply terminate_job_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 481
+#define SERVER_PROTOCOL_VERSION 482
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/server/protocol.def b/server/protocol.def
index 0ff1a6b..a64c318 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -2710,6 +2710,44 @@ enum coords_relative
     VARARG(props,properties);     /* list of properties */
 @END
 
+/* Set a shell property on a window */
+ at REQ(set_sh_window_property)
+    user_handle_t window;
+    unsigned int   fmtid_data1;     /* key: fmtid */
+    unsigned short fmtid_data2;
+    unsigned short fmtid_data3;
+    unsigned char  fmtid_data4_0;
+    unsigned char  fmtid_data4_1;
+    unsigned char  fmtid_data4_2;
+    unsigned char  fmtid_data4_3;
+    unsigned char  fmtid_data4_4;
+    unsigned char  fmtid_data4_5;
+    unsigned char  fmtid_data4_6;
+    unsigned char  fmtid_data4_7;
+    unsigned int   pid;             /* key: pid */
+    VARARG(property,bytes);
+ at REPLY
+ at END
+
+/* Get a shell property from a window */
+ at REQ(get_sh_window_property)
+    user_handle_t window;           /* the window */
+    unsigned int   fmtid_data1;     /* key: fmtid */
+    unsigned short fmtid_data2;
+    unsigned short fmtid_data3;
+    unsigned char  fmtid_data4_0;
+    unsigned char  fmtid_data4_1;
+    unsigned char  fmtid_data4_2;
+    unsigned char  fmtid_data4_3;
+    unsigned char  fmtid_data4_4;
+    unsigned char  fmtid_data4_5;
+    unsigned char  fmtid_data4_6;
+    unsigned char  fmtid_data4_7;
+    unsigned int   pid;             /* key: pid */
+ at REPLY
+    unsigned int  property_size;
+    VARARG(property,bytes);
+ at END
 
 /* Create a window station */
 @REQ(create_winstation)
diff --git a/server/request.h b/server/request.h
index 760466c..3795017 100644
--- a/server/request.h
+++ b/server/request.h
@@ -282,6 +282,8 @@ DECL_HANDLER(set_window_property);
 DECL_HANDLER(remove_window_property);
 DECL_HANDLER(get_window_property);
 DECL_HANDLER(get_window_properties);
+DECL_HANDLER(set_sh_window_property);
+DECL_HANDLER(get_sh_window_property);
 DECL_HANDLER(create_winstation);
 DECL_HANDLER(open_winstation);
 DECL_HANDLER(close_winstation);
@@ -554,6 +556,8 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] =
     (req_handler)req_remove_window_property,
     (req_handler)req_get_window_property,
     (req_handler)req_get_window_properties,
+    (req_handler)req_set_sh_window_property,
+    (req_handler)req_get_sh_window_property,
     (req_handler)req_create_winstation,
     (req_handler)req_open_winstation,
     (req_handler)req_close_winstation,
@@ -1756,6 +1760,37 @@ C_ASSERT( FIELD_OFFSET(struct get_window_properties_request, window) == 12 );
 C_ASSERT( sizeof(struct get_window_properties_request) == 16 );
 C_ASSERT( FIELD_OFFSET(struct get_window_properties_reply, total) == 8 );
 C_ASSERT( sizeof(struct get_window_properties_reply) == 16 );
+C_ASSERT( FIELD_OFFSET(struct set_sh_window_property_request, window) == 12 );
+C_ASSERT( FIELD_OFFSET(struct set_sh_window_property_request, fmtid_data1) == 16 );
+C_ASSERT( FIELD_OFFSET(struct set_sh_window_property_request, fmtid_data2) == 20 );
+C_ASSERT( FIELD_OFFSET(struct set_sh_window_property_request, fmtid_data3) == 22 );
+C_ASSERT( FIELD_OFFSET(struct set_sh_window_property_request, fmtid_data4_0) == 24 );
+C_ASSERT( FIELD_OFFSET(struct set_sh_window_property_request, fmtid_data4_1) == 25 );
+C_ASSERT( FIELD_OFFSET(struct set_sh_window_property_request, fmtid_data4_2) == 26 );
+C_ASSERT( FIELD_OFFSET(struct set_sh_window_property_request, fmtid_data4_3) == 27 );
+C_ASSERT( FIELD_OFFSET(struct set_sh_window_property_request, fmtid_data4_4) == 28 );
+C_ASSERT( FIELD_OFFSET(struct set_sh_window_property_request, fmtid_data4_5) == 29 );
+C_ASSERT( FIELD_OFFSET(struct set_sh_window_property_request, fmtid_data4_6) == 30 );
+C_ASSERT( FIELD_OFFSET(struct set_sh_window_property_request, fmtid_data4_7) == 31 );
+C_ASSERT( FIELD_OFFSET(struct set_sh_window_property_request, pid) == 32 );
+C_ASSERT( sizeof(struct set_sh_window_property_request) == 40 );
+C_ASSERT( sizeof(struct set_sh_window_property_reply) == 8 );
+C_ASSERT( FIELD_OFFSET(struct get_sh_window_property_request, window) == 12 );
+C_ASSERT( FIELD_OFFSET(struct get_sh_window_property_request, fmtid_data1) == 16 );
+C_ASSERT( FIELD_OFFSET(struct get_sh_window_property_request, fmtid_data2) == 20 );
+C_ASSERT( FIELD_OFFSET(struct get_sh_window_property_request, fmtid_data3) == 22 );
+C_ASSERT( FIELD_OFFSET(struct get_sh_window_property_request, fmtid_data4_0) == 24 );
+C_ASSERT( FIELD_OFFSET(struct get_sh_window_property_request, fmtid_data4_1) == 25 );
+C_ASSERT( FIELD_OFFSET(struct get_sh_window_property_request, fmtid_data4_2) == 26 );
+C_ASSERT( FIELD_OFFSET(struct get_sh_window_property_request, fmtid_data4_3) == 27 );
+C_ASSERT( FIELD_OFFSET(struct get_sh_window_property_request, fmtid_data4_4) == 28 );
+C_ASSERT( FIELD_OFFSET(struct get_sh_window_property_request, fmtid_data4_5) == 29 );
+C_ASSERT( FIELD_OFFSET(struct get_sh_window_property_request, fmtid_data4_6) == 30 );
+C_ASSERT( FIELD_OFFSET(struct get_sh_window_property_request, fmtid_data4_7) == 31 );
+C_ASSERT( FIELD_OFFSET(struct get_sh_window_property_request, pid) == 32 );
+C_ASSERT( sizeof(struct get_sh_window_property_request) == 40 );
+C_ASSERT( FIELD_OFFSET(struct get_sh_window_property_reply, property_size) == 8 );
+C_ASSERT( sizeof(struct get_sh_window_property_reply) == 16 );
 C_ASSERT( FIELD_OFFSET(struct create_winstation_request, flags) == 12 );
 C_ASSERT( FIELD_OFFSET(struct create_winstation_request, access) == 16 );
 C_ASSERT( FIELD_OFFSET(struct create_winstation_request, attributes) == 20 );
diff --git a/server/trace.c b/server/trace.c
index 42cff99..3d1259f 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -3214,6 +3214,47 @@ static void dump_get_window_properties_reply( const struct get_window_properties
     dump_varargs_properties( ", props=", cur_size );
 }
 
+static void dump_set_sh_window_property_request( const struct set_sh_window_property_request *req )
+{
+    fprintf( stderr, " window=%08x", req->window );
+    fprintf( stderr, ", fmtid_data1=%08x", req->fmtid_data1 );
+    fprintf( stderr, ", fmtid_data2=%04x", req->fmtid_data2 );
+    fprintf( stderr, ", fmtid_data3=%04x", req->fmtid_data3 );
+    fprintf( stderr, ", fmtid_data4_0=%02x", req->fmtid_data4_0 );
+    fprintf( stderr, ", fmtid_data4_1=%02x", req->fmtid_data4_1 );
+    fprintf( stderr, ", fmtid_data4_2=%02x", req->fmtid_data4_2 );
+    fprintf( stderr, ", fmtid_data4_3=%02x", req->fmtid_data4_3 );
+    fprintf( stderr, ", fmtid_data4_4=%02x", req->fmtid_data4_4 );
+    fprintf( stderr, ", fmtid_data4_5=%02x", req->fmtid_data4_5 );
+    fprintf( stderr, ", fmtid_data4_6=%02x", req->fmtid_data4_6 );
+    fprintf( stderr, ", fmtid_data4_7=%02x", req->fmtid_data4_7 );
+    fprintf( stderr, ", pid=%08x", req->pid );
+    dump_varargs_bytes( ", property=", cur_size );
+}
+
+static void dump_get_sh_window_property_request( const struct get_sh_window_property_request *req )
+{
+    fprintf( stderr, " window=%08x", req->window );
+    fprintf( stderr, ", fmtid_data1=%08x", req->fmtid_data1 );
+    fprintf( stderr, ", fmtid_data2=%04x", req->fmtid_data2 );
+    fprintf( stderr, ", fmtid_data3=%04x", req->fmtid_data3 );
+    fprintf( stderr, ", fmtid_data4_0=%02x", req->fmtid_data4_0 );
+    fprintf( stderr, ", fmtid_data4_1=%02x", req->fmtid_data4_1 );
+    fprintf( stderr, ", fmtid_data4_2=%02x", req->fmtid_data4_2 );
+    fprintf( stderr, ", fmtid_data4_3=%02x", req->fmtid_data4_3 );
+    fprintf( stderr, ", fmtid_data4_4=%02x", req->fmtid_data4_4 );
+    fprintf( stderr, ", fmtid_data4_5=%02x", req->fmtid_data4_5 );
+    fprintf( stderr, ", fmtid_data4_6=%02x", req->fmtid_data4_6 );
+    fprintf( stderr, ", fmtid_data4_7=%02x", req->fmtid_data4_7 );
+    fprintf( stderr, ", pid=%08x", req->pid );
+}
+
+static void dump_get_sh_window_property_reply( const struct get_sh_window_property_reply *req )
+{
+    fprintf( stderr, " property_size=%08x", req->property_size );
+    dump_varargs_bytes( ", property=", cur_size );
+}
+
 static void dump_create_winstation_request( const struct create_winstation_request *req )
 {
     fprintf( stderr, " flags=%08x", req->flags );
@@ -4421,6 +4462,8 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
     (dump_func)dump_remove_window_property_request,
     (dump_func)dump_get_window_property_request,
     (dump_func)dump_get_window_properties_request,
+    (dump_func)dump_set_sh_window_property_request,
+    (dump_func)dump_get_sh_window_property_request,
     (dump_func)dump_create_winstation_request,
     (dump_func)dump_open_winstation_request,
     (dump_func)dump_close_winstation_request,
@@ -4690,6 +4733,8 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
     (dump_func)dump_remove_window_property_reply,
     (dump_func)dump_get_window_property_reply,
     (dump_func)dump_get_window_properties_reply,
+    NULL,
+    (dump_func)dump_get_sh_window_property_reply,
     (dump_func)dump_create_winstation_reply,
     (dump_func)dump_open_winstation_reply,
     NULL,
@@ -4959,6 +5004,8 @@ static const char * const req_names[REQ_NB_REQUESTS] = {
     "remove_window_property",
     "get_window_property",
     "get_window_properties",
+    "set_sh_window_property",
+    "get_sh_window_property",
     "create_winstation",
     "open_winstation",
     "close_winstation",
diff --git a/server/window.c b/server/window.c
index d089149..226ac2a 100644
--- a/server/window.c
+++ b/server/window.c
@@ -39,6 +39,8 @@
 #include "user.h"
 #include "unicode.h"
 
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
 /* a window property */
 struct property
 {
@@ -54,6 +56,25 @@ enum property_type
     PROP_TYPE_ATOM    /* plain atom */
 };
 
+typedef struct
+{
+    struct {
+        unsigned int   data1;
+        unsigned short data2;
+        unsigned short data3;
+        unsigned char  data4[8];
+    } fmtid;
+    unsigned int pid;
+} propkey_t;
+
+/* a shell property */
+struct sh_property
+{
+    struct list   entry;        /* list */
+    propkey_t     key;          /* key (GUID+fmtid) */
+    unsigned int  value_size;   /* size of the data */
+    unsigned char value[1];     /* data */
+};
 
 struct window
 {
@@ -89,6 +110,7 @@ struct window
     int              prop_inuse;      /* number of in-use window properties */
     int              prop_alloc;      /* number of allocated window properties */
     struct property *properties;      /* window properties array */
+    struct list      sh_properties;   /* list of struct sh_property */
     int              nb_extra_bytes;  /* number of extra bytes */
     char             extra_bytes[1];  /* extra bytes storage */
 };
@@ -380,6 +402,83 @@ static inline void destroy_properties( struct window *win )
     free( win->properties );
 }
 
+static inline int propkey_equal(const propkey_t *a, const propkey_t *b)
+{
+    return a->fmtid.data1 == b->fmtid.data1
+        && a->fmtid.data2 == b->fmtid.data2
+        && a->fmtid.data3 == b->fmtid.data3
+        && !memcmp(a->fmtid.data4, b->fmtid.data4, 8)
+        && a->pid == b->pid;
+}
+
+/* set a shell property on the window */
+static inline void set_sh_property( struct window *win, const propkey_t *key,
+                                    unsigned int value_size, const void *value )
+{
+    struct sh_property *cursor;
+    struct sh_property *cursor2;
+
+    /* first, remove any old properties (hopefully just one) with the same key */
+    LIST_FOR_EACH_ENTRY_SAFE( cursor, cursor2, &win->sh_properties, struct sh_property, entry )
+    {
+        if (!propkey_equal( key, &cursor->key ))
+            continue;
+
+        list_remove( &cursor->entry );
+        free( cursor );
+    }
+
+    /* size zero means delete, in that case we're already done */
+    if (value_size == 0)
+        return;
+
+    /* otherwise, we now allocate space and save it */
+    cursor = mem_alloc( sizeof(*cursor) + value_size );
+    if (!cursor)
+        return;
+
+    cursor->key = *key;
+    cursor->value_size = value_size;
+    memcpy( cursor->value, value, value_size );
+
+    list_add_head( &win->sh_properties, &cursor->entry );
+}
+
+/* get a shell property from the window */
+static inline void get_sh_property( struct window *win, const propkey_t *key,
+                                    unsigned int buffer_size, void *buffer,
+                                    unsigned int *property_size)
+{
+    struct sh_property *cursor;
+
+    /* default if no property is found */
+    *property_size = 0;
+
+    /* find it */
+    LIST_FOR_EACH_ENTRY( cursor, &win->sh_properties, struct sh_property, entry )
+    {
+        if (!propkey_equal( key, &cursor->key ))
+            continue;
+
+        *property_size = cursor->value_size;
+        memcpy(buffer, cursor->value, MIN(cursor->value_size, buffer_size));
+        break;
+    }
+}
+
+/* free all shell properties */
+static inline void destroy_sh_properties( struct window *win )
+{
+    struct sh_property *cursor;
+    struct sh_property *cursor2;
+
+    LIST_FOR_EACH_ENTRY_SAFE( cursor, cursor2, &win->sh_properties, struct sh_property, entry )
+    {
+        list_remove( &cursor->entry );
+        free( cursor );
+    }
+}
+
 /* detach a window from its owner thread but keep the window around */
 static void detach_window_thread( struct window *win )
 {
@@ -496,6 +595,7 @@ static struct window *create_window( struct window *parent, struct window *owner
     memset( win->extra_bytes, 0, extra_bytes );
     list_init( &win->children );
     list_init( &win->unlinked );
+    list_init( &win->sh_properties );
 
     /* if parent belongs to a different thread and the window isn't */
     /* top-level, attach the two threads */
@@ -1857,6 +1957,7 @@ void destroy_window( struct window *win )
     free_hotkeys( win->desktop, win->handle );
     free_user_handle( win->handle );
     destroy_properties( win );
+    destroy_sh_properties( win );
     list_remove( &win->entry );
     if (is_desktop_window(win))
     {
@@ -2717,6 +2818,53 @@ DECL_HANDLER(get_window_property)
     }
 }
 
+/* set a shell property */
+DECL_HANDLER(set_sh_window_property)
+{
+    struct window *win = get_window( req->window );
+    propkey_t key = {
+        {
+            req->fmtid_data1,
+            req->fmtid_data2,
+            req->fmtid_data3,
+            {   req->fmtid_data4_0, req->fmtid_data4_1,
+                req->fmtid_data4_2, req->fmtid_data4_3,
+                req->fmtid_data4_4, req->fmtid_data4_5,
+                req->fmtid_data4_6, req->fmtid_data4_7  }
+        },
+        req->pid
+    };
+
+    if (win)
+    {
+        set_sh_property(win, &key, get_req_data_size(), get_req_data());
+    }
+}
+
+/* get a shell property */
+DECL_HANDLER(get_sh_window_property)
+{
+    struct window *win = get_window( req->window );
+    propkey_t key = {
+        {
+            req->fmtid_data1,
+            req->fmtid_data2,
+            req->fmtid_data3,
+            {   req->fmtid_data4_0, req->fmtid_data4_1,
+                req->fmtid_data4_2, req->fmtid_data4_3,
+                req->fmtid_data4_4, req->fmtid_data4_5,
+                req->fmtid_data4_6, req->fmtid_data4_7  }
+        },
+        req->pid
+    };
+
+    if (win)
+    {
+        get_sh_property(win, &key, get_reply_max_size(),
+                        set_reply_data_size( get_reply_max_size() ),
+                        &reply->property_size);
+    }
+}
 
 /* get the list of properties of a window */
 DECL_HANDLER(get_window_properties)
-- 
2.4.3




More information about the wine-devel mailing list