[PATCH 3/6] ole32, propsys: Rework PROPVARIANT (de)serialization

Jonas Kümmerlin rgcjonas at gmail.com
Tue Jul 14 14:47:12 CDT 2015


OLE32.dll now exports a few internal functions for propsys.dll to use.
The newly implemented functions handle way more cases than the original
implementations, properly protect themselves against buffer overflows
and are more thoroughly tested.

This allows to provide a high-quality implementation of
StgDeserializePropVariant and StgSerializePropVariant.

As a byproduct of implementing the tests, numerous InitPropVariant*
routines have been implemented in propsys.dll, and PropVariantCompareEx
has been extended to support all serializable properties.
---
 dlls/ole32/ole32.spec          |    3 +
 dlls/ole32/stg_prop.c          | 1711 ++++++++++++++++++++++++++++++++++------
 dlls/ole32/tests/propvariant.c |   55 +-
 dlls/propsys/propsys.spec      |   28 +-
 dlls/propsys/propvar.c         |  502 +++++++++++-
 dlls/propsys/tests/propsys.c   |  377 +++++++++
 include/propidl.idl            |   10 +
 include/propvarutil.h          |  103 ++-
 8 files changed, 2521 insertions(+), 268 deletions(-)

diff --git a/dlls/ole32/ole32.spec b/dlls/ole32/ole32.spec
index b36e683..8ec9c83 100644
--- a/dlls/ole32/ole32.spec
+++ b/dlls/ole32/ole32.spec
@@ -287,3 +287,6 @@
 @ stdcall WriteFmtUserTypeStg(ptr long ptr)
 @ stub WriteOleStg
 @ stub WriteStringStream
+@ stdcall wine_WritePropertyToBuffer(ptr ptr long ptr long long)
+@ stdcall wine_SerializedPropertySize(ptr ptr long long)
+@ stdcall wine_ReadProperty(ptr ptr long ptr long long ptr ptr)
diff --git a/dlls/ole32/stg_prop.c b/dlls/ole32/stg_prop.c
index da361ce..6cf6c62 100644
--- a/dlls/ole32/stg_prop.c
+++ b/dlls/ole32/stg_prop.c
@@ -1038,183 +1038,1491 @@ static void* WINAPI Allocate_CoTaskMemAlloc(void *this, ULONG size)
     return CoTaskMemAlloc(size);
 }
 
