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 */ +@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); +@REPLY +@END + +/* Get a shell property from a window */ +@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 */ +@REPLY + unsigned int property_size; + VARARG(property,bytes); +@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)