[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