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