Add RegReadUnexpandedString to get an unexpanded REG_EXPAND_SZ value.
Sean Hall committed
Jun 3, 2022 at 17:49 UTC
584213c5ffeca09b3fe24bd5e92f73fd057ac642
8 files changed
+1007
-196
src/burn/engine/search.cpp
+12
-38
@@ -955,15 +955,15 @@ static HRESULT RegistrySearchValue(
955
)
956
{
957
HRESULT hr = S_OK;
958
- DWORD er = ERROR_SUCCESS;
958
LPWSTR sczKey = NULL;
959
LPWSTR sczValue = NULL;
960
HKEY hKey = NULL;
961
DWORD dwType = 0;
963
- DWORD cbData = 0;
962
+ SIZE_T cbData = 0;
963
LPBYTE pData = NULL;
965
- DWORD cch = 0;
964
BURN_VARIANT value = { };
965
+ DWORD dwValue = 0;
966
+ LONGLONG llValue = 0;
967
968
// format key string
969
hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczKey, &sczKey, NULL);
@@ -988,64 +988,38 @@ static HRESULT RegistrySearchValue(
988
ExitOnFailure(hr, "Failed to open registry key.");
989
990
// get value
991
- er = ::RegQueryValueExW(hKey, sczValue, NULL, &dwType, NULL, &cbData);
992
- if (ERROR_FILE_NOT_FOUND == er)
991
+ hr = RegReadValue(hKey, sczValue, pSearch->RegistrySearch.fExpandEnvironment, &pData, &cbData, &dwType);
992
+ if (E_FILENOTFOUND == hr)
993
{
994
// What if there is a hidden variable in sczKey or sczValue?
995
LogStringLine(REPORT_STANDARD, "Registry value not found. Key = '%ls', Value = '%ls'", sczKey, sczValue);
996
997
ExitFunction1(hr = S_OK);
998
}
999
- ExitOnWin32Error(er, hr, "Failed to query registry key value size.");
1000
-
1001
- pData = (LPBYTE)MemAlloc(cbData + sizeof(WCHAR), TRUE); // + sizeof(WCHAR) here to ensure that we always have a null terminator for REG_SZ
1002
- ExitOnNull(pData, hr, E_OUTOFMEMORY, "Failed to allocate memory registry value.");
1003
-
1004
- er = ::RegQueryValueExW(hKey, sczValue, NULL, &dwType, pData, &cbData);
1005
- ExitOnWin32Error(er, hr, "Failed to query registry key value.");
999
+ ExitOnFailure(hr, "Failed to query registry key value.");
1000
1001
switch (dwType)
1002
{
1003
case REG_DWORD:
1010
- if (sizeof(LONG) != cbData)
1004
+ if (memcpy_s(&dwValue, sizeof(DWORD), pData, cbData))
1005
{
1006
ExitFunction1(hr = E_UNEXPECTED);
1007
}
1014
- hr = BVariantSetNumeric(&value, *((LONG*)pData));
1008
+ hr = BVariantSetNumeric(&value, dwValue);
1009
break;
1010
case REG_QWORD:
1017
- if (sizeof(LONGLONG) != cbData)
1011
+ if (memcpy_s(&llValue, sizeof(LONGLONG), pData, cbData))
1012
{
1013
ExitFunction1(hr = E_UNEXPECTED);
1014
}
1021
- hr = BVariantSetNumeric(&value, *((LONGLONG*)pData));
1015
+ hr = BVariantSetNumeric(&value, llValue);
1016
break;
1023
- case REG_EXPAND_SZ:
1024
- if (pSearch->RegistrySearch.fExpandEnvironment)
1025
- {
1026
- hr = StrAlloc(&value.sczValue, cbData);
1027
- ExitOnFailure(hr, "Failed to allocate string buffer.");
1028
- value.Type = BURN_VARIANT_TYPE_STRING;
1029
-
1030
- cch = ::ExpandEnvironmentStringsW((LPCWSTR)pData, value.sczValue, cbData);
1031
- if (cch > cbData)
1032
- {
1033
- hr = StrAlloc(&value.sczValue, cch);
1034
- ExitOnFailure(hr, "Failed to allocate string buffer.");
1035
-
1036
- if (cch != ::ExpandEnvironmentStringsW((LPCWSTR)pData, value.sczValue, cch))
1037
- {
1038
- ExitWithLastError(hr, "Failed to get expand environment string.");
1039
- }
1040
- }
1041
- break;
1042
- }
1043
- __fallthrough;
1017
+ case REG_EXPAND_SZ: __fallthrough;
1018
case REG_SZ:
1019
hr = BVariantSetString(&value, (LPCWSTR)pData, 0, FALSE);
1020
break;
1021
default:
1048
- ExitOnFailure(hr = E_NOTIMPL, "Unsupported registry key value type. Type = '%u'", dwType);
1022
+ ExitWithRootFailure(hr, E_NOTIMPL, "Unsupported registry key value type. Type = '%u'", dwType);
1023
}
1024
ExitOnFailure(hr, "Failed to read registry value.");
1025
src/burn/test/BurnUnitTest/TestRegistryFixture.cpp
+2
-2
@@ -186,7 +186,7 @@ namespace WixBuildTools
186
void TestRegistryFixture::SetUp()
187
{
188
// set mock API's
189
- RegFunctionOverride(TestRegistryFixture_RegCreateKeyExW, TestRegistryFixture_RegOpenKeyExW, TestRegistryFixture_RegDeleteKeyExW, NULL, NULL, NULL, NULL, NULL, NULL);
189
+ RegFunctionOverride(TestRegistryFixture_RegCreateKeyExW, TestRegistryFixture_RegOpenKeyExW, TestRegistryFixture_RegDeleteKeyExW, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
190
191
Registry::CurrentUser->CreateSubKey(TEST_REGISTRY_FIXTURE_HKCU_PATH);
192
Registry::CurrentUser->CreateSubKey(TEST_REGISTRY_FIXTURE_HKCU32_PATH);
@@ -198,7 +198,7 @@ namespace WixBuildTools
198
{
199
Registry::CurrentUser->DeleteSubKeyTree(this->rootPath, false);
200
201
- RegFunctionOverride(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
201
+ RegFunctionOverride(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
202
}
203
204
String^ TestRegistryFixture::GetDirectHkcuPath(REG_KEY_BITNESS bitness, ... array<String^>^ paths)
src/libs/dutil/WixToolset.DUtil/inc/regutil.h
+43
-1
@@ -99,6 +99,15 @@ typedef LSTATUS (APIENTRY *PFN_REGDELETEVALUEW)(
99
__in HKEY hKey,
100
__in_opt LPCWSTR lpValueName
101
);
102
+typedef LSTATUS(APIENTRY *PFN_REGGETVALUEW)(
103
+ __in HKEY hkey,
104
+ __in_opt LPCWSTR lpSubKey,
105
+ __in_opt LPCWSTR lpValue,
106
+ __in DWORD dwFlags,
107
+ __out_opt LPDWORD pdwType,
108
+ __out_bcount_part_opt(*pcbData, *pcbData) __out_data_source(REGISTRY) PVOID pvData,
109
+ __inout_opt LPDWORD pcbData
110
+ );
111
112
/********************************************************************
113
RegInitialize - initializes regutil
@@ -126,9 +135,17 @@ void DAPI RegFunctionOverride(
135
__in_opt PFN_REGQUERYINFOKEYW pfnRegQueryInfoKeyW,
136
__in_opt PFN_REGQUERYVALUEEXW pfnRegQueryValueExW,
137
__in_opt PFN_REGSETVALUEEXW pfnRegSetValueExW,
129
- __in_opt PFN_REGDELETEVALUEW pfnRegDeleteValueW
138
+ __in_opt PFN_REGDELETEVALUEW pfnRegDeleteValueW,
139
+ __in_opt PFN_REGGETVALUEW pfnRegGetValueW
140
);
141
142
+/********************************************************************
143
+ RegFunctionForceFallback - ignore functions only available in newer versions of Windows.
144
+ Typically used for unit testing.
145
+
146
+*********************************************************************/
147
+void DAPI RegFunctionForceFallback();
148
+
149
/********************************************************************
150
RegCreate - creates a registry key.
151
@@ -219,6 +236,20 @@ HRESULT DAPI RegGetType(
236
__out DWORD *pdwType
237
);
238
239
+/********************************************************************
240
+ RegReadBinary - reads a registry key value.
241
+ If fExpand is TRUE and the value is REG_EXPAND_SZ then it will be expanded.
242
+ NOTE: caller is responsible for freeing *ppbBuffer
243
+*********************************************************************/
244
+HRESULT DAPI RegReadValue(
245
+ __in HKEY hk,
246
+ __in_z_opt LPCWSTR wzName,
247
+ __in BOOL fExpand,
248
+ __deref_out_bcount_opt(*pcbBuffer) BYTE** ppbBuffer,
249
+ __inout SIZE_T* pcbBuffer,
250
+ __out DWORD* pdwType
251
+ );
252
+
253
/********************************************************************
254
RegReadBinary - reads a registry key binary value.
255
NOTE: caller is responsible for freeing *ppbBuffer
@@ -240,6 +271,17 @@ HRESULT DAPI RegReadString(
271
__deref_out_z LPWSTR* psczValue
272
);
273
274
+/********************************************************************
275
+ RegReadUnexpandedString - reads a registry key value as a string without expanding it.
276
+
277
+*********************************************************************/
278
+HRESULT DAPI RegReadUnexpandedString(
279
+ __in HKEY hk,
280
+ __in_z_opt LPCWSTR wzName,
281
+ __inout BOOL* pfNeedsExpansion,
282
+ __deref_out_z LPWSTR* psczValue
283
+ );
284
+
285
/********************************************************************
286
RegReadStringArray - reads a registry key value REG_MULTI_SZ value as a string array.
287
src/libs/dutil/WixToolset.DUtil/regutil.cpp
+265
-155
@@ -9,6 +9,7 @@
9
#define RegExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__)
10
#define RegExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__)
11
#define RegExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__)
12
+#define RegExitWithRootFailure(x, e, s, ...) ExitWithRootFailureSource(DUTIL_SOURCE_REGUTIL, x, e, s, __VA_ARGS__)
13
#define RegExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__)
14
#define RegExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_REGUTIL, p, x, e, s, __VA_ARGS__)
15
#define RegExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_REGUTIL, p, x, s, __VA_ARGS__)
@@ -28,10 +29,19 @@ static PFN_REGQUERYINFOKEYW vpfnRegQueryInfoKeyW = ::RegQueryInfoKeyW;
29
static PFN_REGQUERYVALUEEXW vpfnRegQueryValueExW = ::RegQueryValueExW;
30
static PFN_REGSETVALUEEXW vpfnRegSetValueExW = ::RegSetValueExW;
31
static PFN_REGDELETEVALUEW vpfnRegDeleteValueW = ::RegDeleteValueW;
32
+static PFN_REGGETVALUEW vpfnRegGetValueW = NULL;
33
+static PFN_REGGETVALUEW vpfnRegGetValueWFromLibrary = NULL;
34
35
static HMODULE vhAdvApi32Dll = NULL;
36
static BOOL vfRegInitialized = FALSE;
37
38
+static HRESULT GetRegValue(
39
+ __in HKEY hk,
40
+ __in_z_opt LPCWSTR wzName,
41
+ __deref_out_bcount_opt(*pcbBuffer) BYTE* pbBuffer,
42
+ __inout SIZE_T* pcbBuffer,
43
+ __out DWORD* pdwType
44
+ );
45
static HRESULT WriteStringToRegistry(
46
__in HKEY hk,
47
__in_z_opt LPCWSTR wzName,
@@ -46,14 +56,22 @@ DAPI_(HRESULT) RegInitialize()
56
hr = LoadSystemLibrary(L"AdvApi32.dll", &vhAdvApi32Dll);
57
RegExitOnFailure(hr, "Failed to load AdvApi32.dll");
58
49
- // ignore failures - if this doesn't exist, we'll fall back to RegDeleteKeyW
59
+ // Ignore failures - if this doesn't exist, we'll fall back to RegDeleteKeyW.
60
vpfnRegDeleteKeyExWFromLibrary = reinterpret_cast<PFN_REGDELETEKEYEXW>(::GetProcAddress(vhAdvApi32Dll, "RegDeleteKeyExW"));
61
52
- if (NULL == vpfnRegDeleteKeyExW)
62
+ // Ignore failures - if this doesn't exist, we'll fall back to RegQueryValueExW.
63
+ vpfnRegGetValueWFromLibrary = reinterpret_cast<PFN_REGGETVALUEW>(::GetProcAddress(vhAdvApi32Dll, "RegGetValueW"));
64
+
65
+ if (!vpfnRegDeleteKeyExW)
66
{
67
vpfnRegDeleteKeyExW = vpfnRegDeleteKeyExWFromLibrary;
68
}
69
70
+ if (!vpfnRegGetValueW)
71
+ {
72
+ vpfnRegGetValueW = vpfnRegGetValueWFromLibrary;
73
+ }
74
+
75
vfRegInitialized = TRUE;
76
77
LExit:
@@ -68,7 +86,9 @@ DAPI_(void) RegUninitialize()
86
::FreeLibrary(vhAdvApi32Dll);
87
vhAdvApi32Dll = NULL;
88
vpfnRegDeleteKeyExWFromLibrary = NULL;
89
+ vpfnRegGetValueWFromLibrary = NULL;
90
vpfnRegDeleteKeyExW = NULL;
91
+ vpfnRegGetValueW = NULL;
92
}
93
94
vfRegInitialized = FALSE;
@@ -84,7 +104,8 @@ DAPI_(void) RegFunctionOverride(
104
__in_opt PFN_REGQUERYINFOKEYW pfnRegQueryInfoKeyW,
105
__in_opt PFN_REGQUERYVALUEEXW pfnRegQueryValueExW,
106
__in_opt PFN_REGSETVALUEEXW pfnRegSetValueExW,
87
- __in_opt PFN_REGDELETEVALUEW pfnRegDeleteValueW
107
+ __in_opt PFN_REGDELETEVALUEW pfnRegDeleteValueW,
108
+ __in_opt PFN_REGGETVALUEW pfnRegGetValueW
109
)
110
{
111
vpfnRegCreateKeyExW = pfnRegCreateKeyExW ? pfnRegCreateKeyExW : ::RegCreateKeyExW;
@@ -96,6 +117,14 @@ DAPI_(void) RegFunctionOverride(
117
vpfnRegQueryValueExW = pfnRegQueryValueExW ? pfnRegQueryValueExW : ::RegQueryValueExW;
118
vpfnRegSetValueExW = pfnRegSetValueExW ? pfnRegSetValueExW : ::RegSetValueExW;
119
vpfnRegDeleteValueW = pfnRegDeleteValueW ? pfnRegDeleteValueW : ::RegDeleteValueW;
120
+ vpfnRegGetValueW = pfnRegGetValueW ? pfnRegGetValueW : vpfnRegGetValueWFromLibrary;
121
+}
122
+
123
+
124
+DAPI_(void) RegFunctionForceFallback()
125
+{
126
+ vpfnRegDeleteKeyExW = NULL;
127
+ vpfnRegGetValueW = NULL;
128
}
129
130
@@ -358,54 +387,104 @@ LExit:
387
return hr;
388
}
389
361
-DAPI_(HRESULT) RegReadBinary(
390
+DAPI_(HRESULT) RegReadValue(
391
__in HKEY hk,
392
__in_z_opt LPCWSTR wzName,
393
+ __in BOOL fExpand,
394
__deref_out_bcount_opt(*pcbBuffer) BYTE** ppbBuffer,
365
- __out SIZE_T *pcbBuffer
366
- )
395
+ __inout SIZE_T* pcbBuffer,
396
+ __out DWORD* pdwType
397
+ )
398
{
399
HRESULT hr = S_OK;
369
- LPBYTE pbBuffer = NULL;
370
- DWORD er = ERROR_SUCCESS;
371
- DWORD cb = 0;
372
- DWORD dwType = 0;
373
-
374
- er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, NULL, &cb);
375
- RegExitOnWin32Error(er, hr, "Failed to get size of registry value.");
400
+ DWORD dwAttempts = 0;
401
+ LPWSTR sczExpand = NULL;
402
377
- // Zero-length binary values can exist
378
- if (0 < cb)
403
+ hr = GetRegValue(hk, wzName, *ppbBuffer, pcbBuffer, pdwType);
404
+ if (E_FILENOTFOUND == hr)
405
{
380
- pbBuffer = static_cast<LPBYTE>(MemAlloc(cb, FALSE));
381
- RegExitOnNull(pbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer for binary registry value.");
406
+ ExitFunction();
407
+ }
408
+ else if (E_MOREDATA != hr)
409
+ {
410
+ RegExitOnFailure(hr, "Failed to get size of raw registry value.");
411
383
- er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, pbBuffer, &cb);
384
- if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er))
412
+ // Zero-length raw values can exist
413
+ if (!*ppbBuffer && 0 < *pcbBuffer)
414
{
386
- ExitFunction1(hr = E_FILENOTFOUND);
415
+ hr = E_MOREDATA;
416
}
388
- RegExitOnWin32Error(er, hr, "Failed to read registry value.");
417
}
418
391
- if (REG_BINARY == dwType)
419
+ while (E_MOREDATA == hr && dwAttempts < 10)
420
{
393
- *ppbBuffer = pbBuffer;
394
- pbBuffer = NULL;
395
- *pcbBuffer = cb;
421
+ ++dwAttempts;
422
+
423
+ if (*ppbBuffer)
424
+ {
425
+ *ppbBuffer = static_cast<LPBYTE>(MemReAlloc(*ppbBuffer, *pcbBuffer, FALSE));
426
+ }
427
+ else
428
+ {
429
+ *ppbBuffer = static_cast<LPBYTE>(MemAlloc(*pcbBuffer, FALSE));
430
+ }
431
+ RegExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer for raw registry value.");
432
+
433
+ hr = GetRegValue(hk, wzName, *ppbBuffer, pcbBuffer, pdwType);
434
+ if (E_FILENOTFOUND == hr)
435
+ {
436
+ ExitFunction();
437
+ }
438
+ else if (E_MOREDATA != hr)
439
+ {
440
+ RegExitOnFailure(hr, "Failed to read raw registry value.");
441
+ }
442
}
397
- else
443
+
444
+ if (fExpand && SUCCEEDED(hr) && REG_EXPAND_SZ == *pdwType)
445
{
399
- hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE);
400
- RegExitOnRootFailure(hr, "Error reading binary registry value due to unexpected data type: %u", dwType);
446
+ LPWSTR sczValue = reinterpret_cast<LPWSTR>(*ppbBuffer);
447
+ hr = PathExpand(&sczExpand, sczValue, PATH_EXPAND_ENVIRONMENT);
448
+ RegExitOnFailure(hr, "Failed to expand registry value: %ls", sczValue);
449
+
450
+ *ppbBuffer = reinterpret_cast<LPBYTE>(sczExpand);
451
+ *pcbBuffer = (lstrlenW(sczExpand) + 1) * sizeof(WCHAR);
452
+ sczExpand = NULL;
453
+ ReleaseMem(sczValue);
454
}
455
456
LExit:
404
- ReleaseMem(pbBuffer);
457
+ ReleaseStr(sczExpand);
458
459
return hr;
460
}
461
462
+DAPI_(HRESULT) RegReadBinary(
463
+ __in HKEY hk,
464
+ __in_z_opt LPCWSTR wzName,
465
+ __deref_out_bcount_opt(*pcbBuffer) BYTE** ppbBuffer,
466
+ __out SIZE_T* pcbBuffer
467
+ )
468
+{
469
+ HRESULT hr = S_OK;
470
+ DWORD dwType = 0;
471
+
472
+ hr = RegReadValue(hk, wzName, FALSE, ppbBuffer, pcbBuffer, &dwType);
473
+ if (E_FILENOTFOUND == hr)
474
+ {
475
+ ExitFunction();
476
+ }
477
+ RegExitOnFailure(hr, "Failed to read binary registry value.");
478
+
479
+ if (REG_BINARY != dwType)
480
+ {
481
+ RegExitWithRootFailure(hr, HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE), "Error reading binary registry value due to unexpected data type: %u", dwType);
482
+ }
483
+
484
+LExit:
485
+ return hr;
486
+}
487
+
488
489
DAPI_(HRESULT) RegReadString(
490
__in HKEY hk,
@@ -414,65 +493,64 @@ DAPI_(HRESULT) RegReadString(
493
)
494
{
495
HRESULT hr = S_OK;
417
- DWORD er = ERROR_SUCCESS;
496
SIZE_T cbValue = 0;
419
- DWORD cch = 0;
420
- DWORD cb = 0;
497
DWORD dwType = 0;
422
- LPWSTR sczExpand = NULL;
498
499
if (psczValue && *psczValue)
500
{
426
- hr = StrMaxLength(*psczValue, &cbValue);
427
- RegExitOnFailure(hr, "Failed to determine length of string.");
428
-
429
- cch = (DWORD)min(DWORD_MAX, cbValue);
501
+ hr = MemSizeChecked(*psczValue, &cbValue);
502
+ RegExitOnFailure(hr, "Failed to get size of input buffer.");
503
}
504
432
- if (2 > cch)
505
+ hr = RegReadValue(hk, wzName, TRUE, reinterpret_cast<LPBYTE*>(psczValue), &cbValue, &dwType);
506
+ if (E_FILENOTFOUND == hr)
507
{
434
- cch = 2;
435
-
436
- hr = StrAlloc(psczValue, cch);
437
- RegExitOnFailure(hr, "Failed to allocate string to minimum size.");
508
+ ExitFunction();
509
}
510
+ RegExitOnFailure(hr, "Failed to read string registry value.");
511
440
- cb = sizeof(WCHAR) * (cch - 1); // subtract one to ensure there will be a space at the end of the string for the null terminator.
441
- er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(*psczValue), &cb);
442
- if (ERROR_MORE_DATA == er)
512
+ if (REG_EXPAND_SZ != dwType && REG_SZ != dwType)
513
{
444
- cch = cb / sizeof(WCHAR) + 1; // add one to ensure there will be space at the end for the null terminator
445
- hr = StrAlloc(psczValue, cch);
446
- RegExitOnFailure(hr, "Failed to allocate string bigger for registry value.");
447
-
448
- er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(*psczValue), &cb);
514
+ RegExitWithRootFailure(hr, HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE), "Error reading string registry value due to unexpected data type: %u", dwType);
515
}
450
- if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er))
516
+
517
+LExit:
518
+ return hr;
519
+}
520
+
521
+
522
+DAPI_(HRESULT) RegReadUnexpandedString(
523
+ __in HKEY hk,
524
+ __in_z_opt LPCWSTR wzName,
525
+ __inout BOOL* pfNeedsExpansion,
526
+ __deref_out_z LPWSTR* psczValue
527
+ )
528
+{
529
+ HRESULT hr = S_OK;
530
+ SIZE_T cbValue = 0;
531
+ DWORD dwType = 0;
532
+ LPWSTR sczExpand = NULL;
533
+
534
+ if (psczValue && *psczValue)
535
{
452
- ExitFunction1(hr = E_FILENOTFOUND);
536
+ hr = MemSizeChecked(*psczValue, &cbValue);
537
+ RegExitOnFailure(hr, "Failed to get size of input buffer.");
538
}
454
- RegExitOnWin32Error(er, hr, "Failed to read registry key.");
539
456
- if (REG_SZ == dwType || REG_EXPAND_SZ == dwType)
540
+ hr = RegReadValue(hk, wzName, FALSE, reinterpret_cast<LPBYTE*>(psczValue), &cbValue, &dwType);
541
+ if (E_FILENOTFOUND == hr)
542
{
458
- // Always ensure the registry value is null terminated.
459
- (*psczValue)[cch - 1] = L'\0';
460
-
461
- if (REG_EXPAND_SZ == dwType)
462
- {
463
- hr = StrAllocString(&sczExpand, *psczValue, 0);
464
- RegExitOnFailure(hr, "Failed to copy registry value to expand.");
465
-
466
- hr = PathExpand(psczValue, sczExpand, PATH_EXPAND_ENVIRONMENT);
467
- RegExitOnFailure(hr, "Failed to expand registry value: %ls", *psczValue);
468
- }
543
+ ExitFunction();
544
}
470
- else
545
+ RegExitOnFailure(hr, "Failed to read expand string registry value.");
546
+
547
+ if (REG_EXPAND_SZ != dwType && REG_SZ != dwType)
548
{
472
- hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE);
473
- RegExitOnRootFailure(hr, "Error reading string registry value due to unexpected data type: %u", dwType);
549
+ RegExitWithRootFailure(hr, HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE), "Error reading expand string registry value due to unexpected data type: %u", dwType);
550
}
551
552
+ *pfNeedsExpansion = REG_EXPAND_SZ == dwType;
553
+
554
LExit:
555
ReleaseStr(sczExpand);
556
@@ -488,67 +566,57 @@ DAPI_(HRESULT) RegReadStringArray(
566
)
567
{
568
HRESULT hr = S_OK;
491
- DWORD er = ERROR_SUCCESS;
569
DWORD dwNullCharacters = 0;
570
DWORD dwType = 0;
494
- DWORD cb = 0;
495
- DWORD cch = 0;
571
+ SIZE_T cb = 0;
572
+ SIZE_T cch = 0;
573
LPCWSTR wzSource = NULL;
574
LPWSTR sczValue = NULL;
575
499
- er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(sczValue), &cb);
500
- if (0 < cb)
501
- {
502
- cch = cb / sizeof(WCHAR);
503
- hr = StrAlloc(&sczValue, cch);
504
- RegExitOnFailure(hr, "Failed to allocate string for registry value.");
505
-
506
- er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(sczValue), &cb);
507
- }
508
- if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er))
576
+ hr = RegReadValue(hk, wzName, FALSE, reinterpret_cast<LPBYTE*>(&sczValue), &cb, &dwType);
577
+ if (E_FILENOTFOUND == hr)
578
{
510
- ExitFunction1(hr = E_FILENOTFOUND);
579
+ ExitFunction();
580
}
512
- RegExitOnWin32Error(er, hr, "Failed to read registry key.");
581
+ RegExitOnFailure(hr, "Failed to read string array registry value.");
582
514
- if (cb / sizeof(WCHAR) != cch)
583
+ if (REG_MULTI_SZ != dwType)
584
{
516
- hr = E_UNEXPECTED;
517
- RegExitOnFailure(hr, "The size of registry value %ls unexpected changed between 2 reads", wzName);
585
+ RegExitWithRootFailure(hr, HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE), "Tried to read string array, but registry value %ls is of an incorrect type", wzName);
586
}
587
520
- if (REG_MULTI_SZ != dwType)
588
+ cch = cb / sizeof(WCHAR);
589
+
590
+ for (DWORD i = 0; i < cch; ++i)
591
{
522
- hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE);
523
- RegExitOnRootFailure(hr, "Tried to read string array, but registry value %ls is of an incorrect type", wzName);
592
+ if (L'\0' == sczValue[i])
593
+ {
594
+ ++dwNullCharacters;
595
+ }
596
}
597
598
// Value exists, but is empty, so no strings to return.
527
- if (2 > cch)
599
+ if (!cb || 1 == dwNullCharacters && 1 == cch || 2 == dwNullCharacters && 2 == cch)
600
{
601
*prgsczStrings = NULL;
602
*pcStrings = 0;
603
ExitFunction1(hr = S_OK);
604
}
605
534
- // The docs specifically say if the value was written without double-null-termination, it'll get read back without it too.
535
- if (L'\0' != sczValue[cch-1] || L'\0' != sczValue[cch-2])
606
+ if (sczValue[cch - 1] != L'\0')
607
{
537
- hr = E_INVALIDARG;
538
- RegExitOnFailure(hr, "Tried to read string array, but registry value %ls is invalid (isn't double-null-terminated)", wzName);
608
+ // Count the terminating null character that RegReadValue added past the end.
609
+ ++dwNullCharacters;
610
+ Assert(!sczValue[cch]);
611
}
540
-
541
- cch = cb / sizeof(WCHAR);
542
- for (DWORD i = 0; i < cch; ++i)
612
+ else if (cch > 1 && sczValue[cch - 2] == L'\0')
613
{
544
- if (L'\0' == sczValue[i])
545
- {
546
- ++dwNullCharacters;
547
- }
614
+ // Don't count the extra 1 at the end of the properly double-null terminated string.
615
+ --dwNullCharacters;
616
}
617
550
- // There's one string for every null character encountered (except the extra 1 at the end of the string)
551
- *pcStrings = dwNullCharacters - 1;
618
+ // There's one string for every null character encountered.
619
+ *pcStrings = dwNullCharacters;
620
hr = MemEnsureArraySize(reinterpret_cast<LPVOID *>(prgsczStrings), *pcStrings, sizeof(LPWSTR), 0);
621
RegExitOnFailure(hr, "Failed to resize array while reading REG_MULTI_SZ value");
622
@@ -557,11 +625,13 @@ DAPI_(HRESULT) RegReadStringArray(
625
wzSource = sczValue;
626
for (DWORD i = 0; i < *pcStrings; ++i)
627
{
560
- hr = StrAllocString(&(*prgsczStrings)[i], wzSource, 0);
628
+ cch = lstrlenW(wzSource);
629
+
630
+ hr = StrAllocString(&(*prgsczStrings)[i], wzSource, cch);
631
RegExitOnFailure(hr, "Failed to allocate copy of string");
632
633
// Skip past this string
564
- wzSource += lstrlenW(wzSource) + 1;
634
+ wzSource += cch + 1;
635
}
636
#pragma prefast(pop)
637
@@ -579,38 +649,36 @@ DAPI_(HRESULT) RegReadVersion(
649
)
650
{
651
HRESULT hr = S_OK;
582
- DWORD er = ERROR_SUCCESS;
652
DWORD dwType = 0;
584
- DWORD cb = 0;
585
- LPWSTR sczVersion = NULL;
653
+ SIZE_T cb = 0;
654
+ LPWSTR sczValue = NULL;
655
587
- cb = sizeof(DWORD64);
588
- er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(*pdw64Version), &cb);
589
- if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er))
656
+ hr = RegReadValue(hk, wzName, TRUE, reinterpret_cast<LPBYTE*>(&sczValue), &cb, &dwType);
657
+ if (E_FILENOTFOUND == hr)
658
{
591
- ExitFunction1(hr = E_FILENOTFOUND);
659
+ ExitFunction();
660
}
661
+ RegExitOnFailure(hr, "Failed to read version registry value.");
662
663
if (REG_SZ == dwType || REG_EXPAND_SZ == dwType)
664
{
596
- hr = RegReadString(hk, wzName, &sczVersion);
597
- RegExitOnFailure(hr, "Failed to read registry version as string.");
598
-
599
- hr = FileVersionFromStringEx(sczVersion, 0, pdw64Version);
665
+ hr = FileVersionFromStringEx(sczValue, 0, pdw64Version);
666
RegExitOnFailure(hr, "Failed to convert registry string to version.");
667
}
668
else if (REG_QWORD == dwType)
669
{
604
- RegExitOnWin32Error(er, hr, "Failed to read registry key.");
670
+ if (memcpy_s(pdw64Version, sizeof(DWORD64), sczValue, cb))
671
+ {
672
+ RegExitWithRootFailure(hr, E_INVALIDARG, "Failed to copy QWORD version value.");
673
+ }
674
}
675
else // unexpected data type
676
{
608
- hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE);
609
- RegExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType);
677
+ RegExitWithRootFailure(hr, HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE), "Error reading version registry value due to unexpected data type: %u", dwType);
678
}
679
680
LExit:
613
- ReleaseStr(sczVersion);
681
+ ReleaseStr(sczValue);
682
683
return hr;
684
}
@@ -623,37 +691,38 @@ DAPI_(HRESULT) RegReadWixVersion(
691
)
692
{
693
HRESULT hr = S_OK;
626
- DWORD er = ERROR_SUCCESS;
694
DWORD dwType = 0;
628
- DWORD cb = 0;
695
+ SIZE_T cb = 0;
696
DWORD64 dw64Version = 0;
630
- LPWSTR sczVersion = NULL;
697
+ LPWSTR sczValue = NULL;
698
VERUTIL_VERSION* pVersion = NULL;
699
633
- cb = sizeof(DWORD64);
634
- er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(&dw64Version), &cb);
635
- if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er))
700
+ hr = RegReadValue(hk, wzName, TRUE, reinterpret_cast<LPBYTE*>(&sczValue), &cb, &dwType);
701
+ if (E_FILENOTFOUND == hr)
702
{
637
- ExitFunction1(hr = E_FILENOTFOUND);
703
+ ExitFunction();
704
}
705
+ RegExitOnFailure(hr, "Failed to read wix version registry value.");
706
707
if (REG_SZ == dwType || REG_EXPAND_SZ == dwType)
708
{
642
- hr = RegReadString(hk, wzName, &sczVersion);
643
- RegExitOnFailure(hr, "Failed to read registry version as string.");
644
-
645
- hr = VerParseVersion(sczVersion, 0, FALSE, &pVersion);
646
- RegExitOnFailure(hr, "Failed to convert registry string to version.");
709
+ hr = VerParseVersion(sczValue, 0, FALSE, &pVersion);
710
+ RegExitOnFailure(hr, "Failed to convert registry string to wix version.");
711
}
712
else if (REG_QWORD == dwType)
713
{
714
+ if (memcpy_s(&dw64Version, sizeof(DWORD64), sczValue, cb))
715
+ {
716
+ RegExitWithRootFailure(hr, E_INVALIDARG, "Failed to copy QWORD wix version value.");
717
+ }
718
+
719
hr = VerVersionFromQword(dw64Version, &pVersion);
651
- RegExitOnFailure(hr, "Failed to convert registry string to version.");
720
+ RegExitOnFailure(hr, "Failed to convert registry string to wix version.");
721
}
722
else // unexpected data type
723
{
724
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE);
656
- RegExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType);
725
+ RegExitOnRootFailure(hr, "Error reading wix version registry value due to unexpected data type: %u", dwType);
726
}
727
728
*ppVersion = pVersion;
@@ -661,7 +730,7 @@ DAPI_(HRESULT) RegReadWixVersion(
730
731
LExit:
732
ReleaseVerutilVersion(pVersion);
664
- ReleaseStr(sczVersion);
733
+ ReleaseStr(sczValue);
734
735
return hr;
736
}
@@ -673,20 +742,18 @@ DAPI_(HRESULT) RegReadNone(
742
)
743
{
744
HRESULT hr = S_OK;
676
- DWORD er = ERROR_SUCCESS;
745
DWORD dwType = 0;
746
679
- er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, NULL, NULL);
680
- if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er))
747
+ hr = RegGetType(hk, wzName, &dwType);
748
+ if (E_FILENOTFOUND == hr)
749
{
682
- ExitFunction1(hr = E_FILENOTFOUND);
750
+ ExitFunction();
751
}
684
- RegExitOnWin32Error(er, hr, "Failed to query registry key value.");
752
+ RegExitOnFailure(hr, "Error reading none registry value type.");
753
754
if (REG_NONE != dwType)
755
{
688
- hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE);
689
- RegExitOnRootFailure(hr, "Error reading none registry value due to unexpected data type: %u", dwType);
756
+ RegExitWithRootFailure(hr, HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE), "Error reading none registry value due to unexpected data type: %u", dwType);
757
}
758
759
LExit:
@@ -829,12 +896,9 @@ DAPI_(HRESULT) RegWriteStringArray(
896
DWORD dwTotalStringSize = 0;
897
DWORD cbTotalStringSize = 0;
898
DWORD dwTemp = 0;
899
+ DWORD dwRemainingStringSize = 0;
900
833
- if (0 == cValues)
834
- {
835
- wzWriteValue = L"\0";
836
- }
837
- else
901
+ if (cValues)
902
{
903
// Add space for the null terminator
904
dwTotalStringSize = 1;
@@ -850,21 +914,22 @@ DAPI_(HRESULT) RegWriteStringArray(
914
RegExitOnFailure(hr, "Failed to allocate space for string while writing REG_MULTI_SZ");
915
916
wzCopyDestination = sczWriteValue;
853
- dwTemp = dwTotalStringSize;
917
+ dwRemainingStringSize = dwTotalStringSize;
918
for (DWORD i = 0; i < cValues; ++i)
919
{
856
- hr = ::StringCchCopyW(wzCopyDestination, dwTotalStringSize, rgwzValues[i]);
920
+ hr = ::StringCchCopyW(wzCopyDestination, dwRemainingStringSize, rgwzValues[i]);
921
RegExitOnFailure(hr, "failed to copy string: %ls", rgwzValues[i]);
922
859
- dwTemp -= lstrlenW(rgwzValues[i]) + 1;
860
- wzCopyDestination += lstrlenW(rgwzValues[i]) + 1;
923
+ dwTemp = lstrlenW(rgwzValues[i]) + 1;
924
+ dwRemainingStringSize -= dwTemp;
925
+ wzCopyDestination += dwTemp;
926
}
927
928
wzWriteValue = sczWriteValue;
864
- }
929
866
- hr = ::DWordMult(dwTotalStringSize, sizeof(WCHAR), &cbTotalStringSize);
867
- RegExitOnFailure(hr, "Failed to get total string size in bytes");
930
+ hr = ::DWordMult(dwTotalStringSize, sizeof(WCHAR), &cbTotalStringSize);
931
+ RegExitOnFailure(hr, "Failed to get total string size in bytes");
932
+ }
933
934
er = vpfnRegSetValueExW(hk, wzName, 0, REG_MULTI_SZ, reinterpret_cast<const BYTE *>(wzWriteValue), cbTotalStringSize);
935
RegExitOnWin32Error(er, hr, "Failed to set registry value to array of strings (first string of which is): %ls", wzWriteValue);
@@ -1000,6 +1065,48 @@ DAPI_(REGSAM) RegTranslateKeyBitness(
1065
}
1066
}
1067
1068
+static HRESULT GetRegValue(
1069
+ __in HKEY hk,
1070
+ __in_z_opt LPCWSTR wzName,
1071
+ __deref_out_bcount_opt(*pcbBuffer) BYTE* pbBuffer,
1072
+ __inout SIZE_T* pcbBuffer,
1073
+ __out DWORD* pdwType
1074
+ )
1075
+{
1076
+ HRESULT hr = S_OK;
1077
+ DWORD cb = (DWORD)min(DWORD_MAX, *pcbBuffer);
1078
+
1079
+ if (vpfnRegGetValueW)
1080
+ {
1081
+ hr = HRESULT_FROM_WIN32(vpfnRegGetValueW(hk, NULL, wzName, RRF_RT_ANY | RRF_NOEXPAND, pdwType, pbBuffer, &cb));
1082
+ }
1083
+ else
1084
+ {
1085
+ hr = HRESULT_FROM_WIN32(vpfnRegQueryValueExW(hk, wzName, NULL, pdwType, pbBuffer, &cb));
1086
+
1087
+ if (REG_SZ == *pdwType || REG_EXPAND_SZ == *pdwType || REG_MULTI_SZ == *pdwType)
1088
+ {
1089
+ if (E_MOREDATA == hr || S_OK == hr && (cb + sizeof(WCHAR)) > *pcbBuffer)
1090
+ {
1091
+ // Make sure there's room for a null terminator at the end.
1092
+ HRESULT hrAdd = ::DWordAdd(cb, sizeof(WCHAR), &cb);
1093
+
1094
+ hr = FAILED(hrAdd) ? hrAdd : (pbBuffer ? E_MOREDATA : S_OK);
1095
+ }
1096
+ else if (S_OK == hr && pbBuffer)
1097
+ {
1098
+ // Always ensure the registry value is null terminated.
1099
+ WCHAR* pch = reinterpret_cast<WCHAR*>(pbBuffer + cb);
1100
+ *pch = L'\0';
1101
+ }
1102
+ }
1103
+ }
1104
+
1105
+ *pcbBuffer = cb;
1106
+
1107
+ return hr;
1108
+}
1109
+
1110
static HRESULT WriteStringToRegistry(
1111
__in HKEY hk,
1112
__in_z_opt LPCWSTR wzName,
@@ -1013,9 +1120,12 @@ static HRESULT WriteStringToRegistry(
1120
1121
if (wzValue)
1122
{
1016
- hr = ::StringCbLengthW(wzValue, STRSAFE_MAX_CCH * sizeof(TCHAR), &cbValue);
1123
+ hr = ::StringCbLengthW(wzValue, DWORD_MAX, &cbValue);
1124
RegExitOnFailure(hr, "Failed to determine length of registry value: %ls", wzName);
1125
1126
+ // Need to include the null terminator.
1127
+ cbValue += sizeof(WCHAR);
1128
+
1129
er = vpfnRegSetValueExW(hk, wzName, 0, dwType, reinterpret_cast<const BYTE *>(wzValue), static_cast<DWORD>(cbValue));
1130
RegExitOnWin32Error(er, hr, "Failed to set registry value: %ls", wzName);
1131
}
src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj
+1
@@ -61,6 +61,7 @@
61
<!-- Warnings from referencing netstandard dlls -->
62
<DisableSpecificWarnings>4564;4691</DisableSpecificWarnings>
63
</ClCompile>
64
+ <ClCompile Include="RegUtilTest.cpp" />
65
<ClCompile Include="SceUtilTest.cpp" Condition=" Exists('$(SqlCESdkIncludePath)') " />
66
<ClCompile Include="StrUtilTest.cpp" />
67
<ClCompile Include="UriUtilTest.cpp" />
src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters
+3
@@ -54,6 +54,9 @@
54
<ClCompile Include="precomp.cpp">
55
<Filter>Source Files</Filter>
56
</ClCompile>
57
+ <ClCompile Include="RegUtilTest.cpp">
58
+ <Filter>Source Files</Filter>
59
+ </ClCompile>
60
<ClCompile Include="StrUtilTest.cpp">
61
<Filter>Source Files</Filter>
62
</ClCompile>
src/libs/dutil/test/DUtilUnitTest/MonUtilTest.cpp
+2
@@ -481,6 +481,8 @@ namespace DutilTests
481
ReleaseMem(pResults->rgDirectories);
482
ReleaseMem(pResults->rgRegKeys);
483
ReleaseMem(pResults);
484
+
485
+ RegUninitialize();
486
}
487
}
488
};
src/libs/dutil/test/DUtilUnitTest/RegUtilTest.cpp
new
+679
@@ -0,0 +1,679 @@
1
+// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
+
3
+#include "precomp.h"
4
+
5
+using namespace System;
6
+using namespace System::Collections::Generic;
7
+using namespace System::Runtime::InteropServices;
8
+using namespace Xunit;
9
+using namespace WixBuildTools::TestSupport;
10
+
11
+LPCWSTR wzBaseRegKey = L"Software\\RegUtilTest\\";
12
+LPWSTR rgwzMultiValue[2] = { L"First", L"Second" };
13
+LPWSTR rgwzEmptyMultiValue[2] = { L"", L"" };
14
+
15
+HKEY hkBase;
16
+
17
+namespace DutilTests
18
+{
19
+ public ref class RegUtil : IDisposable
20
+ {
21
+ private:
22
+
23
+ void CreateBaseKey()
24
+ {
25
+ HRESULT hr = RegCreate(HKEY_CURRENT_USER, wzBaseRegKey, KEY_ALL_ACCESS, &hkBase);
26
+ NativeAssert::Succeeded(hr, "Failed to create base key.");
27
+ }
28
+
29
+ public:
30
+ RegUtil()
31
+ {
32
+ HRESULT hr = RegInitialize();
33
+ NativeAssert::Succeeded(hr, "RegInitialize failed.");
34
+ }
35
+
36
+ ~RegUtil()
37
+ {
38
+ RegFunctionOverride(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
39
+
40
+ if (hkBase)
41
+ {
42
+ RegDelete(hkBase, NULL, REG_KEY_DEFAULT, TRUE);
43
+ }
44
+
45
+ ReleaseRegKey(hkBase);
46
+
47
+ RegUninitialize();
48
+ }
49
+
50
+ [Fact]
51
+ void RegUtilStringValueTest()
52
+ {
53
+ this->StringValueTest();
54
+ }
55
+
56
+ [Fact]
57
+ void RegUtilStringValueFallbackTest()
58
+ {
59
+ RegFunctionForceFallback();
60
+ this->StringValueTest();
61
+ }
62
+
63
+ void StringValueTest()
64
+ {
65
+ HRESULT hr = S_OK;
66
+ LPWSTR sczValue = NULL;
67
+ LPCWSTR wzValue = L"Value";
68
+
69
+ try
70
+ {
71
+ this->CreateBaseKey();
72
+
73
+ hr = RegWriteString(hkBase, L"String", wzValue);
74
+ NativeAssert::Succeeded(hr, "Failed to write string value.");
75
+
76
+ hr = RegReadString(hkBase, L"String", &sczValue);
77
+ NativeAssert::Succeeded(hr, "Failed to read string value.");
78
+ NativeAssert::StringEqual(wzValue, sczValue);
79
+
80
+ ReleaseNullStr(sczValue);
81
+ hr = StrAllocString(&sczValue, L"e", 0);
82
+ NativeAssert::Succeeded(hr, "Failed to reallocate string value.");
83
+
84
+ hr = RegReadString(hkBase, L"String", &sczValue);
85
+ NativeAssert::Succeeded(hr, "Failed to read string value.");
86
+ NativeAssert::StringEqual(wzValue, sczValue);
87
+ }
88
+ finally
89
+ {
90
+ ReleaseStr(sczValue);
91
+ }
92
+ }
93
+
94
+ [Fact]
95
+ void RegUtilPartialStringValueTest()
96
+ {
97
+ this->PartialStringValueTest();
98
+ }
99
+
100
+ [Fact]
101
+ void RegUtilPartialStringValueFallbackTest()
102
+ {
103
+ RegFunctionForceFallback();
104
+ this->PartialStringValueTest();
105
+ }
106
+
107
+ void PartialStringValueTest()
108
+ {
109
+ HRESULT hr = S_OK;
110
+ LPWSTR sczValue = NULL;
111
+ LPCWSTR wzValue = L"Value";
112
+ BOOL fNeedsExpansion = FALSE;
113
+
114
+ try
115
+ {
116
+ this->CreateBaseKey();
117
+
118
+ // Use API directly to write non-null terminated string.
119
+ hr = HRESULT_FROM_WIN32(::RegSetValueExW(hkBase, L"PartialString", 0, REG_SZ, reinterpret_cast<const BYTE*>(wzValue), 4 * sizeof(WCHAR)));
120
+ NativeAssert::Succeeded(hr, "Failed to write partial string value.");
121
+
122
+ hr = RegReadString(hkBase, L"PartialString", &sczValue);
123
+ NativeAssert::Succeeded(hr, "Failed to read partial string value.");
124
+ NativeAssert::StringEqual(L"Valu", sczValue);
125
+
126
+ hr = RegReadUnexpandedString(hkBase, L"PartialString", &fNeedsExpansion, &sczValue);
127
+ NativeAssert::Succeeded(hr, "Failed to read partial unexpanded string value.");
128
+ NativeAssert::StringEqual(L"Valu", sczValue);
129
+ Assert::False(fNeedsExpansion);
130
+ }
131
+ finally
132
+ {
133
+ ReleaseStr(sczValue);
134
+ }
135
+ }
136
+
137
+ [Fact]
138
+ void RegUtilEmptyStringValueTest()
139
+ {
140
+ this->EmptyStringValueTest();
141
+ }
142
+
143
+ [Fact]
144
+ void RegUtilEmptyStringValueFallbackTest()
145
+ {
146
+ RegFunctionForceFallback();
147
+ this->EmptyStringValueTest();
148
+ }
149
+
150
+ void EmptyStringValueTest()
151
+ {
152
+ HRESULT hr = S_OK;
153
+ LPWSTR sczValue = NULL;
154
+
155
+ try
156
+ {
157
+ this->CreateBaseKey();
158
+
159
+ // Use API directly to write non-null terminated string.
160
+ hr = HRESULT_FROM_WIN32(::RegSetValueExW(hkBase, L"EmptyString", 0, REG_SZ, reinterpret_cast<const BYTE*>(L""), 0));
161
+ NativeAssert::Succeeded(hr, "Failed to write partial string value.");
162
+
163
+ hr = RegReadString(hkBase, L"EmptyString", &sczValue);
164
+ NativeAssert::Succeeded(hr, "Failed to read partial string value.");
165
+ NativeAssert::StringEqual(L"", sczValue);
166
+ }
167
+ finally
168
+ {
169
+ ReleaseStr(sczValue);
170
+ }
171
+ }
172
+
173
+ [Fact]
174
+ void RegUtilExpandStringValueTest()
175
+ {
176
+ this->ExpandStringValueTest();
177
+ }
178
+
179
+ [Fact]
180
+ void RegUtilExpandStringValueFallbackTest()
181
+ {
182
+ RegFunctionForceFallback();
183
+ this->ExpandStringValueTest();
184
+ }
185
+
186
+ void ExpandStringValueTest()
187
+ {
188
+ HRESULT hr = S_OK;
189
+ LPWSTR sczValue = NULL;
190
+ LPCWSTR wzValue = L"Value_%USERNAME%";
191
+ String^ expandedValue = Environment::ExpandEnvironmentVariables(gcnew String(wzValue));
192
+
193
+ try
194
+ {
195
+ this->CreateBaseKey();
196
+
197
+ hr = RegWriteExpandString(hkBase, L"ExpandString", wzValue);
198
+ NativeAssert::Succeeded(hr, "Failed to write expand string value.");
199
+
200
+ hr = RegReadString(hkBase, L"ExpandString", &sczValue);
201
+ NativeAssert::Succeeded(hr, "Failed to read expand string value.");
202
+ WixAssert::StringEqual(expandedValue, gcnew String(sczValue), false);
203
+ }
204
+ finally
205
+ {
206
+ ReleaseStr(sczValue);
207
+ }
208
+ }
209
+
210
+ [Fact]
211
+ void RegUtilNotExpandStringValueTest()
212
+ {
213
+ this->NotExpandStringValueTest();
214
+ }
215
+
216
+ [Fact]
217
+ void RegUtilNotExpandStringValueFallbackTest()
218
+ {
219
+ RegFunctionForceFallback();
220
+ this->NotExpandStringValueTest();
221
+ }
222
+
223
+ void NotExpandStringValueTest()
224
+ {
225
+ HRESULT hr = S_OK;
226
+ LPWSTR sczValue = NULL;
227
+ BOOL fNeedsExpansion = FALSE;
228
+ LPCWSTR wzValue = L"Value_%USERNAME%";
229
+
230
+ try
231
+ {
232
+ this->CreateBaseKey();
233
+
234
+ hr = RegWriteExpandString(hkBase, L"NotExpandString", wzValue);
235
+ NativeAssert::Succeeded(hr, "Failed to write expand string value.");
236
+
237
+ hr = RegReadUnexpandedString(hkBase, L"NotExpandString", &fNeedsExpansion, &sczValue);
238
+ NativeAssert::Succeeded(hr, "Failed to read expand string value.");
239
+ NativeAssert::StringEqual(wzValue, sczValue);
240
+ Assert::True(fNeedsExpansion);
241
+ }
242
+ finally
243
+ {
244
+ ReleaseStr(sczValue);
245
+ }
246
+ }
247
+
248
+ [Fact]
249
+ void RegUtilMultiStringValueTest()
250
+ {
251
+ this->MultiStringValueTest();
252
+ }
253
+
254
+ [Fact]
255
+ void RegUtilMultiStringValueFallbackTest()
256
+ {
257
+ RegFunctionForceFallback();
258
+ this->MultiStringValueTest();
259
+ }
260
+
261
+ void MultiStringValueTest()
262
+ {
263
+ HRESULT hr = S_OK;
264
+ LPWSTR* rgsczStrings = NULL;
265
+ DWORD cStrings = 0;
266
+
267
+ try
268
+ {
269
+ this->CreateBaseKey();
270
+
271
+ hr = RegWriteStringArray(hkBase, L"MultiString", rgwzMultiValue, 2);
272
+ NativeAssert::Succeeded(hr, "Failed to write multi string value.");
273
+
274
+ hr = RegReadStringArray(hkBase, L"MultiString", &rgsczStrings, &cStrings);
275
+ NativeAssert::Succeeded(hr, "Failed to read multi string value.");
276
+ Assert::Equal<DWORD>(2, cStrings);
277
+ NativeAssert::StringEqual(L"First", rgsczStrings[0]);
278
+ NativeAssert::StringEqual(L"Second", rgsczStrings[1]);
279
+ }
280
+ finally
281
+ {
282
+ ReleaseStrArray(rgsczStrings, cStrings);
283
+ }
284
+ }
285
+
286
+ [Fact]
287
+ void RegUtilPartialMultiStringValueTest()
288
+ {
289
+ this->PartialMultiStringValueTest();
290
+ }
291
+
292
+ [Fact]
293
+ void RegUtilPartialMultiStringValueFallbackTest()
294
+ {
295
+ RegFunctionForceFallback();
296
+ this->PartialMultiStringValueTest();
297
+ }
298
+
299
+ void PartialMultiStringValueTest()
300
+ {
301
+ HRESULT hr = S_OK;
302
+ LPWSTR* rgsczStrings = NULL;
303
+ DWORD cStrings = 0;
304
+
305
+ try
306
+ {
307
+ this->CreateBaseKey();
308
+
309
+ // Use API directly to write non-double-null terminated string.
310
+ hr = HRESULT_FROM_WIN32(::RegSetValueExW(hkBase, L"PartialMultiString", 0, REG_MULTI_SZ, reinterpret_cast<const BYTE*>(L"First\0Second"), 13 * sizeof(WCHAR)));
311
+ NativeAssert::Succeeded(hr, "Failed to write partial multi string value.");
312
+
313
+ hr = RegReadStringArray(hkBase, L"PartialMultiString", &rgsczStrings, &cStrings);
314
+ NativeAssert::Succeeded(hr, "Failed to read partial multi string value.");
315
+ Assert::Equal<DWORD>(2, cStrings);
316
+ NativeAssert::StringEqual(L"First", rgsczStrings[0]);
317
+ NativeAssert::StringEqual(L"Second", rgsczStrings[1]);
318
+ }
319
+ finally
320
+ {
321
+ ReleaseStrArray(rgsczStrings, cStrings);
322
+ }
323
+ }
324
+
325
+ [Fact]
326
+ void RegUtilEmptyMultiStringValueTest()
327
+ {
328
+ this->EmptyMultiStringValueTest();
329
+ }
330
+
331
+ [Fact]
332
+ void RegUtilEmptyMultiStringValueFallbackTest()
333
+ {
334
+ RegFunctionForceFallback();
335
+ this->EmptyMultiStringValueTest();
336
+ }
337
+
338
+ void EmptyMultiStringValueTest()
339
+ {
340
+ HRESULT hr = S_OK;
341
+ LPWSTR* rgsczStrings = NULL;
342
+ DWORD cStrings = 0;
343
+
344
+ try
345
+ {
346
+ this->CreateBaseKey();
347
+
348
+ hr = RegWriteStringArray(hkBase, L"EmptyMultiString", rgwzMultiValue, 0);
349
+ NativeAssert::Succeeded(hr, "Failed to write empty multi string value.");
350
+
351
+ hr = RegReadStringArray(hkBase, L"EmptyMultiString", &rgsczStrings, &cStrings);
352
+ NativeAssert::Succeeded(hr, "Failed to read empty multi string value.");
353
+ Assert::Equal<DWORD>(0, cStrings);
354
+ }
355
+ finally
356
+ {
357
+ ReleaseStrArray(rgsczStrings, cStrings);
358
+ }
359
+ }
360
+
361
+ [Fact]
362
+ void RegUtilOneEmptyMultiStringValueTest()
363
+ {
364
+ this->OneEmptyMultiStringValueTest();
365
+ }
366
+
367
+ [Fact]
368
+ void RegUtilOneEmptyMultiStringValueFallbackTest()
369
+ {
370
+ RegFunctionForceFallback();
371
+ this->OneEmptyMultiStringValueTest();
372
+ }
373
+
374
+ void OneEmptyMultiStringValueTest()
375
+ {
376
+ HRESULT hr = S_OK;
377
+ LPWSTR* rgsczStrings = NULL;
378
+ DWORD cStrings = 0;
379
+
380
+ try
381
+ {
382
+ this->CreateBaseKey();
383
+
384
+ hr = RegWriteStringArray(hkBase, L"OneEmptyMultiString", rgwzEmptyMultiValue, 1);
385
+ NativeAssert::Succeeded(hr, "Failed to write one empty multi string value.");
386
+
387
+ hr = RegReadStringArray(hkBase, L"OneEmptyMultiString", &rgsczStrings, &cStrings);
388
+ NativeAssert::Succeeded(hr, "Failed to read one empty multi string value.");
389
+ Assert::Equal<DWORD>(0, cStrings);
390
+ }
391
+ finally
392
+ {
393
+ ReleaseStrArray(rgsczStrings, cStrings);
394
+ }
395
+ }
396
+
397
+ [Fact]
398
+ void RegUtilTwoEmptyMultiStringValueTest()
399
+ {
400
+ this->TwoEmptyMultiStringValueTest();
401
+ }
402
+
403
+ [Fact]
404
+ void RegUtilTwoEmptyMultiStringValueFallbackTest()
405
+ {
406
+ RegFunctionForceFallback();
407
+ this->TwoEmptyMultiStringValueTest();
408
+ }
409
+
410
+ void TwoEmptyMultiStringValueTest()
411
+ {
412
+ HRESULT hr = S_OK;
413
+ LPWSTR* rgsczStrings = NULL;
414
+ DWORD cStrings = 0;
415
+
416
+ try
417
+ {
418
+ this->CreateBaseKey();
419
+
420
+ hr = RegWriteStringArray(hkBase, L"OneEmptyMultiString", rgwzEmptyMultiValue, 2);
421
+ NativeAssert::Succeeded(hr, "Failed to write one empty multi string value.");
422
+
423
+ hr = RegReadStringArray(hkBase, L"OneEmptyMultiString", &rgsczStrings, &cStrings);
424
+ NativeAssert::Succeeded(hr, "Failed to read one empty multi string value.");
425
+ Assert::Equal<DWORD>(2, cStrings);
426
+ NativeAssert::StringEqual(L"", rgsczStrings[0]);
427
+ NativeAssert::StringEqual(L"", rgsczStrings[1]);
428
+ }
429
+ finally
430
+ {
431
+ ReleaseStrArray(rgsczStrings, cStrings);
432
+ }
433
+ }
434
+
435
+ [Fact]
436
+ void RegUtilOnePartialEmptyMultiStringValueTest()
437
+ {
438
+ this->OnePartialEmptyMultiStringValueTest();
439
+ }
440
+
441
+ [Fact]
442
+ void RegUtilOnePartialEmptyMultiStringValueFallbackTest()
443
+ {
444
+ RegFunctionForceFallback();
445
+ this->OnePartialEmptyMultiStringValueTest();
446
+ }
447
+
448
+ void OnePartialEmptyMultiStringValueTest()
449
+ {
450
+ HRESULT hr = S_OK;
451
+ LPWSTR* rgsczStrings = NULL;
452
+ DWORD cStrings = 0;
453
+
454
+ try
455
+ {
456
+ this->CreateBaseKey();
457
+
458
+ // Use API directly to write non-double-null terminated string.
459
+ hr = HRESULT_FROM_WIN32(::RegSetValueExW(hkBase, L"OnePartialEmptyMultiString", 0, REG_MULTI_SZ, reinterpret_cast<const BYTE*>(L""), 1 * sizeof(WCHAR)));
460
+ NativeAssert::Succeeded(hr, "Failed to write partial empty multi string value.");
461
+
462
+ hr = RegReadStringArray(hkBase, L"OnePartialEmptyMultiString", &rgsczStrings, &cStrings);
463
+ NativeAssert::Succeeded(hr, "Failed to read partial empty multi string value.");
464
+ Assert::Equal<DWORD>(0, cStrings);
465
+ }
466
+ finally
467
+ {
468
+ ReleaseStrArray(rgsczStrings, cStrings);
469
+ }
470
+ }
471
+
472
+ [Fact]
473
+ void RegUtilBinaryValueTest()
474
+ {
475
+ this->BinaryValueTest();
476
+ }
477
+
478
+ [Fact]
479
+ void RegUtilBinaryValueFallbackTest()
480
+ {
481
+ RegFunctionForceFallback();
482
+ this->BinaryValueTest();
483
+ }
484
+
485
+ void BinaryValueTest()
486
+ {
487
+ HRESULT hr = S_OK;
488
+ BYTE pbSource[4] = { 1, 2, 3, 4 };
489
+ BYTE* pbBuffer = NULL;
490
+ SIZE_T cbBuffer = 0;
491
+
492
+ try
493
+ {
494
+ this->CreateBaseKey();
495
+
496
+ hr = RegWriteBinary(hkBase, L"Binary", pbSource, 4);
497
+ NativeAssert::Succeeded(hr, "Failed to write binary value.");
498
+
499
+ hr = RegReadBinary(hkBase, L"Binary", &pbBuffer, &cbBuffer);
500
+ NativeAssert::Succeeded(hr, "Failed to read binary value.");
501
+ Assert::Equal<DWORD>(4, cbBuffer);
502
+ Assert::Equal<BYTE>(1, pbBuffer[0]);
503
+ Assert::Equal<BYTE>(2, pbBuffer[1]);
504
+ Assert::Equal<BYTE>(3, pbBuffer[2]);
505
+ Assert::Equal<BYTE>(4, pbBuffer[3]);
506
+ }
507
+ finally
508
+ {
509
+ ReleaseMem(pbBuffer);
510
+ }
511
+ }
512
+
513
+ [Fact]
514
+ void RegUtilEmptyBinaryValueTest()
515
+ {
516
+ this->EmptyBinaryValueTest();
517
+ }
518
+
519
+ [Fact]
520
+ void RegUtilEmptyBinaryValueFallbackTest()
521
+ {
522
+ RegFunctionForceFallback();
523
+ this->EmptyBinaryValueTest();
524
+ }
525
+
526
+ void EmptyBinaryValueTest()
527
+ {
528
+ HRESULT hr = S_OK;
529
+ BYTE* pbBuffer = NULL;
530
+ SIZE_T cbBuffer = 0;
531
+
532
+ try
533
+ {
534
+ this->CreateBaseKey();
535
+
536
+ hr = RegWriteBinary(hkBase, L"Binary", NULL, 0);
537
+ NativeAssert::Succeeded(hr, "Failed to write binary value.");
538
+
539
+ hr = RegReadBinary(hkBase, L"Binary", &pbBuffer, &cbBuffer);
540
+ NativeAssert::Succeeded(hr, "Failed to read binary value.");
541
+ Assert::Equal<DWORD>(0, cbBuffer);
542
+ }
543
+ finally
544
+ {
545
+ ReleaseMem(pbBuffer);
546
+ }
547
+ }
548
+
549
+ [Fact]
550
+ void RegUtilQwordVersionValueTest()
551
+ {
552
+ this->QwordVersionValueTest();
553
+ }
554
+
555
+ [Fact]
556
+ void RegUtilQwordVersionValueFallbackTest()
557
+ {
558
+ RegFunctionForceFallback();
559
+ this->QwordVersionValueTest();
560
+ }
561
+
562
+ void QwordVersionValueTest()
563
+ {
564
+ HRESULT hr = S_OK;
565
+ DWORD64 qwVersion = FILEMAKEVERSION(1, 2, 3, 4);
566
+ DWORD64 qwValue = 0;
567
+
568
+ this->CreateBaseKey();
569
+
570
+ hr = RegWriteQword(hkBase, L"QwordVersion", qwVersion);
571
+ NativeAssert::Succeeded(hr, "Failed to write qword version value.");
572
+
573
+ hr = RegReadVersion(hkBase, L"QwordVersion", &qwValue);
574
+ NativeAssert::Succeeded(hr, "Failed to read qword version value.");
575
+ Assert::Equal<DWORD64>(qwVersion, qwValue);
576
+ }
577
+
578
+ [Fact]
579
+ void RegUtilStringVersionValueTest()
580
+ {
581
+ this->StringVersionValueTest();
582
+ }
583
+
584
+ [Fact]
585
+ void RegUtilStringVersionValueFallbackTest()
586
+ {
587
+ RegFunctionForceFallback();
588
+ this->StringVersionValueTest();
589
+ }
590
+
591
+ void StringVersionValueTest()
592
+ {
593
+ HRESULT hr = S_OK;
594
+ LPCWSTR wzVersion = L"65535.65535.65535.65535";
595
+ DWORD64 qwValue = 0;
596
+
597
+ this->CreateBaseKey();
598
+
599
+ hr = RegWriteString(hkBase, L"StringVersion", wzVersion);
600
+ NativeAssert::Succeeded(hr, "Failed to write string version value.");
601
+
602
+ hr = RegReadVersion(hkBase, L"StringVersion", &qwValue);
603
+ NativeAssert::Succeeded(hr, "Failed to read string version value.");
604
+ Assert::Equal<DWORD64>(MAXDWORD64, qwValue);
605
+ }
606
+
607
+ [Fact]
608
+ void RegUtilQwordWixVersionValueTest()
609
+ {
610
+ this->QwordWixVersionValueTest();
611
+ }
612
+
613
+ [Fact]
614
+ void RegUtilQwordWixVersionValueFallbackTest()
615
+ {
616
+ RegFunctionForceFallback();
617
+ this->QwordWixVersionValueTest();
618
+ }
619
+
620
+ void QwordWixVersionValueTest()
621
+ {
622
+ HRESULT hr = S_OK;
623
+ DWORD64 qwVersion = FILEMAKEVERSION(1, 2, 3, 4);
624
+ VERUTIL_VERSION* pVersion = NULL;
625
+
626
+ try
627
+ {
628
+ this->CreateBaseKey();
629
+
630
+ hr = RegWriteQword(hkBase, L"QwordWixVersion", qwVersion);
631
+ NativeAssert::Succeeded(hr, "Failed to write qword wix version value.");
632
+
633
+ hr = RegReadWixVersion(hkBase, L"QwordWixVersion", &pVersion);
634
+ NativeAssert::Succeeded(hr, "Failed to read qword wix version value.");
635
+ NativeAssert::StringEqual(L"1.2.3.4", pVersion->sczVersion);
636
+ }
637
+ finally
638
+ {
639
+ ReleaseVerutilVersion(pVersion);
640
+ }
641
+ }
642
+
643
+ [Fact]
644
+ void RegUtilStringWixVersionValueTest()
645
+ {
646
+ this->StringWixVersionValueTest();
647
+ }
648
+
649
+ [Fact]
650
+ void RegUtilStringWixVersionValueFallbackTest()
651
+ {
652
+ RegFunctionForceFallback();
653
+ this->StringWixVersionValueTest();
654
+ }
655
+
656
+ void StringWixVersionValueTest()
657
+ {
658
+ HRESULT hr = S_OK;
659
+ LPCWSTR wzVersion = L"65535.65535.65535.65535-abc+def";
660
+ VERUTIL_VERSION* pVersion = NULL;
661
+
662
+ try
663
+ {
664
+ this->CreateBaseKey();
665
+
666
+ hr = RegWriteString(hkBase, L"StringWixVersion", wzVersion);
667
+ NativeAssert::Succeeded(hr, "Failed to write string wix version value.");
668
+
669
+ hr = RegReadWixVersion(hkBase, L"StringWixVersion", &pVersion);
670
+ NativeAssert::Succeeded(hr, "Failed to read string wix version value.");
671
+ NativeAssert::StringEqual(wzVersion, pVersion->sczVersion);
672
+ }
673
+ finally
674
+ {
675
+ ReleaseVerutilVersion(pVersion);
676
+ }
677
+ }
678
+ };
679
+}