[PATCH v2 07/15] propsys: Implement serialization of the in-memory property store

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


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);
 }
 
-- 
2.4.3




More information about the wine-devel mailing list