1 /*
2  * PROJECT:     ReactOS api tests
3  * LICENSE:     LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
4  * PURPOSE:     Tests for MRU List
5  * COPYRIGHT:   Copyright 2023 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
6  */
7 
8 #include <apitest.h>
9 #include <winreg.h>
10 #include <shlwapi.h>
11 #include <shlobj.h>
12 #include <shlobj_undoc.h>
13 #include <shlguid_undoc.h>
14 #include <stdio.h>
15 #include <shlwapi_undoc.h>
16 #include <versionhelpers.h>
17 #include <strsafe.h>
18 #include <wine/test.h>
19 #include <pseh/pseh2.h>
20 
21 #define SUBKEY0 L"Software\\MRUListTest"
22 #define SUBSUBKEY0 L"Software\\MRUListTest\\0"
23 #define TEXT0 L"This is a test."
24 #define TEXT1 L"ReactOS rocks!"
25 
MRUList_DataList_0(void)26 static void MRUList_DataList_0(void)
27 {
28     HRESULT hr;
29     IMruDataList *pList = NULL;
30     UINT iSlot1, iSlot2, iSlot3;
31     DWORD cbText;
32     WCHAR szText[MAX_PATH];
33 
34     hr = CoCreateInstance(CLSID_MruLongList, NULL, CLSCTX_INPROC_SERVER,
35                           IID_IMruDataList, (LPVOID*)&pList);
36     ok_hex(hr, S_OK);
37     if (pList == NULL)
38     {
39         skip("pList was NULL\n");
40         return;
41     }
42 
43     hr = pList->InitData(26, 0, HKEY_CURRENT_USER, SUBKEY0, NULL);
44     ok_hex(hr, S_OK);
45 
46     cbText = (wcslen(TEXT0) + 1) * sizeof(WCHAR);
47     hr = pList->AddData((BYTE*)TEXT0, cbText, &iSlot1);
48     ok_hex(hr, S_OK);
49     ok_int(iSlot1, 0);
50 
51     hr = pList->FindData((BYTE*)TEXT0, cbText, &iSlot2);
52     ok_hex(hr, S_OK);
53     ok_int(iSlot1, iSlot2);
54 
55     cbText = sizeof(szText);
56     hr = pList->GetData(iSlot1, (BYTE*)szText, cbText);
57     ok_hex(hr, S_OK);
58     ok_wstr(szText, TEXT0);
59 
60     cbText = (wcslen(TEXT1) + 1) * sizeof(WCHAR);
61     hr = pList->AddData((BYTE*)TEXT1, cbText, &iSlot3);
62     ok_hex(hr, S_OK);
63     ok_int(iSlot3, 1);
64 
65     pList->Release();
66 }
67 
MRUList_Check(LPCWSTR pszSubKey,LPCWSTR pszValueName,LPCVOID pvData,DWORD cbData)68 static INT MRUList_Check(LPCWSTR pszSubKey, LPCWSTR pszValueName, LPCVOID pvData, DWORD cbData)
69 {
70     BYTE abData[512];
71     LONG error;
72     DWORD dwSize = cbData;
73 
74     error = SHGetValueW(HKEY_CURRENT_USER, pszSubKey, pszValueName, NULL, abData, &dwSize);
75     if (error != ERROR_SUCCESS)
76         return -999;
77 
78 #if 0
79     printf("dwSize: %ld\n", dwSize);
80     for (DWORD i = 0; i < dwSize; ++i)
81     {
82         printf("%02X ", abData[i]);
83     }
84     printf("\n");
85 #endif
86 
87     if (dwSize != cbData)
88         return +999;
89 
90     if (!pvData)
91         return TRUE;
92 
93     return memcmp(abData, pvData, cbData) == 0;
94 }
95 
MRUList_DataList_1(void)96 static void MRUList_DataList_1(void)
97 {
98     HRESULT hr;
99     IMruDataList *pList = NULL;
100     UINT iSlot;
101 
102     hr = CoCreateInstance(CLSID_MruLongList, NULL, CLSCTX_INPROC_SERVER,
103                           IID_IMruDataList, (LPVOID*)&pList);
104     ok_hex(hr, S_OK);
105     if (pList == NULL)
106     {
107         skip("pList was NULL\n");
108         return;
109     }
110 
111     hr = pList->InitData(26, 0, HKEY_CURRENT_USER, SUBKEY0, NULL);
112     ok_hex(hr, S_OK);
113 
114     DWORD cbText = (wcslen(TEXT0) + 1) * sizeof(WCHAR);
115     hr = pList->FindData((BYTE*)TEXT0, cbText, &iSlot);
116     ok_hex(hr, S_OK);
117     ok_int(iSlot, 1);
118 
119     hr = pList->Delete(iSlot);
120     ok_hex(hr, S_OK);
121 
122     iSlot = 0xCAFE;
123     cbText = (wcslen(TEXT0) + 1) * sizeof(WCHAR);
124     hr = pList->FindData((BYTE*)TEXT0, cbText, &iSlot);
125     ok_hex(hr, E_FAIL);
126     ok_int(iSlot, 0xCAFE);
127 
128     pList->Release();
129 }
130 
MRUList_DataList_2(void)131 static void MRUList_DataList_2(void)
132 {
133     HRESULT hr;
134     IMruDataList *pList = NULL;
135 
136     hr = CoCreateInstance(CLSID_MruLongList, NULL, CLSCTX_INPROC_SERVER,
137                           IID_IMruDataList, (LPVOID*)&pList);
138     ok_hex(hr, S_OK);
139     if (pList == NULL)
140     {
141         skip("pList was NULL\n");
142         return;
143     }
144 
145     hr = pList->InitData(26, 0, HKEY_CURRENT_USER, SUBKEY0, NULL);
146     ok_hex(hr, S_OK);
147 
148     WCHAR szText[MAX_PATH];
149     DWORD cbText = sizeof(szText);
150     StringCchCopyW(szText, _countof(szText), L"====");
151     hr = pList->GetData(0, (BYTE*)szText, cbText);
152     ok_hex(hr, S_OK);
153     ok_wstr(szText, L"ABC");
154 
155     StringCchCopyW(szText, _countof(szText), L"====");
156     cbText = sizeof(szText);
157     hr = pList->GetData(1, (BYTE*)szText, cbText);
158     ok_hex(hr, S_OK);
159     ok_wstr(szText, L"XYZ");
160 
161     pList->Release();
162 }
163 
MRUList_DataList(void)164 static void MRUList_DataList(void)
165 {
166     if (IsWindowsVistaOrGreater())
167     {
168         skip("Vista+ doesn't support CLSID_MruLongList\n");
169         return;
170     }
171 
172     SHDeleteKeyW(HKEY_CURRENT_USER, SUBKEY0);
173 
174     LONG error;
175     error = SHSetValueW(HKEY_CURRENT_USER, SUBKEY0, NULL, REG_SZ, L"", sizeof(UNICODE_NULL));
176     ok_long(error, ERROR_SUCCESS);
177 
178     error = SHGetValueW(HKEY_CURRENT_USER, SUBKEY0, NULL, NULL, NULL, NULL);
179     ok_long(error, ERROR_SUCCESS);
180 
181     MRUList_DataList_0();
182     ok_int(MRUList_Check(SUBKEY0, L"MRUListEx", "\x01\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF", 12), TRUE);
183 
184     MRUList_DataList_1();
185     ok_int(MRUList_Check(SUBKEY0, L"MRUListEx", "\x01\x00\x00\x00\xFF\xFF\xFF\xFF", 8), TRUE);
186 
187     error = SHDeleteValueW(HKEY_CURRENT_USER, SUBKEY0, L"MRUList");
188     ok_long(error, ERROR_FILE_NOT_FOUND);
189     error = SHDeleteValueW(HKEY_CURRENT_USER, SUBKEY0, L"MRUListEx");
190     ok_long(error, ERROR_SUCCESS);
191 
192     error = SHSetValueW(HKEY_CURRENT_USER, SUBKEY0, L"MRUList", REG_SZ, L"ab", 3 * sizeof(WCHAR));
193     ok_long(error, ERROR_SUCCESS);
194     error = SHSetValueW(HKEY_CURRENT_USER, SUBKEY0, L"a", REG_BINARY, L"ABC", 4 * sizeof(WCHAR));
195     ok_long(error, ERROR_SUCCESS);
196     error = SHSetValueW(HKEY_CURRENT_USER, SUBKEY0, L"b", REG_BINARY, L"XYZ", 4 * sizeof(WCHAR));
197     ok_long(error, ERROR_SUCCESS);
198 
199     MRUList_DataList_2();
200     ok_int(MRUList_Check(SUBKEY0, L"MRUListEx", "\x00\x00\x00\x00\x01\x00\x00\x00\xFF\xFF\xFF\xFF", 12), TRUE);
201 
202     error = SHDeleteValueW(HKEY_CURRENT_USER, SUBKEY0, L"MRUList");
203     ok_long(error, ERROR_FILE_NOT_FOUND);
204     error = SHDeleteValueW(HKEY_CURRENT_USER, SUBKEY0, L"MRUListEx");
205     ok_long(error, ERROR_SUCCESS);
206 
207     SHDeleteKeyW(HKEY_CURRENT_USER, SUBKEY0);
208 }
209 
MRUList_PidlList_0(void)210 static void MRUList_PidlList_0(void)
211 {
212     HRESULT hr;
213     IMruPidlList *pList = NULL;
214 
215     hr = CoCreateInstance(CLSID_MruPidlList, NULL, CLSCTX_INPROC_SERVER,
216                           IID_IMruPidlList, (LPVOID*)&pList);
217     ok_hex(hr, S_OK);
218     if (pList == NULL)
219     {
220         skip("pList was NULL\n");
221         return;
222     }
223 
224     LONG error;
225 
226     error = SHGetValueW(HKEY_CURRENT_USER, SUBKEY0, NULL, NULL, NULL, NULL);
227     ok_long(error, ERROR_FILE_NOT_FOUND);
228 
229     hr = pList->InitList(32, HKEY_CURRENT_USER, SUBKEY0);
230     ok_hex(hr, S_OK);
231 
232     error = SHGetValueW(HKEY_CURRENT_USER, SUBKEY0, NULL, NULL, NULL, NULL);
233     ok_long(error, ERROR_FILE_NOT_FOUND);
234 
235     LPITEMIDLIST pidl1, pidl2;
236     SHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &pidl1);
237     SHGetSpecialFolderLocation(NULL, CSIDL_PERSONAL, &pidl2);
238 
239     UINT uNodeSlot1 = 0xDEADFACE;
240     hr = pList->UsePidl(pidl1, &uNodeSlot1);
241     ok_hex(uNodeSlot1, 1);
242 
243     // "NodeSlot" value
244     ok_int(MRUList_Check(SUBKEY0, L"NodeSlot", "\x01\x00\x00\x00", 4), TRUE);
245 
246     // "NodeSlots" value (Not "NodeSlot")
247     ok_int(MRUList_Check(SUBKEY0, L"NodeSlots", "\x02", 1), TRUE);
248 
249     UINT uNodeSlot2 = 0xDEADFACE;
250     hr = pList->UsePidl(pidl2, &uNodeSlot2);
251     ok_hex(uNodeSlot2, 2);
252 
253     // "0" value
254     ok_int(MRUList_Check(SUBKEY0, L"0", NULL, 22), TRUE);
255 
256     // "MRUListEx" value
257     ok_int(MRUList_Check(SUBKEY0, L"MRUListEx", "\x00\x00\x00\x00\xFF\xFF\xFF\xFF", 8), TRUE);
258 
259     // "NodeSlot" value
260     ok_int(MRUList_Check(SUBKEY0, L"NodeSlot", "\x01\x00\x00\x00", 4), TRUE);
261 
262     // "NodeSlots" value
263     ok_int(MRUList_Check(SUBKEY0, L"NodeSlots", "\x02\x02", 2), TRUE);
264 
265     // SUBSUBKEY0: "MRUListEx" value
266     ok_int(MRUList_Check(SUBSUBKEY0, L"MRUListEx", "\xFF\xFF\xFF\xFF", 4), TRUE);
267 
268     // SUBSUBKEY0: "NodeSlot" value
269     ok_int(MRUList_Check(SUBSUBKEY0, L"NodeSlot", "\x02\x00\x00\x00", 4), TRUE);
270 
271     // QueryPidl
272     UINT anNodeSlot[2], cNodeSlots;
273     FillMemory(anNodeSlot, sizeof(anNodeSlot), 0xCC);
274     cNodeSlots = 0xDEAD;
275     hr = pList->QueryPidl(pidl1, _countof(anNodeSlot), anNodeSlot, &cNodeSlots);
276     ok_long(hr, S_OK);
277     ok_int(anNodeSlot[0], 1);
278     ok_int(anNodeSlot[1], 0xCCCCCCCC);
279     ok_int(cNodeSlots, 1);
280 
281     hr = pList->PruneKids(pidl1);
282 
283     // "MRUListEx" value
284     ok_int(MRUList_Check(SUBKEY0, L"MRUListEx", "\x00\x00\x00\x00\xFF\xFF\xFF\xFF", 8), TRUE);
285 
286     // "NodeSlot" value
287     ok_int(MRUList_Check(SUBKEY0, L"NodeSlot", "\x01\x00\x00\x00", 4), TRUE);
288 
289     // "NodeSlots" value
290     ok_int(MRUList_Check(SUBKEY0, L"NodeSlots", "\x02\x00", 2), TRUE);
291 
292     FillMemory(anNodeSlot, sizeof(anNodeSlot), 0xCC);
293     cNodeSlots = 0xBEEF;
294     hr = pList->QueryPidl(pidl1, 0, anNodeSlot, &cNodeSlots);
295     ok_long(hr, E_FAIL);
296     ok_int(anNodeSlot[0], 0xCCCCCCCC);
297     ok_int(anNodeSlot[1], 0xCCCCCCCC);
298     ok_int(cNodeSlots, 0);
299 
300     FillMemory(anNodeSlot, sizeof(anNodeSlot), 0xCC);
301     cNodeSlots = 0xDEAD;
302     hr = pList->QueryPidl(pidl1, _countof(anNodeSlot), anNodeSlot, &cNodeSlots);
303     ok_long(hr, S_OK);
304     ok_int(anNodeSlot[0], 1);
305     ok_int(anNodeSlot[1], 0xCCCCCCCC);
306     ok_int(cNodeSlots, 1);
307 
308     FillMemory(anNodeSlot, sizeof(anNodeSlot), 0xCC);
309     cNodeSlots = 0xDEAD;
310     hr = pList->QueryPidl(pidl2, _countof(anNodeSlot), anNodeSlot, &cNodeSlots);
311     ok_long(hr, S_FALSE);
312     ok_int(anNodeSlot[0], 1);
313     ok_int(anNodeSlot[1], 0xCCCCCCCC);
314     ok_int(cNodeSlots, 1);
315 
316     pList->Release();
317     ILFree(pidl1);
318     ILFree(pidl2);
319 }
320 
MRUList_PidlList(void)321 static void MRUList_PidlList(void)
322 {
323     if (IsWindowsVistaOrGreater())
324     {
325         skip("Vista+ doesn't support CLSID_MruPidlList\n");
326         return;
327     }
328 
329     SHDeleteKeyW(HKEY_CURRENT_USER, SUBKEY0);
330 
331     MRUList_PidlList_0();
332 
333     SHDeleteKeyW(HKEY_CURRENT_USER, SUBKEY0);
334 }
335 
START_TEST(MRUList)336 START_TEST(MRUList)
337 {
338     HRESULT hr = CoInitialize(NULL);
339     ok_hex(hr, S_OK);
340 
341     MRUList_DataList();
342     MRUList_PidlList();
343 
344     if (SUCCEEDED(hr))
345         CoUninitialize();
346 }
347