-/* FIXME: there isn't any checking whether the read property extends past the
- * end of the buffer.
+static HRESULT ConvertCodepageSize(const char *str, DWORD cb, DWORD *cbSize,
+                                   UINT codepageFrom, UINT codepageTo, BOOL wcharByteSwap)
+{
+    int    unicodeCharCount;
+    int    narrowCharCount;
+    WCHAR *unicodeString;
+
+    *cbSize = 0;
+
+    if (codepageFrom == CP_UNICODE && cb % 2)
+    {
+        WARN("Unicode string has odd number of bytes\n");
+        return STG_E_INVALIDPARAMETER;
+    }
+
+    /* look for a shortcut */
+    if (codepageFrom == codepageTo)
+    {
+        *cbSize = cb;
+        return S_OK;
+    }
+
+    /* look for another shortcut */
+    if (codepageTo == CP_UNICODE)
+    {
+        unicodeCharCount = MultiByteToWideChar(codepageFrom,
+                                               0,
+                                               str,
+                                               cb,
+                                               NULL,
+                                               0);
+        if (unicodeCharCount < 1)
+            return STG_E_INVALIDPARAMETER;
+
+        *cbSize = unicodeCharCount * sizeof(WCHAR);
+        return S_OK;
+    }
+
+    /* convert to unicode */
+    if (codepageFrom == CP_UNICODE)
+    {
+        unicodeString = CoTaskMemAlloc(cb);
+        if (!unicodeString)
+            return STG_E_INSUFFICIENTMEMORY;
+
+        memcpy(unicodeString, str, cb);
+
+        if (wcharByteSwap)
+        {
+            PropertyStorage_ByteSwapString(unicodeString, cb/2);
+        }
+
+        unicodeCharCount = (int)cb / 2;
+    }
+    else
+    {
+        unicodeCharCount = MultiByteToWideChar(codepageFrom,
+                                               0,
+                                               str,
+                                               cb,
+                                               NULL,
+                                               0);
+        if (unicodeCharCount < 1)
+            return STG_E_INVALIDPARAMETER;
+
+        unicodeString = CoTaskMemAlloc(unicodeCharCount * sizeof(WCHAR));
+        if (!unicodeString)
+            return STG_E_INSUFFICIENTMEMORY;
+
+        MultiByteToWideChar(codepageFrom,
+                            0,
+                            str,
+                            cb,
+                            unicodeString,
+                            unicodeCharCount);
+    }
+
+    /* attempt conversion to the target codepage */
+    narrowCharCount = WideCharToMultiByte(codepageTo,
+                                          0,
+                                          unicodeString,
+                                          unicodeCharCount,
+                                          NULL,
+                                          0,
+                                          NULL,
+                                          NULL);
+
+    CoTaskMemFree(unicodeString);
+    if (narrowCharCount < 1)
+        return STG_E_INVALIDPARAMETER;
+
+    *cbSize = narrowCharCount;
+    return S_OK;
+}
+
+static HRESULT ConvertCodepage(const char *str, DWORD cbStr,
+                               char *strOut, DWORD cbBuf,
+                               UINT codepageFrom, UINT codepageTo, BOOL wcharByteSwap)
+{
+    int unicodeCharCount;
+    int narrowCharCount;
+    WCHAR *unicodeString;
+
+    TRACE("(%p %u %p %u %u %u %d)\n", str, cbStr, strOut, cbBuf, codepageFrom, codepageTo, (int)wcharByteSwap);
+
+    if (codepageFrom == CP_UNICODE && cbStr % 2)
+    {
+        WARN("Unicode string has odd number of bytes\n");
+        return STG_E_INVALIDPARAMETER;
+    }
+
+    /* look for a shortcut */
+    if (codepageFrom == codepageTo)
+    {
+        if (cbBuf < cbStr)
+            return STG_E_INSUFFICIENTMEMORY;
+
+        memcpy(strOut, str, cbStr);
+
+        if (codepageTo == CP_UNICODE && wcharByteSwap)
+        {
+            PropertyStorage_ByteSwapString(strOut, cbStr/2);
+        }
+
+        return S_OK;
+    }
+
+    /* look for another shortcut */
+    if (codepageTo == CP_UNICODE)
+    {
+        unicodeCharCount = MultiByteToWideChar(codepageFrom,
+                                               0,
+                                               str,
+                                               cbStr,
+                                               NULL,
+                                               0);
+
+        if (unicodeCharCount < 1 || unicodeCharCount > cbBuf)
+            return STG_E_INVALIDPARAMETER;
+
+        MultiByteToWideChar(codepageFrom,
+                            0,
+                            str,
+                            cbStr,
+                            (LPWSTR)strOut,
+                            unicodeCharCount);
+        return S_OK;
+    }
+
+    /* convert to unicode */
+    if (codepageFrom == CP_UNICODE)
+    {
+        unicodeString = CoTaskMemAlloc(cbStr);
+        if (!unicodeString)
+            return STG_E_INSUFFICIENTMEMORY;
+
+        memcpy(unicodeString, str, cbStr);
+        unicodeCharCount = (int)cbStr / 2;
+
+        if (wcharByteSwap)
+        {
+            PropertyStorage_ByteSwapString(unicodeString, unicodeCharCount);
+        }
+    }
+    else
+    {
+        unicodeCharCount = MultiByteToWideChar(codepageFrom,
+                                               0,
+                                               str,
+                                               cbStr,
+                                               NULL,
+                                               0);
+        if (unicodeCharCount < 1)
+            return STG_E_INVALIDPARAMETER;
+
+        unicodeString = CoTaskMemAlloc(unicodeCharCount * sizeof(WCHAR));
+        if (!unicodeString)
+            return STG_E_INSUFFICIENTMEMORY;
+
+        MultiByteToWideChar(codepageFrom,
+                            0,
+                            str,
+                            cbStr,
+                            unicodeString,
+                            unicodeCharCount);
+    }
+
+    /* attempt conversion to the target codepage */
+    narrowCharCount = WideCharToMultiByte(codepageTo,
+                                          0,
+                                          unicodeString,
+                                          unicodeCharCount,
+                                          NULL,
+                                          0,
+                                          NULL,
+                                          NULL);
+
+    if (narrowCharCount < 1 || narrowCharCount > cbBuf)
+    {
+        CoTaskMemFree(unicodeString);
+        return STG_E_INVALIDPARAMETER;
+    }
+
+    WideCharToMultiByte(codepageTo,
+                        0,
+                        unicodeString,
+                        unicodeCharCount,
+                        strOut,
+                        narrowCharCount,
+                        NULL,
+                        NULL);
+
+    return S_OK;
+}
+
+/* HACK to hide (ULONG)(something) < 0 from the analyzer */
+static inline int gt(ULONG a, ULONG b) { return a > b; }
+
+/*             wine_ReadProperty [OLE32.@]
+ *
+ * Base implementation for deserializing properties.
+ * Used by:
+ *      - IPropertyStorage
+ *      - StgConvertPropertyToVariant
+ *      - StgDeserializeProperty [propsys.dll]
+ *
+ * This handles all straightforward cases, only crazy stuff like indirect
+ * properties have to be handled by the caller itself.
+ *
+ * The only sane allocator you could pass is NULL, in which case CoTaskMemAlloc
+ * is used. The allocator parameter just there to make the brain-damaged
+ * interface of StgConvertPropertyToVariant work.
+ *
+ * NOTICE: In case the reading fails, a partially initialized PROPVARIANT may be
+ *         returned that has to be freed (using PropVariantClear, unless you
+ *         used a custom allocator, in which case you basically have to
+ *         reimplement PropVariantClear).
+ * TODO: Array handling
+ */
+HRESULT WINAPI wine_ReadProperty(PROPVARIANT *prop, const BYTE *data,
+    ULONG cbData, ULONG *pcbDataUsed, UINT diskCodepage, UINT runningCodepage,
+    void* (WINAPI *allocate)(void *this, ULONG size), void *allocate_data)
+{
+    HRESULT hr = S_OK;
+    ULONG cbData_Start;
+
+    cbData_Start = cbData;
+    if (pcbDataUsed)
+        *pcbDataUsed = 0;
+
+#define FAIL_IF_EOF(cbytes) \
+    do { \
+        if ((ULONG)cbytes > cbData) \
+            return STG_E_INVALIDPARAMETER; \
+    } while (0)
+#define MOVE_FORWARD(bytes) \
+    do { \
+        FAIL_IF_EOF(bytes); \
+        data += bytes; \
+        cbData -= bytes; \
+    } while (0)
+#define READ_WORD_AND_MOVE(location) \
+    do { \
+        FAIL_IF_EOF(2); \
+        StorageUtl_ReadWord(data, 0, (location)); \
+        MOVE_FORWARD(2); \
+    } while (0)
+#define READ_DWORD_AND_MOVE(location) \
+    do { \
+        FAIL_IF_EOF(4); \
+        StorageUtl_ReadDWord(data, 0, (location)); \
+        MOVE_FORWARD(4); \
+    } while (0)
+#define ALLOCATE_OR_FAIL(pptr, cbEl, nEl) \
+    do { \
+        *(pptr) = allocate(allocate_data, (cbEl) * (nEl)); \
+        if (!*(pptr)) \
+        { \
+            return STG_E_INSUFFICIENTMEMORY; \
+        } \
+    } while(0)
+
+#define FOR_POSSIBLE_VECTOR(ctype, singleMember, vectorMember, cbBlock) \
+    ctype *vec; \
+    DWORD  vecsize; \
+    if (!(prop->vt & VT_VECTOR)) \
+    { \
+        vecsize = 1; \
+        vec = &prop->u.singleMember; \
+    } \
+    else \
+    { \
+        READ_DWORD_AND_MOVE(&vecsize); \
+        prop->u.vectorMember.cElems = vecsize; \
+        ALLOCATE_OR_FAIL(&prop->u.vectorMember.pElems, sizeof(ctype), vecsize); \
+        vec = prop->u.vectorMember.pElems; \
+        ZeroMemory(vec, sizeof(ctype) * vecsize); \
+    } \
+    for (; vecsize; --vecsize, ++vec, data += (cbBlock), cbData -= (cbBlock)) \
+        if (gt((ULONG)(cbBlock), cbData)) \
+            return STG_E_INVALIDPARAMETER; \
+        else
+#define FOR_POSSIBLE_VECTOR_OR_SINGLEP(ctype, singleMemberP, vectorMember, cbBlock) \
+    ctype *vec; \
+    DWORD  vecsize; \
+    if (!(prop->vt & VT_VECTOR)) \
+    { \
+        vecsize = 1; \
+        ALLOCATE_OR_FAIL(&prop->u.singleMemberP, sizeof(ctype), 1); \
+        vec = prop->u.singleMemberP; \
+    } \
+    else \
+    { \
+        READ_DWORD_AND_MOVE(&vecsize); \
+        prop->u.vectorMember.cElems = vecsize; \
+        ALLOCATE_OR_FAIL(&prop->u.vectorMember.pElems, sizeof(ctype), vecsize); \
+        vec = prop->u.vectorMember.pElems; \
+        ZeroMemory(vec, sizeof(ctype) * vecsize); \
+    } \
+    for (; vecsize; --vecsize, ++vec, data += (cbBlock), cbData -= (cbBlock)) \
+        if (gt((ULONG)(cbBlock), cbData)) \
+            return STG_E_INVALIDPARAMETER; \
+        else
+#define CASE_OR_VECTOR(vtype) \
+    case vtype: \
+    case VT_VECTOR|vtype:
+
+    if (!allocate)
+        allocate = &Allocate_CoTaskMemAlloc;
+
+    assert(prop);
+    assert(data);
+
+    ZeroMemory(prop, sizeof(*prop));
+
+    READ_DWORD_AND_MOVE((DWORD *)&prop->vt);
+
+    switch (prop->vt)
+    {
+    case VT_EMPTY:
+    case VT_NULL:
+        break;
+    CASE_OR_VECTOR(VT_I1)
+    {
+        FOR_POSSIBLE_VECTOR(char, cVal, cac, 1)
+        {
+            *vec = *(const char *)data;
+            TRACE("Read char 0x%x ('%c')\n", vec[0], vec[0]);
+        }
+        break;
+    }
+    CASE_OR_VECTOR(VT_UI1)
+    {
+        FOR_POSSIBLE_VECTOR(BYTE, bVal, caub, 1)
+        {
+            *vec = *data;
+            TRACE("Read byte 0x%x\n", *vec);
+        }
+        break;
+    }
+    CASE_OR_VECTOR(VT_I2)
+    {
+        FOR_POSSIBLE_VECTOR(SHORT, iVal, cai, 2)
+        {
+            StorageUtl_ReadWord(data, 0, (WORD*)vec);
+            TRACE("Read short %d\n", (int)*vec);
+        }
+        break;
+    }
+    CASE_OR_VECTOR(VT_UI2)
+    CASE_OR_VECTOR(VT_BOOL) /* VARIANT_BOOL == USHORT */
+    {
+        FOR_POSSIBLE_VECTOR(USHORT, uiVal, caui, 2)
+        {
+            StorageUtl_ReadWord(data, 0, vec);
+            TRACE("Read ushort %d\n", (int)*vec);
+        }
+        break;
+    }
+    CASE_OR_VECTOR(VT_INT)
+    CASE_OR_VECTOR(VT_I4)
+    CASE_OR_VECTOR(VT_ERROR) /* SCODE == LONG */
+    {
+        FOR_POSSIBLE_VECTOR(LONG, lVal, cal, 4)
+        {
+            StorageUtl_ReadDWord(data, 0, (DWORD*)vec);
+            TRACE("Read long %u\n", (unsigned)*vec);
+        }
+        break;
+    }
+    CASE_OR_VECTOR(VT_UINT)
+    CASE_OR_VECTOR(VT_UI4)
+    {
+        FOR_POSSIBLE_VECTOR(ULONG, ulVal, caul, 4)
+        {
+            StorageUtl_ReadDWord(data, 0, vec);
+            TRACE("Read ulong %d\n", (int)*vec);
+        }
+        break;
+    }
+    CASE_OR_VECTOR(VT_I8)
+    {
+        FOR_POSSIBLE_VECTOR(LARGE_INTEGER, hVal, cah, 8)
+        {
+            StorageUtl_ReadULargeInteger(data, 0, (ULARGE_INTEGER*)vec);
+            TRACE("Read longlong 0x%08x%08x\n", (unsigned)vec->HighPart, (unsigned)vec->LowPart);
+        }
+        break;
+    }
+    CASE_OR_VECTOR(VT_UI8)
+    {
+        FOR_POSSIBLE_VECTOR(ULARGE_INTEGER, uhVal, cauh, 8)
+        {
+            StorageUtl_ReadULargeInteger(data, 0, vec);
+            TRACE("Read ulonglong 0x%08x%08x\n", (unsigned)vec->HighPart, (unsigned)vec->LowPart);
+        }
+        break;
+    }
+    CASE_OR_VECTOR(VT_LPSTR)
+    {
+        FOR_POSSIBLE_VECTOR(LPSTR, pszVal, calpstr, 0)
+        {
+            DWORD count;
+            DWORD convertedCount;
+
+            READ_DWORD_AND_MOVE(&count);
+            if (diskCodepage == CP_UNICODE && count % 2)
+            {
+                WARN("Unicode string has odd number of bytes\n");
+                return STG_E_INVALIDHEADER;
+            }
+
+            FAIL_IF_EOF(count);
+
+            hr = ConvertCodepageSize((const char*)data,
+                                     count,
+                                     &convertedCount,
+                                     diskCodepage,
+                                     runningCodepage,
+                                     TRUE);
+            if (FAILED(hr))
+                return hr;
+
+            ALLOCATE_OR_FAIL(vec, 1, convertedCount);
+            hr = ConvertCodepage((const char *)data, count,
+                                 *vec, convertedCount,
+                                 diskCodepage,
+                                 runningCodepage,
+                                 TRUE);
+
+            MOVE_FORWARD(count);
+            MOVE_FORWARD((4 - (count%4)) % 4); /* padding */
+
+            /* Ensure NULL termination */
+            if (runningCodepage == CP_UNICODE)
+            {
+                ((WCHAR*)*vec)[convertedCount / sizeof(WCHAR) - 1] = '\0';
+                TRACE("Read string value %s\n",
+                      debugstr_w((PCWSTR)*vec));
+            }
+            else
+            {
+                (*vec)[convertedCount - 1] = '\0';
+                TRACE("Read string value %s\n", debugstr_a(*vec));
+            }
+        }
+        break;
+    }
+    CASE_OR_VECTOR(VT_BSTR)
+    {
+        FOR_POSSIBLE_VECTOR(BSTR, bstrVal, cabstr, 0)
+        {
+            DWORD count, wcount;
+
+            READ_DWORD_AND_MOVE(&count);
+            if (diskCodepage == CP_UNICODE && count % 2)
+            {
+                WARN("Unicode string has odd number of bytes\n");
+                return STG_E_INVALIDHEADER;
+            }
+            else
+            {
+                FAIL_IF_EOF(count);
+
+                if (diskCodepage == CP_UNICODE)
+                    wcount = count / 2;
+                else
+                    wcount = MultiByteToWideChar(diskCodepage, 0, (LPCSTR)data, count, NULL, 0);
+
+                *vec = SysAllocStringLen(NULL, wcount); /* FIXME: use allocator? */
+
+                if (*vec)
+                {
+                    if (diskCodepage == CP_UNICODE)
+                    {
+                        memcpy(*vec, data, count);
+                        PropertyStorage_ByteSwapString(*vec, wcount);
+                    }
+                    else
+                        MultiByteToWideChar(diskCodepage, 0, (LPCSTR)data, count, *vec, wcount);
+
+                    (*vec)[wcount - 1] = '\0';
+                    TRACE("Read string value %s\n", debugstr_w(*vec));
+                }
+                else
+                {
+                    return STG_E_INSUFFICIENTMEMORY;
+                }
+            }
+
+            MOVE_FORWARD(count);
+            MOVE_FORWARD((4 - (count%4)) % 4); /* padding */
+        }
+        break;
+    }
+    case VT_BLOB:
+    {
+        DWORD count;
+
+        READ_DWORD_AND_MOVE(&count);
+        FAIL_IF_EOF(count);
+        prop->u.blob.cbSize = count;
+        prop->u.blob.pBlobData = allocate(allocate_data, count);
+        if (prop->u.blob.pBlobData)
+        {
+            memcpy(prop->u.blob.pBlobData, data, count);
+            TRACE("Read blob value of size %d\n", count);
+
+            MOVE_FORWARD(count);
+            MOVE_FORWARD((4 - (count % 4)) % 4); /* padding to 4 bytes */
+        }
+        else
+            return STG_E_INSUFFICIENTMEMORY;
+        break;
+    }
+    CASE_OR_VECTOR(VT_LPWSTR)
+    {
+        FOR_POSSIBLE_VECTOR(LPWSTR, pwszVal, calpwstr, 0)
+        {
+            DWORD count;
+
+            READ_DWORD_AND_MOVE(&count);
+            FAIL_IF_EOF(count * sizeof(WCHAR));
+            ALLOCATE_OR_FAIL(vec, sizeof(WCHAR), count);
+
+            memcpy(*vec, data, count * sizeof(WCHAR));
+
+            /* make sure string is NULL-terminated */
+            (*vec)[count - 1] = '\0';
+            PropertyStorage_ByteSwapString(*vec, count);
+            TRACE("Read string value %s\n", debugstr_w(*vec));
+
+            MOVE_FORWARD(count * sizeof(WCHAR));
+            if (count % 2)
+                MOVE_FORWARD(sizeof(WCHAR)); /* padding to 4 bytes */
+        }
+        break;
+    }
+    CASE_OR_VECTOR(VT_FILETIME)
+    {
+        ULARGE_INTEGER tmp;
+        FOR_POSSIBLE_VECTOR(FILETIME, filetime, cafiletime, 8)
+        {
+            StorageUtl_ReadULargeInteger(data, 0, &tmp);
+
+            vec->dwLowDateTime  = tmp.LowPart;
+            vec->dwHighDateTime = tmp.HighPart;
+        }
+        break;
+    }
+    CASE_OR_VECTOR(VT_CY)
+    {
+        ULARGE_INTEGER tmp;
+        FOR_POSSIBLE_VECTOR(CY, cyVal, cacy, 8)
+        {
+            StorageUtl_ReadULargeInteger(data, 0, &tmp);
+            vec->int64 = (LONGLONG)tmp.QuadPart;
+        }
+        break;
+    }
+    CASE_OR_VECTOR(VT_CF)
+    {
+        FOR_POSSIBLE_VECTOR_OR_SINGLEP(CLIPDATA, pclipdata, caclipdata, 0)
+        {
+            DWORD len = 0, tag = 0;
+
+            READ_DWORD_AND_MOVE(&len);
+            FAIL_IF_EOF(len);
+            READ_DWORD_AND_MOVE(&tag);
+            if (len > 4)
+            {
+                vec->cbSize = len;
+                vec->ulClipFmt = tag;
+
+                ALLOCATE_OR_FAIL(&vec->pClipData, 1, len - 4);
+
+                memcpy(vec->pClipData, data, len - 4);
+                MOVE_FORWARD(len - 4);
+                MOVE_FORWARD((4 - (len % 4)) % 4); /* padding */
+            }
+            else
+                return STG_E_INVALIDPARAMETER;
+        }
+        break;
+    }
+    CASE_OR_VECTOR(VT_DATE)
+    CASE_OR_VECTOR(VT_R8)
+    {
+        FOR_POSSIBLE_VECTOR(double, dblVal, cadbl, sizeof(double))
+        {
+            memcpy(vec, data, sizeof(double));
+        }
+        break;
+    }
+    CASE_OR_VECTOR(VT_R4)
+    {
+        FOR_POSSIBLE_VECTOR(float, fltVal, caflt, sizeof(float))
+        {
+            memcpy(vec, data, sizeof(double));
+        }
+        break;
+    }
+    case VT_DECIMAL:
+    {
+        DECIMAL *dec;
+
+        FAIL_IF_EOF(16);
+
+        dec = (DECIMAL*)prop;
+        dec->u.scale = data[2];
+        dec->u.sign  = data[3];
+        StorageUtl_ReadDWord(data, 4, &dec->Hi32);
+        StorageUtl_ReadULargeInteger(data, 8, (ULARGE_INTEGER*)&dec->u1);
+
+        MOVE_FORWARD(16);
+        break;
+    }
+    CASE_OR_VECTOR(VT_CLSID)
+    {
+        FOR_POSSIBLE_VECTOR_OR_SINGLEP(GUID, puuid, cauuid, 16)
+        {
+            StorageUtl_ReadGUID(data, 0, vec);
+        }
+        break;
+    }
+    case VT_VECTOR|VT_VARIANT:
+    {
+        FOR_POSSIBLE_VECTOR_OR_SINGLEP(PROPVARIANT, /* dummy */ pvarVal, capropvar, 0)
+        {
+            ULONG cbDataUsed;
+            hr = wine_ReadProperty(vec, data, cbData, &cbDataUsed,
+                                   diskCodepage, runningCodepage, allocate, allocate_data);
+            if (FAILED(hr))
+            {
+                return hr;
+            }
+
+            data += cbDataUsed;
+            cbData -= cbDataUsed;
+        }
+        break;
+    }
+    default:
+        FIXME("unsupported type %d\n", prop->vt);
+        return STG_E_INVALIDPARAMETER;
+    }
+
+    /* Padding to 4 bytes, even for arrays of smaller integers */
+    /* This is important for handling VT_VECTOR|VT_VARIANT */
+    if (pcbDataUsed) {
+        ULONG paddingMissing;
+
+        paddingMissing = (4 - ((cbData_Start - cbData) % 4)) % 4;
+
+        if (cbData >= paddingMissing)
+            cbData -= paddingMissing;
+        else
+            cbData = 0;
+
+        *pcbDataUsed = cbData_Start - cbData;
+    }
+
+#undef FAIL_IF_EOF
+#undef READ_DWORD_AND_MOVE
+#undef READ_WORD_AND_MOVE
+#undef MOVE_FORWARD
+#undef FOR_POSSIBLE_VECTOR
+#undef FOR_POSSIBLE_VECTOR_OR_SINGLEP
+#undef ALLOCATE_OR_FAIL
+#undef CASE_OR_VECTOR
+
+    return hr;
+}
+
+/*
+ * Converts VT_BYREF properties to their non-ref counterparts
+ *
+ * NOTICE: This does not copy arrays, vectors etc., these are shared
+ *         by the input and output properties. Therefore, only one may be
+ *         eventually freed.
+ */
+static HRESULT ScrapeByRefOfProperty(const PROPVARIANT *in, PROPVARIANT *out)
+{
+    if (!(in->vt & VT_BYREF))
+    {
+        /* no ref, no problem */
+        memcpy(out, in, sizeof(*in));
+        return S_OK;
+    }
+
+    out->vt = in->vt & ~VT_BYREF;
+
+    /* BYREF properties all carry a pointer, which must not be NULL here */
+    /* It doesn't matter which one we check */
+    if (!in->u.pcVal)
+        return STG_E_INVALIDPARAMETER;
+
+    if (out->vt & VT_ARRAY)
+    {
+        out->u.parray = *in->u.pparray;
+        return S_OK;
+    }
+
+    switch (out->vt)
+    {
+        case VT_I1:
+            out->u.cVal = *in->u.pcVal;
+            break;
+        case VT_UI1:
+            out->u.bVal = *in->u.pbVal;
+            break;
+        case VT_I2:
+            out->u.iVal = *in->u.piVal;
+            break;
+        case VT_UI2:
+            out->u.uiVal = *in->u.puiVal;
+            break;
+        case VT_I4:
+        case VT_INT:
+            out->u.lVal = *in->u.plVal;
+            break;
+        case VT_UI4:
+        case VT_UINT:
+            out->u.ulVal = *in->u.pulVal;
+            break;
+        case VT_R4:
+            out->u.fltVal = *in->u.pfltVal;
+            break;
+        case VT_R8:
+            out->u.dblVal = *in->u.pdblVal;
+            break;
+        case VT_BOOL:
+            out->u.boolVal = *in->u.pboolVal;
+            break;
+        case VT_DECIMAL:
+            memcpy(out, in->u.pdecVal, sizeof(DECIMAL));
+            out->vt = VT_DECIMAL;
+            break;
+        case VT_ERROR:
+            out->u.scode = *in->u.pscode;
+            break;
+        case VT_CY:
+            memcpy(&out->u.cyVal, in->u.pcyVal, sizeof(CY));
+            break;
+        case VT_DATE:
+            memcpy(&out->u.date, in->u.pdate, sizeof(DATE));
+            break;
+        case VT_BSTR:
+            out->u.bstrVal = *in->u.pbstrVal;
+            break;
+        case VT_UNKNOWN:
+            out->u.punkVal = *in->u.ppunkVal;
+            break;
+        case VT_DISPATCH:
+            out->u.pdispVal = *in->u.ppdispVal;
+            break;
+        default:
+            return STG_E_INVALIDPARAMETER;
+    }
+
+    return S_OK;
+}
+
+/*             wine_SerializedPropertySize [OLE32.@]
+ *
+ * Returns the buffer size needed to serialize the given property.
+ *
+ * This handles all straightforward cases, only crazy stuff like indirect
+ * properties have to be handled by the caller itself.
+ *
+ * TODO: Array handling
+ */
+HRESULT WINAPI wine_SerializedPropertySize(const PROPVARIANT *in_prop,
+    ULONG *pcbSize, UINT diskCodepage, UINT runningCodepage)
+{
+    ULONG size = 4; /* the header */
+    HRESULT hr;
+    PROPVARIANT prop;
+
+    TRACE("(%p %p %u %u)\n", in_prop, pcbSize, diskCodepage, runningCodepage);
+
+    if (!in_prop)
+        return E_INVALIDARG;
+    if (!pcbSize)
+        return E_INVALIDARG;
+
+    *pcbSize = 0;
+
+    hr = ScrapeByRefOfProperty(in_prop, &prop);
+    if (FAILED(hr))
+        return hr;
+
+    switch (prop.vt)
+    {
+        case VT_EMPTY:
+        case VT_NULL:
+            break;
+        case VT_I1:
+        case VT_UI1:
+            size += 1;
+            break;
+        case VT_VECTOR|VT_I1:
+        case VT_VECTOR|VT_UI1:
+            size += 4; /* Vector Header */
+            size += prop.u.cac.cElems * 1;
+            break;
+        case VT_I2:
+        case VT_UI2:
+        case VT_BOOL:
+            size += 2;
+            break;
+        case VT_VECTOR|VT_I2:
+        case VT_VECTOR|VT_UI2:
+        case VT_VECTOR|VT_BOOL:
+            size += 4; /* Vector Header */
+            size += prop.u.cai.cElems * 2;
+            break;
+        case VT_UI4:
+        case VT_I4:
+        case VT_INT:
+        case VT_UINT:
+        case VT_ERROR:
+            size += 4;
+            break;
+        case VT_VECTOR|VT_UI4:
+        case VT_VECTOR|VT_I4:
+        case VT_VECTOR|VT_INT:
+        case VT_VECTOR|VT_UINT:
+        case VT_VECTOR|VT_ERROR:
+            size += 4; /* Vector Header */
+            size += prop.u.cal.cElems * 4;
+            break;
+        case VT_I8:
+        case VT_UI8:
+            size += 8;
+            break;
+        case VT_VECTOR|VT_I8:
+        case VT_VECTOR|VT_UI8:
+            size += 4; /* Vector header */
+            size += prop.u.cah.cElems * 8;
+            break;
+        case VT_LPSTR:
+        {
+            ULONG len;
+            ULONG convertedLen;
+
+            size += 4; /* DWORD byte count */
+
+            if (runningCodepage == CP_UNICODE)
+                len = lstrlenW(prop.u.pwszVal) * sizeof(WCHAR) + 2;
+            else
+                len = lstrlenA(prop.u.pszVal) + 1;
+
+            hr = ConvertCodepageSize(prop.u.pszVal,
+                                     len,
+                                     &convertedLen,
+                                     runningCodepage,
+                                     diskCodepage, FALSE);
+            if (FAILED(hr))
+                return hr;
+
+            size += convertedLen;
+
+            break;
+        }
+        case VT_VECTOR|VT_LPSTR:
+        {
+            ULONG i;
+
+            size += 4; /* Vector Header */
+            for (i = 0; i < prop.u.calpstr.cElems; ++i)
+            {
+                ULONG len;
+                ULONG convertedLen;
+
+                size += 4; /* DWORD byte count */
+
+                if (runningCodepage == CP_UNICODE)
+                    len = lstrlenW((LPCWSTR)prop.u.calpstr.pElems[i]) * sizeof(WCHAR) + sizeof(WCHAR);
+                else
+                    len = lstrlenA(prop.u.calpstr.pElems[i]) + 1;
+
+                hr = ConvertCodepageSize(prop.u.calpstr.pElems[i],
+                                         len,
+                                         &convertedLen,
+                                         runningCodepage,
+                                         diskCodepage, FALSE);
+                if (FAILED(hr))
+                    return hr;
+
+                size += convertedLen;
+
+                size += (4 - (len % 4)) % 4; /* padding */
+            }
+            break;
+        }
+        case VT_BSTR:
+        {
+            /* bstrs are possibly saved as ansi string */
+            if (diskCodepage == CP_UNICODE)
+            {
+                ULONG len = 4; /* DWORD byte count */
+                len += SysStringLen(prop.u.bstrVal) * sizeof(WCHAR) + sizeof(WCHAR);
+                size += len;
+            }
+            else
+            {
+                UINT cw = SysStringLen(prop.u.bstrVal);
+
+                size += 4; /* DWORD byte count */
+                size += WideCharToMultiByte(diskCodepage, 0, prop.u.bstrVal, cw, NULL, 0, NULL, NULL);
+                size += 1; /* terminating null */
+            }
+
+            break;
+        }
+        case VT_VECTOR|VT_BSTR:
+        {
+            ULONG i;
+            size += 4; /* Vector Header */
+            for (i = 0; i < prop.u.cabstr.cElems; ++i)
+            {
+                ULONG len = 4; /* DWORD byte count */
+
+                /* bstrs are possibly saved as ansi string */
+                if (diskCodepage == CP_UNICODE)
+                {
+                    len += SysStringLen(prop.u.cabstr.pElems[i]) * sizeof(WCHAR) + sizeof(WCHAR);
+                }
+                else
+                {
+                    UINT cw = SysStringLen(prop.u.cabstr.pElems[i]);
+
+                    len += WideCharToMultiByte(diskCodepage, 0,
+                                prop.u.cabstr.pElems[i], cw, NULL, 0, NULL, NULL);
+                    len += 1; /* terminating null */
+                }
+
+                len += (4 - (len % 4)) % 4; /* padding */
+                size += len;
+            }
+            break;
+        }
+        case VT_BLOB:
+            size += 4; /* DWORD byte count */
+            size += prop.u.blob.cbSize;
+            break;
+        case VT_LPWSTR:
+            size += 4; /* DWORD char count */
+            size += (lstrlenW(prop.u.pwszVal) + 1) * sizeof(WCHAR);
+            break;
+        case VT_VECTOR|VT_LPWSTR:
+        {
+            ULONG i;
+            size += 4; /* Vector header */
+            for (i = 0; i < prop.u.calpwstr.cElems; ++i)
+            {
+                ULONG len = 4; /* DWORD char count */
+                len += (lstrlenW(prop.u.calpwstr.pElems[i]) + 1) * sizeof(WCHAR);
+                len += (4 - (len % 4)) % 4; /* padding */
+
+                size += len;
+            }
+            break;
+        }
+        case VT_FILETIME:
+        case VT_CY:
+            size += 8; /* 64bit integer */
+            break;
+        case VT_VECTOR|VT_FILETIME:
+        case VT_VECTOR|VT_CY:
+            size += 4; /* Vector header */
+            size += prop.u.cafiletime.cElems * 8;
+            break;
+        case VT_DATE:
+        case VT_R8:
+            size += 8; /* double */
+            break;
+        case VT_VECTOR|VT_DATE:
+        case VT_VECTOR|VT_R8:
+            size += 4; /* Vector header */
+            size += prop.u.cadbl.cElems * 8;
+            break;
+        case VT_R4:
+            size += 4; /* float */
+            break;
+        case VT_VECTOR|VT_R4:
+            size += 4; /* Vector header */
+            size += prop.u.caflt.cElems * 4;
+            break;
+        case VT_DECIMAL:
+            size += 16;
+            break;
+        case VT_CLSID:
+            size += sizeof(GUID);
+            break;
+        case VT_VECTOR|VT_CLSID:
+            size += 4; /* Vector header */
+            size += prop.u.cauuid.cElems * sizeof(GUID);
+            break;
+        case VT_CF:
+            size += 4; /* size field */
+            size += prop.u.pclipdata->cbSize; /* includes tag field */
+            break;
+        case VT_VECTOR|VT_CF:
+        {
+            ULONG i;
+
+            size += 4; /* Vector header */
+            for (i = 0; i < prop.u.caclipdata.cElems; ++i)
+            {
+                ULONG len = 4; /* DWORD size field */
+                len += prop.u.caclipdata.pElems[i].cbSize; /* includes tag field */
+                len += (4 - (len % 4)) % 4; /* padding */
+
+                size += len;
+            }
+            break;
+        }
+        case VT_VECTOR|VT_VARIANT:
+        {
+            HRESULT hr;
+            ULONG vsize;
+            ULONG i;
+
+            size += 4; /* Vector header */
+            for (i = 0; i < prop.u.capropvar.cElems; ++i)
+            {
+                hr = wine_SerializedPropertySize(&prop.u.capropvar.pElems[i],
+                                                 &vsize,
+                                                 diskCodepage, runningCodepage);
+                if (FAILED(hr))
+                    return hr;
+
+                size += vsize;
+                size += (4 - (vsize % 4)) % 4;
+            }
+            break;
+        }
+        default:
+            FIXME("unsupported type %d\n", prop.vt);
+            return STG_E_INVALIDPARAMETER;
+    }
+
+    /* always pad to 4 bytes */
+    size += (4 - (size % 4)) % 4;
+    *pcbSize = size;
+
+    return S_OK;
+}
+
+/*             wine_WritePropertyToBuffer [OLE32.@]
+ *
+ * Base implementation for serializing properties.
+ * Used by:
+ *      - IPropertyStorage
+ *      - StgConvertVariantToProperty
+ *      - StgSerializeProperty [propsys.dll]
+ *
+ * This handles all straightforward cases, only crazy stuff like indirect
+ * properties have to be handled by the caller itself.
+ *
+ * TODO: Array handling
  */
