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 SHPropertyBag Read/Write
5  * COPYRIGHT:   Copyright 2023 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
6  */
7 
8 #include <apitest.h>
9 #include <shlwapi.h>
10 #include <shlobj.h>
11 #include <stdio.h>
12 #include <shlwapi_undoc.h>
13 #include <versionhelpers.h>
14 #include <strsafe.h>
15 
16 #include <pseh/pseh2.h>
17 
18 static LPCWSTR s_pszPropNames[4] = { NULL, NULL, NULL, NULL };
19 static VARTYPE s_vt;
20 static INT s_cRead = 0;
21 static INT s_cWrite = 0;
22 
23 static void ResetTest(VARTYPE vt,
24                   LPCWSTR pszName0 = NULL, LPCWSTR pszName1 = NULL,
25                   LPCWSTR pszName2 = NULL, LPCWSTR pszName3 = NULL)
26 {
27     s_vt = vt;
28     s_cRead = s_cWrite = 0;
29     s_pszPropNames[0] = pszName0;
30     s_pszPropNames[1] = pszName1;
31     s_pszPropNames[2] = pszName2;
32     s_pszPropNames[3] = pszName3;
33 }
34 
35 static SAFEARRAY* CreateByteArray(LPCVOID pvSrc, DWORD cbSize)
36 {
37     SAFEARRAYBOUND Bound;
38     Bound.lLbound = 0;
39     Bound.cElements = cbSize;
40 
41     SAFEARRAY* pArray = SafeArrayCreate(VT_UI1, 1, &Bound);
42     if (!pArray)
43         return NULL;
44 
45     void HUGEP *pvData;
46     HRESULT hr = SafeArrayAccessData(pArray, &pvData);
47     if (FAILED(hr))
48     {
49         SafeArrayDestroy(pArray);
50         return NULL;
51     }
52 
53     CopyMemory(pvData, pvSrc, cbSize);
54     SafeArrayUnaccessData(pArray);
55 
56     return pArray;
57 }
58 
59 class CDummyPropertyBag : public IPropertyBag
60 {
61 public:
62     CDummyPropertyBag()
63     {
64     }
65 
66     // IUnknown
67     STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject) override
68     {
69         ok(FALSE, "Unexpected call\n");
70         return S_OK;
71     }
72     STDMETHODIMP_(ULONG) AddRef() override
73     {
74         ok(FALSE, "Unexpected call\n");
75         return S_OK;
76     }
77     STDMETHODIMP_(ULONG) Release() override
78     {
79         ok(FALSE, "Unexpected call\n");
80         return S_OK;
81     }
82 
83     // IPropertyBag
84     STDMETHODIMP Read(LPCWSTR pszPropName, VARIANT *pvari, IErrorLog *pErrorLog) override
85     {
86         ++s_cRead;
87         ok_int(s_vt, V_VT(pvari));
88         for (size_t i = 0; i < _countof(s_pszPropNames); ++i)
89         {
90             if (s_pszPropNames[i])
91             {
92                 ok_wstr(pszPropName, s_pszPropNames[i]);
93                 s_pszPropNames[i] = NULL;
94 
95                 if (lstrcmpiW(pszPropName, L"RECTL2.top") == 0)
96                     return E_FAIL;
97 
98                 if (lstrcmpiW(pszPropName, L"GUID1") == 0)
99                 {
100                     V_VT(pvari) = (VT_UI1 | VT_ARRAY);
101                     V_ARRAY(pvari) = CreateByteArray(&IID_IShellLinkW, sizeof(IID));
102                     return S_OK;
103                 }
104 
105                 if (lstrcmpiW(pszPropName, L"GUID2") == 0)
106                 {
107                     WCHAR szText[50];
108                     StringFromGUID2(IID_IUnknown, szText, _countof(szText));
109 
110                     V_VT(pvari) = VT_BSTR;
111                     V_BSTR(pvari) = SysAllocString(szText);
112                     return S_OK;
113                 }
114 
115                 if (lstrcmpiW(pszPropName, L"GUID3") == 0)
116                 {
117                     V_VT(pvari) = VT_EMPTY;
118                     V_UI4(pvari) = 0xDEADFACE;
119                     return S_OK;
120                 }
121 
122                 goto Skip1;
123             }
124         }
125         ok(FALSE, "Unexpected call\n");
126 Skip1:
127         return S_OK;
128     }
129 
130     STDMETHODIMP Write(LPCWSTR pszPropName, VARIANT *pvari) override
131     {
132         ++s_cWrite;
133         ok_int(s_vt, V_VT(pvari));
134         for (size_t i = 0; i < _countof(s_pszPropNames); ++i)
135         {
136             if (s_pszPropNames[i])
137             {
138                 ok_wstr(pszPropName, s_pszPropNames[i]);
139                 s_pszPropNames[i] = NULL;
140                 if (lstrcmpiW(pszPropName, L"RECTL2.bottom") == 0)
141                 {
142                     s_vt = VT_EMPTY;
143                     ZeroMemory(&s_pszPropNames, sizeof(s_pszPropNames));
144                     s_pszPropNames[0] = L"RECTL2.right";
145                     return E_FAIL;
146                 }
147                 goto Skip2;
148             }
149         }
150         ok(FALSE, "Unexpected call\n");
151 Skip2:
152         return S_OK;
153     }
154 };
155 
156 static void SHPropertyBag_ReadTest(void)
157 {
158     HRESULT hr;
159     CDummyPropertyBag dummy;
160     BOOL bValue = 0xDEADFACE;
161     SHORT sValue = 0xDEADu;
162     LONG lValue = 0xDEADDEAD;
163     DWORD dwValue = 0xFEEDF00D;
164     BSTR bstr = NULL;
165     POINTL ptl = { 0xEEEE, 0xDDDD };
166     POINTS pts = { 0x2222, 0x3333 };
167     RECTL rcl = { 123, 456, 789, 101112 };
168     GUID guid = { 0 };
169 
170     ResetTest(VT_BOOL, L"BOOL1");
171     hr = SHPropertyBag_ReadBOOL(&dummy, s_pszPropNames[0], &bValue);
172     ok_long(hr, S_OK);
173     ok_int(s_cRead, 1);
174     ok_int(s_cWrite, 0);
175 
176     ResetTest(VT_UI2, L"SHORT1");
177     hr = SHPropertyBag_ReadSHORT(&dummy, s_pszPropNames[0], &sValue);
178     ok_long(hr, S_OK);
179     ok_int(s_cRead, 1);
180     ok_int(s_cWrite, 0);
181 
182     ResetTest(VT_I4, L"LONG1");
183     hr = SHPropertyBag_ReadLONG(&dummy, s_pszPropNames[0], &lValue);
184     ok_long(hr, S_OK);
185     ok_int(s_cRead, 1);
186     ok_int(s_cWrite, 0);
187 
188     ResetTest(VT_UI4, L"DWORD1");
189     hr = SHPropertyBag_ReadDWORD(&dummy, s_pszPropNames[0], &dwValue);
190     ok_long(hr, S_OK);
191     ok_int(s_cRead, 1);
192     ok_int(s_cWrite, 0);
193 
194     ResetTest(VT_BSTR, L"Str1");
195     hr = SHPropertyBag_ReadBSTR(&dummy, s_pszPropNames[0], &bstr);
196     ok_long(hr, S_OK);
197     ok_int(s_cRead, 1);
198     ok_int(s_cWrite, 0);
199     SysFreeString(bstr);
200 
201     ResetTest(VT_I4, L"POINTL1.x", L"POINTL1.y");
202     hr = SHPropertyBag_ReadPOINTL(&dummy, L"POINTL1", &ptl);
203     ok_long(hr, S_OK);
204     ok_int(s_cRead, 2);
205     ok_int(s_cWrite, 0);
206 
207     ResetTest(VT_I4, L"POINTS1.x", L"POINTS1.y");
208     hr = SHPropertyBag_ReadPOINTS(&dummy, L"POINTS1", &pts);
209     ok_long(hr, S_OK);
210     ok_int(s_cRead, 2);
211     ok_int(s_cWrite, 0);
212 
213     ResetTest(VT_I4, L"RECTL1.left", L"RECTL1.top", L"RECTL1.right", L"RECTL1.bottom");
214     hr = SHPropertyBag_ReadRECTL(&dummy, L"RECTL1", &rcl);
215     ok_long(hr, S_OK);
216     ok_int(s_cRead, 4);
217     ok_int(s_cWrite, 0);
218 
219     ResetTest(VT_I4, L"RECTL2.left", L"RECTL2.top", L"RECTL2.right", L"RECTL2.bottom");
220     hr = SHPropertyBag_ReadRECTL(&dummy, L"RECTL2", &rcl);
221     ok_long(hr, E_FAIL);
222     ok_int(s_cRead, 2);
223     ok_int(s_cWrite, 0);
224 
225     ResetTest(VT_EMPTY, L"GUID1");
226     hr = SHPropertyBag_ReadGUID(&dummy, L"GUID1", &guid);
227     ok_long(hr, S_OK);
228     ok_int(s_cRead, 1);
229     ok_int(s_cWrite, 0);
230     ok_int(IsEqualGUID(guid, IID_IShellLinkW), TRUE);
231 
232     ResetTest(VT_EMPTY, L"GUID2");
233     hr = SHPropertyBag_ReadGUID(&dummy, L"GUID2", &guid);
234     ok_long(hr, S_OK);
235     ok_int(s_cRead, 1);
236     ok_int(s_cWrite, 0);
237     ok_int(IsEqualGUID(guid, IID_IUnknown), TRUE);
238 
239     ResetTest(VT_EMPTY, L"GUID3");
240     guid = IID_IExtractIcon;
241     hr = SHPropertyBag_ReadGUID(&dummy, L"GUID3", &guid);
242 
243     if (IsWindowsVistaOrGreater())
244         ok_long(hr, E_INVALIDARG);
245     else
246         ok_long(hr, S_OK);
247 
248     ok_int(s_cRead, 1);
249     ok_int(s_cWrite, 0);
250     ok_int(IsEqualGUID(guid, IID_IExtractIcon), TRUE);
251 }
252 
253 static void SHPropertyBag_WriteTest(void)
254 {
255     HRESULT hr;
256     CDummyPropertyBag dummy;
257 
258     ResetTest(VT_EMPTY, L"EMPTY1");
259     hr = SHPropertyBag_Delete(&dummy, s_pszPropNames[0]);
260     ok_long(hr, S_OK);
261     ok_int(s_cRead, 0);
262     ok_int(s_cWrite, 1);
263 
264     ResetTest(VT_BOOL, L"BOOL1");
265     hr = SHPropertyBag_WriteBOOL(&dummy, s_pszPropNames[0], TRUE);
266     ok_long(hr, S_OK);
267     ok_int(s_cRead, 0);
268     ok_int(s_cWrite, 1);
269 
270     ResetTest(VT_UI2, L"SHORT1");
271     hr = SHPropertyBag_WriteSHORT(&dummy, s_pszPropNames[0], 1);
272     ok_long(hr, S_OK);
273     ok_int(s_cRead, 0);
274     ok_int(s_cWrite, 1);
275 
276     ResetTest(VT_I4, L"LONG1");
277     hr = SHPropertyBag_WriteLONG(&dummy, s_pszPropNames[0], 1);
278     ok_long(hr, S_OK);
279     ok_int(s_cRead, 0);
280     ok_int(s_cWrite, 1);
281 
282     ResetTest(VT_UI4, L"DWORD1");
283     hr = SHPropertyBag_WriteDWORD(&dummy, s_pszPropNames[0], 1);
284     ok_long(hr, S_OK);
285     ok_int(s_cRead, 0);
286     ok_int(s_cWrite, 1);
287 
288     ResetTest(VT_BSTR, L"Str1");
289     hr = SHPropertyBag_WriteStr(&dummy, s_pszPropNames[0], L"1");
290     ok_long(hr, S_OK);
291     ok_int(s_cRead, 0);
292     ok_int(s_cWrite, 1);
293 
294     ResetTest(VT_I4, L"POINTL1.x", L"POINTL1.y");
295     POINTL ptl = { 0xEEEE, 0xDDDD };
296     hr = SHPropertyBag_WritePOINTL(&dummy, L"POINTL1", &ptl);
297     ok_long(hr, S_OK);
298     ok_int(s_cRead, 0);
299     ok_int(s_cWrite, 2);
300 
301     ResetTest(VT_I4, L"POINTS1.x", L"POINTS1.y");
302     POINTS pts = { 0x2222, 0x3333 };
303     hr = SHPropertyBag_WritePOINTS(&dummy, L"POINTS1", &pts);
304     ok_long(hr, S_OK);
305     ok_int(s_cRead, 0);
306     ok_int(s_cWrite, 2);
307 
308     ResetTest(VT_I4, L"RECTL1.left", L"RECTL1.top", L"RECTL1.right", L"RECTL1.bottom");
309     RECTL rcl = { 123, 456, 789, 101112 };
310     hr = SHPropertyBag_WriteRECTL(&dummy, L"RECTL1", &rcl);
311     ok_long(hr, S_OK);
312     ok_int(s_cRead, 0);
313     ok_int(s_cWrite, 4);
314 
315     ResetTest(VT_I4, L"RECTL2.left", L"RECTL2.top", L"RECTL2.right", L"RECTL2.bottom");
316     hr = SHPropertyBag_WriteRECTL(&dummy, L"RECTL2", &rcl);
317     ok_long(hr, S_OK);
318     ok_int(s_cRead, 0);
319     ok_int(s_cWrite, 5);
320 
321     GUID guid;
322     ZeroMemory(&guid, sizeof(guid));
323     ResetTest(VT_BSTR, L"GUID1");
324     hr = SHPropertyBag_WriteGUID(&dummy, L"GUID1", &guid);
325     ok_long(hr, S_OK);
326     ok_int(s_cRead, 0);
327     ok_int(s_cWrite, 1);
328 }
329 
330 static void SHPropertyBag_OnMemory(void)
331 {
332     HRESULT hr;
333     VARIANT vari;
334 
335     IPropertyBag *pPropBag = NULL;
336     hr = SHCreatePropertyBagOnMemory(STGM_READWRITE, IID_IPropertyBag, (void**)&pPropBag);
337     ok_long(hr, S_OK);
338     if (pPropBag == NULL)
339     {
340         skip("pPropBag was NULL\n");
341         return;
342     }
343 
344     VariantInit(&vari);
345     hr = pPropBag->Read(L"InvalidName", &vari, NULL);
346     ok_long(hr, E_FAIL);
347     VariantClear(&vari);
348 
349     VariantInit(&vari);
350     V_VT(&vari) = VT_UI4;
351     V_UI4(&vari) = 0xDEADFACE;
352     hr = pPropBag->Write(L"Name1", &vari);
353     ok_long(hr, S_OK);
354     VariantClear(&vari);
355 
356     VariantInit(&vari);
357     hr = pPropBag->Read(L"Name1", &vari, NULL);
358     ok_long(hr, S_OK);
359     ok_long(V_VT(&vari), VT_UI4);
360     ok_long(V_UI4(&vari), 0xDEADFACE);
361     VariantClear(&vari);
362 
363     pPropBag->Release();
364     pPropBag = NULL;
365 
366     hr = SHCreatePropertyBagOnMemory(STGM_READ, IID_IPropertyBag, (void**)&pPropBag);
367     ok_long(hr, S_OK);
368 
369     VariantInit(&vari);
370     V_VT(&vari) = VT_UI4;
371     V_UI4(&vari) = 0xDEADFACE;
372     hr = pPropBag->Write(L"Name1", &vari);
373     ok_long(hr, (IsWindowsVistaOrGreater() ? S_OK : E_ACCESSDENIED));
374     VariantClear(&vari);
375 
376     VariantInit(&vari);
377     V_VT(&vari) = VT_UI4;
378     V_UI4(&vari) = 0xFEEDF00D;
379     hr = pPropBag->Read(L"Name1", &vari, NULL);
380     if (IsWindowsVistaOrGreater())
381     {
382         ok_long(hr, S_OK);
383         ok_int(V_VT(&vari), VT_UI4);
384         ok_long(V_UI4(&vari), 0xDEADFACE);
385     }
386     else
387     {
388         ok_long(hr, E_FAIL);
389         ok_int(V_VT(&vari), VT_EMPTY);
390         ok_long(V_UI4(&vari), 0xFEEDF00D);
391     }
392     VariantClear(&vari);
393 
394     pPropBag->Release();
395     pPropBag = NULL;
396 
397     hr = SHCreatePropertyBagOnMemory(STGM_WRITE, IID_IPropertyBag, (void**)&pPropBag);
398     ok_long(hr, S_OK);
399 
400     VariantInit(&vari);
401     V_VT(&vari) = VT_UI4;
402     V_UI4(&vari) = 0xDEADFACE;
403     hr = pPropBag->Write(L"Name1", &vari);
404     ok_long(hr, S_OK);
405     VariantClear(&vari);
406 
407     VariantInit(&vari);
408     V_VT(&vari) = VT_UI4;
409     V_UI4(&vari) = 0xFEEDF00D;
410     hr = pPropBag->Read(L"Name1", &vari, NULL);
411     if (IsWindowsVistaOrGreater())
412     {
413         ok_long(hr, S_OK);
414         ok_int(V_VT(&vari), VT_UI4);
415         ok_long(V_UI4(&vari), 0xDEADFACE);
416     }
417     else
418     {
419         ok_long(hr, E_ACCESSDENIED);
420         ok_int(V_VT(&vari), VT_EMPTY);
421         ok_long(V_UI4(&vari), 0xFEEDF00D);
422     }
423     VariantClear(&vari);
424 
425     pPropBag->Release();
426 
427     hr = SHCreatePropertyBagOnMemory(STGM_READWRITE, IID_IPropertyBag2, (void**)&pPropBag);
428     if (IsWindowsVistaOrGreater())
429     {
430         ok_long(hr, E_NOINTERFACE);
431     }
432     else
433     {
434         ok_long(hr, S_OK);
435         pPropBag->Release();
436     }
437 
438     hr = SHCreatePropertyBagOnMemory(STGM_READ, IID_IPropertyBag2, (void**)&pPropBag);
439     if (IsWindowsVistaOrGreater())
440     {
441         ok_long(hr, E_NOINTERFACE);
442     }
443     else
444     {
445         ok_long(hr, S_OK);
446         pPropBag->Release();
447     }
448 
449     hr = SHCreatePropertyBagOnMemory(STGM_WRITE, IID_IPropertyBag2, (void**)&pPropBag);
450     if (IsWindowsVistaOrGreater())
451     {
452         ok_long(hr, E_NOINTERFACE);
453     }
454     else
455     {
456         ok_long(hr, S_OK);
457         pPropBag->Release();
458     }
459 }
460 
461 static void SHPropertyBag_OnRegKey(void)
462 {
463     HKEY hKey, hSubKey;
464     LONG error;
465     VARIANT vari;
466     WCHAR szText[MAX_PATH];
467     IStream *pStream;
468     GUID guid;
469     BYTE guid_and_extra[sizeof(GUID) + sizeof(GUID)];
470 
471     // Create HKCU\Software\ReactOS registry key
472     error = RegCreateKeyW(HKEY_CURRENT_USER, L"Software\\ReactOS", &hKey);
473     if (error)
474     {
475         skip("FAILED to create HKCU\\Software\\ReactOS\n");
476         return;
477     }
478 
479     IPropertyBag *pPropBag;
480     HRESULT hr;
481 
482     // Try to create new registry key
483     RegDeleteKeyW(hKey, L"PropBagTest");
484     hr = SHCreatePropertyBagOnRegKey(hKey, L"PropBagTest", 0,
485                                      IID_IPropertyBag, (void **)&pPropBag);
486     ok_long(hr, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
487 
488     // Try to create new registry key
489     RegDeleteKeyW(hKey, L"PropBagTest");
490     hr = SHCreatePropertyBagOnRegKey(hKey, L"PropBagTest", STGM_READWRITE,
491                                      IID_IPropertyBag, (void **)&pPropBag);
492     ok_long(hr, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
493 
494     // Create new registry key
495     RegDeleteKeyW(hKey, L"PropBagTest");
496     hr = SHCreatePropertyBagOnRegKey(hKey, L"PropBagTest", STGM_CREATE | STGM_READWRITE,
497                                      IID_IPropertyBag, (void **)&pPropBag);
498     if (FAILED(hr))
499     {
500         skip("SHCreatePropertyBagOnRegKey FAILED\n");
501         RegCloseKey(hKey);
502         return;
503     }
504 
505     // Write UI4
506     VariantInit(&vari);
507     V_VT(&vari) = VT_UI4;
508     V_UI4(&vari) = 0xDEADFACE;
509     hr = pPropBag->Write(L"Name1", &vari);
510     ok_long(hr, S_OK);
511     VariantClear(&vari);
512 
513     // Read UI4
514     VariantInit(&vari);
515     hr = pPropBag->Read(L"Name1", &vari, NULL);
516     ok_long(hr, S_OK);
517     ok_long(V_VT(&vari), VT_UI4);
518     ok_long(V_UI4(&vari), 0xDEADFACE);
519     VariantClear(&vari);
520 
521     // Write BSTR
522     VariantInit(&vari);
523     V_VT(&vari) = VT_BSTR;
524     V_BSTR(&vari) = SysAllocString(L"StrValue");
525     hr = pPropBag->Write(L"Name2", &vari);
526     ok_long(hr, S_OK);
527     VariantClear(&vari);
528 
529     // Read BSTR
530     VariantInit(&vari);
531     V_VT(&vari) = VT_BSTR;
532     hr = pPropBag->Read(L"Name2", &vari, NULL);
533     ok_long(hr, S_OK);
534     ok_long(V_VT(&vari), VT_BSTR);
535     ok_wstr(V_BSTR(&vari), L"StrValue");
536     VariantClear(&vari);
537 
538     // Write GUID
539     VariantInit(&vari);
540     V_VT(&vari) = VT_UNKNOWN;
541     V_UNKNOWN(&vari) = SHCreateMemStream((BYTE*)&IID_IShellLinkW, sizeof(IID_IShellLinkW));
542     hr = pPropBag->Write(L"Name4", &vari);
543     ok_long(hr, S_OK);
544     VariantClear(&vari);
545 
546     // Read GUID
547     VariantInit(&vari);
548     V_VT(&vari) = VT_EMPTY;
549     hr = pPropBag->Read(L"Name4", &vari, NULL);
550     if (IsWindowsVistaOrGreater())
551     {
552         ok_long(hr, S_OK);
553         ok_long(V_VT(&vari), VT_UNKNOWN);
554         pStream = (IStream*)V_UNKNOWN(&vari);
555         FillMemory(&guid, sizeof(guid), 0xEE);
556         hr = pStream->Read(&guid, sizeof(guid), NULL);
557         ok_long(hr, S_OK);
558         ok_int(::IsEqualGUID(guid, IID_IShellLinkW), TRUE);
559     }
560     else // XP/2k3 Read is buggy
561     {
562         ok_long(hr, E_FAIL);
563         ok_long(V_VT(&vari), VT_EMPTY);
564     }
565     VariantClear(&vari);
566 
567     pPropBag->Release();
568 
569     // Check registry
570     error = RegOpenKeyExW(hKey, L"PropBagTest", 0, KEY_READ, &hSubKey);
571     ok_long(error, ERROR_SUCCESS);
572     DWORD dwType, dwValue, cbValue = sizeof(dwValue);
573     error = RegQueryValueExW(hSubKey, L"Name1", NULL, &dwType, (BYTE*)&dwValue, &cbValue);
574     ok_long(error, ERROR_SUCCESS);
575     ok_long(dwType, REG_DWORD);
576     ok_long(dwValue, 0xDEADFACE);
577     ok_long(cbValue, sizeof(DWORD));
578     cbValue = sizeof(szText);
579     error = RegQueryValueExW(hSubKey, L"Name2", NULL, &dwType, (BYTE*)szText, &cbValue);
580     ok_long(error, ERROR_SUCCESS);
581     ok_long(dwType, REG_SZ);
582     ok_wstr(szText, L"StrValue");
583     cbValue = sizeof(guid_and_extra);
584     error = RegQueryValueExW(hSubKey, L"Name4", NULL, &dwType, (BYTE*)&guid_and_extra, &cbValue);
585     ok_long(error, ERROR_SUCCESS);
586     ok_long(dwType, REG_BINARY);
587     ok_int(memcmp(&guid_and_extra, &GUID_NULL, sizeof(GUID)), 0);
588     ok_int(memcmp(&guid_and_extra[sizeof(GUID)], &IID_IShellLinkW, sizeof(GUID)), 0);
589     RegCloseKey(hSubKey);
590 
591     // Create as read-only
592     hr = SHCreatePropertyBagOnRegKey(hKey, L"PropBagTest", STGM_READ,
593                                      IID_IPropertyBag, (void **)&pPropBag);
594     ok_long(hr, S_OK);
595 
596     // Read UI4
597     VariantInit(&vari);
598     hr = pPropBag->Read(L"Name1", &vari, NULL);
599     ok_long(hr, S_OK);
600     ok_long(V_VT(&vari), VT_UI4);
601     ok_long(V_UI4(&vari), 0xDEADFACE);
602     VariantClear(&vari);
603 
604     // Write UI4
605     VariantInit(&vari);
606     V_VT(&vari) = VT_UI4;
607     V_UI4(&vari) = 0xDEADFACE;
608     hr = pPropBag->Write(L"Name1", &vari);
609     ok_long(hr, E_ACCESSDENIED);
610     VariantClear(&vari);
611 
612     pPropBag->Release();
613 
614     // Create as write-only IPropertyBag2
615     hr = SHCreatePropertyBagOnRegKey(hKey, L"PropBagTest", STGM_WRITE,
616                                      IID_IPropertyBag2, (void **)&pPropBag);
617     ok_long(hr, S_OK);
618 
619     // Write UI4
620     VariantInit(&vari);
621     V_VT(&vari) = VT_UI4;
622     V_UI4(&vari) = 0xDEADFACE;
623     hr = pPropBag->Write(L"Name3", &vari);
624     ok_long(hr, E_NOTIMPL);
625     VariantClear(&vari);
626 
627     // Read UI4
628     VariantInit(&vari);
629     V_UI4(&vari) = 0xFEEDF00D;
630     hr = pPropBag->Read(L"Name3", &vari, NULL);
631     ok_long(hr, E_NOTIMPL);
632     ok_int(V_VT(&vari), VT_EMPTY);
633     ok_long(V_UI4(&vari), 0xFEEDF00D);
634     VariantClear(&vari);
635 
636     pPropBag->Release();
637 
638     // Clean up
639     RegDeleteKeyW(hKey, L"PropBagTest");
640     RegCloseKey(hKey);
641 }
642 
643 static void SHPropertyBag_SHSetIniStringW(void)
644 {
645     WCHAR szIniFile[MAX_PATH];
646     WCHAR szValue[MAX_PATH];
647     BOOL bRet;
648     DWORD dwRet;
649 
650     ExpandEnvironmentStringsW(L"%TEMP%\\SHSetIniString.ini", szIniFile, _countof(szIniFile));
651 
652     DeleteFileW(szIniFile);
653 
654     trace("%ls\n", szIniFile);
655 
656     bRet = SHSetIniStringW(L"TestSection", L"Key", L"Value", szIniFile);
657     ok_int(bRet, TRUE);
658 
659     WritePrivateProfileStringW(NULL, NULL, NULL, szIniFile);
660 
661     dwRet = SHGetIniStringW(L"TestSection", L"Key", szValue, _countof(szValue), szIniFile);
662     ok_long(dwRet, 5);
663     ok_wstr(szValue, L"Value");
664 
665     bRet = SHSetIniStringW(L"TestSection", L"Key", NULL, szIniFile);
666     ok_int(bRet, TRUE);
667 
668     WritePrivateProfileStringW(NULL, NULL, NULL, szIniFile);
669 
670     dwRet = SHGetIniStringW(L"TestSection", L"Key", szValue, _countof(szValue), szIniFile);
671     ok_long(dwRet, 0);
672     ok_wstr(szValue, L"");
673 
674     bRet = SHSetIniStringW(L"TestSection", L"Key", L"ABC\x3042\x3044\x3046\x2665", szIniFile);
675     ok_int(bRet, TRUE);
676 
677     WritePrivateProfileStringW(NULL, NULL, NULL, szIniFile);
678 
679     dwRet = SHGetIniStringW(L"TestSection", L"Key", szValue, _countof(szValue), szIniFile);
680     ok_long(dwRet, 7);
681     ok_wstr(szValue, L"ABC\x3042\x3044\x3046\x2665");
682 
683     szValue[0] = 0x3000;
684     szValue[1] = UNICODE_NULL;
685     dwRet = SHGetIniStringW(L"TestSection", L"NotExistentKey", szValue, _countof(szValue), szIniFile);
686     ok_long(dwRet, 0);
687     ok_wstr(szValue, L"");
688 
689     DeleteFileW(szIniFile);
690 }
691 
692 static void SHPropertyBag_OnIniFile(void)
693 {
694     WCHAR szIniFile[MAX_PATH], szValue[MAX_PATH];
695     HRESULT hr;
696     IPropertyBag *pPropBag;
697     VARIANT vari;
698     DWORD dwRet;
699 
700     ExpandEnvironmentStringsW(L"%TEMP%\\SHPropertyBag.ini", szIniFile, _countof(szIniFile));
701 
702     DeleteFileW(szIniFile);
703     fclose(_wfopen(szIniFile, L"w"));
704 
705     trace("%ls\n", szIniFile);
706 
707     // read-write
708     hr = SHCreatePropertyBagOnProfileSection(
709         szIniFile,
710         L"TestSection",
711         STGM_READWRITE,
712         IID_IPropertyBag,
713         (void**)&pPropBag);
714     ok_long(hr, S_OK);
715     ok_int(PathFileExistsW(szIniFile), TRUE);
716 
717     // Write UI4
718     VariantInit(&vari);
719     V_VT(&vari) = VT_UI4;
720     V_UI4(&vari) = 0xDEADFACE;
721     hr = pPropBag->Write(L"Name1", &vari);
722     ok_long(hr, S_OK);
723     VariantClear(&vari);
724 
725     // Write BSTR
726     VariantInit(&vari);
727     V_VT(&vari) = VT_BSTR;
728     V_BSTR(&vari) = SysAllocString(L"StrValue");
729     hr = pPropBag->Write(L"Name2", &vari);
730     ok_long(hr, S_OK);
731     VariantClear(&vari);
732 
733     // Write BSTR (dirty UTF-7)
734     VariantInit(&vari);
735     V_VT(&vari) = VT_BSTR;
736     V_BSTR(&vari) = SysAllocString(L"ABC\x3042\x3044\x3046\x2665");
737     hr = pPropBag->Write(L"@Name3", &vari);
738     ok_long(hr, S_OK);
739     VariantClear(&vari);
740 
741     // Write BSTR (clean UTF-7)
742     VariantInit(&vari);
743     V_VT(&vari) = VT_BSTR;
744     V_BSTR(&vari) = SysAllocString(L"1234abc");
745     hr = pPropBag->Write(L"@Name4", &vari);
746     ok_long(hr, S_OK);
747     VariantClear(&vari);
748 
749     pPropBag->Release();
750 
751     // Flush
752     WritePrivateProfileStringW(NULL, NULL, NULL, szIniFile);
753 
754     // Check INI file
755     dwRet = GetPrivateProfileStringW(L"TestSection", L"Name1", L"BAD", szValue, _countof(szValue), szIniFile);
756     ok_long(dwRet, 10);
757     ok_wstr(szValue, L"3735943886");
758 
759     dwRet = GetPrivateProfileStringW(L"TestSection", L"Name2", L"BAD", szValue, _countof(szValue), szIniFile);
760     ok_long(dwRet, 8);
761     ok_wstr(szValue, L"StrValue");
762 
763     GetPrivateProfileStringW(L"TestSection", L"Name3", L"NotFound", szValue, _countof(szValue), szIniFile);
764     ok_int(memcmp(szValue, L"ABC", 3 * sizeof(WCHAR)), 0);
765 
766     GetPrivateProfileStringW(L"TestSection.A", L"Name3", L"NotFound", szValue, _countof(szValue), szIniFile);
767     ok_int(memcmp(szValue, L"ABC", 3 * sizeof(WCHAR)), 0);
768 
769     GetPrivateProfileStringW(L"TestSection.W", L"Name3", L"NotFound", szValue, _countof(szValue), szIniFile);
770     ok_wstr(szValue, L"ABC+MEIwRDBGJmU-"); // UTF-7
771 
772     GetPrivateProfileStringW(L"TestSection", L"Name4", L"NotFound", szValue, _countof(szValue), szIniFile);
773     ok_wstr(szValue, L"1234abc");
774 
775     GetPrivateProfileStringW(L"TestSection.A", L"Name4", L"NotFound", szValue, _countof(szValue), szIniFile);
776     ok_wstr(szValue, L"NotFound");
777 
778     GetPrivateProfileStringW(L"TestSection.W", L"Name4", L"NotFound", szValue, _countof(szValue), szIniFile);
779     ok_wstr(szValue, L"NotFound");
780 
781     // read-only
782     hr = SHCreatePropertyBagOnProfileSection(
783         szIniFile,
784         NULL,
785         STGM_READ,
786         IID_IPropertyBag,
787         (void**)&pPropBag);
788     ok_long(hr, S_OK);
789 
790     // Read UI4
791     VariantInit(&vari);
792     V_VT(&vari) = VT_UI4;
793     hr = pPropBag->Read(L"TestSection\\Name1", &vari, NULL);
794     ok_long(hr, S_OK);
795     ok_long(V_UI4(&vari), 0xDEADFACE);
796     VariantClear(&vari);
797 
798     // Read BSTR
799     VariantInit(&vari);
800     V_VT(&vari) = VT_BSTR;
801     hr = pPropBag->Read(L"TestSection\\Name2", &vari, NULL);
802     ok_long(hr, S_OK);
803     ok_wstr(V_BSTR(&vari), L"StrValue");
804     VariantClear(&vari);
805 
806     // Read BSTR (dirty UTF-7)
807     VariantInit(&vari);
808     V_VT(&vari) = VT_BSTR;
809     hr = pPropBag->Read(L"TestSection\\@Name3", &vari, NULL);
810     ok_long(hr, S_OK);
811     ok_wstr(V_BSTR(&vari), L"ABC\x3042\x3044\x3046\x2665");
812     VariantClear(&vari);
813 
814     // Read BSTR (clean UTF-7)
815     VariantInit(&vari);
816     V_VT(&vari) = VT_BSTR;
817     hr = pPropBag->Read(L"TestSection\\@Name4", &vari, NULL);
818     ok_long(hr, S_OK);
819     ok_wstr(V_BSTR(&vari), L"1234abc");
820     VariantClear(&vari);
821 
822     pPropBag->Release();
823 
824     DeleteFileW(szIniFile);
825 }
826 
827 static void SHPropertyBag_PerScreenRes(void)
828 {
829     HDC hDC = GetDC(NULL);
830     INT cxWidth = GetDeviceCaps(hDC, HORZRES);
831     INT cyHeight = GetDeviceCaps(hDC, VERTRES);
832     INT cMonitors = GetSystemMetrics(SM_CMONITORS);
833     ReleaseDC(NULL, hDC);
834 
835     WCHAR szBuff1[64], szBuff2[64];
836     StringCchPrintfW(szBuff1, _countof(szBuff1), L"%dx%d(%d)", cxWidth, cyHeight, cMonitors);
837 
838     szBuff2[0] = UNICODE_NULL;
839     SHGetPerScreenResName(szBuff2, _countof(szBuff2), 0);
840     ok_wstr(szBuff1, szBuff2);
841 }
842 
843 START_TEST(SHPropertyBag)
844 {
845     SHPropertyBag_ReadTest();
846     SHPropertyBag_WriteTest();
847     SHPropertyBag_OnMemory();
848     SHPropertyBag_OnRegKey();
849     SHPropertyBag_SHSetIniStringW();
850     SHPropertyBag_OnIniFile();
851     SHPropertyBag_PerScreenRes();
852 }
853