[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