1 /* 2 * PROJECT: ReactOS API tests 3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) 4 * PURPOSE: Test for SHSimpleIDListFromPath 5 * COPYRIGHT: Copyright 2024 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) 6 * Copyright 2024 Whindmar Saksit <whindsaks@proton.me> 7 */ 8 9 #include "shelltest.h" 10 #include <shellutils.h> 11 12 enum { 13 DIRBIT = 1, FILEBIT = 2, 14 PT_COMPUTER_REGITEM = 0x2E, 15 }; 16 17 static BYTE GetPIDLType(LPCITEMIDLIST pidl) 18 { 19 // Return the type without the 0x80 flag 20 return pidl && pidl->mkid.cb >= 3 ? (pidl->mkid.abID[0] & 0x7F) : 0; 21 } 22 23 struct FS95 // FileSystem item header 24 { 25 WORD cb; 26 BYTE type; 27 BYTE unknown; 28 UINT size; 29 WORD date, time; 30 WORD att; 31 CHAR name[ANYSIZE_ARRAY]; 32 33 static BOOL IsFS(LPCITEMIDLIST p) 34 { 35 return (p && p->mkid.cb > 2) ? (p->mkid.abID[0] & 0x70) == 0x30 : FALSE; 36 } 37 static FS95* Validate(LPCITEMIDLIST p) 38 { 39 C_ASSERT(FIELD_OFFSET(FS95, name) == 14); 40 return p && p->mkid.cb > FIELD_OFFSET(FS95, name) && IsFS(p) ? (FS95*)p : NULL; 41 } 42 }; 43 44 static int FileStruct_Att(LPCITEMIDLIST pidl) 45 { 46 C_ASSERT(FIELD_OFFSET(FS95, att) == 12); 47 FS95 *p = FS95::Validate(pidl); 48 return p ? p->att : (UINT(1) << 31); 49 } 50 51 #define TEST_CLSID(pidl, type, offset, clsid) \ 52 do { \ 53 ok_long(GetPIDLType(pidl), (type)); \ 54 ok_int(*(CLSID*)((&pidl->mkid.abID[(offset) - sizeof(WORD)])) == clsid, TRUE); \ 55 } while (0) 56 57 START_TEST(SHSimpleIDListFromPath) 58 { 59 HRESULT hr; 60 WCHAR szPath[MAX_PATH]; 61 GetWindowsDirectoryW(szPath, _countof(szPath)); 62 63 // We compare pidl1 and pidl2 64 CComHeapPtr<ITEMIDLIST> pidl1(SHSimpleIDListFromPath(szPath)); 65 CComHeapPtr<ITEMIDLIST> pidl2(ILCreateFromPathW(szPath)); 66 67 // Yes, they are equal logically 68 LPITEMIDLIST pidl1Last = ILFindLastID(pidl1), pidl2Last = ILFindLastID(pidl2); 69 ok_int(ILIsEqual(pidl1, pidl2), TRUE); 70 ok_int(ILIsEqual(pidl1Last, pidl2Last), TRUE); 71 72 // Bind to parent 73 CComPtr<IShellFolder> psf1, psf2; 74 hr = SHBindToParent(pidl1, IID_PPV_ARG(IShellFolder, &psf1), NULL); 75 ok_long(hr, S_OK); 76 hr = SHBindToParent(pidl2, IID_PPV_ARG(IShellFolder, &psf2), NULL); 77 ok_long(hr, S_OK); 78 79 // Get attributes 80 DWORD attrs1 = SFGAO_FOLDER, attrs2 = SFGAO_FOLDER; 81 hr = (psf1 ? psf1->GetAttributesOf(1, &pidl1Last, &attrs1) : E_UNEXPECTED); 82 ok_long(hr, S_OK); 83 hr = (psf2 ? psf2->GetAttributesOf(1, &pidl2Last, &attrs2) : E_UNEXPECTED); 84 ok_long(hr, S_OK); 85 86 // There is the difference in attributes because SHSimpleIDListFromPath 87 // cannot create PIDLs to folders, only files and drives: 88 ok_long((attrs1 & SFGAO_FOLDER), 0); 89 ok_long((attrs2 & SFGAO_FOLDER), SFGAO_FOLDER); 90 91 92 // Make sure the internal details match Windows NT5+ 93 LPITEMIDLIST item; 94 GetSystemDirectoryW(szPath, _countof(szPath)); 95 CComHeapPtr<ITEMIDLIST> pidlSys32(SHSimpleIDListFromPath(szPath)); 96 if (szPath[1] != ':' || PathFindFileNameW(szPath) <= szPath) 97 { 98 skip("Not a local directory %ls\n", szPath); 99 } 100 else if (!(LPITEMIDLIST)pidlSys32) 101 { 102 skip("?\n"); 103 } 104 else 105 { 106 item = ILFindLastID(pidlSys32); 107 ok_long(item->mkid.abID[0] & 0x73, 0x30 | FILEBIT); // This is actually a file PIDL 108 ok_long(FileStruct_Att(item), 0); // Simple PIDL without attributes 109 ok_int(*(UINT*)(&item->mkid.abID[2]), 0); // No size 110 111 ILRemoveLastID(pidlSys32); // Now we should have "c:\Windows" 112 item = ILFindLastID(pidlSys32); 113 ok_long(item->mkid.abID[0] & 0x73, 0x30 | DIRBIT); 114 ok_int(*(UINT*)(&item->mkid.abID[2]), 0); // No size 115 } 116 117 WCHAR drive[4] = { szPath[0], szPath[1], L'\\', L'\0' }; 118 CComHeapPtr<ITEMIDLIST> pidlDrive(SHSimpleIDListFromPath(drive)); 119 if (drive[1] != ':') 120 { 121 skip("Not a local drive %ls\n", drive); 122 } 123 else if (!(LPITEMIDLIST)pidlDrive) 124 { 125 skip("?\n"); 126 } 127 else 128 { 129 item = ILFindLastID(pidlDrive); 130 ok_long(item->mkid.abID[0] & 0x70, 0x20); // Something in My Computer 131 ok_char(item->mkid.abID[1] | 32, drive[0] | 32); 132 } 133 134 CComHeapPtr<ITEMIDLIST> pidlVirt(SHSimpleIDListFromPath(L"x:\\IDontExist")); 135 if (!(LPITEMIDLIST)pidlVirt) 136 { 137 skip("?\n"); 138 } 139 else 140 { 141 item = ILFindLastID(pidlVirt); 142 ok_long(item->mkid.abID[0] & 0x73, 0x30 | FILEBIT); // Yes, a file 143 ok_long(FileStruct_Att(item), 0); // Simple PIDL, no attributes 144 ok_int(*(UINT*)(&item->mkid.abID[2]), 0); // No size 145 146 ILRemoveLastID(pidlVirt); // "x:\" 147 item = ILFindLastID(pidlVirt); 148 ok_long(item->mkid.abID[0] & 0x70, 0x20); // Something in My Computer 149 ok_char(item->mkid.abID[1] | 32, 'x' | 32); // x: 150 } 151 152 LPITEMIDLIST pidl; 153 ok_int((pidl = SHSimpleIDListFromPath(L"c:")) != NULL, TRUE); 154 ILFree(pidl); 155 ok_int((pidl = SHSimpleIDListFromPath(L"c:\\")) != NULL, TRUE); 156 ILFree(pidl); 157 } 158 159 START_TEST(ILCreateFromPath) 160 { 161 WCHAR szPath[MAX_PATH]; 162 LPITEMIDLIST item; 163 164 ok_ptr(ILCreateFromPathW(L"c:\\IDontExist"), NULL); 165 166 GetSystemDirectoryW(szPath, _countof(szPath)); 167 CComHeapPtr<ITEMIDLIST> pidlDir(ILCreateFromPathW(szPath)); 168 if (szPath[1] != ':' || PathFindFileNameW(szPath) <= szPath) 169 { 170 skip("Not a local directory %ls\n", szPath); 171 } 172 else if (!(LPITEMIDLIST)pidlDir) 173 { 174 skip("?\n"); 175 } 176 else 177 { 178 item = ILFindLastID(pidlDir); 179 ok_long(item->mkid.abID[0] & 0x73, 0x30 | DIRBIT); 180 ok_int(*(UINT*)(&item->mkid.abID[2]), 0); // No size 181 } 182 PathAppendW(szPath, L"kernel32.dll"); 183 CComHeapPtr<ITEMIDLIST> pidlFile(ILCreateFromPathW(szPath)); 184 if (!(LPITEMIDLIST)pidlFile) 185 { 186 skip("?\n"); 187 } 188 else 189 { 190 item = ILFindLastID(pidlFile); 191 ok_long(item->mkid.abID[0] & 0x73, 0x30 | FILEBIT); 192 ok_int(*(UINT*)(&item->mkid.abID[2]) > 1024 * 42, TRUE); // At least this large 193 } 194 } 195 196 START_TEST(PIDL) 197 { 198 LPITEMIDLIST pidl; 199 200 pidl = SHCloneSpecialIDList(NULL, CSIDL_DRIVES, FALSE); 201 if (pidl) 202 TEST_CLSID(ILFindLastID(pidl), 0x1f, 4, CLSID_MyComputer); 203 else 204 skip("?\n"); 205 ILFree(pidl); 206 207 pidl = SHCloneSpecialIDList(NULL, CSIDL_PRINTERS, FALSE); 208 if (pidl) 209 TEST_CLSID(ILFindLastID(pidl), 0x71, 14, CLSID_Printers); 210 else 211 skip("?\n"); 212 ILFree(pidl); 213 } 214 215 START_TEST(ILIsEqual) 216 { 217 LPITEMIDLIST p1, p2, pidl; 218 219 p1 = p2 = NULL; 220 ok_int(ILIsEqual(p1, p2), TRUE); 221 222 ITEMIDLIST emptyitem = {}, emptyitem2 = {}; 223 ok_int(ILIsEqual(&emptyitem, &emptyitem2), TRUE); 224 225 ok_int(ILIsEqual(NULL, &emptyitem), FALSE); // These two are not equal for some reason 226 227 p1 = SHCloneSpecialIDList(NULL, CSIDL_DRIVES, FALSE); 228 p2 = SHCloneSpecialIDList(NULL, CSIDL_DRIVES, FALSE); 229 if (p1 && p2) 230 { 231 ok_int(ILIsEqual(p1, p2), TRUE); 232 p1->mkid.abID[0] = PT_COMPUTER_REGITEM; // RegItem in wrong parent 233 ok_int(ILIsEqual(p1, p2), FALSE); 234 } 235 else 236 { 237 skip("Unable to initialize test\n"); 238 } 239 ILFree(p1); 240 ILFree(p2); 241 242 // ILIsParent must compare like ILIsEqual 243 p1 = SHSimpleIDListFromPath(L"c:\\"); 244 p2 = SHSimpleIDListFromPath(L"c:\\dir\\file"); 245 if (p1 && p2) 246 { 247 ok_int(ILIsParent(NULL, p1, FALSE), FALSE); // NULL is always false 248 ok_int(ILIsParent(p1, NULL, FALSE), FALSE); // NULL is always false 249 ok_int(ILIsParent(NULL, NULL, FALSE), FALSE); // NULL is always false 250 ok_int(ILIsParent(p1, p1, FALSE), TRUE); // I'm my own parent 251 ok_int(ILIsParent(p1, p1, TRUE), FALSE); // Self is not immediate 252 ok_int(ILIsParent(p1, p2, FALSE), TRUE); // Grandchild 253 ok_int(ILIsParent(p1, p2, TRUE), FALSE); // Grandchild is not immediate 254 ok_ptr(ILFindChild(p1, p2), ILGetNext(ILGetNext(p2))); // Child is "dir\\file", skip MyComputer and C: 255 ok_int(ILIsEmpty(pidl = ILFindChild(p1, p1)) && pidl, TRUE); // Self 256 ILRemoveLastID(p2); 257 ok_int(ILIsParent(p1, p2, TRUE), TRUE); // Immediate child 258 259 p1->mkid.abID[0] = PT_COMPUTER_REGITEM; // RegItem in wrong parent 260 ok_int(ILIsParent(p1, p2, FALSE), FALSE); 261 } 262 else 263 { 264 skip("Unable to initialize test\n"); 265 } 266 ILFree(p1); 267 ILFree(p2); 268 } 269