[PATCH 4/6] propsys: Implement named properties, serialization on the in-memory store

Jonas Kümmerlin rgcjonas at gmail.com
Tue Jul 14 10:07:44 CDT 2015


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




More information about the wine-devel mailing list