[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 <shlwapi_undoc.h>.
- Add IStreamPidl testcase.
This commit is contained in:
Katayama Hirofumi MZ
2026-05-03 10:10:27 +09:00
committed by GitHub
parent d9d27f8943
commit 0cd3b555b1
6 changed files with 473 additions and 2 deletions
+75
View File
@@ -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__ */
+2 -2
View File
@@ -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)
@@ -8,6 +8,7 @@ list(APPEND SOURCE
AssocQueryString.c
IShellFolderHelpers.cpp
IsQSForward.cpp
IStreamPidl.cpp
PathFileExistsDefExtAndAttributesW.c
PathFindOnPath.c
PathIsUNC.c
@@ -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 <[email protected]>
*/
#include <apitest.h>
#include <shlwapi.h>
#include <shlobj.h>
#include <shlwapi_undoc.h>
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);
}
@@ -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 },
+3
View File
@@ -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(