This implements PropVariantToString, PropVariantToStringAlloc, PropVariantToBSTR and PropVariantChangeType for stringifying numbers and converting between different types of strings.
MSDN specifies a lot more conversion, so there's plenty of room for future improvements. --- dlls/propsys/propsys.spec | 6 +- dlls/propsys/propvar.c | 283 +++++++++++++++++++++++++++++++++++++++++++ dlls/propsys/tests/propsys.c | 80 +++++++++++- include/propvarutil.h | 3 + include/strsafe.h | 17 +++ 5 files changed, 385 insertions(+), 4 deletions(-)
diff --git a/dlls/propsys/propsys.spec b/dlls/propsys/propsys.spec index 50bdf6e..970b5c0 100644 --- a/dlls/propsys/propsys.spec +++ b/dlls/propsys/propsys.spec @@ -106,7 +106,7 @@ @ stub PropVariantGetUInt16Elem @ stub PropVariantGetUInt32Elem @ stub PropVariantGetUInt64Elem -@ stub PropVariantToBSTR +@ stdcall PropVariantToBSTR(ptr ptr) @ stub PropVariantToBoolean @ stub PropVariantToBooleanVector @ stub PropVariantToBooleanVectorAlloc @@ -133,8 +133,8 @@ @ stub PropVariantToInt64VectorAlloc @ stub PropVariantToInt64WithDefault @ stub PropVariantToStrRet -@ stub PropVariantToString -@ stub PropVariantToStringAlloc +@ stdcall PropVariantToString(ptr ptr long) +@ stdcall PropVariantToStringAlloc(ptr ptr) @ stub PropVariantToStringVector @ stub PropVariantToStringVectorAlloc @ stub PropVariantToStringWithDefault diff --git a/dlls/propsys/propvar.c b/dlls/propsys/propvar.c index 54d72ad..f8abd2e 100644 --- a/dlls/propsys/propvar.c +++ b/dlls/propsys/propvar.c @@ -32,6 +32,7 @@ #include "propvarutil.h" #include "mimeole.h" #include "oleauto.h" +#include "strsafe.h"
#include "wine/debug.h" #include "wine/unicode.h" @@ -213,6 +214,263 @@ HRESULT WINAPI PropVariantToUInt64(REFPROPVARIANT propvarIn, ULONGLONG *ret) return hr; }
+static HRESULT PropVariantToString_Size(REFPROPVARIANT propvarIn, UINT *pcch) +{ + HRESULT hr = S_OK; + + TRACE("%p %p\n", propvarIn, pcch); + + *pcch = 0; + + switch (propvarIn->vt) + { + case VT_EMPTY: + *pcch = 1; + break; + case VT_I1: + case VT_I2: + case VT_I4: + case VT_I8: + case VT_INT: + case VT_UI1: + case VT_UI2: + case VT_UI4: + case VT_UI8: + case VT_UINT: + *pcch = 22; /* enough for all 64bit integers */ + break; + case VT_LPWSTR: + *pcch = lstrlenW(propvarIn->u.pwszVal) + 1; + break; + case VT_LPSTR: + /* PropVariantToString converts narrow strings to wide strings */ + *pcch = MultiByteToWideChar(CP_ACP, 0, propvarIn->u.pszVal, -1, NULL, 0); + break; + case VT_BSTR: + *pcch = SysStringLen(propvarIn->u.bstrVal) + 1; + break; + default: + FIXME("Unsupported variant type %d\n", propvarIn->vt); + hr = E_NOTIMPL; + } + + return hr; +} + +/* snprintfW() still doesn't support 64bit integers on 32bit platforms :( */ +static UINT i64_to_wstr(LONGLONG llVal, WCHAR *buf) +{ + UINT size = 0; + LONGLONG i; + + /* special case 0 */ + if (llVal == 0) + { + buf[0] = '0'; + buf[1] = 0; + return 1; + } + + /* calculate size needed */ + if (llVal < 0) + size += 1; + + for (i = llVal; i != 0; i /= 10) + size++; + + /* offset the buffer */ + buf += size; + + /* insert terminating null */ + *buf-- = 0; + + /* begin writing */ + for (i = llVal; i != 0; i /= 10) + { + *buf-- = '0' + abs(i % 10); + } + + if (llVal < 0) + *buf = '-'; + + return size; +} + +static UINT u64_to_wstr(ULONGLONG ullVal, WCHAR *buf) +{ + UINT size = 0; + ULONGLONG i; + + /* special case 0 */ + if (ullVal == 0) + { + buf[0] = '0'; + buf[1] = 0; + return 1; + } + + /* calculate size needed */ + for (i = ullVal; i > 0; i /= 10) + size++; + + /* offset the buffer */ + buf += size; + + /* insert terminating null */ + *buf-- = 0; + + /* begin writing */ + for (i = ullVal; i > 0; i /= 10) + { + *buf-- = '0' + (i % 10); + } + + return size; +} + +HRESULT WINAPI PropVariantToString(REFPROPVARIANT propvar, WCHAR *buf, UINT cch) +{ + HRESULT hr = S_OK; + WCHAR numBuffer[22]; + + TRACE("%p %p %u\n", propvar, buf, cch); + + if (cch < 1) + return E_FAIL; + + switch (propvar->vt) + { + case VT_EMPTY: + *buf = 0; + break; + case VT_I1: + case VT_I2: + case VT_I4: + case VT_I8: + case VT_INT: + { + LONGLONG ll; + + hr = PropVariantToInt64(propvar, &ll); + if (FAILED(hr)) + break; + + i64_to_wstr(ll, numBuffer); + hr = StringCchCopyW(buf, cch, numBuffer); + break; + } + case VT_UI1: + case VT_UI2: + case VT_UI4: + case VT_UI8: + case VT_UINT: + { + ULONGLONG ll; + + hr = PropVariantToUInt64(propvar, &ll); + if (FAILED(hr)) + break; + + u64_to_wstr(ll, numBuffer); + hr = StringCchCopyW(buf, cch, numBuffer); + break; + } + case VT_LPWSTR: + { + hr = StringCchCopyW(buf, cch, propvar->u.pwszVal); + break; + } + case VT_LPSTR: + { + UINT len = MultiByteToWideChar(CP_ACP, 0, propvar->u.pszVal, -1, NULL, 0); + if (len == 0) + { + hr = E_FAIL; + break; + } + + if (len <= cch) /* null terminator is already included */ + { + MultiByteToWideChar(CP_ACP, 0, propvar->u.pszVal, -1, buf, len); + } + else + { + WCHAR *tmp; + + /* Allocate larger buffer, copy truncated result */ + tmp = CoTaskMemAlloc(len * sizeof(WCHAR)); + if (tmp) + { + MultiByteToWideChar(CP_ACP, 0, propvar->u.pszVal, -1, tmp, len); + hr = StringCchCopyW(buf, cch, tmp); + CoTaskMemFree(tmp); + } + else + { + TRACE("No memory left to allocate a temporary buffer\n"); + hr = E_OUTOFMEMORY; + } + } + break; + } + case VT_BSTR: + { + hr = StringCchCopyW(buf, cch, propvar->u.bstrVal); + break; + } + default: + FIXME("Unsupported variant type %d\n", propvar->vt); + hr = E_NOTIMPL; + } + + return hr; +} + +HRESULT WINAPI PropVariantToStringAlloc(REFPROPVARIANT prop, WCHAR **pbuf) +{ + HRESULT hr; + UINT size; + + TRACE("%p %p\n", prop, pbuf); + + hr = PropVariantToString_Size(prop, &size); + if (FAILED(hr)) + return hr; + + *pbuf = CoTaskMemAlloc(size * sizeof(WCHAR)); + if (!*pbuf) + return E_OUTOFMEMORY; + + hr = PropVariantToString(prop, *pbuf, size); + if (FAILED(hr)) + { + CoTaskMemFree(*pbuf); + *pbuf = NULL; + } + + return hr; +} + +HRESULT WINAPI PropVariantToBSTR(REFPROPVARIANT prop, BSTR *pbstr) +{ + HRESULT hr; + WCHAR *buf; + + TRACE("%p %p\n", prop, pbstr); + + hr = PropVariantToStringAlloc(prop, &buf); + if (FAILED(hr)) + return hr; + + *pbstr = SysAllocString(buf); + if (!*pbstr) + hr = E_OUTOFMEMORY; + + CoTaskMemFree(buf); + + return hr; +} + /****************************************************************** * PropVariantChangeType (PROPSYS.@) */ @@ -292,6 +550,31 @@ HRESULT WINAPI PropVariantChangeType(PROPVARIANT *ppropvarDest, REFPROPVARIANT p } return hr; } + case VT_LPWSTR: + { + WCHAR *str; + + hr = PropVariantToStringAlloc(propvarSrc, &str); + if (SUCCEEDED(hr)) + { + ppropvarDest->vt = VT_LPWSTR; + ppropvarDest->u.pwszVal = str; + } + return hr; + } + case VT_BSTR: + { + BSTR str; + + hr = PropVariantToBSTR(propvarSrc, &str); + if (SUCCEEDED(hr)) + { + ppropvarDest->vt = VT_BSTR; + ppropvarDest->u.bstrVal = str; + } + + return hr; + } }
switch (propvarSrc->vt) diff --git a/dlls/propsys/tests/propsys.c b/dlls/propsys/tests/propsys.c index 1199b0e..7be320d 100644 --- a/dlls/propsys/tests/propsys.c +++ b/dlls/propsys/tests/propsys.c @@ -31,6 +31,7 @@ #include "objbase.h" #include "propsys.h" #include "propvarutil.h" +#include "strsafe.h" #include "wine/test.h"
#include "initguid.h" @@ -713,7 +714,7 @@ static void test_PropVariantCompare(void) todo_wine ok(res == 0, "res=%i\n", res);
res = PropVariantCompareEx(&str_2, &i2_2, 0, 0); - todo_wine ok(res == 0, "res=%i\n", res); + ok(res == 0, "res=%i\n", res);
res = PropVariantCompareEx(&str_02, &i2_2, 0, 0); ok(res == -1, "res=%i\n", res); @@ -865,6 +866,82 @@ static void test_intconversions(void) ok(llval == -7, "got wrong value %s\n", debugstr_longlong(llval)); }
+static void test_stringify(void) +{ + PROPVARIANT propvar; + SHORT sVal = -235; + WCHAR sValStr[] = { '-', '2', '3', '5', 0 }; + LONG lVal = 0; + WCHAR lValStr[] = { '0', 0 }; + ULONGLONG ullVal = 0xdeafbabecafebeef; + WCHAR ullValStr[] = { '1','6','0','4','6','2','4','9','3','2','5','9','5','6','6','1','1','8','2','3',0 }; + WCHAR wszVal[] = { 'H','e','l','l','o','W','o','r','l','d',0 }; + char szVal[] = "HelloWorld"; + WCHAR buffer[22]; + WCHAR smallBuffer[10]; + HRESULT hr; + + ZeroMemory(buffer, sizeof(buffer)); + ZeroMemory(smallBuffer, sizeof(smallBuffer)); + + /* Warm up using a small integer */ + propvar.vt = VT_I2; + propvar.u.iVal = sVal; + + hr = PropVariantToString(&propvar, buffer, sizeof(buffer)/sizeof(buffer[0])); + ok(hr == S_OK, "PropVariantToString(VT_I2) failed, hr=%08x\n", hr); + ok(!lstrcmpW(buffer, sValStr), "Unexpected result %s\n", wine_dbgstr_w(buffer)); + + /* Test zero */ + propvar.vt = VT_I4; + propvar.u.lVal = lVal; + + hr = PropVariantToString(&propvar, buffer, sizeof(buffer)/sizeof(buffer[0])); + ok(hr == S_OK, "PropVariantToString(VT_I2) failed, hr=%08x\n", hr); + ok(!lstrcmpW(buffer, lValStr), "Unexpected result %s\n", wine_dbgstr_w(buffer)); + + /* Test bigger integers */ + propvar.vt = VT_UI8; + propvar.u.uhVal.QuadPart = ullVal; + hr = PropVariantToString(&propvar, buffer, sizeof(buffer)/sizeof(buffer[0])); + ok(hr == S_OK, "PropVariantToString(VT_UI8) failed, hr=%08x\n", hr); + ok(!lstrcmpW(buffer, ullValStr), "Unexpected result %s\n", wine_dbgstr_w(buffer)); + + /* Test truncation */ + hr = PropVariantToString(&propvar, smallBuffer, sizeof(smallBuffer)/sizeof(buffer[0])); + ok(hr == STRSAFE_E_INSUFFICIENT_BUFFER, "Unexpected hr=%08x\n", hr); + ok(!memcmp(ullValStr, smallBuffer, lstrlenW(smallBuffer)*sizeof(WCHAR)), + "Unexpected result %s\n", wine_dbgstr_w(smallBuffer)); + + /* Test narrow string conversion */ + propvar.vt = VT_LPSTR; + propvar.u.pszVal = szVal; + + hr = PropVariantToString(&propvar, buffer, sizeof(buffer)/sizeof(buffer[0])); + ok(hr == S_OK, "PropVariantToString(VT_LPSTR) failed, hr=%08x\n", hr); + ok(!lstrcmpW(buffer, wszVal), "Unexpected result %s\n", wine_dbgstr_w(buffer)); + + /* Test wide string copy */ + propvar.vt = VT_LPWSTR; + propvar.u.pwszVal = wszVal; + + hr = PropVariantToString(&propvar, buffer, sizeof(buffer)/sizeof(buffer[0])); + ok(hr == S_OK, "PropVariantToString(VT_LPWSTR) failed, hr=%08x\n", hr); + ok(!lstrcmpW(buffer, wszVal), "Unexpected result %s\n", wine_dbgstr_w(buffer)); + + /* Test bstr */ + propvar.vt = VT_BSTR; + propvar.u.bstrVal = SysAllocString(wszVal); + + hr = PropVariantToString(&propvar, buffer, sizeof(buffer)/sizeof(buffer[0])); + ok(hr == S_OK, "PropVariantToString(VT_BSTR) failed, hr=%08x\n", hr); + ok(!lstrcmpW(buffer, wszVal), "Unexpected result %s\n", wine_dbgstr_w(buffer)); + + SysFreeString(propvar.u.bstrVal); + + /* TODO test all other funny strigifications */ +} + static char *buffer_printable(void *buffer, size_t buffer_size) { char *heap_buffer, *p; @@ -1247,6 +1324,7 @@ START_TEST(propsys) test_InitPropVariantFromBuffer(); test_PropVariantToGUID(); test_PropVariantCompare(); + test_stringify(); test_intconversions(); test_serialization(); } diff --git a/include/propvarutil.h b/include/propvarutil.h index 0a35ca8..3e6defb 100644 --- a/include/propvarutil.h +++ b/include/propvarutil.h @@ -78,6 +78,9 @@ HRESULT WINAPI PropVariantToInt64(REFPROPVARIANT propvarIn, LONGLONG *ret); HRESULT WINAPI PropVariantToUInt16(REFPROPVARIANT propvarIn, USHORT *ret); HRESULT WINAPI PropVariantToUInt32(REFPROPVARIANT propvarIn, ULONG *ret); HRESULT WINAPI PropVariantToUInt64(REFPROPVARIANT propvarIn, ULONGLONG *ret); +HRESULT WINAPI PropVariantToString(REFPROPVARIANT propvarIn, WCHAR *psz, UINT cch); +HRESULT WINAPI PropVariantToBSTR(REFPROPVARIANT propvarIn, BSTR *pbstr); +HRESULT WINAPI PropVariantToStringAlloc(REFPROPVARIANT propvarIn, WCHAR **ppszOut);
#ifdef NO_PROPVAR_INLINES HRESULT InitPropVariantFromBoolean(BOOL fVal, PROPVARIANT *ppropvar); diff --git a/include/strsafe.h b/include/strsafe.h index 842075f..a69d5cc 100644 --- a/include/strsafe.h +++ b/include/strsafe.h @@ -29,4 +29,21 @@ #define STRSAFE_E_INVALID_PARAM ((HRESULT)0x80070075) #define STRSAFE_E_END_OF_FILE ((HRESULT)0x80070026)
+static inline HRESULT StringCchCopyW(WCHAR *pszDest, size_t cchDest, const WCHAR *pszSrc) +{ + /* On windows, STRSAFE_MAX_CCH is also checked. We don't care. */ + if (!cchDest) + return STRSAFE_E_INSUFFICIENT_BUFFER; + + for (; cchDest > 1 && *pszSrc; --cchDest) + *pszDest++ = *pszSrc++; + + *pszDest = 0; /* always null-terminate */ + + if (*pszSrc) /* there is still data left */ + return STRSAFE_E_INSUFFICIENT_BUFFER; + + return S_OK; +} + #endif