-static HRESULT PropertyStorage_ReadProperty(PROPVARIANT *prop, const BYTE *data,
-    UINT codepage, void* (WINAPI *allocate)(void *this, ULONG size), void *allocate_data)
+HRESULT WINAPI wine_WritePropertyToBuffer(const PROPVARIANT *in_prop,
+                                          BYTE *buffer,
+                                          ULONG cbBufSize,
+                                          ULONG *pcbBufSizeUsed,
+                                          UINT diskCodepage,
+                                          UINT runningCodepage)
 {
     HRESULT hr = S_OK;
+    ULONG cbBufSize_Start;
+    PROPVARIANT prop;
 
-    assert(prop);
-    assert(data);
-    StorageUtl_ReadDWord(data, 0, (DWORD *)&prop->vt);
-    data += sizeof(DWORD);
-    switch (prop->vt)
+    TRACE("(%p %p %u %p %u %u)\n", in_prop, buffer, cbBufSize,
+          pcbBufSizeUsed, diskCodepage, runningCodepage);
+
+    cbBufSize_Start = cbBufSize;
+
+    if (pcbBufSizeUsed)
+        *pcbBufSizeUsed = 0;
+
+    hr = ScrapeByRefOfProperty(in_prop, &prop);
+    if (FAILED(hr))
+        return hr;
+
+#define FAIL_IF_EOF(cbytes) \
+    do { \
+        if ((ULONG)cbytes > cbBufSize) \
+            return STG_E_INVALIDPARAMETER; \
+    } while (0)
+#define MOVE_FORWARD(bytes) \
+    do { \
+        FAIL_IF_EOF(bytes); \
+        buffer += bytes; \
+        cbBufSize -= bytes; \
+    } while (0)
+#define WRITE_WORD_AND_MOVE(value) \
+    do { \
+        FAIL_IF_EOF(2); \
+        StorageUtl_WriteWord(buffer, 0, (value)); \
+        MOVE_FORWARD(2); \
+    } while (0)
+#define WRITE_DWORD_AND_MOVE(value) \
+    do { \
+        FAIL_IF_EOF(4); \
+        StorageUtl_WriteDWord(buffer, 0, (value)); \
+        MOVE_FORWARD(4); \
+    } while (0)
+#define PAD_TO_MUL_AND_MOVE(cbLen, cbMul) \
+    do { \
+        FAIL_IF_EOF(((cbMul) - ((cbLen) % (cbMul))) % (cbMul)); \
+        ZeroMemory(buffer, ((cbMul) - ((cbLen) % (cbMul))) % (cbMul)); \
+        MOVE_FORWARD(((cbMul) - ((cbLen) % (cbMul))) % (cbMul)); \
+    } while(0)
+#define FOR_POSSIBLE_VECTOR(ctype, singleMember, vectorMember, cbBlock) \
+    const ctype *vec; \
+    DWORD vecsize; \
+    if (!(prop.vt & VT_VECTOR)) \
+    { \
+        vecsize = 1; \
+        vec = &prop.u.singleMember; \
+    } \
+    else \
+    { \
+        vecsize = prop.u.vectorMember.cElems; \
+        WRITE_DWORD_AND_MOVE(vecsize); \
+        vec = prop.u.vectorMember.pElems; \
+    } \
+    for (; vecsize; --vecsize, ++vec, buffer += (cbBlock), cbBufSize -= (cbBlock)) \
+        if (gt((ULONG)(cbBlock), cbBufSize)) \
+            return STG_E_INVALIDPARAMETER; \
+        else
+
+#define FOR_POSSIBLE_VECTOR_OR_SINGLEP(ctype, singleMemberP, vectorMember, cbBlock) \
+    const ctype *vec; \
+    DWORD vecsize; \
+    if (!(prop.vt & VT_VECTOR)) \
+    { \
+        vecsize = 1; \
+        vec = prop.u.singleMemberP; \
+    } \
+    else \
+    { \
+        vecsize = prop.u.vectorMember.cElems; \
+        WRITE_DWORD_AND_MOVE(vecsize); \
+        vec = prop.u.vectorMember.pElems; \
+    } \
+    for (; vecsize; --vecsize, ++vec, buffer += (cbBlock), cbBufSize -= (cbBlock)) \
+        if (gt((ULONG)(cbBlock), cbBufSize)) \
+            return STG_E_INVALIDPARAMETER; \
+        else
+
+#define CASE_OR_VECTOR(vtype) \
+    case vtype: \
+    case VT_VECTOR|vtype:
+
+    assert(buffer);
+
+    TRACE("Serializing variant of type %d to buffer size %lu\n", prop.vt, (unsigned long)cbBufSize);
+
+    /* variant type */
+    WRITE_DWORD_AND_MOVE((DWORD)prop.vt);
+
+    switch (prop.vt)
     {
     case VT_EMPTY:
     case VT_NULL:
         break;
-    case VT_I1:
-        prop->u.cVal = *(const char *)data;
-        TRACE("Read char 0x%x ('%c')\n", prop->u.cVal, prop->u.cVal);
-        break;
-    case VT_UI1:
-        prop->u.bVal = *data;
-        TRACE("Read byte 0x%x\n", prop->u.bVal);
+    CASE_OR_VECTOR(VT_I1)
+    {
+        FOR_POSSIBLE_VECTOR(char, cVal, cac, 1)
+        {
+            *(char *)buffer = *vec;
+            TRACE("Write char 0x%x ('%c')\n", vec[0], vec[0]);
+        }
         break;
-    case VT_I2:
-        StorageUtl_ReadWord(data, 0, (WORD*)&prop->u.iVal);
-        TRACE("Read short %d\n", prop->u.iVal);
+    }
+    CASE_OR_VECTOR(VT_UI1)
+    {
+        FOR_POSSIBLE_VECTOR(BYTE, bVal, caub, 1)
+        {
+            *buffer = *vec;
+            TRACE("Write byte 0x%x\n", *vec);
+        }
         break;
-    case VT_UI2:
-        StorageUtl_ReadWord(data, 0, &prop->u.uiVal);
-        TRACE("Read ushort %d\n", prop->u.uiVal);
+    }
+    CASE_OR_VECTOR(VT_I2)
+    {
+        FOR_POSSIBLE_VECTOR(SHORT, iVal, cai, 2)
+        {
+            StorageUtl_WriteWord(buffer, 0, *(WORD*)vec);
+            TRACE("Write short %d\n", (int)*vec);
+        }
         break;
-    case VT_INT:
-    case VT_I4:
-        StorageUtl_ReadDWord(data, 0, (DWORD*)&prop->u.lVal);
-        TRACE("Read long %d\n", prop->u.lVal);
+    }
+    CASE_OR_VECTOR(VT_UI2)
+    CASE_OR_VECTOR(VT_BOOL) /* VARIANT_BOOL == USHORT */
+    {
+        FOR_POSSIBLE_VECTOR(USHORT, uiVal, caui, 2)
+        {
+            StorageUtl_WriteWord(buffer, 0, *vec);
+            TRACE("Write ushort %d\n", (int)*vec);
+        }
         break;
-    case VT_UINT:
-    case VT_UI4:
-        StorageUtl_ReadDWord(data, 0, &prop->u.ulVal);
-        TRACE("Read ulong %d\n", prop->u.ulVal);
+    }
+    CASE_OR_VECTOR(VT_INT)
+    CASE_OR_VECTOR(VT_I4)
+    CASE_OR_VECTOR(VT_ERROR) /* SCODE == LONG */
+    {
+        FOR_POSSIBLE_VECTOR(LONG, lVal, cal, 4)
+        {
+            StorageUtl_WriteDWord(buffer, 0, *(DWORD*)vec);
+            TRACE("Write long %d\n", *vec);
+        }
         break;
-    case VT_LPSTR:
+    }
+    CASE_OR_VECTOR(VT_UINT)
+    CASE_OR_VECTOR(VT_UI4)
     {
-        DWORD count;
-       
-        StorageUtl_ReadDWord(data, 0, &count);
-        if (codepage == CP_UNICODE && count % 2)
+        FOR_POSSIBLE_VECTOR(ULONG, ulVal, caul, 4)
         {
-            WARN("Unicode string has odd number of bytes\n");
-            hr = STG_E_INVALIDHEADER;
+            StorageUtl_WriteDWord(buffer, 0, *vec);
+            TRACE("Write ulong %u\n", *vec);
         }
-        else
+        break;
+    }
+    CASE_OR_VECTOR(VT_I8)
+    {
+        FOR_POSSIBLE_VECTOR(LARGE_INTEGER, hVal, cah, 8)
         {
-            prop->u.pszVal = allocate(allocate_data, count);
-            if (prop->u.pszVal)
-            {
-                memcpy(prop->u.pszVal, data + sizeof(DWORD), count);
-                /* This is stored in the code page specified in codepage.
-                 * Don't convert it, the caller will just store it as-is.
-                 */
-                if (codepage == CP_UNICODE)
-                {
-                    /* Make sure it's NULL-terminated */
-                    prop->u.pszVal[count / sizeof(WCHAR) - 1] = '\0';
-                    TRACE("Read string value %s\n",
-                     debugstr_w(prop->u.pwszVal));
-                }
-                else
-                {
-                    /* Make sure it's NULL-terminated */
-                    prop->u.pszVal[count - 1] = '\0';
-                    TRACE("Read string value %s\n", debugstr_a(prop->u.pszVal));
-                }
-            }
-            else
-                hr = STG_E_INSUFFICIENTMEMORY;
+            StorageUtl_WriteULargeInteger(buffer, 0, (ULARGE_INTEGER*)vec);
+            TRACE("Write longlong 0x%lx%08lx\n", (unsigned long)vec->HighPart, (unsigned long)vec->LowPart);
         }
         break;
     }
-    case VT_BSTR:
+    CASE_OR_VECTOR(VT_UI8)
     {
-        DWORD count, wcount;
-
-        StorageUtl_ReadDWord(data, 0, &count);
-        if (codepage == CP_UNICODE && count % 2)
+        FOR_POSSIBLE_VECTOR(ULARGE_INTEGER, uhVal, cauh, 8)
         {
-            WARN("Unicode string has odd number of bytes\n");
-            hr = STG_E_INVALIDHEADER;
+            StorageUtl_WriteULargeInteger(buffer, 0, vec);
+            TRACE("Write ulonglong 0x%lx%08lx\n", (unsigned long)vec->HighPart, (unsigned long)vec->LowPart);
         }
-        else
+        break;
+    }
+    CASE_OR_VECTOR(VT_LPSTR)
+    {
+        FOR_POSSIBLE_VECTOR(LPSTR, pszVal, calpstr, 0)
         {
-            if (codepage == CP_UNICODE)
-                wcount = count / 2;
+            DWORD len;
+            DWORD convertedLen;
+
+            if (runningCodepage == CP_UNICODE)
+                len = lstrlenW((LPCWSTR)*vec) * sizeof(WCHAR) + 2;
             else
-                wcount = MultiByteToWideChar(codepage, 0, (LPCSTR)(data + sizeof(DWORD)), count, NULL, 0);
+                len = lstrlenA(*vec) + 1;
+
+            hr = ConvertCodepageSize(*vec, len, &convertedLen,
+                                     runningCodepage, diskCodepage, FALSE);
+            if (FAILED(hr))
+                return hr;
+
+            WRITE_DWORD_AND_MOVE(convertedLen);
+            FAIL_IF_EOF(convertedLen);
 
-            prop->u.bstrVal = SysAllocStringLen(NULL, wcount); /* FIXME: use allocator? */
+            hr = ConvertCodepage(*vec, len,
+                                 (char*)buffer, convertedLen,
+                                 runningCodepage, diskCodepage, FALSE);
+            if (FAILED(hr))
+                return hr;
 
-            if (prop->u.bstrVal)
+            if (diskCodepage == CP_UNICODE)
             {
-                if (codepage == CP_UNICODE)
-                    memcpy(prop->u.bstrVal, data + sizeof(DWORD), count);
-                else
-                    MultiByteToWideChar(codepage, 0, (LPCSTR)(data + sizeof(DWORD)), count, prop->u.bstrVal, wcount);
+                PropertyStorage_ByteSwapString(buffer, convertedLen/2);
+                TRACE("Write lpstr %s\n", debugstr_w((LPCWSTR)buffer));
+            }
+            else
+            {
+                TRACE("Write lpstr %s\n", debugstr_a((char*)buffer));
+            }
+
+            MOVE_FORWARD(convertedLen);
+            PAD_TO_MUL_AND_MOVE(convertedLen, 4);
+        }
+        break;
+    }
+    CASE_OR_VECTOR(VT_BSTR)
+    {
+        FOR_POSSIBLE_VECTOR(BSTR, bstrVal, cabstr, 0)
+        {
+            /* BSTRs are saved in the codepage */
+            DWORD len = SysStringLen(*vec) + 1;
+
+            if (diskCodepage == CP_UNICODE)
+            {
+                WRITE_DWORD_AND_MOVE(len * sizeof(WCHAR));
 
-                prop->u.bstrVal[wcount - 1] = '\0';
-                TRACE("Read string value %s\n", debugstr_w(prop->u.bstrVal));
+                FAIL_IF_EOF(len * sizeof(WCHAR));
+                memcpy(buffer, *vec, len * sizeof(WCHAR));
+                MOVE_FORWARD(len * sizeof(WCHAR));
+                PAD_TO_MUL_AND_MOVE(len * sizeof(WCHAR), 4);
             }
             else
-                hr = STG_E_INSUFFICIENTMEMORY;
+            {
+                DWORD narrowLen;
+
+                narrowLen = WideCharToMultiByte(diskCodepage, 0, *vec, len, NULL, 0, NULL, NULL);
+
+                WRITE_DWORD_AND_MOVE(narrowLen);
+
+                FAIL_IF_EOF(narrowLen);
+                WideCharToMultiByte(diskCodepage, 0, *vec, len, (char*)buffer, narrowLen, NULL, NULL);
+                MOVE_FORWARD(narrowLen);
+                PAD_TO_MUL_AND_MOVE(narrowLen, 4);
+            }
+            TRACE("Write BSTR %s\n", debugstr_w(*vec));
         }
         break;
     }
     case VT_BLOB:
     {
-        DWORD count;
+        WRITE_DWORD_AND_MOVE(prop.u.blob.cbSize);
+        FAIL_IF_EOF(prop.u.blob.cbSize);
+        memcpy(buffer, prop.u.blob.pBlobData, prop.u.blob.cbSize);
+        MOVE_FORWARD(prop.u.blob.cbSize);
+        PAD_TO_MUL_AND_MOVE(prop.u.blob.cbSize, 4);
 
-        StorageUtl_ReadDWord(data, 0, &count);
-        prop->u.blob.cbSize = count;
-        prop->u.blob.pBlobData = allocate(allocate_data, count);
-        if (prop->u.blob.pBlobData)
+        TRACE("Write blob value of size %d\n", prop.u.blob.cbSize);
+        break;
+    }
+    CASE_OR_VECTOR(VT_LPWSTR)
+    {
+        FOR_POSSIBLE_VECTOR(LPWSTR, pwszVal, calpwstr, 0)
         {
-            memcpy(prop->u.blob.pBlobData, data + sizeof(DWORD), count);
-            TRACE("Read blob value of size %d\n", count);
+            DWORD len;
+
+            len = lstrlenW((LPCWSTR)*vec) + 1;
+            WRITE_DWORD_AND_MOVE(len);
+
+            FAIL_IF_EOF(len * sizeof(WCHAR));
+            memcpy(buffer, *vec, len * sizeof(WCHAR));
+            MOVE_FORWARD(len * sizeof(WCHAR));
+            PAD_TO_MUL_AND_MOVE(len * sizeof(WCHAR), 4);
+            TRACE("Write lpwstr %s\n", debugstr_w(*vec));
         }
-        else
-            hr = STG_E_INSUFFICIENTMEMORY;
         break;
     }
-    case VT_LPWSTR:
+    CASE_OR_VECTOR(VT_FILETIME)
     {
-        DWORD count;
+        ULARGE_INTEGER tmp;
+        FOR_POSSIBLE_VECTOR(FILETIME, filetime, cafiletime, 8)
+        {
+            tmp.LowPart  = vec->dwLowDateTime;
+            tmp.HighPart = vec->dwHighDateTime;
 
-        StorageUtl_ReadDWord(data, 0, &count);
-        prop->u.pwszVal = allocate(allocate_data, count * sizeof(WCHAR));
-        if (prop->u.pwszVal)
+            StorageUtl_WriteULargeInteger(buffer, 0, &tmp);
+        }
+        break;
+    }
+    CASE_OR_VECTOR(VT_CY)
+    {
+        ULARGE_INTEGER tmp;
+        FOR_POSSIBLE_VECTOR(CY, cyVal, cacy, 8)
         {
-            memcpy(prop->u.pwszVal, data + sizeof(DWORD),
-             count * sizeof(WCHAR));
-            /* make sure string is NULL-terminated */
-            prop->u.pwszVal[count - 1] = '\0';
-            PropertyStorage_ByteSwapString(prop->u.pwszVal, count);
-            TRACE("Read string value %s\n", debugstr_w(prop->u.pwszVal));
+            *((LONGLONG*)&tmp.QuadPart) = vec->int64;
+            StorageUtl_WriteULargeInteger(buffer, 0, &tmp);
         }
-        else
-            hr = STG_E_INSUFFICIENTMEMORY;
         break;
     }
-    case VT_FILETIME:
-        StorageUtl_ReadULargeInteger(data, 0,
-         (ULARGE_INTEGER *)&prop->u.filetime);
+    CASE_OR_VECTOR(VT_CF)
+    {
+        FOR_POSSIBLE_VECTOR_OR_SINGLEP(CLIPDATA, pclipdata, caclipdata, 0)
+        {
+            WRITE_DWORD_AND_MOVE(vec->cbSize);
+            WRITE_DWORD_AND_MOVE(vec->ulClipFmt);
+
+            FAIL_IF_EOF(vec->cbSize - 4);
+            memcpy(buffer, vec->pClipData, vec->cbSize - 4);
+            MOVE_FORWARD(vec->cbSize - 4);
+
+            PAD_TO_MUL_AND_MOVE(vec->cbSize, 4);
+            TRACE("Written CLPIDATA with size %lu\n", (unsigned long)vec->cbSize);
+        }
         break;
-    case VT_CF:
+    }
+    CASE_OR_VECTOR(VT_DATE)
+    CASE_OR_VECTOR(VT_R8)
+    {
+        FOR_POSSIBLE_VECTOR(double, dblVal, cadbl, sizeof(double))
         {
-            DWORD len = 0, tag = 0;
+            memcpy(buffer, vec, sizeof(double));
+        }
+        break;
+    }
+    CASE_OR_VECTOR(VT_R4)
+    {
+        FOR_POSSIBLE_VECTOR(float, fltVal, caflt, sizeof(float))
+        {
+            memcpy(buffer, vec, sizeof(double));
+        }
+        break;
+    }
+    case VT_DECIMAL:
+    {
+        DECIMAL *dec;
+
+        FAIL_IF_EOF(16);
+
+        dec = (DECIMAL*)∝
+        buffer[0] = 0;
+        buffer[1] = 0;
+        buffer[2] = dec->u.scale;
+        buffer[3] = dec->u.sign;
+        StorageUtl_WriteDWord(buffer, 4, dec->Hi32);
+        StorageUtl_WriteULargeInteger(buffer, 8, (ULARGE_INTEGER*)&dec->u1);
 
-            StorageUtl_ReadDWord(data, 0, &len);
-            StorageUtl_ReadDWord(data, 4, &tag);
-            if (len > 8)
+        MOVE_FORWARD(16);
+        break;
+    }
+    CASE_OR_VECTOR(VT_CLSID)
+    {
+        FOR_POSSIBLE_VECTOR_OR_SINGLEP(GUID, puuid, cauuid, 16)
+        {
+            StorageUtl_WriteGUID(buffer, 0, vec);
+        }
+        break;
+    }
+    case VT_VECTOR|VT_VARIANT:
+    {
+        FOR_POSSIBLE_VECTOR_OR_SINGLEP(PROPVARIANT, /* dummy */pvarVal, capropvar, 0)
+        {
+            ULONG cbBufferUsed;
+            hr = wine_WritePropertyToBuffer(vec, buffer, cbBufSize,
+                                            &cbBufferUsed, diskCodepage, runningCodepage);
+            if (FAILED(hr))
             {
-                len -= 8;
-                prop->u.pclipdata = allocate(allocate_data, sizeof (CLIPDATA));
-                prop->u.pclipdata->cbSize = len;
-                prop->u.pclipdata->ulClipFmt = tag;
-                prop->u.pclipdata->pClipData = allocate(allocate_data, len - sizeof(prop->u.pclipdata->ulClipFmt));
-                memcpy(prop->u.pclipdata->pClipData, data+8, len - sizeof(prop->u.pclipdata->ulClipFmt));
+                return hr;
             }
-            else
-                hr = STG_E_INVALIDPARAMETER;
+
+            buffer += cbBufferUsed;
+            cbBufSize -= cbBufferUsed;
         }
         break;
+    }
     default:
-        FIXME("unsupported type %d\n", prop->vt);
-        hr = STG_E_INVALIDPARAMETER;
+        FIXME("unsupported type %d\n", prop.vt);
+        return STG_E_INVALIDPARAMETER;
     }
+
+    /* Pad to 4 bytes. Always. */
+    PAD_TO_MUL_AND_MOVE(cbBufSize_Start - cbBufSize, 4);
+
+    if (pcbBufSizeUsed)
+        *pcbBufSizeUsed = cbBufSize_Start - cbBufSize;
+
+#undef FAIL_IF_EOF
+#undef WRITE_DWORD_AND_MOVE
+#undef WRITE_WORD_AND_MOVE
+#undef MOVE_FORWARD
+#undef FOR_POSSIBLE_VECTOR
+#undef FOR_POSSIBLE_VECTOR_OR_SINGLEP
+#undef PAD_TO_MUL_AND_MOVE
+#undef CASE_OR_VECTOR
+
     return hr;
 }
 
