[PATCH v2 04/15] ole32: Rework property (de)serialization
Jonas Kümmerlin
rgcjonas at gmail.com
Sat Jul 18 11:26:43 CDT 2015
The new code tries to protect itself against buffer overflows and
handles a lot more cases.
The implementation of StgConvertPropertyToVariant/StgConvertVariantToProperty
has been improved.
---
dlls/msi/tests/suminfo.c | 4 +-
dlls/ole32/stg_prop.c | 1717 +++++++++++++++++++++++++++++++++-------
dlls/ole32/tests/propvariant.c | 86 +-
include/propidl.idl | 2 +
4 files changed, 1535 insertions(+), 274 deletions(-)
diff --git a/dlls/msi/tests/suminfo.c b/dlls/msi/tests/suminfo.c
index 8c2e292..d121f75 100644
--- a/dlls/msi/tests/suminfo.c
+++ b/dlls/msi/tests/suminfo.c
@@ -438,7 +438,7 @@ static void test_summary_binary(void)
ival = -1;
r = MsiSummaryInfoGetPropertyA(hsuminfo, PID_WORDCOUNT, &type, &ival, NULL, NULL, NULL);
ok(r == ERROR_SUCCESS, "MsiSummaryInfoGetProperty failed\n");
- todo_wine ok( ival == 0, "value incorrect\n");
+ ok( ival == 0, "value incorrect\n");
/* looks like msi adds some of its own values in here */
count = 0;
@@ -447,7 +447,7 @@ static void test_summary_binary(void)
todo_wine ok(count == 10, "prop count incorrect\n");
r = MsiSummaryInfoSetPropertyA( hsuminfo, PID_TITLE, VT_LPSTR, 0, NULL, "Mike" );
- ok(r == ERROR_FUNCTION_FAILED, "MsiSummaryInfoSetProperty failed %u\n", r);
+ todo_wine ok(r == ERROR_FUNCTION_FAILED, "MsiSummaryInfoSetProperty failed %u\n", r);
r = MsiSummaryInfoPersist( hsuminfo );
ok(r == ERROR_FUNCTION_FAILED, "MsiSummaryInfoPersist failed %u\n", r);
diff --git a/dlls/ole32/stg_prop.c b/dlls/ole32/stg_prop.c
index da361ce..f818998 100644
--- a/dlls/ole32/stg_prop.c
+++ b/dlls/ole32/stg_prop.c
@@ -144,6 +144,17 @@ static HRESULT PropertyStorage_PropVariantCopy(PROPVARIANT *prop,
static HRESULT PropertyStorage_StringCopy(LPCSTR src, LCID srcCP, LPSTR *dst,
LCID targetCP);
+/* Like PropertyStorage_StringCopy, but returns the size that needs to be allocated.
+ */
+static HRESULT PropertyStorage_StringCopy_Size(LPCSTR src, LCID srcCP,
+ DWORD *dstSize, LCID targetCP);
+
+/* Like PropertyStorage_StringCopy, but uses a caller-supplied buffer.
+ */
+static HRESULT PropertyStorage_StringCopy_Buffer(LPCSTR src, LCID srcCP,
+ LPSTR dst, DWORD cbDst, LCID targetCP);
+
+
static const IPropertyStorageVtbl IPropertyStorage_Vtbl;
static const IEnumSTATPROPSETSTGVtbl IEnumSTATPROPSETSTG_Vtbl;
static const IEnumSTATPROPSTGVtbl IEnumSTATPROPSTG_Vtbl;
@@ -354,18 +365,81 @@ static HRESULT WINAPI IPropertyStorage_fnReadMultiple(
return hr;
}
-static HRESULT PropertyStorage_StringCopy(LPCSTR src, LCID srcCP, LPSTR *dst,
- LCID dstCP)
+static HRESULT PropertyStorage_StringCopy_Size(LPCSTR src, LCID srcCP,
+ DWORD *dstSize, LCID dstCP)
{
HRESULT hr = S_OK;
- int len;
+ DWORD size;
TRACE("%s, %p, %d, %d\n",
+ srcCP == CP_UNICODE ? debugstr_w((LPCWSTR)src) : debugstr_a(src),
+ dstSize, dstCP, srcCP);
+
+ assert(src);
+ assert(dstSize);
+ *dstSize = 0;
+ if (dstCP == srcCP)
+ {
+ if (dstCP == CP_UNICODE)
+ size = (strlenW((LPCWSTR)src) + 1) * sizeof(WCHAR);
+ else
+ size = strlen(src) + 1;
+ }
+ else
+ {
+ if (dstCP == CP_UNICODE)
+ {
+ size = MultiByteToWideChar(srcCP, 0, src, -1, NULL, 0) * sizeof(WCHAR);
+ }
+ else
+ {
+ LPCWSTR wideStr = NULL;
+ LPWSTR wideStr_tmp = NULL;
+
+ if (srcCP == CP_UNICODE)
+ wideStr = (LPCWSTR)src;
+ else
+ {
+ int len = MultiByteToWideChar(srcCP, 0, src, -1, NULL, 0);
+ wideStr_tmp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+ if (wideStr_tmp)
+ {
+ MultiByteToWideChar(srcCP, 0, src, -1, wideStr_tmp, len);
+ wideStr = wideStr_tmp;
+ }
+ else
+ hr = STG_E_INSUFFICIENTMEMORY;
+ }
+ if (SUCCEEDED(hr))
+ {
+ size = WideCharToMultiByte(dstCP, 0, wideStr, -1, NULL, 0,
+ NULL, NULL);
+ }
+ HeapFree(GetProcessHeap(), 0, wideStr_tmp);
+ }
+ }
+
+ TRACE("returning 0x%08x (%u)\n", hr, size);
+
+ *dstSize = size;
+ return hr;
+}
+
+/* Like PropertyStorage_StringCopy, but uses a user-supplied buffer.
+ */
+static HRESULT PropertyStorage_StringCopy_Buffer(LPCSTR src, LCID srcCP,
+ LPSTR dst, DWORD cbDst,
+ LCID dstCP)
+{
+ HRESULT hr = S_OK;
+ int len;
+
+ TRACE("%s, %p, %u, %d, %d\n",
srcCP == CP_UNICODE ? debugstr_w((LPCWSTR)src) : debugstr_a(src), dst,
- dstCP, srcCP);
+ cbDst, dstCP, srcCP);
assert(src);
assert(dst);
- *dst = NULL;
+
if (dstCP == srcCP)
{
size_t len;
@@ -374,22 +448,21 @@ static HRESULT PropertyStorage_StringCopy(LPCSTR src, LCID srcCP, LPSTR *dst,
len = (strlenW((LPCWSTR)src) + 1) * sizeof(WCHAR);
else
len = strlen(src) + 1;
- *dst = CoTaskMemAlloc(len * sizeof(WCHAR));
- if (!*dst)
- hr = STG_E_INSUFFICIENTMEMORY;
+
+ if (cbDst < len)
+ hr = STG_E_INVALIDPARAMETER;
else
- memcpy(*dst, src, len);
+ memcpy(dst, src, len);
}
else
{
if (dstCP == CP_UNICODE)
{
len = MultiByteToWideChar(srcCP, 0, src, -1, NULL, 0);
- *dst = CoTaskMemAlloc(len * sizeof(WCHAR));
- if (!*dst)
+ if (cbDst < (len * sizeof(WCHAR)))
hr = STG_E_INSUFFICIENTMEMORY;
else
- MultiByteToWideChar(srcCP, 0, src, -1, (LPWSTR)*dst, len);
+ MultiByteToWideChar(srcCP, 0, src, -1, (LPWSTR)dst, len);
}
else
{
@@ -412,20 +485,24 @@ static HRESULT PropertyStorage_StringCopy(LPCSTR src, LCID srcCP, LPSTR *dst,
}
if (SUCCEEDED(hr))
{
+ TRACE("Converted to wide string %s\n", debugstr_w(wideStr));
+
len = WideCharToMultiByte(dstCP, 0, wideStr, -1, NULL, 0,
NULL, NULL);
- *dst = CoTaskMemAlloc(len);
- if (!*dst)
- hr = STG_E_INSUFFICIENTMEMORY;
+ if (cbDst < len)
+ hr = STG_E_INVALIDPARAMETER;
else
{
BOOL defCharUsed = FALSE;
+ BOOL *lpDefCharUsed = &defCharUsed;
+
+ /* for CP_UTF8/CP_UTF7, we may not even supply lpDefChar */
+ if (dstCP == CP_UTF8 || dstCP == CP_UTF7)
+ lpDefCharUsed = NULL;
- if (WideCharToMultiByte(dstCP, 0, wideStr, -1, *dst, len,
- NULL, &defCharUsed) == 0 || defCharUsed)
+ if (WideCharToMultiByte(dstCP, 0, wideStr, -1, dst, len,
+ NULL, lpDefCharUsed) == 0 || defCharUsed)
{
- CoTaskMemFree(*dst);
- *dst = NULL;
hr = HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION);
}
}
@@ -434,7 +511,34 @@ static HRESULT PropertyStorage_StringCopy(LPCSTR src, LCID srcCP, LPSTR *dst,
}
}
TRACE("returning 0x%08x (%s)\n", hr,
- dstCP == CP_UNICODE ? debugstr_w((LPCWSTR)*dst) : debugstr_a(*dst));
+ dstCP == CP_UNICODE ? debugstr_w((LPCWSTR)dst) : debugstr_a(dst));
+ return hr;
+}
+
+static HRESULT PropertyStorage_StringCopy(LPCSTR src, LCID srcCP, LPSTR *dst,
+ LCID dstCP)
+{
+ HRESULT hr;
+ DWORD size;
+
+ assert(src);
+ assert(dst);
+
+ hr = PropertyStorage_StringCopy_Size(src, srcCP, &size, dstCP);
+ if (FAILED(hr))
+ return hr;
+
+ *dst = CoTaskMemAlloc(size);
+ if (!*dst)
+ return STG_E_INSUFFICIENTMEMORY;
+
+ hr = PropertyStorage_StringCopy_Buffer(src, srcCP, *dst, size, dstCP);
+ if (FAILED(hr))
+ {
+ CoTaskMemFree(*dst);
+ *dst = NULL;
+ }
+
return hr;
}
@@ -1003,218 +1107,1328 @@ static HRESULT PropertyStorage_ReadDictionary(PropertyStorage_impl *This,
assert(This->name_to_propid);
assert(This->propid_to_name);
- StorageUtl_ReadDWord(ptr, 0, &numEntries);
- TRACE("Reading %d entries:\n", numEntries);
- ptr += sizeof(DWORD);
- for (i = 0; SUCCEEDED(hr) && i < numEntries; i++)
- {
- PROPID propid;
- DWORD cbEntry;
+ StorageUtl_ReadDWord(ptr, 0, &numEntries);
+ TRACE("Reading %d entries:\n", numEntries);
+ ptr += sizeof(DWORD);
+ for (i = 0; SUCCEEDED(hr) && i < numEntries; i++)
+ {
+ PROPID propid;
+ DWORD cbEntry;
+
+ StorageUtl_ReadDWord(ptr, 0, &propid);
+ ptr += sizeof(PROPID);
+ StorageUtl_ReadDWord(ptr, 0, &cbEntry);
+ ptr += sizeof(DWORD);
+ TRACE("Reading entry with ID 0x%08x, %d bytes\n", propid, cbEntry);
+ /* Make sure the source string is NULL-terminated */
+ if (This->codePage != CP_UNICODE)
+ ptr[cbEntry - 1] = '\0';
+ else
+ *((LPWSTR)ptr + cbEntry / sizeof(WCHAR)) = '\0';
+ hr = PropertyStorage_StoreNameWithId(This, (char*)ptr, This->codePage, propid);
+ if (This->codePage == CP_UNICODE)
+ {
+ /* Unicode entries are padded to DWORD boundaries */
+ if (cbEntry % sizeof(DWORD))
+ ptr += sizeof(DWORD) - (cbEntry % sizeof(DWORD));
+ }
+ ptr += sizeof(DWORD) + cbEntry;
+ }
+ return hr;
+}
+
+static void* WINAPI Allocate_CoTaskMemAlloc(void *this, ULONG size)
+{
+ return CoTaskMemAlloc(size);
+}
+
+/* HACK to hide (ULONG)(something) < 0 from the analyzer */
+static inline int gt(ULONG a, ULONG b) { return a > b; }
+
+/* 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.
+ *
+ * Allocator:
+ * 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.
+ *
+ * Codepages:
+ * Serialized properties have no intrinsic knowledge of their codepage.
+ * VT_LPSTR and VT_BSTR will be interpreted using the given $diskCodepage,
+ * and VT_LPSTR will then be converted to $runningCodepage.
+ * $diskCodepage=CP_UNICODE is a sensible choice, but the OLE storage may
+ * also use some ANSI codepage.
+ * $runningCodepage=CP_ACP is the only sensible choice, but the current
+ * storage implementation passes $runningCodepage=$diskCodepage and converts
+ * narrow strings at a later stage (TODO: change this).
+ *
+ * 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
+ */
+static HRESULT PropertyStorage_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;
+ char *tmpbuf;
+
+ 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);
+
+ /* allocate a temporary buffer to ensure NULL termination */
+ tmpbuf = HeapAlloc(GetProcessHeap(), 0, count + 1);
+ if (!tmpbuf)
+ return STG_E_INSUFFICIENTMEMORY;
+
+ memcpy(tmpbuf, data, count);
+ tmpbuf[count] = 0;
+
+ if (diskCodepage == CP_UNICODE)
+ {
+ PropertyStorage_ByteSwapString((LPWSTR)tmpbuf, count/2);
+ }
+
+ hr = PropertyStorage_StringCopy_Size(
+ tmpbuf,
+ diskCodepage,
+ &convertedCount,
+ runningCodepage);
+ if (FAILED(hr))
+ {
+ HeapFree(GetProcessHeap(), 0, tmpbuf);
+ return hr;
+ }
+
+ *vec = allocate(allocate_data, convertedCount);
+ if (!*vec)
+ {
+ HeapFree(GetProcessHeap(), 0, tmpbuf);
+ return STG_E_INSUFFICIENTMEMORY;
+ }
+
+ hr = PropertyStorage_StringCopy_Buffer(
+ tmpbuf,
+ diskCodepage,
+ *vec, convertedCount,
+ runningCodepage);
+
+ HeapFree(GetProcessHeap(), 0, tmpbuf);
+
+ if (FAILED(hr))
+ return hr;
+
+ MOVE_FORWARD(count);
+ MOVE_FORWARD((4 - (count%4)) % 4); /* padding */
+ }
+ 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(float));
+ }
+ 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 = PropertyStorage_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;
+}
+
+/*
+ * 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 PropertyStorage_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 convertedLen;
+
+ size += 4; /* DWORD byte count */
+
+ hr = PropertyStorage_StringCopy_Size(prop.u.pszVal,
+ runningCodepage,
+ &convertedLen,
+ diskCodepage);
+ 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 convertedLen;
+
+ size += 4; /* DWORD byte count */
+
+ hr = PropertyStorage_StringCopy_Size(prop.u.calpstr.pElems[i],
+ runningCodepage,
+ &convertedLen,
+ diskCodepage);
+ if (FAILED(hr))
+ return hr;
+
+ size += convertedLen;
+
+ size += (4 - (convertedLen % 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 */
- StorageUtl_ReadDWord(ptr, 0, &propid);
- ptr += sizeof(PROPID);
- StorageUtl_ReadDWord(ptr, 0, &cbEntry);
- ptr += sizeof(DWORD);
- TRACE("Reading entry with ID 0x%08x, %d bytes\n", propid, cbEntry);
- /* Make sure the source string is NULL-terminated */
- if (This->codePage != CP_UNICODE)
- ptr[cbEntry - 1] = '\0';
- else
- *((LPWSTR)ptr + cbEntry / sizeof(WCHAR)) = '\0';
- hr = PropertyStorage_StoreNameWithId(This, (char*)ptr, This->codePage, propid);
- if (This->codePage == CP_UNICODE)
+ size += len;
+ }
+ break;
+ }
+ case VT_VECTOR|VT_VARIANT:
{
- /* Unicode entries are padded to DWORD boundaries */
- if (cbEntry % sizeof(DWORD))
- ptr += sizeof(DWORD) - (cbEntry % sizeof(DWORD));
+ HRESULT hr;
+ ULONG vsize;
+ ULONG i;
+
+ size += 4; /* Vector header */
+ for (i = 0; i < prop.u.capropvar.cElems; ++i)
+ {
+ hr = PropertyStorage_SerializedPropertySize(&prop.u.capropvar.pElems[i],
+ &vsize,
+ diskCodepage, runningCodepage);
+ if (FAILED(hr))
+ return hr;
+
+ size += vsize;
+ size += (4 - (vsize % 4)) % 4;
+ }
+ break;
}
- ptr += sizeof(DWORD) + cbEntry;
+ default:
+ FIXME("unsupported type %d\n", prop.vt);
+ return STG_E_INVALIDPARAMETER;
}
- return hr;
-}
-static void* WINAPI Allocate_CoTaskMemAlloc(void *this, ULONG size)
-{
- return CoTaskMemAlloc(size);
+ /* always pad to 4 bytes */
+ size += (4 - (size % 4)) % 4;
+ *pcbSize = size;
+
+ return S_OK;
}
-/* FIXME: there isn't any checking whether the read property extends past the
- * end of the buffer.
+/*
+ * 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.
+ *
+ * Codepages:
+ * Serialized properties have no intrinsic knowledge of their codepage.
+ * VT_LPSTR will be interpreted in the given $runningCodepage, and
+ * VT_LPSTR and VT_BSTR will be converted to the given $diskCodepage.
+ * $diskCodepage=CP_UNICODE is a sensible choice, but the OLE storage may
+ * also use some ANSI codepage.
+ * $runningCodepage=CP_ACP is the only sensible choice, but the current
+ * storage implementation passes $runningCodepage=$diskCodepage and converts
+ * narrow strings at a previous stage (TODO: change this).
+ *
+ * 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 PropertyStorage_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 convertedLen;
+
+ hr = PropertyStorage_StringCopy_Size(*vec,
+ runningCodepage,
+ &convertedLen,
+ diskCodepage);
+ if (FAILED(hr))
+ return hr;
+
+ WRITE_DWORD_AND_MOVE(convertedLen);
+ FAIL_IF_EOF(convertedLen);
+
+ hr = PropertyStorage_StringCopy_Buffer(*vec,
+ runningCodepage,
+ (char*)buffer, convertedLen,
+ diskCodepage);
+ if (FAILED(hr))
+ return hr;
+
+ if (diskCodepage == CP_UNICODE)
+ {
+ PropertyStorage_ByteSwapString(buffer, convertedLen/2);
+ TRACE("Write lpstr %s\n", debugstr_w((LPCWSTR)buffer));
+ }
else
- wcount = MultiByteToWideChar(codepage, 0, (LPCSTR)(data + sizeof(DWORD)), count, NULL, 0);
+ {
+ TRACE("Write lpstr %s\n", debugstr_a((char*)buffer));
+ }
- prop->u.bstrVal = SysAllocStringLen(NULL, wcount); /* FIXME: use allocator? */
+ 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 (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);
+ 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));
+ PropertyStorage_ByteSwapString(buffer, len);
+ 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));
+ PropertyStorage_ByteSwapString(buffer, len);
+ 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);
+ }
+ break;
+ }
+ 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);
}
- else
- hr = STG_E_INSUFFICIENTMEMORY;
break;
}
- case VT_FILETIME:
- StorageUtl_ReadULargeInteger(data, 0,
- (ULARGE_INTEGER *)&prop->u.filetime);
+ CASE_OR_VECTOR(VT_DATE)
+ CASE_OR_VECTOR(VT_R8)
+ {
+ FOR_POSSIBLE_VECTOR(double, dblVal, cadbl, sizeof(double))
+ {
+ memcpy(buffer, vec, sizeof(double));
+ }
break;
- case VT_CF:
+ }
+ CASE_OR_VECTOR(VT_R4)
+ {
+ FOR_POSSIBLE_VECTOR(float, fltVal, caflt, sizeof(float))
{
- DWORD len = 0, tag = 0;
+ memcpy(buffer, vec, sizeof(float));
+ }
+ break;
+ }
+ case VT_DECIMAL:
+ {
+ DECIMAL *dec;
+
+ FAIL_IF_EOF(16);
- StorageUtl_ReadDWord(data, 0, &len);
- StorageUtl_ReadDWord(data, 4, &tag);
- if (len > 8)
+ 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);
+
+ 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 = PropertyStorage_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 +2653,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 = PropertyStorage_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 +2685,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 +2911,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 +2935,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 = PropertyStorage_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 = PropertyStorage_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,12 +3893,17 @@ BOOLEAN WINAPI StgConvertPropertyToVariant(const SERIALIZEDPROPERTYVALUE* prop,
{
HRESULT hr;
- hr = PropertyStorage_ReadProperty(pvar, (const BYTE*)prop, CodePage, Allocate_PMemoryAllocator, pma);
+ hr = PropertyStorage_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);
+
+ /* TODO: get the HRESULT<->NTSTATUS mapping right */
+ RaiseException(0xC000000D /*STATUS_INVALID_PARAMETER*/, 0, 0, NULL);
}
return FALSE;
@@ -2763,7 +3913,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 = PropertyStorage_WritePropertyToBuffer(pvar, (BYTE*)pprop, buffer_size, pcb, CodePage, CP_ACP);
+ }
+ else
+ {
+ hr = PropertyStorage_SerializedPropertySize(pvar, pcb, CodePage, CP_ACP);
+ }
+
+ if (FAILED(hr))
+ {
+ /* FIXME: what should we really do? I couldn't get windows to actually throw an exception */
+ FIXME("should raise exception on failure\n");
+ return NULL;
+ }
- return NULL;
+ return pprop;
}
diff --git a/dlls/ole32/tests/propvariant.c b/dlls/ole32/tests/propvariant.c
index c45ca2a..401bc27 100644
--- a/dlls/ole32/tests/propvariant.c
+++ b/dlls/ole32/tests/propvariant.c
@@ -23,6 +23,7 @@
#include "ddeml.h"
#include "wine/test.h"
+#include "wine/exception.h"
/* invalid in all versions */
#define PROP_INV 0x7f
@@ -438,6 +439,30 @@ 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 const char serialized_invalid[] = {
+ 0xde, 0xad, /* invalid */
+ 0, 0,
+ 'b','o','g','u','s',
+ 0,0,0
+};
+
static void test_propertytovariant(void)
{
HANDLE hole32;
@@ -447,6 +472,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 +521,36 @@ 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);
+
+ /* provoke exceptions */
+ __TRY
+ {
+ ret = pStgConvertPropertyToVariant((SERIALIZEDPROPERTYVALUE*)serialized_invalid,
+ CP_WINUNICODE, &propvar, &allocator);
+ ok(0, "StgConvertPropertyToVariant should have thrown\n");
+ }
+ __EXCEPT_ALL
+ {
+ todo_wine ok(GetExceptionCode() == 0xC00000bb/*STATUS_NOT_SUPPORTED*/,
+ "StgConvertPropertyToVariant threw unexpected exception %x\n", GetExceptionCode());
+ }
+ __ENDTRY;
}
static void test_varianttoproperty(void)
@@ -506,6 +562,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 +587,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)
{
@@ -538,6 +595,15 @@ static void test_varianttoproperty(void)
return;
}
+ len = 4; /* too small! */
+ own_propvalue->dwType = 0xcafebabe;
+ propvalue = pStgConvertVariantToProperty(&propvar, CP_WINUNICODE,
+ own_propvalue, &len, 0, FALSE, 0);
+ ok(NULL == propvalue, "unexpected propvalue %p\n", propvalue);
+ todo_wine ok(len == 8, "unexpected length %d\n", len);
+
+ /*TODO: how can we make it throw an exception? */
+
len = 20;
propvalue = pStgConvertVariantToProperty(&propvar, CP_WINUNICODE, own_propvalue, &len,
0, FALSE, 0);
@@ -588,6 +654,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/include/propidl.idl b/include/propidl.idl
index 2fb8370..5905694 100644
--- a/include/propidl.idl
+++ b/include/propidl.idl
@@ -443,6 +443,8 @@ 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("#define _PROPVARIANT_INIT_DEFINED_")
cpp_quote("#define PropVariantInit(p) memset((p), 0, sizeof(PROPVARIANT))")
--
2.4.3
More information about the wine-devel
mailing list