The in-memory property store now implements the INamedPropertyStore, IPersistSerializedPropStorage(2), IPersistStream interfaces. --- dlls/propsys/propstore.c | 1088 +++++++++++++++++++++++++++++++++++++- dlls/propsys/propsys_classes.idl | 7 +- dlls/propsys/tests/Makefile.in | 2 +- dlls/propsys/tests/propstore.c | 400 +++++++++++++- dlls/propsys/tests/propsys.c | 3 +- 5 files changed, 1478 insertions(+), 22 deletions(-)
diff --git a/dlls/propsys/propstore.c b/dlls/propsys/propstore.c index 9c848fc..7dd36cb 100644 --- a/dlls/propsys/propstore.c +++ b/dlls/propsys/propstore.c @@ -2,6 +2,7 @@ * standard IPropertyStore implementation * * Copyright 2012 Vincent Povirk for CodeWeavers + * 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 @@ -28,9 +29,11 @@ #include "objbase.h" #include "rpcproxy.h" #include "propsys.h" +#include "propvarutil.h" #include "wine/debug.h" #include "wine/unicode.h" #include "wine/list.h" +#include "mimeole.h"
#include "initguid.h" #include "propsys_private.h" @@ -53,11 +56,22 @@ typedef struct { DWORD count; } propstore_format;
+/* FIXME: We should really do something clever, like using a hashtable */ +typedef struct { + struct list entry; + PROPVARIANT propvar; + WCHAR name[1]; +} propstore_named_value; + typedef struct { IPropertyStoreCache IPropertyStoreCache_iface; + IPersistSerializedPropStorage2 IPersistSerializedPropStorage2_iface; + INamedPropertyStore INamedPropertyStore_iface; + IPersistStream IPersistStream_iface; LONG ref; CRITICAL_SECTION lock; struct list formats; /* list of struct propstore_format */ + struct list named; /* list of struct propstore_named_value */ } PropertyStore;
static inline PropertyStore *impl_from_IPropertyStoreCache(IPropertyStoreCache *iface) @@ -65,12 +79,23 @@ static inline PropertyStore *impl_from_IPropertyStoreCache(IPropertyStoreCache * return CONTAINING_RECORD(iface, PropertyStore, IPropertyStoreCache_iface); }
-static HRESULT WINAPI PropertyStore_QueryInterface(IPropertyStoreCache *iface, REFIID iid, - void **ppv) +static inline PropertyStore *impl_from_IPersistSerializedPropStorage2(IPersistSerializedPropStorage2 *iface) { - PropertyStore *This = impl_from_IPropertyStoreCache(iface); - TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv); + return CONTAINING_RECORD(iface, PropertyStore, IPersistSerializedPropStorage2_iface); +}
+static inline PropertyStore *impl_from_INamedPropertyStore(INamedPropertyStore *iface) +{ + return CONTAINING_RECORD(iface, PropertyStore, INamedPropertyStore_iface); +} + +static inline PropertyStore *impl_from_IPersistStream(IPersistStream *iface) +{ + return CONTAINING_RECORD(iface, PropertyStore, IPersistStream_iface); +} + +static HRESULT query_interface(PropertyStore *This, REFIID iid, void **ppv) +{ if (!ppv) return E_INVALIDARG;
if (IsEqualIID(&IID_IUnknown, iid) || IsEqualIID(&IID_IPropertyStore, iid) || @@ -78,6 +103,19 @@ static HRESULT WINAPI PropertyStore_QueryInterface(IPropertyStoreCache *iface, R { *ppv = &This->IPropertyStoreCache_iface; } + else if (IsEqualIID(&IID_IPersistSerializedPropStorage, iid) + || IsEqualIID(&IID_IPersistSerializedPropStorage2, iid)) + { + *ppv = &This->IPersistSerializedPropStorage2_iface; + } + else if (IsEqualIID(&IID_INamedPropertyStore, iid)) + { + *ppv = &This->INamedPropertyStore_iface; + } + else if (IsEqualIID(&IID_IPersistStream, iid) || IsEqualIID(&IID_IPersist, iid)) + { + *ppv = &This->IPersistStream_iface; + } else { FIXME("No interface for %s\n", debugstr_guid(iid)); @@ -89,10 +127,85 @@ static HRESULT WINAPI PropertyStore_QueryInterface(IPropertyStoreCache *iface, R return S_OK; }
-static ULONG WINAPI PropertyStore_AddRef(IPropertyStoreCache *iface) +static HRESULT WINAPI PropertyStore_IPropertyStoreCache_QueryInterface( + IPropertyStoreCache *iface, REFIID iid, void **ppv) +{ + PropertyStore *This = impl_from_IPropertyStoreCache(iface); + + TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv); + + return query_interface(This, iid, ppv); +} + +static HRESULT WINAPI PropertyStore_IPersistSerializedPropStorage_QueryInterface( + IPersistSerializedPropStorage2 *iface, REFIID iid, void **ppv) +{ + PropertyStore *This = impl_from_IPersistSerializedPropStorage2(iface); + + TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv); + + return query_interface(This, iid, ppv); +} + +static HRESULT WINAPI PropertyStore_INamedPropertyStore_QueryInterface( + INamedPropertyStore *iface, REFIID iid, void **ppv) +{ + PropertyStore *This = impl_from_INamedPropertyStore(iface); + + TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv); + + return query_interface(This, iid, ppv); +} + +static HRESULT WINAPI PropertyStore_IPersistStream_QueryInterface( + IPersistStream *iface, REFIID iid, void **ppv) +{ + PropertyStore *This = impl_from_IPersistStream(iface); + + TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv); + + return query_interface(This, iid, ppv); +} + +static ULONG add_ref(PropertyStore *This) +{ + return InterlockedIncrement(&This->ref); +} + +static ULONG WINAPI PropertyStore_IPropertyStoreCache_AddRef(IPropertyStoreCache *iface) { PropertyStore *This = impl_from_IPropertyStoreCache(iface); - ULONG ref = InterlockedIncrement(&This->ref); + ULONG ref = add_ref(This); + + TRACE("(%p) refcount=%u\n", iface, ref); + + return ref; +} + +static ULONG WINAPI PropertyStore_IPersistSerializedPropStorage_AddRef(IPersistSerializedPropStorage2 *iface) +{ + PropertyStore *This = impl_from_IPersistSerializedPropStorage2(iface); + ULONG ref = add_ref(This); + + TRACE("(%p) refcount=%u\n", iface, ref); + + return ref; +} + +static ULONG WINAPI PropertyStore_INamedPropertyStore_AddRef(INamedPropertyStore *iface) +{ + PropertyStore *This = impl_from_INamedPropertyStore(iface); + ULONG ref = add_ref(This); + + TRACE("(%p) refcount=%u\n", iface, ref); + + return ref; +} + +static ULONG WINAPI PropertyStore_IPersistStream_AddRef(IPersistStream *iface) +{ + PropertyStore *This = impl_from_IPersistStream(iface); + ULONG ref = add_ref(This);
TRACE("(%p) refcount=%u\n", iface, ref);
@@ -110,26 +223,73 @@ static void destroy_format(propstore_format *format) HeapFree(GetProcessHeap(), 0, format); }
-static ULONG WINAPI PropertyStore_Release(IPropertyStoreCache *iface) +static void destroy_named(propstore_named_value *value) { - PropertyStore *This = impl_from_IPropertyStoreCache(iface); - ULONG ref = InterlockedDecrement(&This->ref); + PropVariantClear(&value->propvar); + HeapFree(GetProcessHeap(), 0, value); +}
- TRACE("(%p) refcount=%u\n", iface, ref); +static ULONG WINAPI release(PropertyStore *This) +{ + ULONG ref = InterlockedDecrement(&This->ref);
if (ref == 0) { propstore_format *cursor, *cursor2; + propstore_named_value *cnamed, *cnamed2; + This->lock.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&This->lock); LIST_FOR_EACH_ENTRY_SAFE(cursor, cursor2, &This->formats, propstore_format, entry) destroy_format(cursor); + LIST_FOR_EACH_ENTRY_SAFE(cnamed, cnamed2, &This->named, propstore_named_value, entry) + destroy_named(cnamed); HeapFree(GetProcessHeap(), 0, This); }
return ref; }
+static ULONG WINAPI PropertyStore_IPropertyStoreCache_Release(IPropertyStoreCache *iface) +{ + PropertyStore *This = impl_from_IPropertyStoreCache(iface); + ULONG ref = release(This); + + TRACE("(%p) refcount=%u\n", iface, ref); + + return ref; +} + +static ULONG WINAPI PropertyStore_IPersistSerializedPropStorage_Release(IPersistSerializedPropStorage2 *iface) +{ + PropertyStore *This = impl_from_IPersistSerializedPropStorage2(iface); + ULONG ref = release(This); + + TRACE("(%p) refcount=%u\n", iface, ref); + + return ref; +} + +static ULONG WINAPI PropertyStore_INamedPropertyStore_Release(INamedPropertyStore *iface) +{ + PropertyStore *This = impl_from_INamedPropertyStore(iface); + ULONG ref = release(This); + + TRACE("(%p) refcount=%u\n", iface, ref); + + return ref; +} + +static ULONG WINAPI PropertyStore_IPersistStream_Release(IPersistStream *iface) +{ + PropertyStore *This = impl_from_IPersistStream(iface); + ULONG ref = release(This); + + TRACE("(%p) refcount=%u\n", iface, ref); + + return ref; +} + static HRESULT WINAPI PropertyStore_GetCount(IPropertyStoreCache *iface, DWORD *cProps) { @@ -153,6 +313,29 @@ static HRESULT WINAPI PropertyStore_GetCount(IPropertyStoreCache *iface, return S_OK; }
+static HRESULT WINAPI PropertyStore_GetNameCount(INamedPropertyStore *iface, + DWORD *cProps) +{ + PropertyStore *This = impl_from_INamedPropertyStore(iface); + propstore_named_value *value; + + TRACE("%p,%p\n", iface, cProps); + + if (!cProps) + return E_POINTER; + + *cProps = 0; + + EnterCriticalSection(&This->lock); + + LIST_FOR_EACH_ENTRY(value, &This->named, propstore_named_value, entry) + *cProps += 1; + + LeaveCriticalSection(&This->lock); + + return S_OK; +} + static HRESULT WINAPI PropertyStore_GetAt(IPropertyStoreCache *iface, DWORD iProp, PROPERTYKEY *pkey) { @@ -203,6 +386,47 @@ static HRESULT WINAPI PropertyStore_GetAt(IPropertyStoreCache *iface, return hr; }
+static HRESULT WINAPI PropertyStore_GetNameAt(INamedPropertyStore *iface, + DWORD iProp, BSTR *pname) +{ + PropertyStore *This = impl_from_INamedPropertyStore(iface); + propstore_named_value *cursor, *value = NULL; + HRESULT hr; + + TRACE("%p,%d,%p\n", iface, iProp, pname); + + if (!pname) + return E_POINTER; + + EnterCriticalSection(&This->lock); + + LIST_FOR_EACH_ENTRY(cursor, &This->named, propstore_named_value, entry) + { + if (iProp == 0) + { + value = cursor; + break; + } + + iProp -= 1; + } + + if (value) + { + *pname = SysAllocString(value->name); + if (*pname) + hr = S_OK; + else + hr = E_OUTOFMEMORY; + } + else + hr = E_INVALIDARG; + + LeaveCriticalSection(&This->lock); + + return hr; +} + static HRESULT PropertyStore_LookupValue(PropertyStore *This, REFPROPERTYKEY key, BOOL insert, propstore_value **result) { @@ -297,6 +521,42 @@ static HRESULT WINAPI PropertyStore_GetValue(IPropertyStoreCache *iface, return hr; }
+static HRESULT WINAPI PropertyStore_GetNamedValue(INamedPropertyStore *iface, + const WCHAR *name, PROPVARIANT *pv) +{ + PropertyStore *This = impl_from_INamedPropertyStore(iface); + propstore_named_value *cursor, *value = NULL; + + TRACE("%p,%p,%p\n", iface, name, pv); + + if (!name || !pv) + return E_POINTER; + + EnterCriticalSection(&This->lock); + + LIST_FOR_EACH_ENTRY(cursor, &This->named, propstore_named_value, entry) + { + if (0 == lstrcmpW(cursor->name, name)) + { + value = cursor; + break; + } + } + + if (value) + { + PropVariantCopy(pv, &value->propvar); + } + else + { + PropVariantInit(pv); + } + + LeaveCriticalSection(&This->lock); + + return S_OK; +} + static HRESULT WINAPI PropertyStore_SetValue(IPropertyStoreCache *iface, REFPROPERTYKEY key, REFPROPVARIANT propvar) { @@ -325,6 +585,59 @@ static HRESULT WINAPI PropertyStore_SetValue(IPropertyStoreCache *iface, return hr; }
+static HRESULT WINAPI PropertyStore_SetNamedValue(INamedPropertyStore *iface, + const WCHAR *name, REFPROPVARIANT propvar) +{ + PropertyStore *This = impl_from_INamedPropertyStore(iface); + propstore_named_value *cursor, *value = NULL; + HRESULT hr = S_OK; + PROPVARIANT temp; + + TRACE("%p,%p,%p\n", iface, name, propvar); + + hr = PropVariantCopy(&temp, propvar); + if (FAILED(hr)) + return hr; + + EnterCriticalSection(&This->lock); + + LIST_FOR_EACH_ENTRY(cursor, &This->named, propstore_named_value, entry) + { + if (0 == lstrcmpW(name, cursor->name)) + { + value = cursor; + break; + } + } + + if (value) + { + /* replace old value */ + PropVariantClear(&value->propvar); + value->propvar = temp; + } + else + { + /* add new value */ + ULONG namesize = (lstrlenW(name) + 1) * sizeof(WCHAR); + + value = HeapAlloc(GetProcessHeap(), 0, sizeof(propstore_named_value) + namesize); + if (value) + { + value->propvar = temp; + memcpy(value->name, name, namesize); + + list_add_head(&This->named, &value->entry); + } + else + hr = E_OUTOFMEMORY; + } + + LeaveCriticalSection(&This->lock); + + return hr; +} + static HRESULT WINAPI PropertyStore_Commit(IPropertyStoreCache *iface) { FIXME("%p: stub\n", iface); @@ -435,10 +748,719 @@ static HRESULT WINAPI PropertyStore_SetValueAndState(IPropertyStoreCache *iface, return hr; }
-static const IPropertyStoreCacheVtbl PropertyStore_Vtbl = { - PropertyStore_QueryInterface, - PropertyStore_AddRef, - PropertyStore_Release, +static HRESULT WINAPI PropertyStore_SetFlags(IPersistSerializedPropStorage2 *iface, + PERSIST_SPROPSTORE_FLAGS flags) +{ + TRACE("%p,%d: stub\n", iface, flags); + + return S_OK; +} + +static HRESULT WINAPI PropertyStore_GetPropertyStorageSize( + IPersistSerializedPropStorage2 *iface, DWORD *pcb) +{ + DWORD count; + DWORD size = 0; + HRESULT hr = S_OK; + PropertyStore *This = impl_from_IPersistSerializedPropStorage2(iface); + propstore_format *format; + propstore_value *value; + propstore_named_value *named_value; + + TRACE("%p,%p\n", iface, pcb); + + if (!pcb) + return E_POINTER; + + EnterCriticalSection(&This->lock); + + /* As a special case, an empty propstore has size 0 */ + hr = PropertyStore_GetCount(&This->IPropertyStoreCache_iface, &count); + if (FAILED(hr) || !count) + goto out; + + LIST_FOR_EACH_ENTRY(format, &This->formats, propstore_format, entry) + { + size += 4; /* DWORD Storage size */ + size += 4; /* DWORD Storage Version */ + size += 16; /* GUID fmtid */ + + LIST_FOR_EACH_ENTRY(value, &format->values, propstore_value, entry) + { + DWORD propsize; + + size += 4; /* DWORD Size */ + size += 4; /* DWORD pid */ + size += 1; /* Reserved. */ + + hr = wine_SerializedPropertySize(&value->propvar, &propsize, CP_UNICODE, CP_ACP); + if (FAILED(hr)) + { + WARN("Property of type %d is not serializable\n", value->propvar.vt); + size = 0; + goto out; + } + + size += propsize; + } + + size += 4; /* Terminating Element */ + } + + if (!list_empty(&This->named)) + { + size += 4; /* DWORD Storage size */ + size += 4; /* DWORD Storage Version */ + size += 16; /* GUID fmtid */ + + LIST_FOR_EACH_ENTRY(named_value, &This->named, propstore_named_value, entry) + { + DWORD propsize; + DWORD namesize; + + size += 4; /* DWORD Size */ + size += 4; /* DWORD String size */ + size += 1; /* Reserved */ + + namesize = (lstrlenW(named_value->name) + 1) * sizeof(WCHAR); + + hr = wine_SerializedPropertySize(&named_value->propvar, &propsize, CP_UNICODE, CP_ACP); + if (FAILED(hr)) + { + WARN("Property of type %d is not serializable\n", named_value->propvar.vt); + size = 0; + goto out; + } + + size += namesize + propsize; + } + + size += 4; /* Terminating element */ + } + + size += 4; /* Terminating Storage */ + +out: + LeaveCriticalSection(&This->lock); + + *pcb = size; + return hr; +} + +static inline void write_dword(BYTE *buffer, DWORD dw) +{ + buffer[0] = (dw & 0x000000FF); + buffer[1] = (dw & 0x0000FF00) >> 8; + buffer[2] = (dw & 0x00FF0000) >> 16; + buffer[3] = (dw & 0xFF000000) >> 24; +} + +static inline void write_word(BYTE *buffer, WORD w) +{ + buffer[0] = (w & 0x00FF); + buffer[1] = (w & 0xFF00) >> 8; +} + +static inline void write_clsid(BYTE *buffer, const CLSID *id) +{ + write_dword(buffer, id->Data1); + write_word(buffer + 4, id->Data2); + write_word(buffer + 6, id->Data3); + memcpy(buffer + 8, id->Data4, 8); +} + +static inline void write_wstring(BYTE *buffer, const WCHAR *str, ULONG cChar) +{ + for (; cChar; --cChar, buffer += 2, ++str) + { + write_word(buffer, (WORD)*str); + } +} + +static HRESULT WINAPI PropertyStore_GetPropertyStorageBuffer( + IPersistSerializedPropStorage2 *iface, SERIALIZEDPROPSTORAGE *psps, DWORD cb, DWORD *cbUsed) +{ + HRESULT hr = S_OK; + PropertyStore *This = impl_from_IPersistSerializedPropStorage2(iface); + propstore_format *format; + propstore_value *value; + propstore_named_value *named_value; + DWORD cb_Start = cb; + BYTE *buffer = (BYTE*)psps; + +#define REQUIRE_SPACE(cBytes) \ + do { \ + if (cBytes > cb) \ + { \ + hr = E_INVALIDARG; \ + goto out; \ + } \ + } while(0) +#define MOVE(cBytes) \ + do { \ + buffer += cBytes; \ + cb -= cBytes; \ + } while(0) + + TRACE("%p,%p,%d,%p\n", iface, psps, cb, cbUsed); + + if (!psps) + return E_POINTER; + + EnterCriticalSection(&This->lock); + + LIST_FOR_EACH_ENTRY(format, &This->formats, propstore_format, entry) + { + BYTE *pStorageSizeBuf = buffer; /* we set it afterwards */ + DWORD storageSize = 24; /* storage size, storage version, fmtid */ + + REQUIRE_SPACE(24); /* Storage size, storage version, fmtid */ + + write_dword(buffer + 4, 0x53505331); /* DWORD Version */ + write_clsid(buffer + 8, &format->fmtid); + + MOVE(24); + + LIST_FOR_EACH_ENTRY(value, &format->values, propstore_value, entry) + { + DWORD propsize; + DWORD propsizeUsed; + + hr = wine_SerializedPropertySize(&value->propvar, &propsize, CP_UNICODE, CP_ACP); + if (FAILED(hr)) + { + WARN("Property of type %d is not serializable\n", value->propvar.vt); + goto out; + } + + REQUIRE_SPACE(9 + propsize); /* Size, pid, reserved, content */ + + write_dword(buffer, propsize + 9); /* size */ + write_dword(buffer + 4, value->pid); /* pid */ + buffer[8] = 0; /* reserved byte to make sure everything is misaligned */ + + MOVE(9); + + hr = wine_WritePropertyToBuffer(&value->propvar, buffer, + propsize, &propsizeUsed, CP_UNICODE, CP_ACP); + if (FAILED(hr)) + { + WARN("Couldn't serialize property of type %d, hr=%08x\n", + value->propvar.vt, (unsigned)hr); + goto out; + } + + if (propsizeUsed != propsize) + { + WARN("Serialized property size calculation was off by %u bytes\n", + propsize - propsizeUsed); + } + + MOVE(propsize); + storageSize += propsize + 9; + } + + /* Terminating Element */ + REQUIRE_SPACE(4); + ZeroMemory(buffer, 4); + MOVE(4); + storageSize += 4; + + write_dword(pStorageSizeBuf, storageSize); + } + + if (!list_empty(&This->named)) + { + BYTE *pStorageSizeBuf = buffer; /* we set it afterwards */ + DWORD storageSize = 24; /* storage size, storage version, fmtid */ + + REQUIRE_SPACE(24); /* Storage size, storage version, fmtid */ + + write_dword(buffer + 4, 0x53505331); /* DWORD Version */ + write_clsid(buffer + 8, &FMTID_NamedProperties); + + MOVE(24); + + LIST_FOR_EACH_ENTRY(named_value, &This->named, propstore_named_value, entry) + { + DWORD propsize; + DWORD propsizeUsed; + DWORD namesize; + + namesize = (lstrlenW(named_value->name) + 1) * sizeof(WCHAR); + TRACE("Writing named property with name %s:%u\n", wine_dbgstr_w(named_value->name), namesize); + + hr = wine_SerializedPropertySize(&named_value->propvar, &propsize, CP_UNICODE, CP_ACP); + if (FAILED(hr)) + { + WARN("Property of type %d is not serializable\n", named_value->propvar.vt); + goto out; + } + + REQUIRE_SPACE(9 + namesize + propsize); + + write_dword(buffer, 9 + namesize + propsize); + write_dword(buffer + 4, namesize); + buffer[8] = 0; + + MOVE(9); + + write_wstring(buffer, named_value->name, namesize/2); + MOVE(namesize); + + hr = wine_WritePropertyToBuffer(&named_value->propvar, buffer, + propsize, &propsizeUsed, CP_UNICODE, CP_ACP); + if (FAILED(hr)) + { + WARN("Couldn't serialize property of type %d, hr=%08x\n", + named_value->propvar.vt, (unsigned)hr); + goto out; + } + + if (propsizeUsed != propsize) + { + WARN("Serialized property size calculation was off by %u bytes\n", + propsize - propsizeUsed); + } + + MOVE(propsize); + storageSize += namesize + propsize + 9; + } + + /* Terminating Element */ + REQUIRE_SPACE(4); + ZeroMemory(buffer, 4); + MOVE(4); + storageSize += 4; + + write_dword(pStorageSizeBuf, storageSize); + } + + /* Terminating Storage */ + REQUIRE_SPACE(4); + ZeroMemory(buffer, 4); + MOVE(4); + +#undef REQUIRE_SPACE +#undef MOVE + +out: + LeaveCriticalSection(&This->lock); + + if (cbUsed) + *cbUsed = SUCCEEDED(hr) ? cb_Start - cb : 0; + + return hr; +} + +static HRESULT WINAPI PropertyStore_GetPropertyStorage( + IPersistSerializedPropStorage2 *iface, SERIALIZEDPROPSTORAGE **ppsps, DWORD *pcb) +{ + HRESULT hr; + DWORD size; + DWORD usedSize; + PropertyStore *This = impl_from_IPersistSerializedPropStorage2(iface); + + TRACE("%p,%p,%p\n", iface, ppsps, pcb); + + if (!ppsps || !pcb) + return E_POINTER; + + EnterCriticalSection(&This->lock); + + hr = PropertyStore_GetPropertyStorageSize(iface, &size); + + if (SUCCEEDED(hr)) + { + if (size == 0) + { + usedSize = 0; + *ppsps = NULL; + } + else if ((*ppsps = CoTaskMemAlloc(size))) + { + hr = PropertyStore_GetPropertyStorageBuffer(iface, *ppsps, size, &usedSize); + if (FAILED(hr)) + { + CoTaskMemFree(*ppsps); + } + + if (size != usedSize) + { + WARN("Buffer calculation was off by %u bytes\n", size - usedSize); + } + } + else + { + hr = E_OUTOFMEMORY; + } + } + + LeaveCriticalSection(&This->lock); + + if (SUCCEEDED(hr)) + { + *pcb = usedSize; + } + else + { + *ppsps = NULL; + *pcb = 0; + } + + return hr; +} + +static inline void read_dword(const BYTE *buffer, DWORD *dw) +{ + *dw = buffer[0] + | (buffer[1] << 8) + | (buffer[2] << 16) + | (buffer[3] << 24); +} + +static inline void read_word(const BYTE *buffer, WORD *w) +{ + *w = buffer[0] | (buffer[1] << 8); +} + +static inline void read_clsid(const BYTE *buffer, CLSID *id) +{ + read_dword(buffer, &id->Data1); + read_word(buffer + 4, &id->Data2); + read_word(buffer + 6, &id->Data3); + memcpy(id->Data4, buffer + 8, 8); +} + +static inline void read_wstring(const BYTE *buffer, WCHAR *str, ULONG cChar) +{ + for (; cChar; --cChar, ++str, buffer += 2) + { + read_word(buffer, (WORD*)str); + } +} + +static HRESULT WINAPI PropertyStore_SetPropertyStorage( + IPersistSerializedPropStorage2 *iface, const SERIALIZEDPROPSTORAGE *psps, DWORD cb) +{ + HRESULT hr = S_OK; + PropertyStore *This = impl_from_IPersistSerializedPropStorage2(iface); + const BYTE *buffer = (const BYTE*)psps; + +#define REQUIRE_SPACE(cBytes) \ + do { \ + if (cBytes > cb) \ + { \ + WARN("Trying to read %u bytes at offset %u where only %u are available\n", \ + cBytes, (unsigned)(buffer - (const BYTE*)psps), cb); \ + hr = E_INVALIDARG; \ + goto out; \ + } \ + } while(0) +#define MOVE(cBytes) \ + do { \ + buffer += cBytes; \ + cb -= cBytes; \ + } while(0) + + TRACE("%p,%p,%d\n", iface, psps, cb); + + /*FIXME: Should we clear existing properties in the store? */ + + if (!psps && !cb) /* special case: empty storage */ + return S_OK; + + if (!psps) + return E_POINTER; + + EnterCriticalSection(&This->lock); + + for (;;) + { + DWORD storageSize; + DWORD version; + CLSID fmtid; + + REQUIRE_SPACE(4); + + /* Read size field */ + read_dword(buffer, &storageSize); + + if (storageSize == 0) /* final element */ + { + MOVE(4); + break; + } + + REQUIRE_SPACE(24); + read_dword(buffer + 4, &version); + read_clsid(buffer + 8, &fmtid); + + if (version != 0x53505331) + { + WARN("Found invalid version 0x%X\n", version); + hr = E_INVALIDARG; + goto out; + } + + MOVE(24); + + if (IsEqualGUID(&fmtid, &FMTID_NamedProperties)) + { + for (;;) + { + DWORD propsize; + DWORD namesize; + PROPVARIANT prop; + WCHAR *name; + + REQUIRE_SPACE(4); + read_dword(buffer, &propsize); + + if (propsize == 0) /* final element */ + { + MOVE(4); + break; + } + + REQUIRE_SPACE(9); + read_dword(buffer + 4, &namesize); + + if (namesize % 2 || namesize > (propsize - 9 - 4)) + { + WARN("unicode string has invalid number of bytes\n"); + hr = E_INVALIDARG; + goto out; + } + + if (buffer[8]) + WARN("reserved byte should be zero, but is 0x%X\n", (unsigned)buffer[4]); + + REQUIRE_SPACE(propsize); /* includes header */ + MOVE(9); + + /* read the name */ + name = CoTaskMemAlloc(namesize + sizeof(WCHAR)); + if (!name) + { + WARN("not enough memory for reading name\n"); + hr = E_OUTOFMEMORY; + goto out; + } + + read_wstring(buffer, name, namesize/2); + name[namesize/2] = 0; /* just to be safe */ + MOVE(namesize); + + /* read the property */ + hr = StgDeserializePropVariant( + (const SERIALIZEDPROPERTYVALUE *)buffer, propsize - 9, &prop); + if (FAILED(hr)) + { + WARN("Couldn't deserialize property, hr=%08x\n", (unsigned)hr); + CoTaskMemFree(name); + goto out; + } + + /* add it to the store */ + hr = PropertyStore_SetNamedValue(&This->INamedPropertyStore_iface, name, &prop); + PropVariantClear(&prop); + CoTaskMemFree(name); + + if (FAILED(hr)) + { + WARN("Couldn't save deserialized property in store, hr=%08x\n", (unsigned)hr); + goto out; + } + + MOVE(propsize - namesize - 9); + } + } + else + { + for (;;) + { + DWORD propsize; + DWORD pid; + PROPVARIANT prop; + PROPERTYKEY key; + + REQUIRE_SPACE(4); + read_dword(buffer, &propsize); + + if (propsize == 0) /* final element */ + { + MOVE(4); + break; + } + + REQUIRE_SPACE(9); + read_dword(buffer + 4, &pid); + + if (buffer[8]) + WARN("reserved byte should be zero, but is 0x%X\n", (unsigned)buffer[4]); + + REQUIRE_SPACE(propsize); /* includes header */ + MOVE(9); + + /* read the property */ + hr = StgDeserializePropVariant( + (const SERIALIZEDPROPERTYVALUE *)buffer, propsize - 9, &prop); + if (FAILED(hr)) + { + WARN("Couldn't deserialize property, hr=%08x\n", (unsigned)hr); + goto out; + } + + /* add it to the store */ + key.fmtid = fmtid; + key.pid = pid; + + hr = PropertyStore_SetValue(&This->IPropertyStoreCache_iface, &key, &prop); + PropVariantClear(&prop); + + if (FAILED(hr)) + { + WARN("Couldn't save deserialized property in store, hr=%08x\n", (unsigned)hr); + goto out; + } + + MOVE(propsize - 9); + } + } + } + +#undef REQUIRE_SPACE +#undef MOVE + +out: + LeaveCriticalSection(&This->lock); + + return hr; +} + +static HRESULT WINAPI PropertyStore_GetClassID(IPersistStream *iface, CLSID *pclsid) +{ + TRACE("%p,%p\n", iface, pclsid); + + if (!pclsid) + return E_POINTER; + + *pclsid = CLSID_InMemoryPropertyStore; + return S_OK; +} + +static HRESULT WINAPI PropertyStore_IsDirty(IPersistStream *iface) +{ + TRACE("%p: stub\n", iface); + + return S_FALSE; +} + +static HRESULT WINAPI PropertyStore_Load(IPersistStream *iface, IStream *stream) +{ + PropertyStore *This = impl_from_IPersistStream(iface); + HRESULT hr = S_OK; + DWORD count; + BYTE sizeBuffer[4]; + BYTE *buffer; + + TRACE("%p,%p\n", iface, stream); + + if (!stream) + return E_POINTER; + + hr = IStream_Read(stream, sizeBuffer, 4, &count); + if (hr != S_OK) + return hr; + + read_dword(sizeBuffer, &count); + buffer = CoTaskMemAlloc(count); + if (!buffer) + return E_OUTOFMEMORY; + + hr = IStream_Read(stream, buffer, count, &count); + if (hr == S_OK) + { + hr = PropertyStore_SetPropertyStorage(&This->IPersistSerializedPropStorage2_iface, + (const SERIALIZEDPROPSTORAGE *)buffer, + count); + } + + CoTaskMemFree(buffer); + + /* MSDN mandates generic error codes */ + if (hr != S_OK && hr != E_OUTOFMEMORY) + hr = E_FAIL; + + return hr; +} + +static HRESULT WINAPI PropertyStore_Save(IPersistStream *iface, + IStream *stream, + BOOL clearDirty) +{ + PropertyStore *This = impl_from_IPersistStream(iface); + HRESULT hr = S_OK; + DWORD count; + DWORD written; + BYTE sizeBuffer[4]; + SERIALIZEDPROPSTORAGE *buffer; + + TRACE("%p %p %d\n", iface, stream, (int)clearDirty); + + if (!stream) + return E_POINTER; + + hr = PropertyStore_GetPropertyStorage(&This->IPersistSerializedPropStorage2_iface, + &buffer, &count); + if (FAILED(hr)) + goto out; + + write_dword(sizeBuffer, count); + + hr = IStream_Write(stream, sizeBuffer, 4, &written); + if (hr != S_OK) + goto out; + + hr = IStream_Write(stream, buffer, count, &written); + + CoTaskMemFree(buffer); + +out: + /* MSDN mandates generic error codes */ + if (hr != S_OK && hr != STG_E_MEDIUMFULL) + hr = STG_E_CANTSAVE; + + return hr; +} + +static HRESULT WINAPI PropertyStore_GetSizeMax(IPersistStream *iface, ULARGE_INTEGER *pcbSize) +{ + PropertyStore *This = impl_from_IPersistStream(iface); + HRESULT hr = S_OK; + DWORD size; + + TRACE("%p,%p", iface, pcbSize); + + if (!pcbSize) + return E_POINTER; + + hr = PropertyStore_GetPropertyStorageSize(&This->IPersistSerializedPropStorage2_iface, + &size); + if (SUCCEEDED(hr)) + { + pcbSize->QuadPart = size; + pcbSize->QuadPart += 4; + } + + return hr; +} + +static const IPropertyStoreCacheVtbl PropertyStore_IPropertyStoreCache_Vtbl = { + PropertyStore_IPropertyStoreCache_QueryInterface, + PropertyStore_IPropertyStoreCache_AddRef, + PropertyStore_IPropertyStoreCache_Release, PropertyStore_GetCount, PropertyStore_GetAt, PropertyStore_GetValue, @@ -450,6 +1472,38 @@ static const IPropertyStoreCacheVtbl PropertyStore_Vtbl = { PropertyStore_SetValueAndState };
+static const IPersistSerializedPropStorage2Vtbl PropertyStore_IPersistSerializedPropStorage_Vtbl = { + PropertyStore_IPersistSerializedPropStorage_QueryInterface, + PropertyStore_IPersistSerializedPropStorage_AddRef, + PropertyStore_IPersistSerializedPropStorage_Release, + PropertyStore_SetFlags, + PropertyStore_SetPropertyStorage, + PropertyStore_GetPropertyStorage, + PropertyStore_GetPropertyStorageSize, + PropertyStore_GetPropertyStorageBuffer +}; + +static const INamedPropertyStoreVtbl PropertyStore_INamedPropertyStore_Vtbl = { + PropertyStore_INamedPropertyStore_QueryInterface, + PropertyStore_INamedPropertyStore_AddRef, + PropertyStore_INamedPropertyStore_Release, + PropertyStore_GetNamedValue, + PropertyStore_SetNamedValue, + PropertyStore_GetNameCount, + PropertyStore_GetNameAt +}; + +static const IPersistStreamVtbl PropertyStore_IPersistStream_Vtbl = { + PropertyStore_IPersistStream_QueryInterface, + PropertyStore_IPersistStream_AddRef, + PropertyStore_IPersistStream_Release, + PropertyStore_GetClassID, + PropertyStore_IsDirty, + PropertyStore_Load, + PropertyStore_Save, + PropertyStore_GetSizeMax +}; + HRESULT PropertyStore_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv) { PropertyStore *This; @@ -464,11 +1518,15 @@ HRESULT PropertyStore_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv This = HeapAlloc(GetProcessHeap(), 0, sizeof(PropertyStore)); if (!This) return E_OUTOFMEMORY;
- This->IPropertyStoreCache_iface.lpVtbl = &PropertyStore_Vtbl; + This->IPropertyStoreCache_iface.lpVtbl = &PropertyStore_IPropertyStoreCache_Vtbl; + This->IPersistSerializedPropStorage2_iface.lpVtbl = &PropertyStore_IPersistSerializedPropStorage_Vtbl; + This->INamedPropertyStore_iface.lpVtbl = &PropertyStore_INamedPropertyStore_Vtbl; + This->IPersistStream_iface.lpVtbl = &PropertyStore_IPersistStream_Vtbl; This->ref = 1; InitializeCriticalSection(&This->lock); This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PropertyStore.lock"); list_init(&This->formats); + list_init(&This->named);
ret = IPropertyStoreCache_QueryInterface(&This->IPropertyStoreCache_iface, iid, ppv); IPropertyStoreCache_Release(&This->IPropertyStoreCache_iface); diff --git a/dlls/propsys/propsys_classes.idl b/dlls/propsys/propsys_classes.idl index 02555a3..0d5589b 100644 --- a/dlls/propsys/propsys_classes.idl +++ b/dlls/propsys/propsys_classes.idl @@ -25,4 +25,9 @@ threading(both), uuid(9a02e012-6303-4e1e-b9a1-630f802592c5) ] -coclass InMemoryPropertyStore { interface IPropertyStoreCache; } +coclass InMemoryPropertyStore { + interface IPropertyStoreCache; + interface IPersistSerializedPropStorage2; + interface INamedPropertyStore; + interface IPersistStream; +} diff --git a/dlls/propsys/tests/Makefile.in b/dlls/propsys/tests/Makefile.in index d07d675..2913673 100644 --- a/dlls/propsys/tests/Makefile.in +++ b/dlls/propsys/tests/Makefile.in @@ -1,5 +1,5 @@ TESTDLL = propsys.dll -IMPORTS = propsys ole32 oleaut32 +IMPORTS = propsys ole32 oleaut32 uuid
C_SRCS = \ propstore.c \ diff --git a/dlls/propsys/tests/propstore.c b/dlls/propsys/tests/propstore.c index 01500dd..e69ce73 100644 --- a/dlls/propsys/tests/propstore.c +++ b/dlls/propsys/tests/propstore.c @@ -2,6 +2,7 @@ * Unit tests for IPropertyStore and related interfaces * * Copyright 2012 Vincent Povirk + * 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 @@ -30,6 +31,7 @@ #include "objbase.h" #include "propsys.h" #include "wine/test.h" +#include "propvarutil.h"
#include "initguid.h"
@@ -196,21 +198,194 @@ static void test_inmemorystore(void) IPropertyStoreCache_Release(propcache); }
-static void test_persistserialized(void) +static void test_namedpropertystore(void) { IPropertyStore *propstore; - IPersistSerializedPropStorage *serialized; + INamedPropertyStore *named; + HRESULT hr; + PROPVARIANT propvar; + const WCHAR wcsJava[] = { 'J', 'a', 'v', 'a', 0 }; + const WCHAR wcsBlub[] = { 'B', 'l', 'u', 'b', 0 }; + DWORD count; + BSTR name; + + hr = CoCreateInstance(&CLSID_InMemoryPropertyStore, NULL, CLSCTX_INPROC_SERVER, + &IID_IPropertyStore, (void**)&propstore); + ok(hr == S_OK, "CoCreateInstance failed, hr=%x\n", hr); + + hr = IPropertyStore_QueryInterface(propstore, &IID_INamedPropertyStore, + (void**)&named); + ok(hr == S_OK, "QueryInterface failed, hr=%x\n", hr); + + if (FAILED(hr)) + { + skip("IPersistSerializedPropStorage not supported\n"); + return; + } + + InitPropVariantFromUInt32(0xcafebabe, &propvar); + + hr = INamedPropertyStore_SetNamedValue(named, wcsJava, &propvar); + ok(hr == S_OK, "SetNamedValue failed, hr=%x\n", hr); + + hr = INamedPropertyStore_GetNameCount(named, &count); + ok(hr == S_OK, "GetNameCount failed, hr=%x\n", hr); + ok(count == 1, "Expected 1 element, got %u\n", count); + + hr = INamedPropertyStore_GetNameAt(named, 0, &name); + ok(hr == S_OK, "GetNameAt failed, hr=%x\n", hr); + ok(0 == lstrcmpW(name, wcsJava), "Unexpected name %s\n", wine_dbgstr_w(name)); + + SysFreeString(name); + + PropVariantInit(&propvar); + hr = INamedPropertyStore_GetNamedValue(named, wcsJava, &propvar); + ok(hr == S_OK, "GetNamedValue failed, hr=%x\n", hr); + ok(propvar.vt == VT_UI4, "Unexpected vt %d\n", propvar.vt); + ok(U(propvar).ulVal == 0xcafebabe, "Unexpected value %x\n", U(propvar).ulVal); + + InitPropVariantFromInt16(2523, &propvar); + hr = INamedPropertyStore_SetNamedValue(named, wcsBlub, &propvar); + ok(hr == S_OK, "SetNamedValue failed, hr=%x\n", hr); + + hr = INamedPropertyStore_GetNameCount(named, &count); + ok(hr == S_OK, "GetNameCount failed, hr=%x\n", hr); + ok(count == 2, "Expected 2 elements, got %u\n", count); + + InitPropVariantFromUInt32(0xdeadbeef, &propvar); + hr = INamedPropertyStore_SetNamedValue(named, wcsJava, &propvar); + ok(hr == S_OK, "SetNameValue failed, hr=%x\n", hr); + + hr = INamedPropertyStore_GetNameCount(named, &count); + ok(hr == S_OK, "GetNameCount failed, hr=%x\n", hr); + ok(count == 2, "Expected 2 elements, got %u\n", count); + + PropVariantInit(&propvar); + hr = INamedPropertyStore_GetNamedValue(named, wcsJava, &propvar); + ok(hr == S_OK, "GetNamedValue failed, hr=%x\n", hr); + ok(propvar.vt == VT_UI4, "Unexpected vt %d\n", propvar.vt); + ok(U(propvar).ulVal == 0xdeadbeef, "Unexpected value %x\n", U(propvar).ulVal); +} + +static void test_persistserialized(void) +{ + IPropertyStore *propstore = NULL; + INamedPropertyStore *named = NULL; + IPersistSerializedPropStorage *serialized = NULL; + IPersistStream *persiststream = NULL; HRESULT hr; SERIALIZEDPROPSTORAGE *result; DWORD result_size; + HGLOBAL hSerialized; + + WCHAR hello[] = { 'H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd', 0 }; + WCHAR wcsJava[] = { 'J', 'a', 'v', 'a', 0 }; + + /* The spec doesn't impose any order. + * Since we have two properties, there are two valid serializations */ + BYTE expected_result1[] = { + /* WTF: Contrary to what the spec says, the store size field is missing! */ + + 0x45, 0x00, 0x00, 0x00, /* 1st Storage size (69 bytes) */ + + 0x31, 0x53, 0x50, 0x53, /* Version (DWORD) 0x53505331 */ + + /* fmtid GUID 0x7b317433, 0xdfa3, 0x4c44, 0xad, 0x3e, 0x2f, 0x80, 0x4b, 0x90, 0xdb, 0xf4 */ + 0x33, 0x74, 0x31, 0x7b, 0xa3, 0xdf, 0x44, 0x4c, + 0xad, 0x3e, 0x2f, 0x80, 0x4b, 0x90, 0xdb, 0xf4, + + 0x29, 0x00, 0x00, 0x00, /* Value size */ + 0x02, 0x00, 0x00, 0x00, /* PID */ + 0x00, /* random reserved byte to make everything misaligned */ + + 0x1f, 0x00, 0x00, 0x00, /* Variant Type */ + 0x0b, 0x00, 0x00, 0x00, /* String length */ + + /* UTF-16 string: "HelloWorld", padded to 4 bytes */ + 0x48, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, 0x00, + 0x6f, 0x00, 0x57, 0x00, 0x6f, 0x00, 0x72, 0x00, + 0x6c, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, /* Final value with size 0 */ + + 0x37, 0x00, 0x00, 0x00, /* 2nd Storage size */ + 0x31, 0x53, 0x50, 0x53, /* Version */ + + /* fmtid named properties */ + 0x05, 0xd5, 0xcd, 0xd5, 0x9c, 0x2e, 0x1b, 0x10, + 0x93, 0x97, 0x08, 0x00, 0x2b, 0x2c, 0xf9, 0xae, + + 0x1b, 0x00, 0x00, 0x00, /* Value size */ + 0x0a, 0x00, 0x00, 0x00, /* Name size */ + 0x00, /* random reserved byte to ensure misalignment */ + + /* UTF-16 string "Java" */ + 0x4a, 0x00, 0x61, 0x00, 0x76, 0x00, 0x61, 0x00, 0x00, 0x00, + + 0x13, 0x00, 0x00, 0x00, /* Variant Type */ + 0xbe, 0xba, 0xfe, 0xca, /* Variant Value */ + + 0x00, 0x00, 0x00, 0x00, /* Final value with size 0 */ + 0x00, 0x00, 0x00, 0x00 /* Final storage with size 0 */ + }; + BYTE expected_result2[] = { + /* WTF: Contrary to what the spec says, the store size field is missing! */ + + 0x37, 0x00, 0x00, 0x00, /* 2nd Storage size */ + 0x31, 0x53, 0x50, 0x53, /* Version */ + + /* fmtid named properties */ + 0x05, 0xd5, 0xcd, 0xd5, 0x9c, 0x2e, 0x1b, 0x10, + 0x93, 0x97, 0x08, 0x00, 0x2b, 0x2c, 0xf9, 0xae, + + 0x1b, 0x00, 0x00, 0x00, /* Value size */ + 0x0a, 0x00, 0x00, 0x00, /* Name size */ + 0x00, /* random reserved byte to ensure misalignment */ + + /* UTF-16 string "Java" */ + 0x4a, 0x00, 0x61, 0x00, 0x76, 0x00, 0x61, 0x00, 0x00, 0x00, + + 0x13, 0x00, 0x00, 0x00, /* Variant Type */ + 0xbe, 0xba, 0xfe, 0xca, /* Variant Value */ + + 0x00, 0x00, 0x00, 0x00, /* Final value with size 0 */ + + 0x45, 0x00, 0x00, 0x00, /* 1st Storage size (69 bytes) */ + + 0x31, 0x53, 0x50, 0x53, /* Version (DWORD) 0x53505331 */ + + /* fmtid GUID 0x7b317433, 0xdfa3, 0x4c44, 0xad, 0x3e, 0x2f, 0x80, 0x4b, 0x90, 0xdb, 0xf4 */ + 0x33, 0x74, 0x31, 0x7b, 0xa3, 0xdf, 0x44, 0x4c, + 0xad, 0x3e, 0x2f, 0x80, 0x4b, 0x90, 0xdb, 0xf4, + + 0x29, 0x00, 0x00, 0x00, /* Value size */ + 0x02, 0x00, 0x00, 0x00, /* PID */ + 0x00, /* random reserved byte to make everything misaligned */ + + 0x1f, 0x00, 0x00, 0x00, /* Variant Type */ + 0x0b, 0x00, 0x00, 0x00, /* String length */ + + /* UTF-16 string: "HelloWorld", padded to 4 bytes */ + 0x48, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, 0x00, + 0x6f, 0x00, 0x57, 0x00, 0x6f, 0x00, 0x72, 0x00, + 0x6c, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, /* Final value with size 0 */ + + 0x00, 0x00, 0x00, 0x00 /* Final storage with size 0 */ + };
hr = CoCreateInstance(&CLSID_InMemoryPropertyStore, NULL, CLSCTX_INPROC_SERVER, &IID_IPropertyStore, (void**)&propstore); ok(hr == S_OK, "CoCreateInstance failed, hr=%x\n", hr);
+ hr = IPropertyStore_QueryInterface(propstore, &IID_INamedPropertyStore, + (void**)&named); + ok(hr == S_OK, "QueryInterface failed, hr=%x\n", hr); + hr = IPropertyStore_QueryInterface(propstore, &IID_IPersistSerializedPropStorage, (void**)&serialized); - todo_wine ok(hr == S_OK, "QueryInterface failed, hr=%x\n", hr); + ok(hr == S_OK, "QueryInterface failed, hr=%x\n", hr);
if (FAILED(hr)) { @@ -244,7 +419,225 @@ static void test_persistserialized(void) ok(hr == S_OK, "GetCount failed, hr=%x\n", hr); ok(result_size == 0, "expecting 0, got %d\n", result_size);
+ { + PROPVARIANT vHello; + PROPVARIANT vUlong; + PROPERTYKEY kHello = { PKEY_WineTest, PID_FIRST_USABLE }; + + vHello.vt = VT_LPWSTR; + vHello.u.pwszVal = hello; + vUlong.vt = VT_UI4; + vUlong.u.ulVal = 0xCAFEBABE; + + hr = INamedPropertyStore_SetNamedValue(named, wcsJava, &vUlong); + ok(hr == S_OK, "SetValue failed, hr=%08x\n", hr); + + hr = IPropertyStore_SetValue(propstore, &kHello, &vHello); + ok(hr == S_OK, "SetValue failed, hr=%08x\n", hr); + } + + hr = IPersistSerializedPropStorage_GetPropertyStorage(serialized, &result, &result_size); + ok(hr == S_OK, "GetPropertyStorage failed, hr=%x\n", hr); + ok(result != NULL, "GetPropertyStorage returned NULL where it shouldn't\n"); + + /* compare the result */ + if (result) + { + BOOL same; + + same = result_size == sizeof(expected_result1) + && (!memcmp(expected_result1, result, result_size) + || !memcmp(expected_result2, result, result_size)); + ok(same, "GetPropertyStorage returned unexpected result of size %u (%u expected)\n", + result_size, (unsigned)sizeof(expected_result1)); + + if (!same) + { + size_t i; + + printf("Got result: {"); + for (i = 0; i < result_size; ++i) + { + printf("0x%02x, ", (unsigned)((BYTE*)result)[i]); + } + printf("}\n"); + } + } + + /* Load it again into a new property store */ + if (result) + { + IPropertyStore *propstore2 = NULL; + IPersistSerializedPropStorage *serialized2 = NULL; + INamedPropertyStore *named2 = NULL; + PROPVARIANT vHello; + PROPVARIANT vJava; + PROPERTYKEY key; + + hr = CoCreateInstance(&CLSID_InMemoryPropertyStore, NULL, CLSCTX_INPROC_SERVER, + &IID_IPropertyStore, (void**)&propstore2); + ok(hr == S_OK, "CoCreateInstance failed, hr=%x\n", hr); + + hr = IPropertyStore_QueryInterface(propstore2, &IID_IPersistSerializedPropStorage, + (void**)&serialized2); + ok(hr == S_OK, "QueryInterface failed, hr=%x\n", hr); + + hr = IPropertyStore_QueryInterface(propstore2, &IID_INamedPropertyStore, + (void**)&named2); + ok(hr == S_OK, "QueryInterface failed, hr=%x\n", hr); + + hr = IPersistSerializedPropStorage_SetPropertyStorage(serialized2, result, result_size); + ok(hr == S_OK, "SetPropertyStorage failed, hr=%x\n", hr); + + hr = IPropertyStore_GetCount(propstore2, &result_size); + ok(hr == S_OK, "GetCount failed, hr=%x\n", hr); + ok(result_size == 1, "expecting 1, got %d\n", result_size); + + hr = INamedPropertyStore_GetNameCount(named2, &result_size); + ok(hr == S_OK, "GetNameCount failed, hr=%x\n", hr); + ok(result_size == 1, "expecting 1, got %d\n", result_size); + + key.fmtid = PKEY_WineTest; + key.pid = PID_FIRST_USABLE; + + hr = IPropertyStore_GetValue(propstore2, &key, &vHello); + ok(hr == S_OK, "GetValue failed, hr=%x\n", hr); + + ok(vHello.vt == VT_LPWSTR, "Variant has wrong type %d\n", vHello.vt); + ok(lstrcmpW(vHello.u.pwszVal, hello) == 0, + "Variant has wrong value %s\n", wine_dbgstr_w(vHello.u.pwszVal)); + + hr = INamedPropertyStore_GetNamedValue(named2, wcsJava, &vJava); + ok(hr == S_OK, "GetNamedValue failed, hr=%x\n", hr); + + ok(vJava.vt == VT_UI4, "Variant has wrong type %d\n", vJava.vt); + ok(vJava.u.ulVal == 0xCAFEBABE, "Variant has wrong value %X\n", vJava.u.ulVal); + + PropVariantClear(&vHello); + PropVariantClear(&vJava); + + IPropertyStore_Release(propstore2); + INamedPropertyStore_Release(named2); + IPersistSerializedPropStorage_Release(serialized2); + + CoTaskMemFree(result); + } + + /* Serialize using IPersistStream */ + hr = IPropertyStore_QueryInterface(propstore, &IID_IPersistStream, (void**)&persiststream); + ok(hr == S_OK, "QueryInterface(IPersistStream) failed, hr=%x\n", hr); + + if (persiststream) + { + IStream *stream; + CLSID clazz; + BYTE *mem; + DWORD size; + + hr = CreateStreamOnHGlobal(NULL, FALSE, &stream); + ok(hr == S_OK, "Failed to create stream on HGLOBAL, hr=%x\n", hr); + + /* check the CLSID */ + hr = IPersistStream_GetClassID(persiststream, &clazz); + ok(hr == S_OK, "Failed to retrieve CLSID, hr=%x\n", hr); + ok(IsEqualGUID(&clazz, &CLSID_InMemoryPropertyStore), + "Wrong CLSID %s returned\n", wine_dbgstr_guid(&clazz)); + + hr = IPersistStream_Save(persiststream, stream, TRUE); + ok(hr == S_OK, "IPersistStream::Save failed, hr=%x\n", hr); + + /* The HGLOBAL should now contain one of the possible serializations, + * prefixed with the length of the following serialized storage. */ + hr = GetHGlobalFromStream(stream, &hSerialized); + ok(hr == S_OK, "WTF: Can't retrieve HGLOBAL from stream, hr=%x\n", hr); + + ok(GlobalSize(hSerialized)-4 == sizeof(expected_result1), + "Serialized result has invalid size %lu, expected %lu\n", + (unsigned long)GlobalSize(hSerialized), (unsigned long)sizeof(expected_result1) + 4); + + mem = GlobalLock(hSerialized); + ok(mem != NULL, "WTF: Can't lock HGLOBAL"); + + size = mem[0] | (mem[1] << 8) | (mem[2] << 16) | (mem[3] << 24); + + ok(size == sizeof(expected_result1), + "Serialized result encodes invalid size %lu, expected %lu\n", + (unsigned long)size, (unsigned long)sizeof(expected_result1)); + ok(memcmp(mem+4, expected_result1, sizeof(expected_result1)) == 0 + || memcmp(mem+4, expected_result2, sizeof(expected_result2)) == 0, + "Serialized result differs from expected result\n"); + GlobalUnlock(hSerialized); + + IPersistStream_Release(persiststream); + IStream_Release(stream); + } + + /* Deserialize using IPersistStream */ + if (hSerialized) + { + IStream *stream; + IPropertyStore *propstore2 = NULL; + IPersistStream *persiststream2 = NULL; + INamedPropertyStore *named2 = NULL; + PROPVARIANT vHello; + PROPVARIANT vJava; + PROPERTYKEY key; + + hr = CreateStreamOnHGlobal(hSerialized, FALSE, &stream); + ok(hr == S_OK, "Failed to create stream on HGLOBAL, hr=%x\n", hr); + + hr = CoCreateInstance(&CLSID_InMemoryPropertyStore, NULL, CLSCTX_INPROC_SERVER, + &IID_IPropertyStore, (void**)&propstore2); + ok(hr == S_OK, "CoCreateInstance failed, hr=%x\n", hr); + + hr = IPropertyStore_QueryInterface(propstore2, &IID_IPersistStream, + (void**)&persiststream2); + ok(hr == S_OK, "QueryInterface failed, hr=%x\n", hr); + + hr = IPropertyStore_QueryInterface(propstore2, &IID_INamedPropertyStore, + (void**)&named2); + ok(hr == S_OK, "QueryInterface failed, hr=%x\n", hr); + + hr = IPersistStream_Load(persiststream2, stream); + ok(hr == S_OK, "IPersistStream::Load failed, hr=%x\n", hr); + + hr = IPropertyStore_GetCount(propstore2, &result_size); + ok(hr == S_OK, "GetCount failed, hr=%x\n", hr); + ok(result_size == 1, "expecting 1, got %d\n", result_size); + + hr = INamedPropertyStore_GetNameCount(named2, &result_size); + ok(hr == S_OK, "GetNameCount failed, hr=%x\n", hr); + ok(result_size == 1, "expecting 1, got %d\n", result_size); + + key.fmtid = PKEY_WineTest; + key.pid = PID_FIRST_USABLE; + + hr = IPropertyStore_GetValue(propstore2, &key, &vHello); + ok(hr == S_OK, "GetValue failed, hr=%x\n", hr); + + ok(vHello.vt == VT_LPWSTR, "Variant has wrong type %d\n", vHello.vt); + ok(lstrcmpW(vHello.u.pwszVal, hello) == 0, + "Variant has wrong value %s\n", wine_dbgstr_w(vHello.u.pwszVal)); + + hr = INamedPropertyStore_GetNamedValue(named2, wcsJava, &vJava); + ok(hr == S_OK, "GetNamedValue failed, hr=%x\n", hr); + + ok(vJava.vt == VT_UI4, "Variant has wrong type %d\n", vJava.vt); + ok(vJava.u.ulVal == 0xCAFEBABE, "Variant has wrong value %X\n", vJava.u.ulVal); + + PropVariantClear(&vHello); + PropVariantClear(&vJava); + + IPropertyStore_Release(propstore2); + INamedPropertyStore_Release(named2); + IPersistStream_Release(persiststream2); + IStream_Release(stream); + + GlobalFree(hSerialized); + } + IPropertyStore_Release(propstore); + INamedPropertyStore_Release(named); IPersistSerializedPropStorage_Release(serialized); }
@@ -253,6 +646,7 @@ START_TEST(propstore) CoInitialize(NULL);
test_inmemorystore(); + test_namedpropertystore(); test_persistserialized();
CoUninitialize(); diff --git a/dlls/propsys/tests/propsys.c b/dlls/propsys/tests/propsys.c index 311d610..acc35fa 100644 --- a/dlls/propsys/tests/propsys.c +++ b/dlls/propsys/tests/propsys.c @@ -29,12 +29,11 @@ #include "windef.h" #include "winbase.h" #include "objbase.h" -#include "initguid.h" #include "propsys.h" #include "propvarutil.h" #include "wine/test.h"
-DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0); +#include "initguid.h" DEFINE_GUID(dummy_guid, 0xdeadbeef, 0xdead, 0xbeef, 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0xba, 0xbe); DEFINE_GUID(expect_guid, 0x12345678, 0x1234, 0x1234, 0x12, 0x34, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12);