@@ -1439,11 +2747,14 @@ static HRESULT PropertyStorage_ReadFromStream(PropertyStorage_impl *This)
             else
             {
                 PROPVARIANT prop;
+                HRESULT hr;
 
                 PropVariantInit(&prop);
-                if (SUCCEEDED(PropertyStorage_ReadProperty(&prop,
-                 buf + idOffset->dwOffset - sizeof(PROPERTYSECTIONHEADER),
-                 This->codePage, Allocate_CoTaskMemAlloc, NULL)))
+                hr = wine_ReadProperty(&prop,
+                        buf + idOffset->dwOffset - sizeof(PROPERTYSECTIONHEADER),
+                        sectionHdr.cbSection - idOffset->dwOffset,
+                        NULL, This->codePage, This->codePage, NULL, NULL);
+                if (SUCCEEDED(hr))
                 {
                     TRACE("Read property with ID 0x%08x, type %d\n",
                      idOffset->propid, prop.vt);
@@ -1468,6 +2779,11 @@ static HRESULT PropertyStorage_ReadFromStream(PropertyStorage_impl *This)
                          idOffset->propid, &prop, This->codePage);
                     }
                 }
+                else
+                {
+                    WARN("Failed to read property with ID 0x%08x, hr=0x%08x\n",
+                         idOffset->propid, hr);
+                }
                 PropVariantClear(&prop);
             }
         }
