Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343/* * PROJECT: ReactOS API tests * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) * PURPOSE: Test for SHSimpleIDListFromPath * COPYRIGHT: Copyright 2024 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) * Copyright 2024 Whindmar Saksit <whindsaks@proton.me> */
#include "shelltest.h"#include <shellutils.h>
enum { DIRBIT = 1, FILEBIT = 2, PT_COMPUTER_REGITEM = 0x2E, PT_INTERNET_URL = 0x61, PT_CONTROLS_OLDREGITEM = 0x70, PT_CONTROLS_NEWREGITEM = 0x71,};
static BYTE GetPIDLType(LPCITEMIDLIST pidl){ // Return the type without the 0x80 flag return pidl && pidl->mkid.cb >= 3 ? (pidl->mkid.abID[0] & 0x7F) : 0;}
struct FS95 // FileSystem item header{ WORD cb; BYTE type; BYTE unknown; UINT size; WORD date, time; WORD att; CHAR name[ANYSIZE_ARRAY];
static BOOL IsFS(LPCITEMIDLIST p) { return (p && p->mkid.cb > 2) ? (p->mkid.abID[0] & 0x70) == 0x30 : FALSE; } static FS95* Validate(LPCITEMIDLIST p) { C_ASSERT(FIELD_OFFSET(FS95, name) == 14); return p && p->mkid.cb > FIELD_OFFSET(FS95, name) && IsFS(p) ? (FS95*)p : NULL; }};
static int FileStruct_Att(LPCITEMIDLIST pidl){ C_ASSERT(FIELD_OFFSET(FS95, att) == 12); FS95 *p = FS95::Validate(pidl); return p ? p->att : (UINT(1) << 31);}
static HRESULT ParseDisplayName(PWSTR pszPath, LPITEMIDLIST *ppidl, IBindCtx *pBC = NULL){ CComPtr<IShellFolder> pSF; HRESULT hr = SHGetDesktopFolder(&pSF); return FAILED(hr) ? hr : pSF->ParseDisplayName(NULL, pBC, pszPath, NULL, ppidl, NULL);}
static HRESULT GetDisplayNameOf(IShellFolder *pSF, LPCITEMIDLIST pidl, UINT Flags, PWSTR Buf, UINT Cap){ STRRET sr; HRESULT hr = pSF->GetDisplayNameOf(pidl, Flags, &sr); if (SUCCEEDED(hr)) hr = StrRetToBufW(&sr, pidl, Buf, Cap); return hr;}
static HRESULT GetDisplayNameOf(PCIDLIST_ABSOLUTE pidl, UINT Flags, PWSTR Buf, UINT Cap){ CComPtr<IShellFolder> pSF; PCUITEMID_CHILD pidlChild; HRESULT hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &pSF), &pidlChild); return FAILED(hr) ? hr : GetDisplayNameOf(pSF, pidlChild, Flags, Buf, Cap);}
#define TEST_CLSID(pidl, type, offset, clsid) \ do { \ ok_long(GetPIDLType(pidl), (type)); \ ok_int(*(CLSID*)((&pidl->mkid.abID[(offset) - sizeof(WORD)])) == clsid, TRUE); \ } while (0)
START_TEST(SHSimpleIDListFromPath){ HRESULT hr; WCHAR szPath[MAX_PATH]; GetWindowsDirectoryW(szPath, _countof(szPath));
// We compare pidl1 and pidl2 CComHeapPtr<ITEMIDLIST> pidl1(SHSimpleIDListFromPath(szPath)); CComHeapPtr<ITEMIDLIST> pidl2(ILCreateFromPathW(szPath));
// Yes, they are equal logically LPITEMIDLIST pidl1Last = ILFindLastID(pidl1), pidl2Last = ILFindLastID(pidl2); ok_int(ILIsEqual(pidl1, pidl2), TRUE); ok_int(ILIsEqual(pidl1Last, pidl2Last), TRUE);
// Bind to parent CComPtr<IShellFolder> psf1, psf2; hr = SHBindToParent(pidl1, IID_PPV_ARG(IShellFolder, &psf1), NULL); ok_long(hr, S_OK); hr = SHBindToParent(pidl2, IID_PPV_ARG(IShellFolder, &psf2), NULL); ok_long(hr, S_OK);
// Get attributes DWORD attrs1 = SFGAO_FOLDER, attrs2 = SFGAO_FOLDER; hr = (psf1 ? psf1->GetAttributesOf(1, &pidl1Last, &attrs1) : E_UNEXPECTED); ok_long(hr, S_OK); hr = (psf2 ? psf2->GetAttributesOf(1, &pidl2Last, &attrs2) : E_UNEXPECTED); ok_long(hr, S_OK);
// There is the difference in attributes because SHSimpleIDListFromPath // cannot create PIDLs to folders, only files and drives: ok_long((attrs1 & SFGAO_FOLDER), 0); ok_long((attrs2 & SFGAO_FOLDER), SFGAO_FOLDER);
// Make sure the internal details match Windows NT5+ LPITEMIDLIST item; GetSystemDirectoryW(szPath, _countof(szPath)); CComHeapPtr<ITEMIDLIST> pidlSys32(SHSimpleIDListFromPath(szPath)); if (szPath[1] != ':' || PathFindFileNameW(szPath) <= szPath) { skip("Not a local directory %ls\n", szPath); } else if (!(LPITEMIDLIST)pidlSys32) { skip("?\n"); } else { item = ILFindLastID(pidlSys32); ok_long(item->mkid.abID[0] & 0x73, 0x30 | FILEBIT); // This is actually a file PIDL ok_long(FileStruct_Att(item), 0); // Simple PIDL without attributes ok_int(*(UINT*)(&item->mkid.abID[2]), 0); // No size
ILRemoveLastID(pidlSys32); // Now we should have "c:\Windows" item = ILFindLastID(pidlSys32); ok_long(item->mkid.abID[0] & 0x73, 0x30 | DIRBIT); ok_int(*(UINT*)(&item->mkid.abID[2]), 0); // No size }
WCHAR drive[4] = { szPath[0], szPath[1], L'\\', L'\0' }; CComHeapPtr<ITEMIDLIST> pidlDrive(SHSimpleIDListFromPath(drive)); if (drive[1] != ':') { skip("Not a local drive %ls\n", drive); } else if (!(LPITEMIDLIST)pidlDrive) { skip("?\n"); } else { item = ILFindLastID(pidlDrive); ok_long(item->mkid.abID[0] & 0x70, 0x20); // Something in My Computer ok_char(item->mkid.abID[1] | 32, drive[0] | 32); }
CComHeapPtr<ITEMIDLIST> pidlVirt(SHSimpleIDListFromPath(L"x:\\IDontExist")); if (!(LPITEMIDLIST)pidlVirt) { skip("?\n"); } else { item = ILFindLastID(pidlVirt); ok_long(item->mkid.abID[0] & 0x73, 0x30 | FILEBIT); // Yes, a file ok_long(FileStruct_Att(item), 0); // Simple PIDL, no attributes ok_int(*(UINT*)(&item->mkid.abID[2]), 0); // No size
ILRemoveLastID(pidlVirt); // "x:\" item = ILFindLastID(pidlVirt); ok_long(item->mkid.abID[0] & 0x70, 0x20); // Something in My Computer ok_char(item->mkid.abID[1] | 32, 'x' | 32); // x: }
LPITEMIDLIST pidl; ok_int((pidl = SHSimpleIDListFromPath(L"c:")) != NULL, LOBYTE(GetVersion()) >= 6); ILFree(pidl); ok_int((pidl = SHSimpleIDListFromPath(L"c:\\")) != NULL, TRUE); ILFree(pidl);}
START_TEST(ILCreateFromPath){ WCHAR szPath[MAX_PATH]; LPITEMIDLIST item;
ok_ptr(ILCreateFromPathW(L"c:\\IDontExist"), NULL);
GetSystemDirectoryW(szPath, _countof(szPath)); CComHeapPtr<ITEMIDLIST> pidlDir(ILCreateFromPathW(szPath)); if (szPath[1] != ':' || PathFindFileNameW(szPath) <= szPath) { skip("Not a local directory %ls\n", szPath); } else if (!(LPITEMIDLIST)pidlDir) { skip("?\n"); } else { item = ILFindLastID(pidlDir); ok_long(item->mkid.abID[0] & 0x73, 0x30 | DIRBIT); ok_int(*(UINT*)(&item->mkid.abID[2]), 0); // No size } PathAppendW(szPath, L"kernel32.dll"); CComHeapPtr<ITEMIDLIST> pidlFile(ILCreateFromPathW(szPath)); if (!(LPITEMIDLIST)pidlFile) { skip("?\n"); } else { item = ILFindLastID(pidlFile); ok_long(item->mkid.abID[0] & 0x73, 0x30 | FILEBIT); ok_int(*(UINT*)(&item->mkid.abID[2]) > 1024 * 42, TRUE); // At least this large }}
START_TEST(PIDL){ CCoInit ComInit; LPITEMIDLIST pidl;
pidl = SHCloneSpecialIDList(NULL, CSIDL_DRIVES, FALSE); if (pidl) TEST_CLSID(ILFindLastID(pidl), 0x1f, 4, CLSID_MyComputer); else skip("?\n"); ILFree(pidl);
pidl = SHCloneSpecialIDList(NULL, CSIDL_PRINTERS, FALSE); if (pidl) { // Accept both the old and new format from the special folder API (NT5 vs NT6) LPITEMIDLIST pidlLeaf = ILFindLastID(pidl); if (LOBYTE(GetVersion()) < 6) TEST_CLSID(pidlLeaf, PT_CONTROLS_OLDREGITEM, 4, CLSID_Printers); else TEST_CLSID(pidlLeaf, PT_CONTROLS_NEWREGITEM, 14, CLSID_Printers);
// The Control Panel should always return the new format when parsing LPITEMIDLIST pidl2; WCHAR szParse[MAX_PATH]; if (SUCCEEDED(GetDisplayNameOf(pidl, SHGDN_FORPARSING, szParse, _countof(szParse))) && SUCCEEDED(ParseDisplayName(szParse, &pidl2))) { TEST_CLSID(ILFindLastID(pidl2), PT_CONTROLS_NEWREGITEM, 14, CLSID_Printers); ILFree(pidl2); } else { skip("Failed to parse in Control Panel\n"); } } else { skip("?\n"); } ILFree(pidl);
CComPtr<IShellFolder> pInternet; HRESULT hr = SHCoCreateInstance(NULL, &CLSID_Internet, NULL, IID_PPV_ARG(IShellFolder, &pInternet)); if (SUCCEEDED(hr)) { PCWSTR pszUrl = L"http://example.com/page?query&foo=bar"; PIDLIST_RELATIVE pidlUrl; hr = pInternet->ParseDisplayName(NULL, NULL, const_cast<PWSTR>(pszUrl), NULL, &pidlUrl, NULL); ok_long(hr, S_OK); if (hr == S_OK) { ok_int(pidlUrl->mkid.abID[0], PT_INTERNET_URL); WCHAR buf[MAX_PATH]; hr = GetDisplayNameOf(pInternet, pidlUrl, SHGDN_FORPARSING, buf, _countof(buf)); ok_long(hr, S_OK); if (SUCCEEDED(hr)) { ok(!lstrcmpiW(buf, pszUrl), "FORPARSING must match URL\n"); } ILFree(pidlUrl); } }}
START_TEST(ILIsEqual){ LPITEMIDLIST p1, p2, pidl;
p1 = p2 = NULL; ok_int(ILIsEqual(p1, p2), TRUE);
ITEMIDLIST emptyitem = {}, emptyitem2 = {}; ok_int(ILIsEqual(&emptyitem, &emptyitem2), TRUE);
ok_int(ILIsEqual(NULL, &emptyitem), FALSE); // These two are not equal for some reason
p1 = SHCloneSpecialIDList(NULL, CSIDL_DRIVES, FALSE); p2 = SHCloneSpecialIDList(NULL, CSIDL_DRIVES, FALSE); if (p1 && p2) { ok_int(ILIsEqual(p1, p2), TRUE); p1->mkid.abID[0] = PT_COMPUTER_REGITEM; // RegItem in wrong parent ok_int(ILIsEqual(p1, p2), FALSE); } else { skip("Unable to initialize test\n"); } ILFree(p1); ILFree(p2);
// ILIsParent must compare like ILIsEqual p1 = SHSimpleIDListFromPath(L"c:\\"); p2 = SHSimpleIDListFromPath(L"c:\\dir\\file"); if (p1 && p2) { ok_int(ILIsParent(NULL, p1, FALSE), FALSE); // NULL is always false ok_int(ILIsParent(p1, NULL, FALSE), FALSE); // NULL is always false ok_int(ILIsParent(NULL, NULL, FALSE), FALSE); // NULL is always false ok_int(ILIsParent(p1, p1, FALSE), TRUE); // I'm my own parent ok_int(ILIsParent(p1, p1, TRUE), FALSE); // Self is not immediate ok_int(ILIsParent(p1, p2, FALSE), TRUE); // Grandchild ok_int(ILIsParent(p1, p2, TRUE), FALSE); // Grandchild is not immediate ok_ptr(ILFindChild(p1, p2), ILGetNext(ILGetNext(p2))); // Child is "dir\\file", skip MyComputer and C: ok_int(ILIsEmpty(pidl = ILFindChild(p1, p1)) && pidl, TRUE); // Self ILRemoveLastID(p2); ok_int(ILIsParent(p1, p2, TRUE), TRUE); // Immediate child
p1->mkid.abID[0] = PT_COMPUTER_REGITEM; // RegItem in wrong parent ok_int(ILIsParent(p1, p2, FALSE), FALSE); } else { skip("Unable to initialize test\n"); } ILFree(p1); ILFree(p2);}