[PATCH 4/4] ole32: Handle optional WCHAR data when loading item moniker.

Nikolay Sivov nsivov at codeweavers.com
Fri Dec 13 04:06:56 CST 2019


Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---
 dlls/ole32/itemmoniker.c   | 128 +++++++++++++++++++++++--------------
 dlls/ole32/tests/moniker.c |  75 ++++++++++++++++++++--
 2 files changed, 151 insertions(+), 52 deletions(-)

diff --git a/dlls/ole32/itemmoniker.c b/dlls/ole32/itemmoniker.c
index 2d325ff50c..bd6fd6fd87 100644
--- a/dlls/ole32/itemmoniker.c
+++ b/dlls/ole32/itemmoniker.c
@@ -157,72 +157,106 @@ static HRESULT WINAPI ItemMonikerImpl_IsDirty(IMoniker* iface)
     return S_FALSE;
 }
 
-/******************************************************************************
- *        ItemMoniker_Load
- ******************************************************************************/
-static HRESULT WINAPI ItemMonikerImpl_Load(IMoniker* iface,IStream* pStm)
+static HRESULT item_moniker_load_string_record(IStream *stream, WCHAR **ret)
 {
-    ItemMonikerImpl *This = impl_from_IMoniker(iface);
-    HRESULT res;
-    DWORD delimiterLength,nameLength,lenW;
-    CHAR *itemNameA,*itemDelimiterA;
-    ULONG bread;
-
-    TRACE("\n");
+    DWORD str_len, read_len, lenW, i;
+    HRESULT hr = S_OK;
+    char *buffer;
+    WCHAR *str;
 
-    /* for more details about data read by this function see comments of ItemMonikerImpl_Save function */
-
-    /* read item delimiter string length + 1 */
-    res=IStream_Read(pStm,&delimiterLength,sizeof(DWORD),&bread);
-    if (bread != sizeof(DWORD))
+    IStream_Read(stream, &str_len, sizeof(str_len), &read_len);
+    if (read_len != sizeof(str_len))
         return E_FAIL;
 
-    /* read item delimiter string */
-    if (!(itemDelimiterA=HeapAlloc(GetProcessHeap(),0,delimiterLength)))
+    if (!str_len)
+    {
+        heap_free(*ret);
+        *ret = NULL;
+        return S_OK;
+    }
+
+    if (!(buffer = heap_alloc(str_len)))
         return E_OUTOFMEMORY;
-    res=IStream_Read(pStm,itemDelimiterA,delimiterLength,&bread);
-    if (bread != delimiterLength)
+
+    IStream_Read(stream, buffer, str_len, &read_len);
+    if (read_len != str_len)
     {
-        HeapFree( GetProcessHeap(), 0, itemDelimiterA );
+        heap_free(buffer);
         return E_FAIL;
     }
 
-    lenW = MultiByteToWideChar( CP_ACP, 0, itemDelimiterA, -1, NULL, 0 );
-    This->itemDelimiter=HeapReAlloc(GetProcessHeap(),0,This->itemDelimiter,lenW*sizeof(WCHAR));
-    if (!This->itemDelimiter)
+    /* Skip ansi buffer, it must be null terminated. */
+    i = 0;
+    while (i < str_len && buffer[i])
+        i++;
+
+    if (buffer[i])
     {
-        HeapFree( GetProcessHeap(), 0, itemDelimiterA );
-        return E_OUTOFMEMORY;
+        WARN("Expected null terminated ansi name.\n");
+        hr = E_FAIL;
+        goto end;
     }
-    MultiByteToWideChar( CP_ACP, 0, itemDelimiterA, -1, This->itemDelimiter, lenW );
-    HeapFree( GetProcessHeap(), 0, itemDelimiterA );
 
-    /* read item name string length + 1*/
-    res=IStream_Read(pStm,&nameLength,sizeof(DWORD),&bread);
-    if (bread != sizeof(DWORD))
-        return E_FAIL;
+    if (i < str_len - 1)
+    {
+        str_len -= i + 1;
 
-    /* read item name string */
-    if (!(itemNameA=HeapAlloc(GetProcessHeap(),0,nameLength)))
-        return E_OUTOFMEMORY;
-    res=IStream_Read(pStm,itemNameA,nameLength,&bread);
-    if (bread != nameLength)
+        if (str_len % sizeof(WCHAR))
+        {
+            WARN("Unexpected Unicode name length %d.\n", str_len);
+            hr = E_FAIL;
+            goto end;
+        }
+
+        str = heap_alloc(str_len + sizeof(WCHAR));
+        if (str)
+        {
+            memcpy(str, &buffer[i + 1], str_len);
+            str[str_len / sizeof(WCHAR)] = 0;
+        }
+    }
+    else
     {
-        HeapFree( GetProcessHeap(), 0, itemNameA );
-        return E_FAIL;
+        lenW = MultiByteToWideChar(CP_ACP, 0, buffer, -1, NULL, 0);
+        str = heap_alloc(lenW * sizeof(WCHAR));
+        if (str)
+            MultiByteToWideChar(CP_ACP, 0, buffer, -1, str, lenW);
     }
 
-    lenW = MultiByteToWideChar( CP_ACP, 0, itemNameA, -1, NULL, 0 );
-    This->itemName=HeapReAlloc(GetProcessHeap(),0,This->itemName,lenW*sizeof(WCHAR));
-    if (!This->itemName)
+    if (str)
     {
-        HeapFree( GetProcessHeap(), 0, itemNameA );
-        return E_OUTOFMEMORY;
+        heap_free(*ret);
+        *ret = str;
     }
-    MultiByteToWideChar( CP_ACP, 0, itemNameA, -1, This->itemName, lenW );
-    HeapFree( GetProcessHeap(), 0, itemNameA );
+    else
+        hr = E_OUTOFMEMORY;
 
-    return res;
+end:
+    heap_free(buffer);
+
+    return hr;
+}
+
+/******************************************************************************
+ *        ItemMoniker_Load
+ ******************************************************************************/
+static HRESULT WINAPI ItemMonikerImpl_Load(IMoniker *iface, IStream *stream)
+{
+    ItemMonikerImpl *This = impl_from_IMoniker(iface);
+    HRESULT hr;
+
+    TRACE("(%p, %p)\n", iface, stream);
+
+    /* Delimiter and name use the same record structure: 4 bytes byte-length field, followed by
+       string data. Data starts with single byte null-terminated string, WCHAR non-terminated
+       string optionally follows. Length of WCHAR string is determined as a difference between total
+       byte-length and single byte string length. */
+
+    hr = item_moniker_load_string_record(stream, &This->itemDelimiter);
+    if (SUCCEEDED(hr))
+        hr = item_moniker_load_string_record(stream, &This->itemName);
+
+    return hr;
 }
 
 /******************************************************************************
diff --git a/dlls/ole32/tests/moniker.c b/dlls/ole32/tests/moniker.c
index 8b88ae4a0d..d911ce57e9 100644
--- a/dlls/ole32/tests/moniker.c
+++ b/dlls/ole32/tests/moniker.c
@@ -1674,9 +1674,37 @@ static void test_file_monikers(void)
 
 static void test_item_moniker(void)
 {
+    static const char item_moniker_unicode_delim_stream[] =
+    {
+        0x05, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, '!',
+        0x00, 0x02, 0x00, 0x00, 0x00,  'A', 0x00,
+    };
+    static const char item_moniker_unicode_item_stream[] =
+    {
+        0x02, 0x00, 0x00, 0x00,  '!', 0x00, 0x05, 0x00,
+        0x00, 0x00, 0xff, 0xff, 0x00,  'B', 0x00,
+    };
+    static const char item_moniker_unicode_delim_item_stream[] =
+    {
+        0x05, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00,  '!',
+        0x00, 0x06, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+        0x00,  'C', 0x00,
+    };
+    static struct
+    {
+        const char *data;
+        int data_len;
+        const WCHAR *display_name;
+    }
+    item_moniker_data[] =
+    {
+        { item_moniker_unicode_delim_stream, sizeof(item_moniker_unicode_delim_stream), L"!A" },
+        { item_moniker_unicode_item_stream, sizeof(item_moniker_unicode_item_stream), L"!B" },
+        { item_moniker_unicode_delim_item_stream, sizeof(item_moniker_unicode_delim_item_stream), L"!C" },
+    };
+    IMoniker *moniker, *moniker2;
     HRESULT hr;
-    IMoniker *moniker;
-    DWORD moniker_type;
+    DWORD moniker_type, i;
     DWORD hash;
     IBindCtx *bindctx;
     IMoniker *inverse;
@@ -1684,6 +1712,9 @@ static void test_item_moniker(void)
     static const WCHAR wszDelimiter[] = {'!',0};
     static const WCHAR wszObjectName[] = {'T','e','s','t',0};
     static const WCHAR expected_display_name[] = { '!','T','e','s','t',0 };
+    WCHAR *display_name;
+    LARGE_INTEGER pos;
+    IStream *stream;
 
     hr = CreateItemMoniker(NULL, wszObjectName, &moniker);
     ok(hr == S_OK, "Failed to create item moniker, hr %#x.\n", hr);
@@ -1727,6 +1758,43 @@ static void test_item_moniker(void)
         expected_item_moniker_comparison_data5, sizeof(expected_item_moniker_comparison_data5),
         58, L"abTest");
 
+    /* Serialize and load back. */
+    hr = CreateItemMoniker(NULL, L"object", &moniker2);
+    ok(hr == S_OK, "Failed to create item moniker, hr %#x.\n", hr);
+
+    hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
+    ok(hr == S_OK, "Failed to create a stream, hr %#x.\n", hr);
+
+    hr = CreateBindCtx(0, &bindctx);
+    ok(hr == S_OK, "Failed to create bind context, hr %#x.\n", hr);
+
+    for (i = 0; i < ARRAY_SIZE(item_moniker_data); ++i)
+    {
+        pos.QuadPart = 0;
+        hr = IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
+        ok(hr == S_OK, "Failed to seek stream, hr %#x.\n", hr);
+
+        hr = IStream_Write(stream, item_moniker_data[i].data, item_moniker_data[i].data_len, NULL);
+        ok(hr == S_OK, "Failed to write stream contents, hr %#x.\n", hr);
+
+        pos.QuadPart = 0;
+        hr = IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
+        ok(hr == S_OK, "Failed to seek stream, hr %#x.\n", hr);
+
+        hr = IMoniker_Load(moniker2, stream);
+        ok(hr == S_OK, "Failed to load moniker, hr %#x.\n", hr);
+
+        hr = IMoniker_GetDisplayName(moniker2, bindctx, NULL, &display_name);
+        ok(hr == S_OK, "Failed to get display name, hr %#x.\n", hr);
+        ok(!lstrcmpW(display_name, item_moniker_data[i].display_name), "%d: unexpected display name %s.\n",
+                i, wine_dbgstr_w(display_name));
+
+        CoTaskMemFree(display_name);
+    }
+
+    IStream_Release(stream);
+
+    IMoniker_Release(moniker2);
     IMoniker_Release(moniker);
 
     hr = CreateItemMoniker(wszDelimiter, wszObjectName, &moniker);
@@ -1756,9 +1824,6 @@ static void test_item_moniker(void)
         "dwMkSys != MKSYS_ITEMMONIKER, instead was 0x%08x\n",
         moniker_type);
 
-    hr = CreateBindCtx(0, &bindctx);
-    ok_ole_success(hr, CreateBindCtx);
-
     /* IsRunning test */
     hr = IMoniker_IsRunning(moniker, NULL, NULL, NULL);
     ok(hr == E_INVALIDARG, "IMoniker_IsRunning should return E_INVALIDARG, not 0x%08x\n", hr);
-- 
2.24.0




More information about the wine-devel mailing list