@@ -1689,7 +3005,9 @@ static HRESULT PropertyStorage_WritePropertyToStream(PropertyStorage_impl *This,
     LARGE_INTEGER seek;
     PROPERTYIDOFFSET propIdOffset;
     ULONG count;
-    DWORD dwType, bytesWritten;
+    DWORD bytesWritten;
+    BYTE *propBuffer = NULL;
+    ULONG bufferUsed = 0;
 
     assert(var);
     assert(sectionOffset);
@@ -1711,112 +3029,33 @@ static HRESULT PropertyStorage_WritePropertyToStream(PropertyStorage_impl *This,
     hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
     if (FAILED(hr))
         goto end;
-    StorageUtl_WriteDWord((LPBYTE)&dwType, 0, var->vt);
-    hr = IStream_Write(This->stm, &dwType, sizeof(dwType), &count);
-    if (FAILED(hr))
-        goto end;
-    *sectionOffset += sizeof(dwType);
 
-    switch (var->vt)
-    {
-    case VT_EMPTY:
-    case VT_NULL:
-        bytesWritten = 0;
-        break;
-    case VT_I1:
-    case VT_UI1:
-        hr = IStream_Write(This->stm, &var->u.cVal, sizeof(var->u.cVal),
-         &count);
-        bytesWritten = count;
-        break;
-    case VT_I2:
-    case VT_UI2:
+    hr = wine_SerializedPropertySize(var, &count, This->codePage, This->codePage);
+    if (FAILED(hr))
     {
-        WORD wTemp;
-
-        StorageUtl_WriteWord((LPBYTE)&wTemp, 0, var->u.iVal);
-        hr = IStream_Write(This->stm, &wTemp, sizeof(wTemp), &count);
-        bytesWritten = count;
-        break;
+        WARN("Property space could not be calculated: hr=%08x\n", hr);
+        goto end;
     }
-    case VT_I4:
-    case VT_UI4:
-    {
-        DWORD dwTemp;
 
-        StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, var->u.lVal);
-        hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
-        bytesWritten = count;
-        break;
-    }
-    case VT_LPSTR:
+    propBuffer = CoTaskMemAlloc(count);
+    if (!propBuffer)
     {
-        DWORD len, dwTemp;
-
-        if (This->codePage == CP_UNICODE)
-            len = (lstrlenW(var->u.pwszVal) + 1) * sizeof(WCHAR);
-        else
-            len = lstrlenA(var->u.pszVal) + 1;
-        StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, len);
-        hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
-        if (FAILED(hr))
-            goto end;
-        hr = IStream_Write(This->stm, var->u.pszVal, len, &count);
-        bytesWritten = count + sizeof(DWORD);
-        break;
+        hr = STG_E_INSUFFICIENTMEMORY;
+        goto end;
     }
-    case VT_LPWSTR:
-    {
-        DWORD len = lstrlenW(var->u.pwszVal) + 1, dwTemp;
 
-        StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, len);
-        hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
-        if (FAILED(hr))
-            goto end;
-        hr = IStream_Write(This->stm, var->u.pwszVal, len * sizeof(WCHAR),
-         &count);
-        bytesWritten = count + sizeof(DWORD);
-        break;
-    }
-    case VT_FILETIME:
+    hr = wine_WritePropertyToBuffer(var, propBuffer, count, &bufferUsed, This->codePage, This->codePage);
+    if (SUCCEEDED(hr))
     {
-        FILETIME temp;
+        if (bufferUsed != count)
+            WARN("Property buffer calculation was off by %d bytes\n", (int)(count - bufferUsed));
 
-        StorageUtl_WriteULargeInteger((BYTE *)&temp, 0,
-         (const ULARGE_INTEGER *)&var->u.filetime);
-        hr = IStream_Write(This->stm, &temp, sizeof(FILETIME), &count);
+        hr = IStream_Write(This->stm, propBuffer, bufferUsed, &count);
         bytesWritten = count;
-        break;
-    }
-    case VT_CF:
-    {
-        DWORD cf_hdr[2], len;
-
-        len = var->u.pclipdata->cbSize;
-        StorageUtl_WriteDWord((LPBYTE)&cf_hdr[0], 0, len + 8);
-        StorageUtl_WriteDWord((LPBYTE)&cf_hdr[1], 0, var->u.pclipdata->ulClipFmt);
-        hr = IStream_Write(This->stm, cf_hdr, sizeof(cf_hdr), &count);
-        if (FAILED(hr))
-            goto end;
-        hr = IStream_Write(This->stm, var->u.pclipdata->pClipData,
-                           len - sizeof(var->u.pclipdata->ulClipFmt), &count);
-        if (FAILED(hr))
-            goto end;
-        bytesWritten = count + sizeof cf_hdr;
-        break;
     }
-    case VT_CLSID:
+    else
     {
-        CLSID temp;
-
-        StorageUtl_WriteGUID((BYTE *)&temp, 0, var->u.puuid);
-        hr = IStream_Write(This->stm, &temp, sizeof(temp), &count);
-        bytesWritten = count;
-        break;
-    }
-    default:
-        FIXME("unsupported type: %d\n", var->vt);
-        return STG_E_INVALIDPARAMETER;
+        WARN("Property could not be written: hr=%08x\n", hr);
     }
 
     if (SUCCEEDED(hr))
@@ -2748,11 +3987,14 @@ BOOLEAN WINAPI StgConvertPropertyToVariant(const SERIALIZEDPROPERTYVALUE* prop,
 {
     HRESULT hr;
 
-    hr = PropertyStorage_ReadProperty(pvar, (const BYTE*)prop, CodePage, Allocate_PMemoryAllocator, pma);
+    hr = wine_ReadProperty(pvar, (const BYTE*)prop, /*FIXME*/0xFFFFFFFF, NULL,
+                           CodePage, CP_ACP, Allocate_PMemoryAllocator, pma);
 
     if (FAILED(hr))
     {
         FIXME("should raise C++ exception on failure\n");
+        /* FIXME: can't clear partially initialized PROPVARIANT because
+         *        PropVariantClear can't deal with a custom Free function */
         PropVariantInit(pvar);
     }
 
@@ -2763,7 +4005,32 @@ SERIALIZEDPROPERTYVALUE* WINAPI StgConvertVariantToProperty(const PROPVARIANT *p
     USHORT CodePage, SERIALIZEDPROPERTYVALUE *pprop, ULONG *pcb, PROPID pid,
     BOOLEAN fReserved, ULONG *pcIndirect)
 {
-    FIXME("%p,%d,%p,%p,%d,%d,%p\n", pvar, CodePage, pprop, pcb, pid, fReserved, pcIndirect);
+    HRESULT hr;
+    ULONG buffer_size;
+
+    TRACE("%p,%d,%p,%p,%d,%d,%p\n", pvar, CodePage, pprop, pcb, pid, fReserved, pcIndirect);
+
+    if (pprop && pcb)
+        buffer_size = *pcb;
+    else
+        buffer_size = (ULONG)-1;
+
+    /* TODO: indirect properties and other crazyness */
+    if (pprop)
+    {
+        hr = wine_WritePropertyToBuffer(pvar, (BYTE*)pprop, buffer_size, pcb, CodePage, CP_ACP);
+    }
+    else
+    {
+        hr = wine_SerializedPropertySize(pvar, pcb, CodePage, CP_ACP);
+    }
+
+    if (FAILED(hr))
+    {
+        FIXME("should raise exception on failure\n");
+        *pcb = 0;
+        return NULL;
+    }
 
-    return NULL;
+    return pprop;
 }
diff --git a/dlls/ole32/tests/propvariant.c b/dlls/ole32/tests/propvariant.c
index c45ca2a..d9b4320 100644
--- a/dlls/ole32/tests/propvariant.c
+++ b/dlls/ole32/tests/propvariant.c
@@ -438,6 +438,23 @@ static const char serialized_bstr_mb[] = {
     0,0,0,0
 };
 
+static const char serialized_ascii_str_wc[] = {
+    30,0, /* VT_LPSTR */
+    0,0, /* padding */
+    10,0,0,0, /* size */
+    't',0,'e',0,
+    's',0,'t',0,
+    0,0,0,0
+};
+
+static const char serialized_ascii_str_mb[] = {
+    30,0, /* VT_LPSTR */
+    0,0, /* padding */
+    5,0,0,0, /* size */
+    't','e','s','t',
+    0,0,0,0
+};
+
 static void test_propertytovariant(void)
 {
     HANDLE hole32;
@@ -447,6 +464,7 @@ static void test_propertytovariant(void)
     struct _PMemoryAllocator_vtable vtable;
     BOOLEAN ret;
     static const WCHAR test_string[] = {'t','e','s','t',0};
+    static const char test_astring[] = "test";
 
     hole32 = GetModuleHandleA("ole32");
 
@@ -495,6 +513,22 @@ static void test_propertytovariant(void)
     ok(propvar.vt == VT_BSTR, "unexpected vt %x\n", propvar.vt);
     ok(!lstrcmpW(U(propvar).bstrVal, test_string), "unexpected string value\n");
     PropVariantClear(&propvar);
+
+    ret = pStgConvertPropertyToVariant((SERIALIZEDPROPERTYVALUE*)serialized_ascii_str_wc,
+        CP_WINUNICODE, &propvar, &allocator);
+
+    ok(ret == 0, "StgConvertPropertyToVariant returned %i\n", ret);
+    ok(propvar.vt == VT_LPSTR, "unexpected vt %x\n", propvar.vt);
+    ok(!lstrcmpA(U(propvar).pszVal, test_astring), "unexpected string value '%s'\n", U(propvar).pszVal);
+    PropVariantClear(&propvar);
+
+    ret = pStgConvertPropertyToVariant((SERIALIZEDPROPERTYVALUE*)serialized_ascii_str_mb,
+        CP_UTF8, &propvar, &allocator);
+
+    ok(ret == 0, "StgConvertPropertyToVariant returned %i\n", ret);
+    ok(propvar.vt == VT_LPSTR, "unexpected vt %x\n", propvar.vt);
+    ok(!lstrcmpA(U(propvar).pszVal, test_astring), "unexpected string value '%s'\n", U(propvar).pszVal);
+    PropVariantClear(&propvar);
 }
 
 static void test_varianttoproperty(void)
@@ -506,6 +540,7 @@ static void test_varianttoproperty(void)
         const PROPVARIANT*,USHORT,SERIALIZEDPROPERTYVALUE*,ULONG*,PROPID,BOOLEAN,ULONG*);
     ULONG len;
     static const WCHAR test_string[] = {'t','e','s','t',0};
+    static char test_astring[] = "test";
     BSTR test_string_bstr;
 
     hole32 = GetModuleHandleA("ole32");
@@ -530,7 +565,7 @@ static void test_varianttoproperty(void)
         0, FALSE, 0);
 
     ok(propvalue == NULL, "got nonnull propvalue\n");
-    todo_wine ok(len == 8, "unexpected length %d\n", len);
+    ok(len == 8, "unexpected length %d\n", len);
 
     if (len == 0xdeadbeef)
     {
@@ -588,6 +623,24 @@ static void test_varianttoproperty(void)
 
     SysFreeString(test_string_bstr);
 
+    propvar.vt = VT_LPSTR;
+    U(propvar).pszVal = test_astring;
+    len = 20;
+    propvalue = pStgConvertVariantToProperty(&propvar, CP_WINUNICODE, own_propvalue, &len,
+        0, FALSE, 0);
+
+    ok(propvalue == own_propvalue, "unexpected propvalue %p\n", propvalue);
+    ok(len == 20, "unexpected length %d\n", len);
+    ok(!memcmp(propvalue, serialized_ascii_str_wc, 20), "got wrong data\n");
+
+    len = 20;
+    propvalue = pStgConvertVariantToProperty(&propvar, CP_UTF8, own_propvalue, &len,
+        0, FALSE, 0);
+
+    ok(propvalue == own_propvalue, "unexpected propvalue %p\n", propvalue);
+    ok(len == 16, "unexpected length %d\n", len);
+    ok(!memcmp(propvalue, serialized_ascii_str_mb, 16), "got wrong data\n");
+
     HeapFree(GetProcessHeap(), 0, own_propvalue);
 }
 
diff --git a/dlls/propsys/propsys.spec b/dlls/propsys/propsys.spec
index 92b6d7c..50bdf6e 100644
--- a/dlls/propsys/propsys.spec
+++ b/dlls/propsys/propsys.spec
@@ -27,24 +27,24 @@
 @ stdcall -private DllGetClassObject(ptr ptr ptr)
 @ stdcall -private DllRegisterServer()
 @ stdcall -private DllUnregisterServer()
-@ stub InitPropVariantFromBooleanVector
+@ stdcall InitPropVariantFromBooleanVector(ptr long ptr)
 @ stdcall InitPropVariantFromBuffer(ptr long ptr)
-@ stub InitPropVariantFromCLSID
-@ stub InitPropVariantFromDoubleVector
-@ stub InitPropVariantFromFileTime
-@ stub InitPropVariantFromFileTimeVector
+@ stdcall InitPropVariantFromCLSID(ptr ptr)
+@ stdcall InitPropVariantFromDoubleVector(ptr long ptr)
+@ stdcall InitPropVariantFromFileTime(ptr ptr)
+@ stdcall InitPropVariantFromFileTimeVector(ptr long ptr)
 @ stdcall InitPropVariantFromGUIDAsString(ptr ptr)
-@ stub InitPropVariantFromInt16Vector
-@ stub InitPropVariantFromInt32Vector
-@ stub InitPropVariantFromInt64Vector
+@ stdcall InitPropVariantFromInt16Vector(ptr long ptr)
+@ stdcall InitPropVariantFromInt32Vector(ptr long ptr)
+@ stdcall InitPropVariantFromInt64Vector(ptr long ptr)
 @ stub InitPropVariantFromPropVariantVectorElem
 @ stub InitPropVariantFromResource
 @ stub InitPropVariantFromStrRet
 @ stub InitPropVariantFromStringAsVector
-@ stub InitPropVariantFromStringVector
-@ stub InitPropVariantFromUInt16Vector
-@ stub InitPropVariantFromUInt32Vector
-@ stub InitPropVariantFromUInt64Vector
+@ stdcall InitPropVariantFromStringVector(ptr long ptr)
+@ stdcall InitPropVariantFromUInt16Vector(ptr long ptr)
+@ stdcall InitPropVariantFromUInt32Vector(ptr long ptr)
+@ stdcall InitPropVariantFromUInt64Vector(ptr long ptr)
 @ stub InitPropVariantVectorFromPropVariant
 @ stub InitVariantFromBooleanArray
 @ stdcall InitVariantFromBuffer(ptr long ptr)
@@ -151,8 +151,8 @@
 @ stub PropVariantToUInt64VectorAlloc
 @ stub PropVariantToUInt64WithDefault
 @ stub PropVariantToVariant
-@ stub StgDeserializePropVariant
-@ stub StgSerializePropVariant
+@ stdcall StgDeserializePropVariant(ptr long ptr)
+@ stdcall StgSerializePropVariant(ptr ptr ptr)
 @ stub VariantCompare
 @ stub VariantGetBooleanElem
 @ stub VariantGetDoubleElem
diff --git a/dlls/propsys/propvar.c b/dlls/propsys/propvar.c
index fd22c9d..e7ba3f3 100644
--- a/dlls/propsys/propvar.c
+++ b/dlls/propsys/propvar.c
@@ -30,6 +30,8 @@
 #include "winuser.h"
 #include "shlobj.h"
 #include "propvarutil.h"
+#include "mimeole.h"
+#include "oleauto.h"
 
 #include "wine/debug.h"
 #include "wine/unicode.h"
@@ -346,6 +348,20 @@ HRESULT WINAPI InitVariantFromGUIDAsString(REFGUID guid, VARIANT *pvar)
     return S_OK;
 }
 
+HRESULT WINAPI InitPropVariantFromCLSID(REFCLSID clsid, PROPVARIANT *ppropvar)
+{
+    TRACE("(%p %p)\n", clsid, ppropvar);
+
+    ppropvar->u.puuid = CoTaskMemAlloc(sizeof(CLSID));
+    if (!ppropvar->u.puuid)
+        return E_OUTOFMEMORY;
+
+    *ppropvar->u.puuid = *clsid;
+    ppropvar->vt = VT_CLSID;
+
+    return S_OK;
+}
+
 HRESULT WINAPI InitPropVariantFromBuffer(const VOID *pv, UINT cb, PROPVARIANT *ppropvar)
 {
     TRACE("(%p %u %p)\n", pv, cb, ppropvar);
@@ -391,6 +407,95 @@ HRESULT WINAPI InitVariantFromBuffer(const VOID *pv, UINT cb, VARIANT *pvar)
     return S_OK;
 }
 
