diff --git a/dlls/ole32/itemmoniker.c b/dlls/ole32/itemmoniker.c index 2d325ff50cb..bd6fd6fd87d 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; } +static HRESULT item_moniker_load_string_record(IStream *stream, WCHAR **ret) +{ + DWORD str_len, read_len, lenW, i; + HRESULT hr = S_OK; + char *buffer; + WCHAR *str; + + IStream_Read(stream, &str_len, sizeof(str_len), &read_len); + if (read_len != sizeof(str_len)) + return E_FAIL; + + if (!str_len) + { + heap_free(*ret); + *ret = NULL; + return S_OK; + } + + if (!(buffer = heap_alloc(str_len))) + return E_OUTOFMEMORY; + + IStream_Read(stream, buffer, str_len, &read_len); + if (read_len != str_len) + { + heap_free(buffer); + return E_FAIL; + } + + /* Skip ansi buffer, it must be null terminated. */ + i = 0; + while (i < str_len && buffer[i]) + i++; + + if (buffer[i]) + { + WARN("Expected null terminated ansi name.\n"); + hr = E_FAIL; + goto end; + } + + if (i < str_len - 1) + { + str_len -= i + 1; + + 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 + { + 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); + } + + if (str) + { + heap_free(*ret); + *ret = str; + } + else + hr = E_OUTOFMEMORY; + +end: + heap_free(buffer); + + return hr; +} + /****************************************************************************** * ItemMoniker_Load ******************************************************************************/ -static HRESULT WINAPI ItemMonikerImpl_Load(IMoniker* iface,IStream* pStm) +static HRESULT WINAPI ItemMonikerImpl_Load(IMoniker *iface, IStream *stream) { ItemMonikerImpl *This = impl_from_IMoniker(iface); - HRESULT res; - DWORD delimiterLength,nameLength,lenW; - CHAR *itemNameA,*itemDelimiterA; - ULONG bread; + HRESULT hr; - TRACE("\n"); + TRACE("(%p, %p)\n", iface, stream); - /* for more details about data read by this function see comments of ItemMonikerImpl_Save function */ + /* 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. */ - /* read item delimiter string length + 1 */ - res=IStream_Read(pStm,&delimiterLength,sizeof(DWORD),&bread); - if (bread != sizeof(DWORD)) - return E_FAIL; + hr = item_moniker_load_string_record(stream, &This->itemDelimiter); + if (SUCCEEDED(hr)) + hr = item_moniker_load_string_record(stream, &This->itemName); - /* read item delimiter string */ - if (!(itemDelimiterA=HeapAlloc(GetProcessHeap(),0,delimiterLength))) - return E_OUTOFMEMORY; - res=IStream_Read(pStm,itemDelimiterA,delimiterLength,&bread); - if (bread != delimiterLength) - { - HeapFree( GetProcessHeap(), 0, itemDelimiterA ); - 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) - { - HeapFree( GetProcessHeap(), 0, itemDelimiterA ); - return E_OUTOFMEMORY; - } - 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; - - /* read item name string */ - if (!(itemNameA=HeapAlloc(GetProcessHeap(),0,nameLength))) - return E_OUTOFMEMORY; - res=IStream_Read(pStm,itemNameA,nameLength,&bread); - if (bread != nameLength) - { - HeapFree( GetProcessHeap(), 0, itemNameA ); - return E_FAIL; - } - - lenW = MultiByteToWideChar( CP_ACP, 0, itemNameA, -1, NULL, 0 ); - This->itemName=HeapReAlloc(GetProcessHeap(),0,This->itemName,lenW*sizeof(WCHAR)); - if (!This->itemName) - { - HeapFree( GetProcessHeap(), 0, itemNameA ); - return E_OUTOFMEMORY; - } - MultiByteToWideChar( CP_ACP, 0, itemNameA, -1, This->itemName, lenW ); - HeapFree( GetProcessHeap(), 0, itemNameA ); - - return res; + return hr; } /****************************************************************************** diff --git a/dlls/ole32/tests/moniker.c b/dlls/ole32/tests/moniker.c index 8b88ae4a0d7..d911ce57e91 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);