aboutsummaryrefslogtreecommitdiff
path: root/src/libs/dutil/WixToolset.DUtil
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2022-06-03 17:49:15 -0500
committerSean Hall <r.sean.hall@gmail.com>2022-06-07 19:44:36 -0500
commit584213c5ffeca09b3fe24bd5e92f73fd057ac642 (patch)
tree45619273a56248d662dda598bca3076efa8a5135 /src/libs/dutil/WixToolset.DUtil
parent648f370f7966b2738c1446601057d888bbd2c70f (diff)
downloadwix-584213c5ffeca09b3fe24bd5e92f73fd057ac642.tar.gz
wix-584213c5ffeca09b3fe24bd5e92f73fd057ac642.tar.bz2
wix-584213c5ffeca09b3fe24bd5e92f73fd057ac642.zip
Add RegReadUnexpandedString to get an unexpanded REG_EXPAND_SZ value.
Diffstat (limited to 'src/libs/dutil/WixToolset.DUtil')
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/regutil.h44
-rw-r--r--src/libs/dutil/WixToolset.DUtil/regutil.cpp420
2 files changed, 308 insertions, 156 deletions
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/regutil.h b/src/libs/dutil/WixToolset.DUtil/inc/regutil.h
index 76d2d7cb..f5b40291 100644
--- a/src/libs/dutil/WixToolset.DUtil/inc/regutil.h
+++ b/src/libs/dutil/WixToolset.DUtil/inc/regutil.h
@@ -99,6 +99,15 @@ typedef LSTATUS (APIENTRY *PFN_REGDELETEVALUEW)(
99 __in HKEY hKey, 99 __in HKEY hKey,
100 __in_opt LPCWSTR lpValueName 100 __in_opt LPCWSTR lpValueName
101 ); 101 );
102typedef 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 );
102 111
103/******************************************************************** 112/********************************************************************
104 RegInitialize - initializes regutil 113 RegInitialize - initializes regutil
@@ -126,10 +135,18 @@ void DAPI RegFunctionOverride(
126 __in_opt PFN_REGQUERYINFOKEYW pfnRegQueryInfoKeyW, 135 __in_opt PFN_REGQUERYINFOKEYW pfnRegQueryInfoKeyW,
127 __in_opt PFN_REGQUERYVALUEEXW pfnRegQueryValueExW, 136 __in_opt PFN_REGQUERYVALUEEXW pfnRegQueryValueExW,
128 __in_opt PFN_REGSETVALUEEXW pfnRegSetValueExW, 137 __in_opt PFN_REGSETVALUEEXW pfnRegSetValueExW,
129 __in_opt PFN_REGDELETEVALUEW pfnRegDeleteValueW 138 __in_opt PFN_REGDELETEVALUEW pfnRegDeleteValueW,
139 __in_opt PFN_REGGETVALUEW pfnRegGetValueW
130 ); 140 );
131 141
132/******************************************************************** 142/********************************************************************
143 RegFunctionForceFallback - ignore functions only available in newer versions of Windows.
144 Typically used for unit testing.
145
146*********************************************************************/
147void DAPI RegFunctionForceFallback();
148
149/********************************************************************
133 RegCreate - creates a registry key. 150 RegCreate - creates a registry key.
134 151
135*********************************************************************/ 152*********************************************************************/
@@ -220,6 +237,20 @@ HRESULT DAPI RegGetType(
220 ); 237 );
221 238
222/******************************************************************** 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*********************************************************************/
244HRESULT 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/********************************************************************
223 RegReadBinary - reads a registry key binary value. 254 RegReadBinary - reads a registry key binary value.
224 NOTE: caller is responsible for freeing *ppbBuffer 255 NOTE: caller is responsible for freeing *ppbBuffer
225*********************************************************************/ 256*********************************************************************/
@@ -241,6 +272,17 @@ HRESULT DAPI RegReadString(
241 ); 272 );
242 273
243/******************************************************************** 274/********************************************************************
275 RegReadUnexpandedString - reads a registry key value as a string without expanding it.
276
277*********************************************************************/
278HRESULT 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/********************************************************************
244 RegReadStringArray - reads a registry key value REG_MULTI_SZ value as a string array. 286 RegReadStringArray - reads a registry key value REG_MULTI_SZ value as a string array.
245 287
246*********************************************************************/ 288*********************************************************************/
diff --git a/src/libs/dutil/WixToolset.DUtil/regutil.cpp b/src/libs/dutil/WixToolset.DUtil/regutil.cpp
index 64224d42..584966ed 100644
--- a/src/libs/dutil/WixToolset.DUtil/regutil.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/regutil.cpp
@@ -9,6 +9,7 @@
9#define RegExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) 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__) 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__) 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__)
12#define RegExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__) 13#define RegExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__)
13#define RegExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_REGUTIL, p, x, e, s, __VA_ARGS__) 14#define RegExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_REGUTIL, p, x, e, s, __VA_ARGS__)
14#define RegExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_REGUTIL, p, x, 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;
28static PFN_REGQUERYVALUEEXW vpfnRegQueryValueExW = ::RegQueryValueExW; 29static PFN_REGQUERYVALUEEXW vpfnRegQueryValueExW = ::RegQueryValueExW;
29static PFN_REGSETVALUEEXW vpfnRegSetValueExW = ::RegSetValueExW; 30static PFN_REGSETVALUEEXW vpfnRegSetValueExW = ::RegSetValueExW;
30static PFN_REGDELETEVALUEW vpfnRegDeleteValueW = ::RegDeleteValueW; 31static PFN_REGDELETEVALUEW vpfnRegDeleteValueW = ::RegDeleteValueW;
32static PFN_REGGETVALUEW vpfnRegGetValueW = NULL;
33static PFN_REGGETVALUEW vpfnRegGetValueWFromLibrary = NULL;
31 34
32static HMODULE vhAdvApi32Dll = NULL; 35static HMODULE vhAdvApi32Dll = NULL;
33static BOOL vfRegInitialized = FALSE; 36static BOOL vfRegInitialized = FALSE;
34 37
38static 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 );
35static HRESULT WriteStringToRegistry( 45static HRESULT WriteStringToRegistry(
36 __in HKEY hk, 46 __in HKEY hk,
37 __in_z_opt LPCWSTR wzName, 47 __in_z_opt LPCWSTR wzName,
@@ -46,14 +56,22 @@ DAPI_(HRESULT) RegInitialize()
46 hr = LoadSystemLibrary(L"AdvApi32.dll", &vhAdvApi32Dll); 56 hr = LoadSystemLibrary(L"AdvApi32.dll", &vhAdvApi32Dll);
47 RegExitOnFailure(hr, "Failed to load AdvApi32.dll"); 57 RegExitOnFailure(hr, "Failed to load AdvApi32.dll");
48 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.
50 vpfnRegDeleteKeyExWFromLibrary = reinterpret_cast<PFN_REGDELETEKEYEXW>(::GetProcAddress(vhAdvApi32Dll, "RegDeleteKeyExW")); 60 vpfnRegDeleteKeyExWFromLibrary = reinterpret_cast<PFN_REGDELETEKEYEXW>(::GetProcAddress(vhAdvApi32Dll, "RegDeleteKeyExW"));
51 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)
53 { 66 {
54 vpfnRegDeleteKeyExW = vpfnRegDeleteKeyExWFromLibrary; 67 vpfnRegDeleteKeyExW = vpfnRegDeleteKeyExWFromLibrary;
55 } 68 }
56 69
70 if (!vpfnRegGetValueW)
71 {
72 vpfnRegGetValueW = vpfnRegGetValueWFromLibrary;
73 }
74
57 vfRegInitialized = TRUE; 75 vfRegInitialized = TRUE;
58 76
59LExit: 77LExit:
@@ -68,7 +86,9 @@ DAPI_(void) RegUninitialize()
68 ::FreeLibrary(vhAdvApi32Dll); 86 ::FreeLibrary(vhAdvApi32Dll);
69 vhAdvApi32Dll = NULL; 87 vhAdvApi32Dll = NULL;
70 vpfnRegDeleteKeyExWFromLibrary = NULL; 88 vpfnRegDeleteKeyExWFromLibrary = NULL;
89 vpfnRegGetValueWFromLibrary = NULL;
71 vpfnRegDeleteKeyExW = NULL; 90 vpfnRegDeleteKeyExW = NULL;
91 vpfnRegGetValueW = NULL;
72 } 92 }
73 93
74 vfRegInitialized = FALSE; 94 vfRegInitialized = FALSE;
@@ -84,7 +104,8 @@ DAPI_(void) RegFunctionOverride(
84 __in_opt PFN_REGQUERYINFOKEYW pfnRegQueryInfoKeyW, 104 __in_opt PFN_REGQUERYINFOKEYW pfnRegQueryInfoKeyW,
85 __in_opt PFN_REGQUERYVALUEEXW pfnRegQueryValueExW, 105 __in_opt PFN_REGQUERYVALUEEXW pfnRegQueryValueExW,
86 __in_opt PFN_REGSETVALUEEXW pfnRegSetValueExW, 106 __in_opt PFN_REGSETVALUEEXW pfnRegSetValueExW,
87 __in_opt PFN_REGDELETEVALUEW pfnRegDeleteValueW 107 __in_opt PFN_REGDELETEVALUEW pfnRegDeleteValueW,
108 __in_opt PFN_REGGETVALUEW pfnRegGetValueW
88 ) 109 )
89{ 110{
90 vpfnRegCreateKeyExW = pfnRegCreateKeyExW ? pfnRegCreateKeyExW : ::RegCreateKeyExW; 111 vpfnRegCreateKeyExW = pfnRegCreateKeyExW ? pfnRegCreateKeyExW : ::RegCreateKeyExW;
@@ -96,6 +117,14 @@ DAPI_(void) RegFunctionOverride(
96 vpfnRegQueryValueExW = pfnRegQueryValueExW ? pfnRegQueryValueExW : ::RegQueryValueExW; 117 vpfnRegQueryValueExW = pfnRegQueryValueExW ? pfnRegQueryValueExW : ::RegQueryValueExW;
97 vpfnRegSetValueExW = pfnRegSetValueExW ? pfnRegSetValueExW : ::RegSetValueExW; 118 vpfnRegSetValueExW = pfnRegSetValueExW ? pfnRegSetValueExW : ::RegSetValueExW;
98 vpfnRegDeleteValueW = pfnRegDeleteValueW ? pfnRegDeleteValueW : ::RegDeleteValueW; 119 vpfnRegDeleteValueW = pfnRegDeleteValueW ? pfnRegDeleteValueW : ::RegDeleteValueW;
120 vpfnRegGetValueW = pfnRegGetValueW ? pfnRegGetValueW : vpfnRegGetValueWFromLibrary;
121}
122
123
124DAPI_(void) RegFunctionForceFallback()
125{
126 vpfnRegDeleteKeyExW = NULL;
127 vpfnRegGetValueW = NULL;
99} 128}
100 129
101 130
@@ -358,54 +387,104 @@ LExit:
358 return hr; 387 return hr;
359} 388}
360 389
361DAPI_(HRESULT) RegReadBinary( 390DAPI_(HRESULT) RegReadValue(
362 __in HKEY hk, 391 __in HKEY hk,
363 __in_z_opt LPCWSTR wzName, 392 __in_z_opt LPCWSTR wzName,
393 __in BOOL fExpand,
364 __deref_out_bcount_opt(*pcbBuffer) BYTE** ppbBuffer, 394 __deref_out_bcount_opt(*pcbBuffer) BYTE** ppbBuffer,
365 __out SIZE_T *pcbBuffer 395 __inout SIZE_T* pcbBuffer,
366 ) 396 __out DWORD* pdwType
397 )
367{ 398{
368 HRESULT hr = S_OK; 399 HRESULT hr = S_OK;
369 LPBYTE pbBuffer = NULL; 400 DWORD dwAttempts = 0;
370 DWORD er = ERROR_SUCCESS; 401 LPWSTR sczExpand = NULL;
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.");
376 402
377 // Zero-length binary values can exist 403 hr = GetRegValue(hk, wzName, *ppbBuffer, pcbBuffer, pdwType);
378 if (0 < cb) 404 if (E_FILENOTFOUND == hr)
379 { 405 {
380 pbBuffer = static_cast<LPBYTE>(MemAlloc(cb, FALSE)); 406 ExitFunction();
381 RegExitOnNull(pbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer for binary registry value."); 407 }
408 else if (E_MOREDATA != hr)
409 {
410 RegExitOnFailure(hr, "Failed to get size of raw registry value.");
382 411
383 er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, pbBuffer, &cb); 412 // Zero-length raw values can exist
384 if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) 413 if (!*ppbBuffer && 0 < *pcbBuffer)
385 { 414 {
386 ExitFunction1(hr = E_FILENOTFOUND); 415 hr = E_MOREDATA;
387 } 416 }
388 RegExitOnWin32Error(er, hr, "Failed to read registry value.");
389 } 417 }
390 418
391 if (REG_BINARY == dwType) 419 while (E_MOREDATA == hr && dwAttempts < 10)
392 { 420 {
393 *ppbBuffer = pbBuffer; 421 ++dwAttempts;
394 pbBuffer = NULL; 422
395 *pcbBuffer = cb; 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 }
396 } 442 }
397 else 443
444 if (fExpand && SUCCEEDED(hr) && REG_EXPAND_SZ == *pdwType)
398 { 445 {
399 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); 446 LPWSTR sczValue = reinterpret_cast<LPWSTR>(*ppbBuffer);
400 RegExitOnRootFailure(hr, "Error reading binary registry value due to unexpected data type: %u", dwType); 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);
401 } 454 }
402 455
403LExit: 456LExit:
404 ReleaseMem(pbBuffer); 457 ReleaseStr(sczExpand);
405 458
406 return hr; 459 return hr;
407} 460}
408 461
462DAPI_(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
484LExit:
485 return hr;
486}
487
409 488
410DAPI_(HRESULT) RegReadString( 489DAPI_(HRESULT) RegReadString(
411 __in HKEY hk, 490 __in HKEY hk,
@@ -414,65 +493,64 @@ DAPI_(HRESULT) RegReadString(
414 ) 493 )
415{ 494{
416 HRESULT hr = S_OK; 495 HRESULT hr = S_OK;
417 DWORD er = ERROR_SUCCESS;
418 SIZE_T cbValue = 0; 496 SIZE_T cbValue = 0;
419 DWORD cch = 0;
420 DWORD cb = 0;
421 DWORD dwType = 0; 497 DWORD dwType = 0;
422 LPWSTR sczExpand = NULL;
423 498
424 if (psczValue && *psczValue) 499 if (psczValue && *psczValue)
425 { 500 {
426 hr = StrMaxLength(*psczValue, &cbValue); 501 hr = MemSizeChecked(*psczValue, &cbValue);
427 RegExitOnFailure(hr, "Failed to determine length of string."); 502 RegExitOnFailure(hr, "Failed to get size of input buffer.");
428
429 cch = (DWORD)min(DWORD_MAX, cbValue);
430 } 503 }
431 504
432 if (2 > cch) 505 hr = RegReadValue(hk, wzName, TRUE, reinterpret_cast<LPBYTE*>(psczValue), &cbValue, &dwType);
506 if (E_FILENOTFOUND == hr)
433 { 507 {
434 cch = 2; 508 ExitFunction();
435
436 hr = StrAlloc(psczValue, cch);
437 RegExitOnFailure(hr, "Failed to allocate string to minimum size.");
438 } 509 }
510 RegExitOnFailure(hr, "Failed to read string registry value.");
439 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. 512 if (REG_EXPAND_SZ != dwType && REG_SZ != dwType)
441 er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(*psczValue), &cb);
442 if (ERROR_MORE_DATA == er)
443 { 513 {
444 cch = cb / sizeof(WCHAR) + 1; // add one to ensure there will be space at the end for the null terminator 514 RegExitWithRootFailure(hr, HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE), "Error reading string registry value due to unexpected data type: %u", dwType);
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);
449 } 515 }
450 if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) 516
517LExit:
518 return hr;
519}
520
521
522DAPI_(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)
451 { 535 {
452 ExitFunction1(hr = E_FILENOTFOUND); 536 hr = MemSizeChecked(*psczValue, &cbValue);
537 RegExitOnFailure(hr, "Failed to get size of input buffer.");
453 } 538 }
454 RegExitOnWin32Error(er, hr, "Failed to read registry key.");
455 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)
457 { 542 {
458 // Always ensure the registry value is null terminated. 543 ExitFunction();
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 }
469 } 544 }
470 else 545 RegExitOnFailure(hr, "Failed to read expand string registry value.");
546
547 if (REG_EXPAND_SZ != dwType && REG_SZ != dwType)
471 { 548 {
472 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); 549 RegExitWithRootFailure(hr, HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE), "Error reading expand string registry value due to unexpected data type: %u", dwType);
473 RegExitOnRootFailure(hr, "Error reading string registry value due to unexpected data type: %u", dwType);
474 } 550 }
475 551
552 *pfNeedsExpansion = REG_EXPAND_SZ == dwType;
553
476LExit: 554LExit:
477 ReleaseStr(sczExpand); 555 ReleaseStr(sczExpand);
478 556
@@ -488,67 +566,57 @@ DAPI_(HRESULT) RegReadStringArray(
488 ) 566 )
489{ 567{
490 HRESULT hr = S_OK; 568 HRESULT hr = S_OK;
491 DWORD er = ERROR_SUCCESS;
492 DWORD dwNullCharacters = 0; 569 DWORD dwNullCharacters = 0;
493 DWORD dwType = 0; 570 DWORD dwType = 0;
494 DWORD cb = 0; 571 SIZE_T cb = 0;
495 DWORD cch = 0; 572 SIZE_T cch = 0;
496 LPCWSTR wzSource = NULL; 573 LPCWSTR wzSource = NULL;
497 LPWSTR sczValue = NULL; 574 LPWSTR sczValue = NULL;
498 575
499 er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(sczValue), &cb); 576 hr = RegReadValue(hk, wzName, FALSE, reinterpret_cast<LPBYTE*>(&sczValue), &cb, &dwType);
500 if (0 < cb) 577 if (E_FILENOTFOUND == hr)
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))
509 { 578 {
510 ExitFunction1(hr = E_FILENOTFOUND); 579 ExitFunction();
511 } 580 }
512 RegExitOnWin32Error(er, hr, "Failed to read registry key."); 581 RegExitOnFailure(hr, "Failed to read string array registry value.");
513 582
514 if (cb / sizeof(WCHAR) != cch) 583 if (REG_MULTI_SZ != dwType)
515 { 584 {
516 hr = E_UNEXPECTED; 585 RegExitWithRootFailure(hr, HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE), "Tried to read string array, but registry value %ls is of an incorrect type", wzName);
517 RegExitOnFailure(hr, "The size of registry value %ls unexpected changed between 2 reads", wzName);
518 } 586 }
519 587
520 if (REG_MULTI_SZ != dwType) 588 cch = cb / sizeof(WCHAR);
589
590 for (DWORD i = 0; i < cch; ++i)
521 { 591 {
522 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); 592 if (L'\0' == sczValue[i])
523 RegExitOnRootFailure(hr, "Tried to read string array, but registry value %ls is of an incorrect type", wzName); 593 {
594 ++dwNullCharacters;
595 }
524 } 596 }
525 597
526 // Value exists, but is empty, so no strings to return. 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)
528 { 600 {
529 *prgsczStrings = NULL; 601 *prgsczStrings = NULL;
530 *pcStrings = 0; 602 *pcStrings = 0;
531 ExitFunction1(hr = S_OK); 603 ExitFunction1(hr = S_OK);
532 } 604 }
533 605
534 // The docs specifically say if the value was written without double-null-termination, it'll get read back without it too. 606 if (sczValue[cch - 1] != L'\0')
535 if (L'\0' != sczValue[cch-1] || L'\0' != sczValue[cch-2])
536 { 607 {
537 hr = E_INVALIDARG; 608 // Count the terminating null character that RegReadValue added past the end.
538 RegExitOnFailure(hr, "Tried to read string array, but registry value %ls is invalid (isn't double-null-terminated)", wzName); 609 ++dwNullCharacters;
610 Assert(!sczValue[cch]);
539 } 611 }
540 612 else if (cch > 1 && sczValue[cch - 2] == L'\0')
541 cch = cb / sizeof(WCHAR);
542 for (DWORD i = 0; i < cch; ++i)
543 { 613 {
544 if (L'\0' == sczValue[i]) 614 // Don't count the extra 1 at the end of the properly double-null terminated string.
545 { 615 --dwNullCharacters;
546 ++dwNullCharacters;
547 }
548 } 616 }
549 617
550 // There's one string for every null character encountered (except the extra 1 at the end of the string) 618 // There's one string for every null character encountered.
551 *pcStrings = dwNullCharacters - 1; 619 *pcStrings = dwNullCharacters;
552 hr = MemEnsureArraySize(reinterpret_cast<LPVOID *>(prgsczStrings), *pcStrings, sizeof(LPWSTR), 0); 620 hr = MemEnsureArraySize(reinterpret_cast<LPVOID *>(prgsczStrings), *pcStrings, sizeof(LPWSTR), 0);
553 RegExitOnFailure(hr, "Failed to resize array while reading REG_MULTI_SZ value"); 621 RegExitOnFailure(hr, "Failed to resize array while reading REG_MULTI_SZ value");
554 622
@@ -557,11 +625,13 @@ DAPI_(HRESULT) RegReadStringArray(
557 wzSource = sczValue; 625 wzSource = sczValue;
558 for (DWORD i = 0; i < *pcStrings; ++i) 626 for (DWORD i = 0; i < *pcStrings; ++i)
559 { 627 {
560 hr = StrAllocString(&(*prgsczStrings)[i], wzSource, 0); 628 cch = lstrlenW(wzSource);
629
630 hr = StrAllocString(&(*prgsczStrings)[i], wzSource, cch);
561 RegExitOnFailure(hr, "Failed to allocate copy of string"); 631 RegExitOnFailure(hr, "Failed to allocate copy of string");
562 632
563 // Skip past this string 633 // Skip past this string
564 wzSource += lstrlenW(wzSource) + 1; 634 wzSource += cch + 1;
565 } 635 }
566#pragma prefast(pop) 636#pragma prefast(pop)
567 637
@@ -579,38 +649,36 @@ DAPI_(HRESULT) RegReadVersion(
579 ) 649 )
580{ 650{
581 HRESULT hr = S_OK; 651 HRESULT hr = S_OK;
582 DWORD er = ERROR_SUCCESS;
583 DWORD dwType = 0; 652 DWORD dwType = 0;
584 DWORD cb = 0; 653 SIZE_T cb = 0;
585 LPWSTR sczVersion = NULL; 654 LPWSTR sczValue = NULL;
586 655
587 cb = sizeof(DWORD64); 656 hr = RegReadValue(hk, wzName, TRUE, reinterpret_cast<LPBYTE*>(&sczValue), &cb, &dwType);
588 er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(*pdw64Version), &cb); 657 if (E_FILENOTFOUND == hr)
589 if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er))
590 { 658 {
591 ExitFunction1(hr = E_FILENOTFOUND); 659 ExitFunction();
592 } 660 }
661 RegExitOnFailure(hr, "Failed to read version registry value.");
593 662
594 if (REG_SZ == dwType || REG_EXPAND_SZ == dwType) 663 if (REG_SZ == dwType || REG_EXPAND_SZ == dwType)
595 { 664 {
596 hr = RegReadString(hk, wzName, &sczVersion); 665 hr = FileVersionFromStringEx(sczValue, 0, pdw64Version);
597 RegExitOnFailure(hr, "Failed to read registry version as string.");
598
599 hr = FileVersionFromStringEx(sczVersion, 0, pdw64Version);
600 RegExitOnFailure(hr, "Failed to convert registry string to version."); 666 RegExitOnFailure(hr, "Failed to convert registry string to version.");
601 } 667 }
602 else if (REG_QWORD == dwType) 668 else if (REG_QWORD == dwType)
603 { 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 }
605 } 674 }
606 else // unexpected data type 675 else // unexpected data type
607 { 676 {
608 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); 677 RegExitWithRootFailure(hr, HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE), "Error reading version registry value due to unexpected data type: %u", dwType);
609 RegExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType);
610 } 678 }
611 679
612LExit: 680LExit:
613 ReleaseStr(sczVersion); 681 ReleaseStr(sczValue);
614 682
615 return hr; 683 return hr;
616} 684}
@@ -623,37 +691,38 @@ DAPI_(HRESULT) RegReadWixVersion(
623 ) 691 )
624{ 692{
625 HRESULT hr = S_OK; 693 HRESULT hr = S_OK;
626 DWORD er = ERROR_SUCCESS;
627 DWORD dwType = 0; 694 DWORD dwType = 0;
628 DWORD cb = 0; 695 SIZE_T cb = 0;
629 DWORD64 dw64Version = 0; 696 DWORD64 dw64Version = 0;
630 LPWSTR sczVersion = NULL; 697 LPWSTR sczValue = NULL;
631 VERUTIL_VERSION* pVersion = NULL; 698 VERUTIL_VERSION* pVersion = NULL;
632 699
633 cb = sizeof(DWORD64); 700 hr = RegReadValue(hk, wzName, TRUE, reinterpret_cast<LPBYTE*>(&sczValue), &cb, &dwType);
634 er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(&dw64Version), &cb); 701 if (E_FILENOTFOUND == hr)
635 if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er))
636 { 702 {
637 ExitFunction1(hr = E_FILENOTFOUND); 703 ExitFunction();
638 } 704 }
705 RegExitOnFailure(hr, "Failed to read wix version registry value.");
639 706
640 if (REG_SZ == dwType || REG_EXPAND_SZ == dwType) 707 if (REG_SZ == dwType || REG_EXPAND_SZ == dwType)
641 { 708 {
642 hr = RegReadString(hk, wzName, &sczVersion); 709 hr = VerParseVersion(sczValue, 0, FALSE, &pVersion);
643 RegExitOnFailure(hr, "Failed to read registry version as string."); 710 RegExitOnFailure(hr, "Failed to convert registry string to wix version.");
644
645 hr = VerParseVersion(sczVersion, 0, FALSE, &pVersion);
646 RegExitOnFailure(hr, "Failed to convert registry string to version.");
647 } 711 }
648 else if (REG_QWORD == dwType) 712 else if (REG_QWORD == dwType)
649 { 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
650 hr = VerVersionFromQword(dw64Version, &pVersion); 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.");
652 } 721 }
653 else // unexpected data type 722 else // unexpected data type
654 { 723 {
655 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); 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);
657 } 726 }
658 727
659 *ppVersion = pVersion; 728 *ppVersion = pVersion;
@@ -661,7 +730,7 @@ DAPI_(HRESULT) RegReadWixVersion(
661 730
662LExit: 731LExit:
663 ReleaseVerutilVersion(pVersion); 732 ReleaseVerutilVersion(pVersion);
664 ReleaseStr(sczVersion); 733 ReleaseStr(sczValue);
665 734
666 return hr; 735 return hr;
667} 736}
@@ -673,20 +742,18 @@ DAPI_(HRESULT) RegReadNone(
673 ) 742 )
674{ 743{
675 HRESULT hr = S_OK; 744 HRESULT hr = S_OK;
676 DWORD er = ERROR_SUCCESS;
677 DWORD dwType = 0; 745 DWORD dwType = 0;
678 746
679 er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, NULL, NULL); 747 hr = RegGetType(hk, wzName, &dwType);
680 if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) 748 if (E_FILENOTFOUND == hr)
681 { 749 {
682 ExitFunction1(hr = E_FILENOTFOUND); 750 ExitFunction();
683 } 751 }
684 RegExitOnWin32Error(er, hr, "Failed to query registry key value."); 752 RegExitOnFailure(hr, "Error reading none registry value type.");
685 753
686 if (REG_NONE != dwType) 754 if (REG_NONE != dwType)
687 { 755 {
688 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); 756 RegExitWithRootFailure(hr, HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE), "Error reading none registry value due to unexpected data type: %u", dwType);
689 RegExitOnRootFailure(hr, "Error reading none registry value due to unexpected data type: %u", dwType);
690 } 757 }
691 758
692LExit: 759LExit:
@@ -829,12 +896,9 @@ DAPI_(HRESULT) RegWriteStringArray(
829 DWORD dwTotalStringSize = 0; 896 DWORD dwTotalStringSize = 0;
830 DWORD cbTotalStringSize = 0; 897 DWORD cbTotalStringSize = 0;
831 DWORD dwTemp = 0; 898 DWORD dwTemp = 0;
899 DWORD dwRemainingStringSize = 0;
832 900
833 if (0 == cValues) 901 if (cValues)
834 {
835 wzWriteValue = L"\0";
836 }
837 else
838 { 902 {
839 // Add space for the null terminator 903 // Add space for the null terminator
840 dwTotalStringSize = 1; 904 dwTotalStringSize = 1;
@@ -850,21 +914,22 @@ DAPI_(HRESULT) RegWriteStringArray(
850 RegExitOnFailure(hr, "Failed to allocate space for string while writing REG_MULTI_SZ"); 914 RegExitOnFailure(hr, "Failed to allocate space for string while writing REG_MULTI_SZ");
851 915
852 wzCopyDestination = sczWriteValue; 916 wzCopyDestination = sczWriteValue;
853 dwTemp = dwTotalStringSize; 917 dwRemainingStringSize = dwTotalStringSize;
854 for (DWORD i = 0; i < cValues; ++i) 918 for (DWORD i = 0; i < cValues; ++i)
855 { 919 {
856 hr = ::StringCchCopyW(wzCopyDestination, dwTotalStringSize, rgwzValues[i]); 920 hr = ::StringCchCopyW(wzCopyDestination, dwRemainingStringSize, rgwzValues[i]);
857 RegExitOnFailure(hr, "failed to copy string: %ls", rgwzValues[i]); 921 RegExitOnFailure(hr, "failed to copy string: %ls", rgwzValues[i]);
858 922
859 dwTemp -= lstrlenW(rgwzValues[i]) + 1; 923 dwTemp = lstrlenW(rgwzValues[i]) + 1;
860 wzCopyDestination += lstrlenW(rgwzValues[i]) + 1; 924 dwRemainingStringSize -= dwTemp;
925 wzCopyDestination += dwTemp;
861 } 926 }
862 927
863 wzWriteValue = sczWriteValue; 928 wzWriteValue = sczWriteValue;
864 }
865 929
866 hr = ::DWordMult(dwTotalStringSize, sizeof(WCHAR), &cbTotalStringSize); 930 hr = ::DWordMult(dwTotalStringSize, sizeof(WCHAR), &cbTotalStringSize);
867 RegExitOnFailure(hr, "Failed to get total string size in bytes"); 931 RegExitOnFailure(hr, "Failed to get total string size in bytes");
932 }
868 933
869 er = vpfnRegSetValueExW(hk, wzName, 0, REG_MULTI_SZ, reinterpret_cast<const BYTE *>(wzWriteValue), cbTotalStringSize); 934 er = vpfnRegSetValueExW(hk, wzName, 0, REG_MULTI_SZ, reinterpret_cast<const BYTE *>(wzWriteValue), cbTotalStringSize);
870 RegExitOnWin32Error(er, hr, "Failed to set registry value to array of strings (first string of which is): %ls", wzWriteValue); 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(
1000 } 1065 }
1001} 1066}
1002 1067
1068static 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
1003static HRESULT WriteStringToRegistry( 1110static HRESULT WriteStringToRegistry(
1004 __in HKEY hk, 1111 __in HKEY hk,
1005 __in_z_opt LPCWSTR wzName, 1112 __in_z_opt LPCWSTR wzName,
@@ -1013,9 +1120,12 @@ static HRESULT WriteStringToRegistry(
1013 1120
1014 if (wzValue) 1121 if (wzValue)
1015 { 1122 {
1016 hr = ::StringCbLengthW(wzValue, STRSAFE_MAX_CCH * sizeof(TCHAR), &cbValue); 1123 hr = ::StringCbLengthW(wzValue, DWORD_MAX, &cbValue);
1017 RegExitOnFailure(hr, "Failed to determine length of registry value: %ls", wzName); 1124 RegExitOnFailure(hr, "Failed to determine length of registry value: %ls", wzName);
1018 1125
1126 // Need to include the null terminator.
1127 cbValue += sizeof(WCHAR);
1128
1019 er = vpfnRegSetValueExW(hk, wzName, 0, dwType, reinterpret_cast<const BYTE *>(wzValue), static_cast<DWORD>(cbValue)); 1129 er = vpfnRegSetValueExW(hk, wzName, 0, dwType, reinterpret_cast<const BYTE *>(wzValue), static_cast<DWORD>(cbValue));
1020 RegExitOnWin32Error(er, hr, "Failed to set registry value: %ls", wzName); 1130 RegExitOnWin32Error(er, hr, "Failed to set registry value: %ls", wzName);
1021 } 1131 }