+#define DEFINE_INIT_FROM_VECTOR(ctype, functype, vtype, usuffix) \
+    HRESULT WINAPI InitPropVariantFrom##functype##Vector(const ctype *pval, ULONG cEl, PROPVARIANT *ppropvar) \
+    { \
+        TRACE("(%p %u %p)\n", pval, cEl, ppropvar); \
+        \
+        ppropvar->u.ca##usuffix.pElems = CoTaskMemAlloc(cEl * sizeof(ctype)); \
+        if(!ppropvar->u.ca##usuffix.pElems) \
+            return E_OUTOFMEMORY; \
+        \
+        ppropvar->vt = VT_VECTOR|VT_##vtype; \
+        ppropvar->u.ca##usuffix.cElems = cEl; \
+        memcpy(ppropvar->u.ca##usuffix.pElems, pval, cEl * sizeof(ctype)); \
+        return S_OK; \
+    }
+
+DEFINE_INIT_FROM_VECTOR(SHORT, Int16, I2, i)
+DEFINE_INIT_FROM_VECTOR(USHORT, UInt16, UI2, ui)
+DEFINE_INIT_FROM_VECTOR(LONG, Int32, I4, l)
+DEFINE_INIT_FROM_VECTOR(ULONG, UInt32, UI4, ul)
+DEFINE_INIT_FROM_VECTOR(LONGLONG, Int64, I8, h)
+DEFINE_INIT_FROM_VECTOR(ULONGLONG, UInt64, UI8, uh)
+DEFINE_INIT_FROM_VECTOR(double, Double, R8, dbl)
+DEFINE_INIT_FROM_VECTOR(FILETIME, FileTime, FILETIME, filetime)
+
+#undef DEFINE_INIT_FROM_VECTOR
+
+HRESULT WINAPI InitPropVariantFromBooleanVector(const BOOL *pbool, ULONG cEl, PROPVARIANT *ppropvar)
+{
+    ULONG i;
+    TRACE("(%p %u %p)\n", pbool, cEl, ppropvar);
+
+    ppropvar->u.cabool.pElems = CoTaskMemAlloc(cEl * sizeof(VARIANT_BOOL));
+    if (!ppropvar->u.cabool.pElems)
+        return E_OUTOFMEMORY;
+
+    ppropvar->vt = VT_VECTOR|VT_BOOL;
+    ppropvar->u.cabool.cElems = cEl;
+
+    for (i = 0; i < cEl; ++i)
+    {
+        ppropvar->u.cabool.pElems[i] = pbool[i] ? VARIANT_TRUE : VARIANT_FALSE;
+    }
+
+    return S_OK;
+}
+
+HRESULT WINAPI InitPropVariantFromFileTime(const FILETIME *pft, PROPVARIANT *pprop)
+{
+    TRACE("(%p %p)\n", pft, pprop);
+
+    pprop->vt = VT_FILETIME;
+    pprop->u.filetime = *pft;
+
+    return S_OK;
+}
+
+HRESULT WINAPI InitPropVariantFromStringVector(const WCHAR **pstr, ULONG c, PROPVARIANT *pprop)
+{
+    ULONG i;
+    TRACE("(%p %u %p)\n", pstr, c, pprop);
+
+    pprop->u.calpwstr.pElems = CoTaskMemAlloc(c * sizeof(WCHAR *));
+    if (!pprop->u.calpwstr.pElems)
+        return E_OUTOFMEMORY;
+
+    ZeroMemory(pprop->u.calpwstr.pElems, c * sizeof(WCHAR *));
+
+    pprop->vt = VT_VECTOR|VT_LPWSTR;
+    pprop->u.calpwstr.cElems = c;
+
+    for (i = 0; i < c; ++i)
+    {
+        ULONG len;
+
+        len = lstrlenW(pstr[i]) + 1;
+
+        pprop->u.calpwstr.pElems[i] = CoTaskMemAlloc(len * sizeof(WCHAR));
+        if (!pprop->u.calpwstr.pElems[i])
+        {
+            PropVariantClear(pprop); /* release already allocated memory */
+            return E_OUTOFMEMORY;
+        }
+
+        memcpy(pprop->u.calpwstr.pElems[i], pstr[i], len * sizeof(WCHAR));
+    }
+
+    return S_OK;
+}
+
 static inline DWORD PROPVAR_HexToNum(const WCHAR *hex)
 {
     DWORD ret;
@@ -511,7 +616,17 @@ static BOOL isemptyornull(const PROPVARIANT *propvar)
         }
         return i == propvar->u.parray->cDims;
     }
-    /* FIXME: vectors, byrefs, errors? */
+    if ((propvar->vt & VT_VECTOR) == VT_VECTOR)
+    {
+        /* all vector structs are equal */
+        return !propvar->u.cac.cElems || !propvar->u.cac.pElems;
+    }
+    if ((propvar->vt & VT_BYREF) == VT_BYREF)
+    {
+        /* all pointers are equal */
+        return !propvar->u.punkVal;
+    }
+    /* FIXME: errors? */
     return FALSE;
 }
 
@@ -555,36 +670,321 @@ INT WINAPI PropVariantCompareEx(REFPROPVARIANT propvar1, REFPROPVARIANT propvar2
     else \
         res = 0; \
     } while (0)
+#define CMP_INT_VECTOR(var) do { \
+    if (propvar1->u.var.cElems > propvar2_converted->u.var.cElems) \
+    { res = 1; } \
+    else if (propvar1->u.var.cElems < propvar2_converted->u.var.cElems) \
+    { res = -1; } \
+    else \
+    { \
+        ULONG i; \
+        res = 0; \
+        for (i = 0; i < propvar1->u.var.cElems; ++i) \
+        { \
+            if (propvar1->u.var.pElems[i] > propvar2_converted->u.var.pElems[i]) \
+            { \
+                res = 1; \
+                break; \
+            } \
+            else if (propvar1->u.var.pElems[i] < propvar2_converted->u.var.pElems[i]) \
+            { \
+                res = -1; \
+                break; \
+            } \
+        } \
+    } \
+    }  while(0)
+
+#define INT_CASE(vtype, svar, vecvar) \
+    case vtype: \
+        CMP_INT_VALUE(svar); \
+        break; \
+    case VT_VECTOR|vtype: \
+        CMP_INT_VECTOR(vecvar); \
+        break; \
 
     switch (propvar1->vt)
     {
-    case VT_I1:
-        CMP_INT_VALUE(cVal);
-        break;
-    case VT_UI1:
-        CMP_INT_VALUE(bVal);
-        break;
-    case VT_I2:
-        CMP_INT_VALUE(iVal);
-        break;
-    case VT_UI2:
-        CMP_INT_VALUE(uiVal);
-        break;
-    case VT_I4:
+    INT_CASE(VT_I1, cVal, cac)
+    INT_CASE(VT_UI1, bVal, caub)
+    INT_CASE(VT_I2, iVal, cai)
+    INT_CASE(VT_UI2, uiVal, caui)
+    INT_CASE(VT_I4, lVal, cal)
+    INT_CASE(VT_UI4, ulVal, caul)
+    INT_CASE(VT_BOOL, boolVal, cabool)
+    INT_CASE(VT_R4, fltVal, caflt)
+    INT_CASE(VT_R8, dblVal, cadbl)
+    INT_CASE(VT_DATE, date, cadate) /* DATE == double */
+    INT_CASE(VT_ERROR, scode, cascode) /* == ULONG */
+    case VT_INT: /* does not appear as vector */
         CMP_INT_VALUE(lVal);
         break;
-    case VT_UI4:
-        CMP_INT_VALUE(uiVal);
+    case VT_UINT: /* does not appear as vector */
+        CMP_INT_VALUE(ulVal);
         break;
     case VT_I8:
         CMP_INT_VALUE(hVal.QuadPart);
         break;
+    case VT_VECTOR|VT_I8:
+        if (propvar1->u.cah.cElems > propvar2_converted->u.cah.cElems)
+            res = 1;
+        else if (propvar1->u.cah.cElems < propvar2_converted->u.cah.cElems)
+            res = -1;
+        else
+        {
+            ULONG i;
+            res = 0;
+            for (i = 0; i < propvar1->u.cah.cElems; ++i)
+            {
+                if (propvar1->u.cah.pElems[i].QuadPart > propvar2_converted->u.cah.pElems[i].QuadPart)
+                {
+                    res = 1;
+                    break;
+                }
+                else if (propvar1->u.cah.pElems[i].QuadPart < propvar2_converted->u.cah.pElems[i].QuadPart)
+                {
+                    res = -1;
+                    break;
+                }
+            }
+        }
+        break;
     case VT_UI8:
         CMP_INT_VALUE(uhVal.QuadPart);
         break;
-    case VT_BSTR:
+    case VT_VECTOR|VT_UI8:
+        if (propvar1->u.cauh.cElems > propvar2_converted->u.cauh.cElems)
+            res = 1;
+        else if (propvar1->u.cauh.cElems < propvar2_converted->u.cauh.cElems)
+            res = -1;
+        else
+        {
+            ULONG i;
+            res = 0;
+            for (i = 0; i < propvar1->u.cauh.cElems; ++i)
+            {
+                if (propvar1->u.cauh.pElems[i].QuadPart > propvar2_converted->u.cauh.pElems[i].QuadPart)
+                {
+                    res = 1;
+                    break;
+                }
+                else if (propvar1->u.cauh.pElems[i].QuadPart < propvar2_converted->u.cauh.pElems[i].QuadPart)
+                {
+                    res = -1;
+                    break;
+                }
+            }
+        }
+        break;
+    case VT_FILETIME:
+    {
+        CMP_INT_VALUE(filetime.dwHighDateTime);
+        if (res == 0)
+            CMP_INT_VALUE(filetime.dwLowDateTime);
+
+        break;
+    }
+    case VT_VECTOR|VT_FILETIME:
+    {
+        ULONG i;
+
+        CMP_INT_VALUE(cafiletime.cElems);
+        if (res)
+            break;
+
+        for (i = 0; i < propvar1->u.cafiletime.cElems; ++i)
+        {
+            CMP_INT_VALUE(cafiletime.pElems[i].dwHighDateTime);
+            if (res)
+                break;
+
+            CMP_INT_VALUE(cafiletime.pElems[i].dwLowDateTime);
+            if (res)
+                break;
+        }
+        break;
+    }
+    case VT_LPSTR:
+        res = lstrcmpA(propvar1->u.pszVal, propvar2_converted->u.pszVal);
+        break;
+    case VT_VECTOR|VT_LPSTR:
+    {
+        ULONG i;
+
+        CMP_INT_VALUE(calpstr.cElems);
+        if (res)
+            break;
+
+        for (i = 0; i < propvar1->u.calpstr.cElems; ++i)
+        {
+            /* FIXME: Use string flags. */
+            res = lstrcmpA(propvar1->u.calpstr.pElems[i], propvar2_converted->u.calpstr.pElems[i]);
+
+            if (res)
+                break;
+        }
+        break;
+    }
+    case VT_BSTR:   /* BSTR and LPWSTR are NOT EQUAL in general, but here */
+    case VT_LPWSTR:
         /* FIXME: Use string flags. */
-        res = lstrcmpW(propvar1->u.bstrVal, propvar2->u.bstrVal);
+        res = lstrcmpW(propvar1->u.bstrVal, propvar2_converted->u.bstrVal);
+        break;
+    case VT_VECTOR|VT_BSTR:
+    case VT_VECTOR|VT_LPWSTR:
+    {
+        ULONG i;
+
+        CMP_INT_VALUE(calpwstr.cElems);
+        if (res)
+            break;
+
+        for (i = 0; i < propvar1->u.calpwstr.cElems; ++i)
+        {
+            /* FIXME: Use string flags. */
+            res = lstrcmpW(propvar1->u.calpwstr.pElems[i], propvar2_converted->u.calpwstr.pElems[i]);
+
+            if (res)
+                break;
+        }
+        break;
+    }
+    case VT_CLSID:
+        /* IsEqualUUID only compares for equality :( */
+        CMP_INT_VALUE(puuid->Data1);
+        if (res)
+            break;
+
+        CMP_INT_VALUE(puuid->Data2);
+        if (res)
+            break;
+
+        CMP_INT_VALUE(puuid->Data3);
+        if (res)
+            break;
+
+        res = memcmp(propvar1->u.puuid->Data4, propvar2_converted->u.puuid->Data4, 8);
+        break;
+    case VT_VECTOR|VT_CLSID:
+    {
+        ULONG i;
+
+        CMP_INT_VALUE(cauuid.cElems);
+        if (res)
+            break;
+
+        for (i = 0; i < propvar1->u.cauuid.cElems; ++i)
+        {
+            CMP_INT_VALUE(cauuid.pElems[i].Data1);
+            if (res)
+                break;
+
+            CMP_INT_VALUE(cauuid.pElems[i].Data2);
+            if (res)
+                break;
+
+            CMP_INT_VALUE(cauuid.pElems[i].Data3);
+            if (res)
+                break;
+
+            res = memcmp(propvar1->u.cauuid.pElems[i].Data4,
+                         propvar2_converted->u.cauuid.pElems[i].Data4,
+                         8);
+
+            if (res)
+                break;
+        }
+        break;
+    }
+    case VT_CF:
+        CMP_INT_VALUE(pclipdata->cbSize);
+        if (res)
+            break;
+
+        CMP_INT_VALUE(pclipdata->ulClipFmt);
+        if (res)
+            break;
+
+        res = memcmp(propvar1->u.pclipdata->pClipData,
+                     propvar2_converted->u.pclipdata->pClipData,
+                     propvar1->u.pclipdata->cbSize - 4);
+        break;
+    case VT_VECTOR|VT_CF:
+    {
+        ULONG i;
+
+        CMP_INT_VALUE(caclipdata.cElems);
+        if (res)
+            break;
+
+        for (i = 0; i < propvar1->u.caclipdata.cElems; ++i)
+        {
+            CMP_INT_VALUE(caclipdata.pElems[i].cbSize);
+            if (res)
+                break;
+
+            CMP_INT_VALUE(caclipdata.pElems[i].ulClipFmt);
+            if (res)
+                break;
+
+            res = memcmp(propvar1->u.caclipdata.pElems[i].pClipData,
+                         propvar2_converted->u.caclipdata.pElems[i].pClipData,
+                         propvar1->u.caclipdata.pElems[i].cbSize - 4);
+
+            if (res)
+                break;
+        }
+        break;
+    }
+    case VT_VECTOR|VT_VARIANT:
+    {
+        ULONG i;
+
+        CMP_INT_VALUE(capropvar.cElems);
+        if (res)
+            break;
+
+        for (i = 0; i < propvar1->u.capropvar.cElems; ++i)
+        {
+            res = PropVariantCompareEx(&propvar1->u.capropvar.pElems[i],
+                                       &propvar2_converted->u.capropvar.pElems[i],
+                                       unit, flags);
+
+            if (res)
+                break;
+        }
+        break;
+    }
+    case VT_DECIMAL:
+    {
+        HRESULT hr;
+        DECIMAL dec1 = *(DECIMAL*)propvar1;
+        DECIMAL dec2 = *(DECIMAL*)propvar2_converted;
+        dec1.wReserved = 0;
+        dec2.wReserved = 0;
+
+        hr = VarDecCmp(&dec1, &dec2);
+        if (hr == VARCMP_LT)
+            res = -1;
+        else if (hr == VARCMP_GT)
+            res = 1;
+        else if (hr == VARCMP_EQ)
+            res = 0;
+        else
+        {
+            WARN("Comparing DECIMALS: hr=%08x\n", (unsigned)hr);
+            res = -1;
+        }
+        break;
+    }
+    case VT_BLOB:
+        CMP_INT_VALUE(blob.cbSize);
+        if (res)
+            break;
+
+        res = memcmp(propvar1->u.blob.pBlobData,
+                     propvar2_converted->u.blob.pBlobData,
+                     propvar1->u.blob.cbSize);
         break;
     default:
         FIXME("vartype %d not handled\n", propvar1->vt);
@@ -597,3 +997,69 @@ INT WINAPI PropVariantCompareEx(REFPROPVARIANT propvar1, REFPROPVARIANT propvar2
 
     return res;
 }
+
+HRESULT WINAPI StgDeserializePropVariant(const SERIALIZEDPROPERTYVALUE *pprop,
+                                         ULONG cbMax,
+                                         PROPVARIANT *pvar)
+{
+    HRESULT hr;
+
+    TRACE("(%p, %lu, %p)\n", pprop, (unsigned long)cbMax, pvar);
+
+    if (!pprop || !pvar)
+        return E_INVALIDARG;
+
+    hr = wine_ReadProperty(pvar, (BYTE*)pprop, cbMax, NULL, CP_UNICODE, CP_ACP, NULL, NULL);
+    if (FAILED(hr))
+    {
+        PropVariantClear(pvar);
+        return hr;
+    }
+
+    return S_OK;
+}
+
+HRESULT WINAPI StgSerializePropVariant(const PROPVARIANT *pVar,
+                                       SERIALIZEDPROPERTYVALUE **ppProp,
+                                       ULONG *pcb)
+{
+    HRESULT hr;
+    ULONG   space;
+    ULONG   space_used;
+    BYTE   *buffer = NULL;
+
+    TRACE("(%p, %p, %p)\n", pVar, ppProp, pcb);
+
+    if (!ppProp || !pcb)
+        return E_INVALIDARG;
+
+    *ppProp = NULL;
+    *pcb    = 0;
+
+    hr = wine_SerializedPropertySize(pVar, &space, CP_UNICODE, CP_ACP);
+    if (FAILED(hr))
+        return hr;
+
+    buffer = CoTaskMemAlloc(space);
+    if (!buffer)
+        return E_OUTOFMEMORY;
+
+    hr = wine_WritePropertyToBuffer(pVar, buffer, space, &space_used, CP_UNICODE, CP_ACP);
+    if (FAILED(hr))
+    {
+        WARN("Failed to serialize property of type %d: hr=%08x\n", pVar->vt, hr);
+        CoTaskMemFree(buffer);
+
+        return hr;
+    }
+
+    if (space - space_used)
+    {
+        WARN("Buffer size calculation was off by %u bytes\n", space - space_used);
+    }
+
+    *ppProp = (SERIALIZEDPROPERTYVALUE*)buffer;
+    *pcb    = space_used;
+
+    return S_OK;
+}
diff --git a/dlls/propsys/tests/propsys.c b/dlls/propsys/tests/propsys.c
index 99d1c46..311d610 100644
--- a/dlls/propsys/tests/propsys.c
+++ b/dlls/propsys/tests/propsys.c
@@ -866,6 +866,382 @@ static void test_intconversions(void)
     ok(llval == -7, "got wrong value %s\n", debugstr_longlong(llval));
 }
 
