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