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
ResetTest(VARTYPE vt,LPCWSTR pszName0=NULL,LPCWSTR pszName1=NULL,LPCWSTR pszName2=NULL,LPCWSTR pszName3=NULL)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
CreateByteArray(LPCVOID pvSrc,DWORD cbSize)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:
CDummyPropertyBag()62 CDummyPropertyBag()
63 {
64 }
65
66 // IUnknown
QueryInterface(REFIID riid,void ** ppvObject)67 STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject) override
68 {
69 ok(FALSE, "Unexpected call\n");
70 return S_OK;
71 }
AddRef()72 STDMETHODIMP_(ULONG) AddRef() override
73 {
74 ok(FALSE, "Unexpected call\n");
75 return S_OK;
76 }
Release()77 STDMETHODIMP_(ULONG) Release() override
78 {
79 ok(FALSE, "Unexpected call\n");
80 return S_OK;
81 }
82
83 // IPropertyBag
Read(LPCWSTR pszPropName,VARIANT * pvari,IErrorLog * pErrorLog)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
Write(LPCWSTR pszPropName,VARIANT * pvari)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
SHPropertyBag_ReadTest(void)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
SHPropertyBag_WriteTest(void)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
SHPropertyBag_OnMemory(void)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
SHPropertyBag_OnRegKey(void)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
SHPropertyBag_SHSetIniStringW(void)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
SHPropertyBag_OnIniFile(void)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
SHPropertyBag_PerScreenRes(void)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
START_TEST(SHPropertyBag)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