+static char *buffer_printable(void *buffer, size_t buffer_size)
+{
+    char *heap_buffer, *p;
+    unsigned char *source_buffer;
+
+    p = heap_buffer = HeapAlloc(GetProcessHeap(), 0, buffer_size * 4 + 5);
+    if (!heap_buffer)
+        return NULL;
+
+    source_buffer = buffer;
+    *p++ = '"';
+
+    for (; buffer_size; --buffer_size)
+    {
+        if ((*source_buffer >= '0' && *source_buffer <= '9')
+            || (*source_buffer >= 'a' && *source_buffer <= 'z')
+            || (*source_buffer >= 'A' && *source_buffer <= 'Z')
+            || *source_buffer == ' ' || *source_buffer == ',')
+        {
+            *p++ = (char)*source_buffer++;
+        }
+        else
+        {
+            sprintf(p, "\\%03o", (unsigned)*source_buffer++);
+            p += 4;
+        }
+    }
+
+    *p++ = '"';
+    *p++ = '\0';
+
+    return heap_buffer;
+}
+
+static void ok_compare(const char *type,
+                       BYTE *expected_buffer,
+                       size_t expected_buffer_size,
+                       BYTE *got_buffer,
+                       size_t got_buffer_size)
+{
+    int same = got_buffer_size == expected_buffer_size &&
+        !memcmp(expected_buffer, got_buffer, got_buffer_size);
+
+    char *got = buffer_printable(got_buffer, got_buffer_size);
+    char *expected = buffer_printable(expected_buffer, expected_buffer_size);
+
+    ok(same, "Comparing serialized %s: Expected %s:%lu but got %s:%lu\n", type, expected, (unsigned long)expected_buffer_size, got, (unsigned long)got_buffer_size);
+
+    HeapFree(GetProcessHeap(), 0, got);
+    HeapFree(GetProcessHeap(), 0, expected);
+}
+
+static void check_serialize_func(const char *type, const PROPVARIANT *prop, BYTE *expected, size_t expected_size)
+{
+    PROPVARIANT out;
+    HRESULT hr;
+    INT cmp;
+    SERIALIZEDPROPERTYVALUE *serialized = NULL;
+    ULONG                    serialized_size = 0;
+
+    PropVariantInit(&out);
+
+    hr = StgSerializePropVariant(prop, &serialized, &serialized_size);
+    ok(hr == S_OK, "Serializing %s: %08x\n", type, (unsigned)hr);
+
+    ok_compare(type, expected, expected_size, (BYTE*)serialized, (size_t)serialized_size);
+
+    hr = StgDeserializePropVariant(serialized, serialized_size, &out);
+    /* WTF: Win8 chokes on VT_DECIMAL, won't deserialize it but whatever it
+            deserializes compares equal to the original value                */
+    ok(hr == S_OK || broken(hr == E_FAIL && prop->vt == VT_DECIMAL), "Deserializing %s: %08x\n", type, (unsigned)hr);
+
+    cmp = PropVariantCompare(prop, &out);
+    ok(cmp == 0, "Deserialized %s is different from original value!\n", type);
+
+    PropVariantClear(&out);
+    CoTaskMemFree(serialized);
+}
+
+#define CHECK_SERIALIZE(type, prop, buffer_str_literal) \
+    do { \
+        unsigned char _str[] = buffer_str_literal; \
+        check_serialize_func(type, prop, _str, sizeof(_str) - 1); \
+    } while (0)
+
+#define DO_INT_TEST(ctype, functype, single_buffer_str, vector_buffer_str, values...) \
+    do { \
+        PROPVARIANT v; \
+        HRESULT hr; \
+        ctype vbuffer[] = { values }; \
+        \
+        /* first try a single value */ \
+        PropVariantInit(&v); \
+        hr = InitPropVariantFrom##functype(vbuffer[0], &v); \
+        ok(hr == S_OK, "Initializing PROPVARIANT from " #ctype ": %08x\n", (unsigned)hr); \
+        \
+        CHECK_SERIALIZE(#ctype, &v, single_buffer_str); \
+        \
+        PropVariantClear(&v); \
+        \
+        /* then try a vector */ \
+        PropVariantInit(&v); \
+        hr = InitPropVariantFrom##functype##Vector(vbuffer, sizeof(vbuffer)/sizeof(vbuffer[0]), &v); \
+        ok(hr == S_OK, "Initializing PROPVARIANT from " #ctype " Vector: %08x\n", (unsigned)hr); \
+        \
+        CHECK_SERIALIZE(#ctype " Vector", &v, vector_buffer_str); \
+        \
+        PropVariantClear(&v); \
+    } while(0)
+
+/* FIXME: In wine, this really tests wine_ReadProperty/wine_WritePropertyToBuffer
+ * in OLE32.dll, but propsys.dll provides the only non-braindamaged interface to
+ * property (de)serialization. Therefore, we test it here.
+ */
+static void test_serialization(void)
+{
+    HRESULT hr;
+
+    DO_INT_TEST(SHORT, Int16, "\002\000\000\000\040\000\000\000", "\002\020\000\000\003\000\000\000\040\000\167\362\052\000\000\000", 32, -3465, 42);
+    DO_INT_TEST(USHORT, UInt16, "\022\000\000\000\040\000\000\000", "\022\020\000\000\003\000\000\000\040\000\376\377\052\000\000\000", 32, 65534, 42);
+    DO_INT_TEST(LONG, Int32, "\003\000\000\000\040\000\000\000", "\003\020\000\000\003\000\000\000\040\000\000\000\066\113\005\000\312\366\377\377", 32, 346934, -2358);
+    DO_INT_TEST(ULONG, UInt32, "\023\000\000\000\357\276\255\336", "\023\020\000\000\005\000\000\000\357\276\255\336\276\272\376\312\276\272\255\336\376\312\357\276\357\276\276\272", 0xDEADBEEF, 0xCAFEBABE, 0xDEADBABE, 0xBEEFCAFE, 0xBABEBEEF);
+    DO_INT_TEST(LONGLONG, Int64, "\024\000\000\000\065\065\043\316\114\000\000\000", "\024\020\000\000\003\000\000\000\065\065\043\316\114\000\000\000\341\030\011\376\377\377\377\377\343\226\003\000\000\000\000\000", 329875928373, -32958239, 235235);
+    DO_INT_TEST(ULONGLONG, UInt64, "\025\000\000\000\276\272\357\276\376\312\355\015", "\025\020\000\000\001\000\000\000\276\272\357\276\376\312\355\015", 0xDEDCAFEBEEFBABE);
+    DO_INT_TEST(BOOL, Boolean, "\013\000\000\000\377\377\000\000", "\013\020\000\000\005\000\000\000\377\377\000\000\377\377\000\000\000\000\000\000", TRUE, FALSE, TRUE, FALSE, FALSE);
+
+    /* FileTime */
+    {
+        PROPVARIANT vSingle;
+        PROPVARIANT vVector;
+        FILETIME ftVec[] = { { 0xDEADBEEF, 0xCAFEBABE }, { 0xDEADBABE, 0xCAFEBEEF }, { 239508, 2484 } };
+
+        PropVariantInit(&vSingle); PropVariantInit(&vVector);
+
+        hr = InitPropVariantFromFileTime(&ftVec[0], &vSingle);
+        ok(hr == S_OK, "InitPropVariantFromFileTime: %08x\n", (unsigned)hr);
+
+        hr = InitPropVariantFromFileTimeVector(ftVec, sizeof(ftVec)/sizeof(ftVec[0]), &vVector);
+        ok(hr == S_OK, "InitPropVariantFromFileTimeVector: %08x\n", (unsigned)hr);
+
+        CHECK_SERIALIZE("FILETIME", &vSingle, "\100\000\000\000\357\276\255\336\276\272\376\312");
+        CHECK_SERIALIZE("FILETIME Vector", &vVector, "\100\020\000\000\003\000\000\000\357\276\255\336\276\272\376\312\276\272\255\336\357\276\376\312\224\247\003\000\264\011\000\000");
+
+        PropVariantClear(&vSingle); PropVariantClear(&vVector);
+    }
+
+    /* Int8 / Uint8 */
+    {
+        PROPVARIANT vc;
+        PROPVARIANT vac;
+        PROPVARIANT vb;
+        PROPVARIANT vab;
+
+        char cVec[] = "Hello World, How are You?";
+        BYTE bVec[] = { 0xDE, 0xAD, 0xCA, 0xFE, 0xBA };
+
+        vc.vt = VT_I1;
+        vc.u.cVal = cVec[0];
+
+        vac.vt = VT_VECTOR|VT_I1;
+        vac.u.cac.cElems = sizeof(cVec)/sizeof(cVec[0]);
+        vac.u.cac.pElems = cVec;
+
+        vb.vt = VT_UI1;
+        vb.u.bVal = bVec[0];
+
+        vab.vt = VT_VECTOR|VT_UI1;
+        vab.u.caub.cElems = sizeof(bVec)/sizeof(bVec[0]);
+        vab.u.caub.pElems = bVec;
+
+        CHECK_SERIALIZE("char", &vc, "\020\000\000\000\110\000\000\000");
+        CHECK_SERIALIZE("char vector", &vac, "\020\020\000\000\032\000\000\000\110\145\154\154\157\040\127\157\162\154\144\054\040\110\157\167\040\141\162\145\040\131\157\165\077\000\000\000");
+        CHECK_SERIALIZE("byte", &vb, "\021\000\000\000\336\000\000\000");
+        CHECK_SERIALIZE("byte vector", &vab, "\021\020\000\000\005\000\000\000\336\255\312\376\272\000\000\000");
+    }
+
+    /* Float */
+    {
+        PROPVARIANT vSingle;
+        PROPVARIANT vVector;
+
+        FLOAT fVec[] = { 3.14156778354f, 0.239852935758f, 128471284.354f, -523525.236f };
+
+        PropVariantInit(&vSingle); PropVariantInit(&vVector);
+
+        vSingle.vt = VT_R4;
+        vSingle.u.fltVal = fVec[0];
+        vVector.vt = VT_VECTOR|VT_R4;
+        vVector.u.caflt.cElems = sizeof(fVec)/sizeof(fVec[0]);
+        vVector.u.caflt.pElems = fVec;
+
+        CHECK_SERIALIZE("float", &vSingle, "\004\000\000\000\162\017\111\100");
+        CHECK_SERIALIZE("float vector", &vVector, "\004\020\000\000\004\000\000\000\162\017\111\100\002\234\165\076\037\012\365\114\250\240\377\310");
+    }
+
+    /* LPSTR */
+    /* The serialization routine converts these to UTF-16 and back to CP_ACP
+     * on return */
+    {
+        PROPVARIANT vSingle;
+        PROPVARIANT vVector;
+
+        char str1[] = "HelloWorld";
+        char str2[] = "aBc";
+        char str3[] = "Blub";
+        char *arr[] = { str1, str2, str3 };
+
+        vSingle.vt = VT_LPSTR;
+        vSingle.u.pszVal = str1;
+
+        vVector.vt = VT_VECTOR|VT_LPSTR;
+        vVector.u.calpstr.cElems = sizeof(arr)/sizeof(arr[0]);
+        vVector.u.calpstr.pElems = arr;
+
+        CHECK_SERIALIZE("lpstr", &vSingle, "\036\000\000\000\026\000\000\000H\000e\000l\000l\000o\000W\000o\000r\000l\000d\000\000\000\000\000");
+        CHECK_SERIALIZE("lpstr vector", &vVector, "\036\020\000\000\003\000\000\000\026\000\000\000H\000e\000l\000l\000o\000W\000o\000r\000l\000d\000\000\000\000\000\010\000\000\000a\000B\000c\000\000\000\012\000\000\000B\000l\000u\000b\000\000\000\000\000");
+    }
+
+    /* LPWSTR */
+    {
+        PROPVARIANT vSingle;
+        PROPVARIANT vVector;
+
+        WCHAR str1[] = { 'H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd', 0 };
+        WCHAR str2[] = { 'a', 'B', 'c', 0 };
+        WCHAR str3[] = { 'B', 'l', 'u', 'b', 0 };
+        /* disclaimer: I don't speak chinese, I just want to test some Unicode characters */
+        WCHAR str4[] = { 0x4E40, 0x4FB0, 0x4F47, 0x4EB9, 0 };
+        const WCHAR *arr[] = { str1, str2, str3, str4 };
+
+        PropVariantInit(&vSingle); PropVariantInit(&vVector);
+
+        hr = InitPropVariantFromString(str1, &vSingle);
+        ok(hr == S_OK, "InitPropVariantFromString: %08x\n", (unsigned)hr);
+
+        hr = InitPropVariantFromStringVector(arr, sizeof(arr)/sizeof(arr[0]), &vVector);
+
+        CHECK_SERIALIZE("lpwstr", &vSingle, "\037\000\000\000\013\000\000\000\110\000\145\000\154\000\154\000\157\000\127\000\157\000\162\000\154\000\144\000\000\000\000\000");
+        CHECK_SERIALIZE("lpwstr vector", &vVector, "\037\020\000\000\004\000\000\000\013\000\000\000\110\000\145\000\154\000\154\000\157\000\127\000\157\000\162\000\154\000\144\000\000\000\000\000\004\000\000\000\141\000\102\000\143\000\000\000\005\000\000\000\102\000\154\000\165\000\142\000\000\000\000\000\005\000\000\000\100\116\260\117\107\117\271\116\000\000\000\000");
+
+        PropVariantClear(&vSingle);
+        PropVariantClear(&vVector);
+    }
+
+    /* BSTR */
+    {
+        PROPVARIANT vSingle;
+        PROPVARIANT vVector;
+
+        WCHAR str1[] = { 'H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd', 0 };
+        WCHAR str2[] = { 'a', 'B', 'c', 0 };
+        WCHAR str3[] = { 'B', 'l', 'u', 'b', 0 };
+        /* disclaimer: I don't speak chinese, I just want to test some Unicode characters */
+        WCHAR str4[] = { 0x4E40, 0x4FB0, 0x4F47, 0x4EB9, 0 };
+
+        BSTR arr[4];
+
+        arr[0] = SysAllocString(str1); /*FIXME: handle NULL*/
+        arr[1] = SysAllocString(str2);
+        arr[2] = SysAllocString(str3);
+        arr[3] = SysAllocString(str4);
+
+        vSingle.vt = VT_BSTR;
+        vSingle.u.bstrVal = arr[0];
+        vVector.vt = VT_VECTOR|VT_BSTR;
+        vVector.u.cabstr.cElems = sizeof(arr)/sizeof(arr[0]);
+        vVector.u.cabstr.pElems = arr;
+
+        CHECK_SERIALIZE("BSTR", &vSingle, "\010\000\000\000\026\000\000\000H\000e\000l\000l\000o\000W\000o\000r\000l\000d\000\000\000\000\000");
+        CHECK_SERIALIZE("BSTR vector", &vVector, "\010\020\000\000\004\000\000\000\026\000\000\000H\000e\000l\000l\000o\000W\000o\000r\000l\000d\000\000\000\000\000\010\000\000\000a\000B\000c\000\000\000\012\000\000\000B\000l\000u\000b\000\000\000\000\000\012\000\000\000\100N\260OGO\271N\000\000\000\000");
+
+        SysFreeString(arr[0]);
+        SysFreeString(arr[1]);
+        SysFreeString(arr[2]);
+        SysFreeString(arr[3]);
+    }
+
+    /* Clipdata */
+    {
+        PROPVARIANT vSingle;
+        PROPVARIANT vVector;
+
+        char data1[] = "Hello World, how are you today?";
+        char data2[] = "Boo";
+
+        CLIPDATA cfArr[] = {
+            {
+                sizeof(data1) + 4,
+                42,
+                (BYTE*)data1
+            }, {
+                sizeof(data2) + 4,
+                0xBABE,
+                (BYTE*)data2
+            }
+
+        };
+
+        vSingle.vt = VT_CF;
+        vSingle.u.pclipdata = &cfArr[0];
+
+        vVector.vt = VT_VECTOR|VT_CF;
+        vVector.u.caclipdata.cElems = sizeof(cfArr)/sizeof(cfArr[0]);
+        vVector.u.caclipdata.pElems = cfArr;
+
+        CHECK_SERIALIZE("CF", &vSingle, "G\000\000\000\044\000\000\000\052\000\000\000Hello World, how are you today\077\000");
+        CHECK_SERIALIZE("CF vector", &vVector, "G\020\000\000\002\000\000\000\044\000\000\000\052\000\000\000Hello World, how are you today\077\000\010\000\000\000\276\272\000\000Boo\000");
+    }
+
+    /* CLSID */
+    {
+        PROPVARIANT vSingle;
+        PROPVARIANT vVector;
+
+        CLSID arr[] = {
+            {  0x557cf406, 0x1a04, 0x11d3, { 0x9a,0x73,0x00,0x00,0xf8,0x1e,0xf3,0x2e } },
+            {  0xDEADBEEF, 0xCAFE, 0xBABE, { 0xDE,0xAD,0xBA,0xBE,0xCA,0xFE,0xBE,0xEF } }
+        };
+
+        InitPropVariantFromCLSID(&arr[0], &vSingle);
+
+        vVector.vt = VT_VECTOR|VT_CLSID;
+        vVector.u.cauuid.cElems = sizeof(arr)/sizeof(arr[0]);
+        vVector.u.cauuid.pElems = arr;
+
+        CHECK_SERIALIZE("CLSID", &vSingle, "H\000\000\000\006\364\174U\004\032\323\021\232s\000\000\370\036\363\056");
+        CHECK_SERIALIZE("CLSID", &vVector, "H\020\000\000\002\000\000\000\006\364\174U\004\032\323\021\232s\000\000\370\036\363\056\357\276\255\336\376\312\276\272\336\255\272\276\312\376\276\357");
+
+        PropVariantClear(&vSingle);
+    }
+
+    /* DECIMAL */
+    {
+        PROPVARIANT v;
+        DECIMAL *asDecimal = (DECIMAL*)&v;
+
+        PropVariantInit(&v);
+        asDecimal->u.sign = DECIMAL_NEG;
+        asDecimal->u.scale = 5;
+        asDecimal->Hi32 = 0xCAFEBABE;
+        asDecimal->u1.Lo64 = 0xDEADBABECAFEBEEF;
+        v.vt = VT_DECIMAL;
+
+
+        CHECK_SERIALIZE("DECIMAL", &v, "\016\000\000\000\000\000\005\200\276\272\376\312\357\276\376\312\276\272\255\336");
+    }
+
+    /* VARIANT */
+    /* Unfortunately, we may only apply one level of variants :( */
+    {
+        PROPVARIANT vVec[2];
+        PROPVARIANT v;
+
+        InitPropVariantFromUInt32(0xDEADBABE, &vVec[0]);
+        InitPropVariantFromInt16(1342, &vVec[1]);
+
+        v.vt = VT_VECTOR|VT_VARIANT;
+        v.u.capropvar.cElems = sizeof(vVec)/sizeof(vVec[0]);
+        v.u.capropvar.pElems = vVec;
+
+        CHECK_SERIALIZE("VARIANT vector", &v, "\014\020\000\000\002\000\000\000\023\000\000\000\276\272\255\336\002\000\000\000\076\005\000\000");
+    }
+
+    /* BLOB */
+    {
+        PROPVARIANT vBlob;
+        char buffer[] = "Hello, World";
+
+        vBlob.vt = VT_BLOB;
+        vBlob.u.blob.cbSize = sizeof(buffer);
+        vBlob.u.blob.pBlobData = (BYTE*)buffer;
+
+        CHECK_SERIALIZE("BLOB", &vBlob, "A\000\000\000\015\000\000\000Hello, World\000\000\000\000");
+    }
+}
+
 START_TEST(propsys)
 {
     test_PSStringFromPropertyKey();
@@ -876,4 +1252,5 @@ START_TEST(propsys)
     test_PropVariantToGUID();
     test_PropVariantCompare();
     test_intconversions();
+    test_serialization();
 }
diff --git a/include/propidl.idl b/include/propidl.idl
index fbe80d8..c4acb42 100644
--- a/include/propidl.idl
+++ b/include/propidl.idl
@@ -85,6 +85,7 @@ interface IPropertyStorage : IUnknown
       type *pElems; \
     } name
 
+  TYPEDEF_CA(char, CAC);
   TYPEDEF_CA(unsigned char, CAUB);
   TYPEDEF_CA(short, CAI);
   TYPEDEF_CA(USHORT, CAUI);
@@ -137,10 +138,13 @@ interface IPropertyStorage : IUnknown
     [case(VT_CF)]                         CLIPDATA *pclipdata;
     [case(VT_STREAM, VT_STREAMED_OBJECT)] IStream *pStream;
     [case(VT_STORAGE, VT_STORED_OBJECT)]  IStorage *pStorage;
+    [case(VT_DISPATCH)]                   IDispatch *pdispVal;
+    [case(VT_UNKNOWN)]                    IUnknown *punkVal;
     [case(VT_BSTR)]                       BSTR bstrVal;
     [case(VT_BSTR_BLOB)]                  BSTRBLOB bstrblobVal;
     [case(VT_LPSTR)]                      LPSTR pszVal;
     [case(VT_LPWSTR)]                     LPWSTR pwszVal;
+    [case(VT_I1|VT_VECTOR)]               CAC cac;
     [case(VT_UI1|VT_VECTOR)]              CAUB caub;
     [case(VT_I2|VT_VECTOR)]               CAI cai;
     [case(VT_UI2|VT_VECTOR)]              CAUI caui;
@@ -439,6 +443,12 @@ typedef struct SERIALIZEDPROPERTYVALUE {
 cpp_quote("HRESULT WINAPI FreePropVariantArray(ULONG,PROPVARIANT*);")
 cpp_quote("HRESULT WINAPI PropVariantClear(PROPVARIANT*);")
 cpp_quote("HRESULT WINAPI PropVariantCopy(PROPVARIANT*,const PROPVARIANT*);")
+cpp_quote("SERIALIZEDPROPERTYVALUE* WINAPI StgConvertVariantToProperty(const PROPVARIANT *pvar, USHORT CodePage, SERIALIZEDPROPERTYVALUE *pprop, ULONG *pcb, PROPID pid, BOOLEAN fReserved, ULONG *pcIndirect);")
+cpp_quote("BOOLEAN WINAPI StgConvertPropertyToVariant(const SERIALIZEDPROPERTYVALUE* prop, USHORT CodePage, PROPVARIANT* pvar, void* pma);")
+cpp_quote("")
+cpp_quote("HRESULT WINAPI wine_ReadProperty(PROPVARIANT *prop, const BYTE *data, ULONG cbData, ULONG *pcbDataLeft, UINT diskCodePage, UINT runningCodePage, void* (WINAPI *allocate)(void *this, ULONG size), void *allocate_data);")
+cpp_quote("HRESULT WINAPI wine_SerializedPropertySize(const PROPVARIANT *in_prop, ULONG *pcbSize, UINT diskCodePage, UINT runningCodePage);")
+cpp_quote("HRESULT WINAPI wine_WritePropertyToBuffer(const PROPVARIANT *in_prop, BYTE *buffer, ULONG cbBufSize, ULONG *pcbBufSizeUsed, UINT diskCodePage, UINT runningCodePage);")
 cpp_quote("")
 cpp_quote("#define _PROPVARIANT_INIT_DEFINED_")
 cpp_quote("#define PropVariantInit(p) memset((p), 0, sizeof(PROPVARIANT))")
diff --git a/include/propvarutil.h b/include/propvarutil.h
index 4791543..a2ec2af 100644
--- a/include/propvarutil.h
+++ b/include/propvarutil.h
@@ -21,6 +21,7 @@
 
 #include <shtypes.h>
 #include <shlwapi.h>
+#include <propidl.h>
 
 enum tagPROPVAR_CHANGE_FLAGS
 {
@@ -63,6 +64,7 @@ HRESULT WINAPI PropVariantChangeType(PROPVARIANT *ppropvarDest, REFPROPVARIANT p
                                      PROPVAR_CHANGE_FLAGS flags, VARTYPE vt);
 HRESULT WINAPI InitPropVariantFromGUIDAsString(REFGUID guid, PROPVARIANT *ppropvar);
 HRESULT WINAPI InitVariantFromGUIDAsString(REFGUID guid, VARIANT *pvar);
+HRESULT WINAPI InitPropVariantFromCLSID(REFCLSID clsid, PROPVARIANT *ppropvar);
 HRESULT WINAPI InitPropVariantFromBuffer(const VOID *pv, UINT cb, PROPVARIANT *ppropvar);
 HRESULT WINAPI InitVariantFromBuffer(const VOID *pv, UINT cb, VARIANT *pvar);
 HRESULT WINAPI PropVariantToGUID(const PROPVARIANT *ppropvar, GUID *guid);
@@ -77,42 +79,117 @@ HRESULT WINAPI PropVariantToUInt16(REFPROPVARIANT propvarIn, USHORT *ret);
 HRESULT WINAPI PropVariantToUInt32(REFPROPVARIANT propvarIn, ULONG *ret);
 HRESULT WINAPI PropVariantToUInt64(REFPROPVARIANT propvarIn, ULONGLONG *ret);
 
-#ifdef __cplusplus
-
+#ifdef NO_PROPVAR_INLINES
 HRESULT InitPropVariantFromBoolean(BOOL fVal, PROPVARIANT *ppropvar);
 HRESULT InitPropVariantFromString(PCWSTR psz, PROPVARIANT *ppropvar);
 HRESULT InitPropVariantFromInt64(LONGLONG llVal, PROPVARIANT *ppropvar);
+#endif
 
+HRESULT WINAPI InitPropVariantFromInt16Vector(const SHORT *pv, ULONG c, PROPVARIANT *pprop);
+HRESULT WINAPI InitPropVariantFromUInt16Vector(const USHORT *pv, ULONG c, PROPVARIANT *pprop);
+HRESULT WINAPI InitPropVariantFromInt32Vector(const LONG *pv, ULONG c, PROPVARIANT *pprop);
+HRESULT WINAPI InitPropVariantFromUInt32Vector(const ULONG *pv, ULONG c, PROPVARIANT *pprop);
+HRESULT WINAPI InitPropVariantFromInt64Vector(const LONGLONG *pv, ULONG c, PROPVARIANT *pprop);
+HRESULT WINAPI InitPropVariantFromUInt64Vector(const ULONGLONG *pv, ULONG c, PROPVARIANT *pprop);
+HRESULT WINAPI InitPropVariantFromBooleanVector(const BOOL *pv, ULONG c, PROPVARIANT *pprop);
+HRESULT WINAPI InitPropVariantFromFileTime(const FILETIME *pft, PROPVARIANT *pprop);
+HRESULT WINAPI InitPropVariantFromFileTimeVector(const FILETIME *pv, ULONG c, PROPVARIANT *pprop);
+HRESULT WINAPI InitPropVariantFromStringVector(const WCHAR **pstr, ULONG c, PROPVARIANT *pprop);
+
+HRESULT WINAPI StgSerializePropVariant(const PROPVARIANT *ppropvar,
+                                       SERIALIZEDPROPERTYVALUE **ppProp,
+                                       ULONG *pcb);
+HRESULT WINAPI StgDeserializePropVariant(const SERIALIZEDPROPERTYVALUE *pprop,
+                                         ULONG cbMax,
+                                         PROPVARIANT *ppropvar);
+
+
+/* FIXME: Make this available only if the compiler supports the inline keyword */
 #ifndef NO_PROPVAR_INLINES
 
-inline HRESULT InitPropVariantFromBoolean(BOOL fVal, PROPVARIANT *ppropvar)
+#ifdef NONAMELESSUNION
+# define PROPVARIANT_U(x) (x).u
+#else
+# define PROPVARIANT_U(x) (x)
+#endif
+
+static inline HRESULT InitPropVariantFromInt16(SHORT val, PROPVARIANT *ppropvar)
+{
+    ppropvar->vt = VT_I2;
+    PROPVARIANT_U(*ppropvar).iVal = val;
+    return S_OK;
+}
+
+static inline HRESULT InitPropVariantFromUInt16(USHORT val, PROPVARIANT *ppropvar)
+{
+    ppropvar->vt = VT_UI2;
+    PROPVARIANT_U(*ppropvar).uiVal = val;
+    return S_OK;
+}
+
+static inline HRESULT InitPropVariantFromInt32(LONG val, PROPVARIANT *ppropvar)
+{
+    ppropvar->vt = VT_I4;
+    PROPVARIANT_U(*ppropvar).lVal = val;
+    return S_OK;
+}
+
+static inline HRESULT InitPropVariantFromUInt32(ULONG val, PROPVARIANT *ppropvar)
+{
+    ppropvar->vt = VT_UI4;
+    PROPVARIANT_U(*ppropvar).ulVal = val;
+    return S_OK;
+}
+
+static inline HRESULT InitPropVariantFromBoolean(BOOL fVal, PROPVARIANT *ppropvar)
 {
     ppropvar->vt = VT_BOOL;
-    ppropvar->boolVal = fVal ? VARIANT_TRUE : VARIANT_FALSE;
+    PROPVARIANT_U(*ppropvar).boolVal = fVal ? VARIANT_TRUE : VARIANT_FALSE;
     return S_OK;
 }
 
-inline HRESULT InitPropVariantFromString(PCWSTR psz, PROPVARIANT *ppropvar)
+static inline HRESULT InitPropVariantFromString(PCWSTR psz, PROPVARIANT *ppropvar)
 {
-    HRESULT hres;
+    ULONG len;
 
-    hres = SHStrDupW(psz, &ppropvar->pwszVal);
-    if(SUCCEEDED(hres))
+    len = lstrlenW(psz) + 1;
+    PROPVARIANT_U(*ppropvar).pwszVal = CoTaskMemAlloc(len * sizeof(WCHAR));
+    if (PROPVARIANT_U(*ppropvar).pwszVal)
+    {
         ppropvar->vt = VT_LPWSTR;
+        memcpy(PROPVARIANT_U(*ppropvar).pwszVal, psz, len * sizeof(WCHAR));
+
+        return S_OK;
+    }
     else
+    {
         PropVariantInit(ppropvar);
-
-    return hres;
+        return E_OUTOFMEMORY;
+    }
 }
 
-inline HRESULT InitPropVariantFromInt64(LONGLONG llVal, PROPVARIANT *ppropvar)
+static inline HRESULT InitPropVariantFromInt64(LONGLONG llVal, PROPVARIANT *ppropvar)
 {
     ppropvar->vt = VT_I8;
-    ppropvar->hVal.QuadPart = llVal;
+    PROPVARIANT_U(*ppropvar).hVal.QuadPart = llVal;
     return S_OK;
 }
 
-#endif
+
+static inline HRESULT InitPropVariantFromUInt64(ULONGLONG val, PROPVARIANT *ppropvar)
+{
+    ppropvar->vt = VT_UI8;
+    PROPVARIANT_U(*ppropvar).uhVal.QuadPart = val;
+    return S_OK;
+}
+
+static inline INT PropVariantCompare(REFPROPVARIANT v1, REFPROPVARIANT v2)
+{
+    return PropVariantCompareEx(v1, v2, PVCU_DEFAULT, PVCF_DEFAULT);
+}
+
+#undef PROPVARIANT_U
+
 #endif
 
 #endif /* __WINE_PROPVARUTIL_H */
-- 
2.4.3




More information about the wine-devel mailing list