From 0cd3b555b1f12f7d8396d88823c1e0fa49d45982 Mon Sep 17 00:00:00 2001 From: Katayama Hirofumi MZ Date: Sun, 3 May 2026 10:10:27 +0900 Subject: [PATCH] [SHLWAPI][SHLWAPI_APITEST][SDK] Support IStream_ReadPidl and IStream_WritePidl (#8918) Implementing missing features... JIRA issue: CORE-19278 - Implement IStream_ReadPidl and IStream_WritePidl functions. - Add prototypes to . - Add IStreamPidl testcase. --- dll/win32/shlwapi/istream.c | 75 ++++ dll/win32/shlwapi/shlwapi.spec | 4 +- .../rostests/apitests/shlwapi/CMakeLists.txt | 1 + .../rostests/apitests/shlwapi/IStreamPidl.cpp | 390 ++++++++++++++++++ modules/rostests/apitests/shlwapi/testlist.c | 2 + sdk/include/reactos/shlwapi_undoc.h | 3 + 6 files changed, 473 insertions(+), 2 deletions(-) create mode 100644 modules/rostests/apitests/shlwapi/IStreamPidl.cpp diff --git a/dll/win32/shlwapi/istream.c b/dll/win32/shlwapi/istream.c index e773599c45f..85b56ec2272 100644 --- a/dll/win32/shlwapi/istream.c +++ b/dll/win32/shlwapi/istream.c @@ -30,6 +30,9 @@ #define NO_SHLWAPI_REG #define NO_SHLWAPI_PATH #include "shlwapi.h" +#ifdef __REACTOS__ +#include "shlobj.h" +#endif #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(shell); @@ -673,3 +676,75 @@ HRESULT WINAPI IStream_Size(IStream *lpStream, ULARGE_INTEGER* lpulSize) *lpulSize = statstg.cbSize; return hRet; } + +#ifdef __REACTOS__ +/************************************************************************* + * IStream_ReadPidl [SHLWAPI.512] + * + * https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/util/istream/readpidl.htm + */ +HRESULT WINAPI +IStream_ReadPidl(_In_ IStream *pstm, _Out_ LPITEMIDLIST *ppidlOut) +{ + LPITEMIDLIST pidl, pidlEnd; + LPSHITEMID pItem; + UINT cbSize; + HRESULT hr; + + *ppidlOut = NULL; + + hr = SHIStream_Read(pstm, &cbSize, sizeof(cbSize)); + if (FAILED(hr)) + return hr; + + if (cbSize < sizeof(USHORT)) + return E_INVALIDARG; + + pidl = CoTaskMemAlloc(cbSize); + if (!pidl) + return E_OUTOFMEMORY; + + hr = SHIStream_Read(pstm, pidl, cbSize); + if (FAILED(hr)) + { + CoTaskMemFree(pidl); + return hr; + } + + pidlEnd = (LPITEMIDLIST)((PBYTE)pidl + cbSize - sizeof(USHORT)); + for (pItem = &pidl->mkid; pItem <= (LPSHITEMID)pidlEnd; + pItem = (LPSHITEMID)((PBYTE)pItem + pItem->cb)) + { + if (!pItem->cb) + break; + } + + if ((LPITEMIDLIST)pItem == pidlEnd && !pItem->cb) + { + *ppidlOut = pidl; + hr = S_OK; + } + else + { + CoTaskMemFree(pidl); + hr = E_INVALIDARG; + } + + return hr; +} + +/************************************************************************* + * IStream_WritePidl [SHLWAPI.513] + * + * https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/util/istream/writepidl.htm + */ +HRESULT WINAPI +IStream_WritePidl(_In_ IStream *pstm, _In_ LPCITEMIDLIST pidlWrite) +{ + UINT cbSize = ILGetSize(pidlWrite); + HRESULT hr = SHIStream_Write(pstm, &cbSize, sizeof(cbSize)); + if (FAILED(hr)) + return hr; + return SHIStream_Write(pstm, pidlWrite, cbSize); +} +#endif /* def __REACTOS__ */ diff --git a/dll/win32/shlwapi/shlwapi.spec b/dll/win32/shlwapi/shlwapi.spec index 82055a8c358..1e706500db0 100644 --- a/dll/win32/shlwapi/shlwapi.spec +++ b/dll/win32/shlwapi/shlwapi.spec @@ -509,8 +509,8 @@ 509 stdcall -noname IUnknown_OnFocusChangeIS(ptr ptr long) 510 stdcall -noname SHLockSharedEx(ptr long long) 511 stdcall -noname PathFileExistsDefExtAndAttributesW(wstr long ptr) -512 stub -ordinal IStream_ReadPidl -513 stub -ordinal IStream_WritePidl +512 stdcall -noname IStream_ReadPidl(ptr ptr) +513 stdcall -noname IStream_WritePidl(ptr ptr) 514 stdcall -noname IUnknown_ProfferService(ptr ptr ptr ptr) 515 stdcall -ordinal SHGetViewStatePropertyBag(ptr wstr long ptr ptr) 516 stdcall -noname SKGetValueW(long wstr wstr ptr ptr ptr) diff --git a/modules/rostests/apitests/shlwapi/CMakeLists.txt b/modules/rostests/apitests/shlwapi/CMakeLists.txt index 6b03f10ecc1..e4dbaf756dd 100644 --- a/modules/rostests/apitests/shlwapi/CMakeLists.txt +++ b/modules/rostests/apitests/shlwapi/CMakeLists.txt @@ -8,6 +8,7 @@ list(APPEND SOURCE AssocQueryString.c IShellFolderHelpers.cpp IsQSForward.cpp + IStreamPidl.cpp PathFileExistsDefExtAndAttributesW.c PathFindOnPath.c PathIsUNC.c diff --git a/modules/rostests/apitests/shlwapi/IStreamPidl.cpp b/modules/rostests/apitests/shlwapi/IStreamPidl.cpp new file mode 100644 index 00000000000..19a7124362c --- /dev/null +++ b/modules/rostests/apitests/shlwapi/IStreamPidl.cpp @@ -0,0 +1,390 @@ +/* + * PROJECT: ReactOS api tests + * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) + * PURPOSE: Tests for g_fnIStream_ReadPidl and g_fnIStream_WritePidl + * COPYRIGHT: Copyright 2026 Katayama Hirofumi MZ + */ + +#include +#include +#include +#include + +typedef HRESULT (WINAPI *FN_IStream_ReadPidl)(IStream *, _Out_ LPITEMIDLIST *); +typedef HRESULT (WINAPI *FN_IStream_WritePidl)(IStream *, LPCITEMIDLIST); +typedef UINT (WINAPI *FN_ILGetSize)(LPCITEMIDLIST); + +static FN_IStream_ReadPidl g_fnIStream_ReadPidl = NULL; +static FN_IStream_WritePidl g_fnIStream_WritePidl = NULL; +static FN_ILGetSize g_fnILGetSize = NULL; + +static LPITEMIDLIST MakeSimplePidl(const BYTE *data, UINT dataLen) +{ + UINT cbItem = (UINT)(sizeof(USHORT) + dataLen); + UINT cbTotal = cbItem + sizeof(USHORT); + LPITEMIDLIST pidl = (LPITEMIDLIST)CoTaskMemAlloc(cbTotal); + if (!pidl) + return NULL; + ZeroMemory(pidl, cbTotal); + pidl->mkid.cb = (USHORT)cbItem; + if (dataLen) + memcpy(pidl->mkid.abID, data, dataLen); + return pidl; +} + +static void RewindStream(IStream *pstm) +{ + LARGE_INTEGER li; + li.QuadPart = 0; + pstm->Seek(li, STREAM_SEEK_SET, NULL); +} + +static void Test_RoundTrip_SimplePidl(void) +{ + const BYTE data[] = {0x11, 0x22, 0x33}; + LPITEMIDLIST pidlSrc = MakeSimplePidl(data, sizeof(data)); + LPITEMIDLIST pidlDst = NULL; + HRESULT hr; + IStream *pstm; + + ok(pidlSrc != NULL, "pidlSrc was NULL.\n"); + if (!pidlSrc) + { + skip("pidlSrc was NULL.\n"); + return; + } + + pstm = SHCreateMemStream(NULL, 0); + ok(pstm != NULL, "pstm was NULL.\n"); + if (!pstm) + { + skip("pstm was NULL.\n"); + return; + } + + hr = g_fnIStream_WritePidl(pstm, pidlSrc); + ok_hr(hr, S_OK); + + RewindStream(pstm); + hr = g_fnIStream_ReadPidl(pstm, &pidlDst); + ok_hr(hr, S_OK); + ok(pidlDst != NULL, "pidlDst was NULL\n"); + + if (pidlDst) + { + UINT cbSrc = g_fnILGetSize(pidlSrc); + UINT cbDst = g_fnILGetSize(pidlDst); + ok_int(cbSrc, cbDst); + ok_int(memcmp(pidlSrc, pidlDst, cbSrc), 0); + CoTaskMemFree(pidlDst); + } + + pstm->Release(); + CoTaskMemFree(pidlSrc); +} + +static void Test_RoundTrip_EmptyPidl(void) +{ + LPITEMIDLIST pidlSrc = (LPITEMIDLIST)CoTaskMemAlloc(sizeof(USHORT)); + LPITEMIDLIST pidlDst = NULL; + HRESULT hr; + IStream *pstm; + + ok(pidlSrc != NULL, "CoTaskMemAlloc failed\n"); + if (!pidlSrc) + { + skip("pidlSrc was NULL\n"); + return; + } + + ZeroMemory(pidlSrc, sizeof(USHORT)); + + pstm = SHCreateMemStream(NULL, 0); + ok(pstm != NULL, "pstm was NULL\n"); + if (!pstm) + { + skip("pstm was NULL\n"); + return; + } + + hr = g_fnIStream_WritePidl(pstm, pidlSrc); + ok_hr(hr, S_OK); + + RewindStream(pstm); + hr = g_fnIStream_ReadPidl(pstm, &pidlDst); + ok_hr(hr, S_OK); + ok(pidlDst != NULL, "pidlDst was NULL\n"); + + if (pidlDst) + { + UINT cbSrc = g_fnILGetSize(pidlSrc); + UINT cbDst = g_fnILGetSize(pidlDst); + ok_int(cbSrc, cbDst); + ok_int(memcmp(pidlSrc, pidlDst, cbSrc), 0); + CoTaskMemFree(pidlDst); + } + + pstm->Release(); + CoTaskMemFree(pidlSrc); +} + +static void Test_RoundTrip_MultiItemPidl(void) +{ + UINT cbTotal = 4 + 5 + 2; + LPITEMIDLIST pidlSrc = (LPITEMIDLIST)CoTaskMemAlloc(cbTotal); + LPITEMIDLIST pidlDst = NULL; + PBYTE p; + HRESULT hr; + IStream *pstm; + + ok(pidlSrc != NULL, "CoTaskMemAlloc failed\n"); + if (!pidlSrc) + { + skip("pidlSrc was NULL\n"); + return; + } + + ZeroMemory(pidlSrc, cbTotal); + p = (PBYTE)pidlSrc; + + *(USHORT *)p = 4; p += 2; + *p++ = 0xAA; *p++ = 0xBB; + + *(USHORT *)p = 5; p += 2; + *p++ = 0x01; *p++ = 0x02; *p++ = 0x03; + + pstm = SHCreateMemStream(NULL, 0); + ok(pstm != NULL, "pstm was NULL\n"); + if (!pstm) + { + skip("pstm was NULL\n"); + return; + } + + hr = g_fnIStream_WritePidl(pstm, pidlSrc); + ok_hr(hr, S_OK); + + RewindStream(pstm); + hr = g_fnIStream_ReadPidl(pstm, &pidlDst); + ok_hr(hr, S_OK); + ok(pidlDst != NULL, "pidlDst was NULL\n"); + + if (pidlDst) + { + ok_int(g_fnILGetSize(pidlSrc), g_fnILGetSize(pidlDst)); + ok_int(memcmp(pidlSrc, pidlDst, g_fnILGetSize(pidlSrc)), 0); + CoTaskMemFree(pidlDst); + } + + pstm->Release(); + CoTaskMemFree(pidlSrc); +} + +static void Test_Read_EmptyStream(void) +{ + IStream *pstm = SHCreateMemStream(NULL, 0); + LPITEMIDLIST pidl = NULL; + HRESULT hr; + + ok(pstm != NULL, "pstm was NULL\n"); + if (!pstm) + { + skip("pstm was NULL\n"); + return; + } + + hr = g_fnIStream_ReadPidl(pstm, &pidl); + ok(FAILED(hr), "hr was wrongly succeeded\n"); + ok(pidl == NULL, "pidl was not NULL\n"); + + pstm->Release(); +} + +static void Test_Read_TooSmallCbSize(void) +{ + IStream *pstm = SHCreateMemStream(NULL, 0); + LPITEMIDLIST pidl = NULL; + UINT cbSize = 1; + ULONG cbWritten; + HRESULT hr; + + ok(pstm != NULL, "pstm was NULL.\n"); + if (!pstm) + { + skip("pstm was NULL.\n"); + return; + } + + pstm->Write(&cbSize, sizeof(cbSize), &cbWritten); + + RewindStream(pstm); + hr = g_fnIStream_ReadPidl(pstm, &pidl); + ok(FAILED(hr), "hr was 0x%X\n", hr); + ok(pidl == NULL, "pidl was not NULL\n"); + + pstm->Release(); +} + +static void Test_Read_TruncatedData(void) +{ + IStream *pstm = SHCreateMemStream(NULL, 0); + LPITEMIDLIST pidl = NULL; + UINT cbSize = 100; + ULONG cbWritten; + HRESULT hr; + + ok(pstm != NULL, "pstm was NULL.\n"); + if (!pstm) + { + skip("pstm was NULL.\n"); + return; + } + + pstm->Write(&cbSize, sizeof(cbSize), &cbWritten); + + RewindStream(pstm); + hr = g_fnIStream_ReadPidl(pstm, &pidl); + ok(FAILED(hr), "hr was 0x%X\n", hr); + ok(pidl == NULL, "pidl was not NULL\n"); + + pstm->Release(); +} + +static void Test_Read_MissingTerminator(void) +{ + IStream *pstm = SHCreateMemStream(NULL, 0); + LPITEMIDLIST pidl = NULL; + UINT cbSize = 4; + BYTE rawData[4] = {0x04, 0x00, 0x11, 0x22}; + ULONG cbWritten; + HRESULT hr; + + ok(pstm != NULL, "pstm was NULL.\n"); + if (!pstm) + { + skip("pstm was NULL.\n"); + return; + } + + pstm->Write(&cbSize, sizeof(cbSize), &cbWritten); + pstm->Write(rawData, cbSize, &cbWritten); + + RewindStream(pstm); + hr = g_fnIStream_ReadPidl(pstm, &pidl); + ok(FAILED(hr), "hr was 0x%X\n", hr); + ok(pidl == NULL, "pidl was not NULL\n"); + + pstm->Release(); +} + +static void Test_Write_StreamPosition(void) +{ + const BYTE data[] = {0xDE, 0xAD}; + LPITEMIDLIST pidlSrc = MakeSimplePidl(data, sizeof(data)); + IStream *pstm = SHCreateMemStream(NULL, 0); + ULARGE_INTEGER pos; + LARGE_INTEGER zero; + HRESULT hr; + UINT expectedPos; + + ok(pidlSrc != NULL, "pidlSrc was NULL\n"); + if (!pidlSrc) + { + skip("pidlSrc was NULL\n"); + return; + } + + ok(pstm != NULL, "pstm was NULL.\n"); + if (!pstm) + { + skip("pstm was NULL.\n"); + return; + } + + zero.QuadPart = 0; + + hr = g_fnIStream_WritePidl(pstm, pidlSrc); + ok_hr(hr, S_OK); + + pstm->Seek(zero, STREAM_SEEK_CUR, &pos); + + expectedPos = sizeof(UINT) + g_fnILGetSize(pidlSrc); + ok_eq_longlong(pos.QuadPart, expectedPos); + + pstm->Release(); + CoTaskMemFree(pidlSrc); +} + +static void Test_Read_OutputNullOnFailure(void) +{ + IStream *pstm = SHCreateMemStream(NULL, 0); + LPITEMIDLIST pidl = (LPITEMIDLIST)UlongToPtr(0xDEADBEEF); + HRESULT hr; + + ok(pstm != NULL, "pstm was NULL.\n"); + if (!pstm) + { + skip("pstm was NULL.\n"); + return; + } + + hr = g_fnIStream_ReadPidl(pstm, &pidl); + ok(FAILED(hr), "hr was 0x%X\n", hr); + ok(pidl == NULL, "pidl was not NULL\n"); + + pstm->Release(); +} + +START_TEST(IStreamPidl) +{ + HINSTANCE hSHLWAPI = LoadLibraryW(L"shlwapi"); + if (!hSHLWAPI) + { + skip("shlwapi not found\n"); + return; + } + + g_fnIStream_ReadPidl = (FN_IStream_ReadPidl)GetProcAddress(hSHLWAPI, MAKEINTRESOURCEA(512)); + g_fnIStream_WritePidl = (FN_IStream_WritePidl)GetProcAddress(hSHLWAPI, MAKEINTRESOURCEA(513)); + if (!g_fnIStream_ReadPidl || !g_fnIStream_WritePidl) + { + skip("IStream_ReadPidl or IStream_WritePidl not found\n"); + FreeLibrary(hSHLWAPI); + return; + } + + HINSTANCE hSHELL32 = LoadLibraryW(L"shell32"); + if (!hSHELL32) + { + skip("shell32 not found\n"); + FreeLibrary(hSHLWAPI); + return; + } + + g_fnILGetSize = (FN_ILGetSize)GetProcAddress(hSHELL32, "ILGetSize"); + if (!g_fnILGetSize) + { + skip("ILGetSize not found\n"); + FreeLibrary(hSHELL32); + FreeLibrary(hSHLWAPI); + return; + } + + HRESULT hrCoInit = CoInitialize(NULL); + + Test_RoundTrip_SimplePidl(); + Test_RoundTrip_EmptyPidl(); + Test_RoundTrip_MultiItemPidl(); + Test_Read_EmptyStream(); + Test_Read_TooSmallCbSize(); + Test_Read_TruncatedData(); + Test_Read_MissingTerminator(); + Test_Write_StreamPosition(); + Test_Read_OutputNullOnFailure(); + + if (SUCCEEDED(hrCoInit)) + CoUninitialize(); + + FreeLibrary(hSHELL32); + FreeLibrary(hSHLWAPI); +} diff --git a/modules/rostests/apitests/shlwapi/testlist.c b/modules/rostests/apitests/shlwapi/testlist.c index 4a4ee6b154c..196ddaea91d 100644 --- a/modules/rostests/apitests/shlwapi/testlist.c +++ b/modules/rostests/apitests/shlwapi/testlist.c @@ -6,6 +6,7 @@ extern void func_PathFileExistsDefExtAndAttributesW(void); extern void func_PathFindOnPath(void); extern void func_IShellFolderHelpers(void); extern void func_IsQSForward(void); +extern void func_IStreamPidl(void); extern void func_isuncpath(void); extern void func_isuncpathserver(void); extern void func_isuncpathservershare(void); @@ -27,6 +28,7 @@ const struct test winetest_testlist[] = { "PathFindOnPath", func_PathFindOnPath }, { "IShellFolderHelpers", func_IShellFolderHelpers }, { "IsQSForward", func_IsQSForward }, + { "IStreamPidl", func_IStreamPidl }, { "PathIsUNC", func_isuncpath }, { "PathIsUNCServer", func_isuncpathserver }, { "PathIsUNCServerShare", func_isuncpathservershare }, diff --git a/sdk/include/reactos/shlwapi_undoc.h b/sdk/include/reactos/shlwapi_undoc.h index b82c8e3176a..6a6f3012a48 100644 --- a/sdk/include/reactos/shlwapi_undoc.h +++ b/sdk/include/reactos/shlwapi_undoc.h @@ -144,6 +144,9 @@ HRESULT WINAPI SHWriteDataBlockList(IStream* lpStream, LPDBLIST lpList); HRESULT WINAPI SHReadDataBlockList(IStream* lpStream, LPDBLIST* lppList); VOID WINAPI SHFreeDataBlockList(LPDBLIST lpList); +HRESULT WINAPI IStream_ReadPidl(_In_ IStream *pstm, _Out_ LPITEMIDLIST *ppidlOut); +HRESULT WINAPI IStream_WritePidl(_In_ IStream *pstm, _In_ LPCITEMIDLIST pidlWrite); + LONG WINAPI RegCreateKeyExWrapW(