[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