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 { DIRBIT = 1, FILEBIT = 2 };
13 
14 static BYTE GetPIDLType(LPCITEMIDLIST pidl)
15 {
16     // Return the type without the 0x80 flag
17     return pidl && pidl->mkid.cb >= 3 ? (pidl->mkid.abID[0] & 0x7F) : 0;
18 }
19 
20 struct FS95 // FileSystem item header
21 {
22     WORD cb;
23     BYTE type;
24     BYTE unknown;
25     UINT size;
26     WORD date, time;
27     WORD att;
28     CHAR name[ANYSIZE_ARRAY];
29 
30     static BOOL IsFS(LPCITEMIDLIST p)
31     {
32         return (p && p->mkid.cb > 2) ? (p->mkid.abID[0] & 0x70) == 0x30 : FALSE;
33     }
34     static FS95* Validate(LPCITEMIDLIST p)
35     {
36         C_ASSERT(FIELD_OFFSET(FS95, name) == 14);
37         return p && p->mkid.cb > FIELD_OFFSET(FS95, name) && IsFS(p) ? (FS95*)p : NULL;
38     }
39 };
40 
41 static int FileStruct_Att(LPCITEMIDLIST pidl)
42 {
43     C_ASSERT(FIELD_OFFSET(FS95, att) == 12);
44     FS95 *p = FS95::Validate(pidl);
45     return p ? p->att : (UINT(1) << 31);
46 }
47 
48 #define TEST_CLSID(pidl, type, offset, clsid) \
49     do { \
50         ok_long(GetPIDLType(pidl), (type)); \
51         ok_int(*(CLSID*)((&pidl->mkid.abID[(offset) - sizeof(WORD)])) == clsid, TRUE); \
52     } while (0)
53 
54 START_TEST(SHSimpleIDListFromPath)
55 {
56     HRESULT hr;
57     WCHAR szPath[MAX_PATH];
58     GetWindowsDirectoryW(szPath, _countof(szPath));
59 
60     // We compare pidl1 and pidl2
61     CComHeapPtr<ITEMIDLIST> pidl1(SHSimpleIDListFromPath(szPath));
62     CComHeapPtr<ITEMIDLIST> pidl2(ILCreateFromPathW(szPath));
63 
64     // Yes, they are equal logically
65     LPITEMIDLIST pidl1Last = ILFindLastID(pidl1), pidl2Last = ILFindLastID(pidl2);
66     ok_int(ILIsEqual(pidl1, pidl2), TRUE);
67     ok_int(ILIsEqual(pidl1Last, pidl2Last), TRUE);
68 
69     // Bind to parent
70     CComPtr<IShellFolder> psf1, psf2;
71     hr = SHBindToParent(pidl1, IID_PPV_ARG(IShellFolder, &psf1), NULL);
72     ok_long(hr, S_OK);
73     hr = SHBindToParent(pidl2, IID_PPV_ARG(IShellFolder, &psf2), NULL);
74     ok_long(hr, S_OK);
75 
76     // Get attributes
77     DWORD attrs1 = SFGAO_FOLDER, attrs2 = SFGAO_FOLDER;
78     hr = (psf1 ? psf1->GetAttributesOf(1, &pidl1Last, &attrs1) : E_UNEXPECTED);
79     ok_long(hr, S_OK);
80     hr = (psf2 ? psf2->GetAttributesOf(1, &pidl2Last, &attrs2) : E_UNEXPECTED);
81     ok_long(hr, S_OK);
82 
83     // There is the difference in attributes because SHSimpleIDListFromPath
84     // cannot create PIDLs to folders, only files and drives:
85     ok_long((attrs1 & SFGAO_FOLDER), 0);
86     ok_long((attrs2 & SFGAO_FOLDER), SFGAO_FOLDER);
87 
88 
89     // Make sure the internal details match Windows NT5+
90     LPITEMIDLIST item;
91     GetSystemDirectoryW(szPath, _countof(szPath));
92     CComHeapPtr<ITEMIDLIST> pidlSys32(SHSimpleIDListFromPath(szPath));
93     if (szPath[1] != ':' || PathFindFileNameW(szPath) <= szPath)
94     {
95         skip("Not a local directory %ls\n", szPath);
96     }
97     else if (!(LPITEMIDLIST)pidlSys32)
98     {
99         skip("?\n");
100     }
101     else
102     {
103         item = ILFindLastID(pidlSys32);
104         ok_long(item->mkid.abID[0] & 0x73, 0x30 | FILEBIT); // This is actually a file PIDL
105         ok_long(FileStruct_Att(item), 0); // Simple PIDL without attributes
106         ok_int(*(UINT*)(&item->mkid.abID[2]), 0); // No size
107 
108         ILRemoveLastID(pidlSys32); // Now we should have "c:\Windows"
109         item = ILFindLastID(pidlSys32);
110         ok_long(item->mkid.abID[0] & 0x73, 0x30 | DIRBIT);
111         ok_int(*(UINT*)(&item->mkid.abID[2]), 0); // No size
112     }
113 
114     WCHAR drive[4] = { szPath[0], szPath[1], L'\\', L'\0' };
115     CComHeapPtr<ITEMIDLIST> pidlDrive(SHSimpleIDListFromPath(drive));
116     if (drive[1] != ':')
117     {
118         skip("Not a local drive %ls\n", drive);
119     }
120     else if (!(LPITEMIDLIST)pidlDrive)
121     {
122         skip("?\n");
123     }
124     else
125     {
126         item = ILFindLastID(pidlDrive);
127         ok_long(item->mkid.abID[0] & 0x70, 0x20); // Something in My Computer
128         ok_char(item->mkid.abID[1] | 32, drive[0] | 32);
129     }
130 
131     CComHeapPtr<ITEMIDLIST> pidlVirt(SHSimpleIDListFromPath(L"x:\\IDontExist"));
132     if (!(LPITEMIDLIST)pidlVirt)
133     {
134         skip("?\n");
135     }
136     else
137     {
138         item = ILFindLastID(pidlVirt);
139         ok_long(item->mkid.abID[0] & 0x73, 0x30 | FILEBIT); // Yes, a file
140         ok_long(FileStruct_Att(item), 0); // Simple PIDL, no attributes
141         ok_int(*(UINT*)(&item->mkid.abID[2]), 0); // No size
142 
143         ILRemoveLastID(pidlVirt); // "x:\"
144         item = ILFindLastID(pidlVirt);
145         ok_long(item->mkid.abID[0] & 0x70, 0x20); // Something in My Computer
146         ok_char(item->mkid.abID[1] | 32, 'x' | 32); // x:
147     }
148 }
149 
150 START_TEST(ILCreateFromPath)
151 {
152     WCHAR szPath[MAX_PATH];
153     LPITEMIDLIST item;
154 
155     ok_ptr(ILCreateFromPathW(L"c:\\IDontExist"), NULL);
156 
157     GetSystemDirectoryW(szPath, _countof(szPath));
158     CComHeapPtr<ITEMIDLIST> pidlDir(ILCreateFromPathW(szPath));
159     if (szPath[1] != ':' || PathFindFileNameW(szPath) <= szPath)
160     {
161         skip("Not a local directory %ls\n", szPath);
162     }
163     else if (!(LPITEMIDLIST)pidlDir)
164     {
165         skip("?\n");
166     }
167     else
168     {
169         item = ILFindLastID(pidlDir);
170         ok_long(item->mkid.abID[0] & 0x73, 0x30 | DIRBIT);
171         ok_int(*(UINT*)(&item->mkid.abID[2]), 0); // No size
172     }
173     PathAppendW(szPath, L"kernel32.dll");
174     CComHeapPtr<ITEMIDLIST> pidlFile(ILCreateFromPathW(szPath));
175     if (!(LPITEMIDLIST)pidlFile)
176     {
177         skip("?\n");
178     }
179     else
180     {
181         item = ILFindLastID(pidlFile);
182         ok_long(item->mkid.abID[0] & 0x73, 0x30 | FILEBIT);
183         ok_int(*(UINT*)(&item->mkid.abID[2]) > 1024 * 42, TRUE); // At least this large
184     }
185 }
186 
187 START_TEST(PIDL)
188 {
189     LPITEMIDLIST pidl;
190 
191     pidl = SHCloneSpecialIDList(NULL, CSIDL_DRIVES, FALSE);
192     if (pidl)
193         TEST_CLSID(ILFindLastID(pidl), 0x1f, 4, CLSID_MyComputer);
194     else
195         skip("?\n");
196     ILFree(pidl);
197 
198     pidl = SHCloneSpecialIDList(NULL, CSIDL_PRINTERS, FALSE);
199     if (pidl)
200         TEST_CLSID(ILFindLastID(pidl), 0x71, 14, CLSID_Printers);
201     else
202         skip("?\n");
203     ILFree(pidl);
204 }
205