[SHLWAPI][SHLWAPI_APITEST][SDK] Support NextPathA/W (#9039)

Implementing missing features...
JIRA issue: CORE-19278
- Implement NextPathA and
  NextPathW functions.
- Modify shlwapi.spec.
- Add prototypes into
  <shlwapi_undoc.h>.
- Add NextPath testcase.
This commit is contained in:
Katayama Hirofumi MZ
2026-06-03 21:38:21 +09:00
committed by GitHub
parent af10f35a5d
commit fe5b75083e
6 changed files with 257 additions and 2 deletions
+2 -2
View File
@@ -446,8 +446,8 @@
446 stdcall -noname PathFileExistsAndAttributesW(wstr ptr)
447 stdcall -noname FixSlashesAndColonA(str)
448 stdcall -noname FixSlashesAndColonW(wstr)
449 stub -noname NextPathA
450 stub -noname NextPathW
449 stdcall -noname NextPathA(str ptr long)
450 stdcall -noname NextPathW(wstr ptr long)
451 stdcall -noname CharUpperNoDBCSA(str)
452 stdcall -noname CharUpperNoDBCSW(wstr)
453 stdcall -noname CharLowerNoDBCSA(str)
+79
View File
@@ -878,3 +878,82 @@ SHDialogBox(
SHDIALOG data = { fn, pThis };
return DialogBoxParamA(hInstance, lpTemplateName, hWndParent, SHDialogProc, (LPARAM)&data);
}
/*************************************************************************
* NextPathA [SHLWAPI.449]
*
* See NextPathW.
*/
EXTERN_C PSTR WINAPI
NextPathA(
_In_ PCSTR pszStart,
_Out_writes_(cchDest) PSTR pszDest,
_In_ UINT cchDest)
{
if (!pszStart)
return NULL;
PCSTR pchStart = pszStart;
while (*pchStart == ';')
++pchStart;
if (!*pchStart)
return NULL;
PSTR pchEnd = StrChrA(pchStart, ';');
if (!pchEnd)
pchEnd = (PSTR)(pchStart + lstrlenA(pchStart));
const UINT cchSegment = (UINT)(pchEnd - pchStart);
const UINT cchToCopy = min(cchSegment + 1, cchDest);
lstrcpynA(pszDest, pchStart, cchToCopy);
pszDest[cchSegment] = ANSI_NULL;
PathRemoveBlanksA(pszDest);
if (!*pszDest)
return NULL;
return (*pchEnd == ';') ? (pchEnd + 1) : pchEnd;
}
/*************************************************************************
* NextPathW [SHLWAPI.450]
*
* Extracts the next path from a semicolon-separated path string (Unicode version)
*
* @param pszStart Parsing start position (semicolon-separated path string)
* @param pszDest Buffer to store the extracted path
* @param cchDest Buffer size (number of characters)
* @return Pointer to the beginning of the next path. NULL if there are no more paths.
*/
EXTERN_C PWSTR WINAPI
NextPathW(
_In_ PCWSTR pszStart,
_Out_writes_(cchDest) PWSTR pszDest,
_In_ UINT cchDest)
{
if (!pszStart)
return NULL;
PCWSTR pchStart = pszStart;
while (*pchStart == L';')
++pchStart;
if (!*pchStart)
return NULL;
PWSTR pchEnd = StrChrW(pchStart, L';');
if (!pchEnd)
pchEnd = (PWSTR)(pchStart + lstrlenW(pchStart));
const UINT cchSegment = (UINT)(pchEnd - pchStart);
const UINT cchToCopy = min(cchSegment + 1, cchDest);
lstrcpynW(pszDest, pchStart, cchToCopy);
pszDest[cchSegment] = UNICODE_NULL;
PathRemoveBlanksW(pszDest);
if (!*pszDest)
return NULL;
return (*pchEnd == L';') ? (pchEnd + 1) : pchEnd;
}
@@ -10,6 +10,7 @@ list(APPEND SOURCE
IShellFolderHelpers.cpp
IsQSForward.cpp
IStreamPidl.cpp
NextPath.c
PathFileExistsDefExtAndAttributesW.c
PathFindOnPath.c
PathIsUNC.c
@@ -0,0 +1,158 @@
/*
* PROJECT: ReactOS api tests
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: Tests for NextPathA/W
* COPYRIGHT: Copyright 2026 Katayama Hirofumi MZ <[email protected]>
*/
#include <apitest.h>
#include <shlwapi.h>
typedef PSTR (WINAPI *FN_NextPathA)(PCSTR, PSTR, UINT);
typedef PWSTR (WINAPI *FN_NextPathW)(PCWSTR, PWSTR, UINT);
static FN_NextPathA s_pNextPathA = NULL;
static FN_NextPathW s_pNextPathW = NULL;
static void TEST_NextPathA(void)
{
PSTR pch;
CHAR sz[MAX_PATH];
/* NULL pszStart returns NULL */
pch = s_pNextPathA(NULL, sz, _countof(sz));
ok(pch == NULL, "pch was %p\n", pch);
/* Basic semicolon-separated paths */
pch = s_pNextPathA("C:\\TEST1;C:\\TEST2;C:\\TEST3", sz, _countof(sz));
ok_str(sz, "C:\\TEST1");
pch = s_pNextPathA(pch, sz, _countof(sz));
ok_str(sz, "C:\\TEST2");
pch = s_pNextPathA(pch, sz, _countof(sz));
ok_str(sz, "C:\\TEST3");
pch = s_pNextPathA(pch, sz, _countof(sz));
ok(pch == NULL, "pch was %p\n", pch);
/* Whitespace-only segment */
pch = s_pNextPathA("C:\\TEST1; ;C:\\TEST3", sz, _countof(sz));
ok_str(sz, "C:\\TEST1");
pch = s_pNextPathA(pch, sz, _countof(sz));
ok(pch == NULL, "pch was %p\n", pch);
/* Empty string: no paths at all */
pch = s_pNextPathA("", sz, _countof(sz));
ok(pch == NULL, "empty string: pch was %p\n", pch);
/* Leading semicolons are skipped */
pch = s_pNextPathA(";;;C:\\TEST1", sz, _countof(sz));
ok_str(sz, "C:\\TEST1");
ok(pch != NULL, "leading semicolons: pch should not be NULL\n");
/* Trailing semicolon */
pch = s_pNextPathA("C:\\TEST1;", sz, _countof(sz));
ok_str(sz, "C:\\TEST1");
pch = s_pNextPathA(pch, sz, _countof(sz));
ok(pch == NULL, "trailing semicolon: pch was %p\n", pch);
/* Only semicolons */
pch = s_pNextPathA(";;;", sz, _countof(sz));
ok(pch == NULL, "only semicolons: pch was %p\n", pch);
/* Path with surrounding spaces */
pch = s_pNextPathA(" C:\\TEST1 ;C:\\TEST2", sz, _countof(sz));
ok_str(sz, "C:\\TEST1");
pch = s_pNextPathA(pch, sz, _countof(sz));
ok_str(sz, "C:\\TEST2");
/* Single path, no semicolon */
pch = s_pNextPathA("C:\\SINGLE", sz, _countof(sz));
ok_str(sz, "C:\\SINGLE");
pch = s_pNextPathA(pch, sz, _countof(sz));
ok(pch == NULL, "single path: pch was %p\n", pch);
/* cchDest = 0 */
sz[0] = '*';
sz[1] = ANSI_NULL;
pch = s_pNextPathA("C:\\TEST1;C:\\TEST2;C:\\TEST3", sz, 0);
ok_str(pch, "C:\\TEST2;C:\\TEST3");
ok_str(sz, "*");
}
static void TEST_NextPathW(void)
{
PWSTR pch;
WCHAR sz[MAX_PATH];
/* NULL pszStart returns NULL */
pch = s_pNextPathW(NULL, sz, _countof(sz));
ok(pch == NULL, "pch was %p\n", pch);
/* Basic semicolon-separated paths */
pch = s_pNextPathW(L"C:\\TEST1;C:\\TEST2;C:\\TEST3", sz, _countof(sz));
ok_wstr(sz, L"C:\\TEST1");
pch = s_pNextPathW(pch, sz, _countof(sz));
ok_wstr(sz, L"C:\\TEST2");
pch = s_pNextPathW(pch, sz, _countof(sz));
ok_wstr(sz, L"C:\\TEST3");
pch = s_pNextPathW(pch, sz, _countof(sz));
ok(pch == NULL, "pch was %p\n", pch);
/* Whitespace-only segment */
pch = s_pNextPathW(L"C:\\TEST1; ;C:\\TEST3", sz, _countof(sz));
ok_wstr(sz, L"C:\\TEST1");
pch = s_pNextPathW(pch, sz, _countof(sz));
ok(pch == NULL, "pch was %p\n", pch);
/* Empty string */
pch = s_pNextPathW(L"", sz, _countof(sz));
ok(pch == NULL, "empty string: pch was %p\n", pch);
/* Leading semicolons are skipped */
pch = s_pNextPathW(L";;;C:\\TEST1", sz, _countof(sz));
ok_wstr(sz, L"C:\\TEST1");
ok(pch != NULL, "leading semicolons: pch should not be NULL\n");
/* Trailing semicolon */
pch = s_pNextPathW(L"C:\\TEST1;", sz, _countof(sz));
ok_wstr(sz, L"C:\\TEST1");
pch = s_pNextPathW(pch, sz, _countof(sz));
ok(pch == NULL, "trailing semicolon: pch was %p\n", pch);
/* Only semicolons */
pch = s_pNextPathW(L";;;", sz, _countof(sz));
ok(pch == NULL, "only semicolons: pch was %p\n", pch);
/* Path with surrounding spaces */
pch = s_pNextPathW(L" C:\\TEST1 ;C:\\TEST2", sz, _countof(sz));
ok_wstr(sz, L"C:\\TEST1");
pch = s_pNextPathW(pch, sz, _countof(sz));
ok_wstr(sz, L"C:\\TEST2");
/* Single path, no semicolon */
pch = s_pNextPathW(L"C:\\SINGLE", sz, _countof(sz));
ok_wstr(sz, L"C:\\SINGLE");
pch = s_pNextPathW(pch, sz, _countof(sz));
ok(pch == NULL, "single path: pch was %p\n", pch);
/* cchDest = 0 */
sz[0] = L'*';
sz[1] = UNICODE_NULL;
pch = s_pNextPathW(L"C:\\TEST1;C:\\TEST2;C:\\TEST3", sz, 0);
ok_wstr(pch, L"C:\\TEST2;C:\\TEST3");
ok_wstr(sz, L"*");
}
START_TEST(NextPath)
{
HINSTANCE hSHLWAPI = GetModuleHandleA("shlwapi");
s_pNextPathA = (FN_NextPathA)GetProcAddress(hSHLWAPI, MAKEINTRESOURCEA(449));
s_pNextPathW = (FN_NextPathW)GetProcAddress(hSHLWAPI, MAKEINTRESOURCEA(450));
if (!s_pNextPathA || !s_pNextPathW)
{
skip("NextPath not found\n");
return;
}
TEST_NextPathA();
TEST_NextPathW();
}
@@ -8,6 +8,7 @@ extern void func_PathFindOnPath(void);
extern void func_IShellFolderHelpers(void);
extern void func_IsQSForward(void);
extern void func_IStreamPidl(void);
extern void func_NextPath(void);
extern void func_PathIsUNC(void);
extern void func_PathIsUNCServer(void);
extern void func_PathIsUNCServerShare(void);
@@ -33,6 +34,7 @@ const struct test winetest_testlist[] =
{ "IShellFolderHelpers", func_IShellFolderHelpers },
{ "IsQSForward", func_IsQSForward },
{ "IStreamPidl", func_IStreamPidl },
{ "NextPath", func_NextPath },
{ "PathIsUNC", func_PathIsUNC },
{ "PathIsUNCServer", func_PathIsUNCServer },
{ "PathIsUNCServerShare", func_PathIsUNCServerShare },
+15
View File
@@ -60,10 +60,25 @@ SHRestrictionLookup(
BOOL WINAPI SHAboutInfoA(LPSTR lpszDest, DWORD dwDestLen);
BOOL WINAPI SHAboutInfoW(LPWSTR lpszDest, DWORD dwDestLen);
PSTR WINAPI
NextPathA(
_In_ PCSTR pszStart,
_Out_writes_(cchDest) PSTR pszDest,
_In_ UINT cchDest);
PWSTR WINAPI
NextPathW(
_In_ PCWSTR pszStart,
_Out_writes_(cchDest) PWSTR pszDest,
_In_ UINT cchDest);
#ifdef UNICODE
#define SHAboutInfo SHAboutInfoW
#define NextPath NextPathW
#else
#define SHAboutInfo SHAboutInfoA
#define NextPath NextPathA
#endif
HRESULT WINAPI CLSIDFromStringWrap(_In_ LPCWSTR idstr, _Out_ CLSID *id);