[PATCH 5/6] propsys: Partially implement property stringification

Jonas Kümmerlin rgcjonas at gmail.com
Tue Jul 14 10:07:45 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       | 319 +++++++++++++++++++++++++++++++++++++++++++
 dlls/propsys/tests/propsys.c |  80 ++++++++++-
 include/propvarutil.h        |   3 +
 4 files changed, 404 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 e7ba3f3..df195aa 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"
@@ -211,6 +212,299 @@ 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 = 0;
+        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 = 21; /* enough for all 64bit integers */
+        break;
+    case VT_LPWSTR:
+        *pcch = lstrlenW(propvarIn->u.pwszVal);
+        break;
+    case VT_LPSTR:
+        *pcch = MultiByteToWideChar(CP_ACP, 0, propvarIn->u.pszVal, -1, NULL, 0);
+        break;
+    case VT_BSTR:
+        *pcch = SysStringLen(propvarIn->u.bstrVal);
+        break;
+    default:
+        FIXME("Unsupported variant type %d\n", propvarIn->vt);
+        hr = E_NOTIMPL;
+    }
+
+    return hr;
+}
+
+/* FIXME: It is 2015, why do I have to write this? */
+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++;
+
+    TRACE("Required size: %u chars\n", 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;
+        UINT numLen;
+
+        hr = PropVariantToInt64(propvar, &ll);
+        if (FAILED(hr))
+            break;
+
+        numLen = i64_to_wstr(ll, numBuffer);
+
+        if (numLen < cch)
+        {
+            memcpy(buf, numBuffer, (numLen+1)*sizeof(WCHAR));
+        }
+        else
+        {
+            memcpy(buf, numBuffer, (cch-1)*sizeof(WCHAR));
+            buf[cch - 1] = 0;
+            hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+        }
+
+        break;
+    }
+    case VT_UI1:
+    case VT_UI2:
+    case VT_UI4:
+    case VT_UI8:
+    case VT_UINT:
+    {
+        ULONGLONG ll;
+        UINT numLen;
+
+        hr = PropVariantToUInt64(propvar, &ll);
+        if (FAILED(hr))
+            break;
+
+        numLen = u64_to_wstr(ll, numBuffer);
+
+        if (numLen < cch)
+        {
+            memcpy(buf, numBuffer, (numLen+1)*sizeof(WCHAR));
+        }
+        else
+        {
+            memcpy(buf, numBuffer, (cch-1)*sizeof(WCHAR));
+            buf[cch - 1] = 0;
+            hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+        }
+
+        break;
+    }
+    case VT_LPWSTR:
+    {
+        UINT len = lstrlenW(propvar->u.pwszVal);
+
+        if (len < cch)
+        {
+            memcpy(buf, propvar->u.pwszVal, (len+1)*sizeof(WCHAR));
+        }
+        else
+        {
+            memcpy(buf, propvar->u.pwszVal, (cch-1)*sizeof(WCHAR));
+            buf[cch-1] = 0;
+            hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+        }
+        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)
+        {
+            MultiByteToWideChar(CP_ACP, 0, propvar->u.pszVal, -1, buf, len+1);
+        }
+        else
+        {
+            /* TODO: allocate large buffer, copy truncated result */
+            FIXME("Truncating narrow string not implemented\n");
+            hr = E_NOTIMPL;
+        }
+        break;
+    }
+    case VT_BSTR:
+    {
+        UINT len = SysStringLen(propvar->u.bstrVal);
+
+        if (len < cch)
+        {
+            memcpy(buf, propvar->u.bstrVal, len*sizeof(WCHAR));
+            buf[len] = 0;
+        }
+        else
+        {
+            memcpy(buf, propvar->u.bstrVal, (cch-1)*sizeof(WCHAR));
+            buf[cch-1] = 0;
+            hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+        }
+        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+1) * sizeof(WCHAR));
+    if (!*pbuf)
+        return E_OUTOFMEMORY;
+
+    hr = PropVariantToString(prop, *pbuf, size+1);
+    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.@)
  */
@@ -290,6 +584,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 acc35fa..347df4f 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;
@@ -1250,6 +1327,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 a2ec2af..571588a 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);
-- 
2.4.3




More information about the wine-devel mailing list