[PATCH v2 09/15] propsys: Partially implement property stringification
Jonas Kümmerlin
rgcjonas at gmail.com
Sat Jul 18 11:26:48 CDT 2015
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
--
2.4.3
More information about the wine-devel
mailing list