The in-memory property store now supports IPersistSerializedPropStorage2. --- dlls/propsys/propstore.c | 625 +++++++++++++++++++++++++++++++++++++++ dlls/propsys/propsys_classes.idl | 1 + dlls/propsys/tests/propstore.c | 216 +++++++++++++- 3 files changed, 839 insertions(+), 3 deletions(-)
diff --git a/dlls/propsys/propstore.c b/dlls/propsys/propstore.c index 3f028e9..1f5f58f 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 @@ -32,6 +33,7 @@ #include "wine/debug.h" #include "wine/unicode.h" #include "wine/list.h" +#include "mimeole.h"
#include "initguid.h" #include "propsys_private.h" @@ -63,6 +65,7 @@ typedef struct {
typedef struct { IPropertyStoreCache IPropertyStoreCache_iface; + IPersistSerializedPropStorage2 IPersistSerializedPropStorage2_iface; INamedPropertyStore INamedPropertyStore_iface; LONG ref; CRITICAL_SECTION lock; @@ -75,6 +78,11 @@ static inline PropertyStore *impl_from_IPropertyStoreCache(IPropertyStoreCache * return CONTAINING_RECORD(iface, PropertyStore, IPropertyStoreCache_iface); }
+static inline PropertyStore *impl_from_IPersistSerializedPropStorage2(IPersistSerializedPropStorage2 *iface) +{ + return CONTAINING_RECORD(iface, PropertyStore, IPersistSerializedPropStorage2_iface); +} + static inline PropertyStore *impl_from_INamedPropertyStore(INamedPropertyStore *iface) { return CONTAINING_RECORD(iface, PropertyStore, INamedPropertyStore_iface); @@ -89,6 +97,11 @@ static HRESULT query_interface(PropertyStore *This, REFIID iid, void **ppv) { *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; @@ -114,6 +127,16 @@ static HRESULT WINAPI PropertyStore_IPropertyStoreCache_QueryInterface( 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) { @@ -139,6 +162,16 @@ static ULONG WINAPI PropertyStore_IPropertyStoreCache_AddRef(IPropertyStoreCache 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); @@ -197,6 +230,16 @@ static ULONG WINAPI PropertyStore_IPropertyStoreCache_Release(IPropertyStoreCach 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); @@ -668,6 +711,576 @@ static HRESULT WINAPI PropertyStore_SetValueAndState(IPropertyStoreCache *iface, return hr; }
+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 = StgSerializePropVariant(&value->propvar, NULL, &propsize); + 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 = StgSerializePropVariant(&named_value->propvar, NULL, &propsize); + 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; + SERIALIZEDPROPERTYVALUE *tmp = NULL; + + hr = StgSerializePropVariant(&value->propvar, &tmp, &propsize); + if (FAILED(hr)) + { + WARN("Property of type %d is not serializable\n", value->propvar.vt); + goto out; + } + + if (cb < (9 + propsize)) /* Size, pid, reserved, content */ + { + CoTaskMemFree(buffer); + hr = E_INVALIDARG; + goto out; + } + + write_dword(buffer, propsize + 9); /* size */ + write_dword(buffer + 4, value->pid); /* pid */ + buffer[8] = 0; /* reserved byte to make sure everything is misaligned */ + + memcpy(buffer + 9, tmp, propsize); + CoTaskMemFree(tmp); + + MOVE(9 + 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; + SERIALIZEDPROPERTYVALUE *tmp; + 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 = StgSerializePropVariant(&named_value->propvar, &tmp, &propsize); + if (FAILED(hr)) + { + WARN("Property of type %d is not serializable\n", named_value->propvar.vt); + goto out; + } + + if ((9 + namesize + propsize) > cb) + { + CoTaskMemFree(tmp); + hr = E_INVALIDARG; + goto out; + } + + write_dword(buffer, 9 + namesize + propsize); + write_dword(buffer + 4, namesize); + buffer[8] = 0; + + write_wstring(buffer + 9, named_value->name, namesize/2); + memcpy(buffer + 9 + namesize, tmp, propsize); + + MOVE(9 + namesize + 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 (cb == 0) /* 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 const IPropertyStoreCacheVtbl PropertyStore_IPropertyStoreCache_Vtbl = { PropertyStore_IPropertyStoreCache_QueryInterface, PropertyStore_IPropertyStoreCache_AddRef, @@ -683,6 +1296,17 @@ static const IPropertyStoreCacheVtbl PropertyStore_IPropertyStoreCache_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, @@ -708,6 +1332,7 @@ HRESULT PropertyStore_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv if (!This) return E_OUTOFMEMORY;
This->IPropertyStoreCache_iface.lpVtbl = &PropertyStore_IPropertyStoreCache_Vtbl; + This->IPersistSerializedPropStorage2_iface.lpVtbl = &PropertyStore_IPersistSerializedPropStorage_Vtbl; This->INamedPropertyStore_iface.lpVtbl = &PropertyStore_INamedPropertyStore_Vtbl; This->ref = 1; InitializeCriticalSection(&This->lock); diff --git a/dlls/propsys/propsys_classes.idl b/dlls/propsys/propsys_classes.idl index cdbbb77..b4fd550 100644 --- a/dlls/propsys/propsys_classes.idl +++ b/dlls/propsys/propsys_classes.idl @@ -28,4 +28,5 @@ coclass InMemoryPropertyStore { interface IPropertyStoreCache; interface INamedPropertyStore; + interface IPersistSerializedPropStorage2; } diff --git a/dlls/propsys/tests/propstore.c b/dlls/propsys/tests/propstore.c index 67ab80a..a91e456 100644 --- a/dlls/propsys/tests/propstore.c +++ b/dlls/propsys/tests/propstore.c @@ -269,19 +269,121 @@ static void test_namedpropertystore(void)
static void test_persistserialized(void) { - IPropertyStore *propstore; - IPersistSerializedPropStorage *serialized; + IPropertyStore *propstore = NULL; + INamedPropertyStore *named = NULL; + IPersistSerializedPropStorage *serialized = NULL; HRESULT hr; SERIALIZEDPROPSTORAGE *result; DWORD result_size;
+ 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)) { @@ -311,11 +413,119 @@ static void test_persistserialized(void) hr = IPersistSerializedPropStorage_SetPropertyStorage(serialized, NULL, 0); ok(hr == S_OK, "SetPropertyStorage failed, hr=%x\n", hr);
+ hr = IPersistSerializedPropStorage_SetPropertyStorage(serialized, (void*)0xdeadcafe, 0); + ok(hr == S_OK, "SetPropertyStorage failed, hr=%x\n", hr); + hr = IPropertyStore_GetCount(propstore, &result_size); 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); + } + IPropertyStore_Release(propstore); + INamedPropertyStore_Release(named); IPersistSerializedPropStorage_Release(serialized); }