terminal/src/propslib/RegistrySerialization.cpp

460 lines
21 KiB
C++
Raw Normal View History

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "RegistrySerialization.hpp"
#pragma hdrstop
Consolidate the color palette APIs (#11784) This PR merges the default colors and cursor color into the main color table, enabling us to simplify the `ConGetSet` and `ITerminalApi` interfaces, with just two methods required for getting and setting any form of color palette entry. The is a follow-up to the color table standardization in #11602, and a another small step towards de-duplicating `AdaptDispatch` and `TerminalDispatch` for issue #3849. It should also make it easier to support color queries (#3718) and a configurable bold color (#5682) in the future. On the conhost side, default colors could originally be either indexed positions in the 16-color table, or separate standalone RGB values. With the new system, the default colors will always be in the color table, so we just need to track their index positions. To make this work, those positions need to be calculated at startup based on the loaded registry/shortcut settings, and updated when settings are changed (this is handled in `CalculateDefaultColorIndices`). But the plus side is that it's now much easier to lookup the default color values for rendering. For now the default colors in Windows Terminal use hardcoded positions, because it doesn't need indexed default colors like conhost. But in the future I'd like to extend the index handling to both terminals, so we can eventually support the VT525 indexed color operations. As for the cursor color, that was previously stored in the `Cursor` class, which meant that it needed to be copied around in various places where cursors were being instantiated. Now that it's managed separately in the color table, a lot of that code is no longer required. ## Validation Some of the unit test initialization code needed to be updated to setup the color table and default index values as required for the new system. There were also some adjustments needed to account for API changes, in particular for methods that now take index values for the default colors in place of COLORREFs. But for the most part, the essential behavior of the tests remains unchanged. I've also run a variety of manual tests looking at the legacy console APIs as well as the various VT color sequences, and checking that everything works as expected when color schemes are changed, both in Windows Terminal and conhost, and in the latter case with both indexed colors and RGB values. Closes #11768
2021-11-23 19:28:55 +01:00
#define SET_FIELD_AND_SIZE(x) UFIELD_OFFSET(Settings, x), RTL_FIELD_SIZE(Settings, x)
#define NT_TESTNULL(var) (((var) == nullptr) ? STATUS_NO_MEMORY : STATUS_SUCCESS)
DWORD RegistrySerialization::ToWin32RegistryType(const _RegPropertyType type)
{
switch (type)
{
case _RegPropertyType::Boolean:
case _RegPropertyType::Dword:
case _RegPropertyType::Word:
case _RegPropertyType::Byte:
case _RegPropertyType::Coordinate:
return REG_DWORD;
case _RegPropertyType::String:
return REG_SZ;
default:
return REG_NONE;
}
}
// clang-format off
// Registry settings to load (not all of them, some are special)
const RegistrySerialization::_RegPropertyMap RegistrySerialization::s_PropertyMappings[] =
{
//+---------------------------------+-----------------------------------------------+-----------------------------------------------+
//| Property type | Property Name | Corresponding Settings field |
{ _RegPropertyType::Word, CONSOLE_REGISTRY_POPUPATTR, SET_FIELD_AND_SIZE(_wPopupFillAttribute), },
{ _RegPropertyType::Boolean, CONSOLE_REGISTRY_INSERTMODE, SET_FIELD_AND_SIZE(_bInsertMode) },
{ _RegPropertyType::Boolean, CONSOLE_REGISTRY_LINESELECTION, SET_FIELD_AND_SIZE(_bLineSelection) },
{ _RegPropertyType::Boolean, CONSOLE_REGISTRY_FILTERONPASTE, SET_FIELD_AND_SIZE(_fFilterOnPaste) },
{ _RegPropertyType::Boolean, CONSOLE_REGISTRY_LINEWRAP, SET_FIELD_AND_SIZE(_bWrapText) },
{ _RegPropertyType::Boolean, CONSOLE_REGISTRY_CTRLKEYSHORTCUTS_DISABLED, SET_FIELD_AND_SIZE(_fCtrlKeyShortcutsDisabled) },
{ _RegPropertyType::Boolean, CONSOLE_REGISTRY_QUICKEDIT, SET_FIELD_AND_SIZE(_bQuickEdit) },
{ _RegPropertyType::Byte, CONSOLE_REGISTRY_WINDOWALPHA, SET_FIELD_AND_SIZE(_bWindowAlpha) },
{ _RegPropertyType::Coordinate, CONSOLE_REGISTRY_FONTSIZE, SET_FIELD_AND_SIZE(_dwFontSize) },
{ _RegPropertyType::Dword, CONSOLE_REGISTRY_FONTFAMILY, SET_FIELD_AND_SIZE(_uFontFamily) },
{ _RegPropertyType::Dword, CONSOLE_REGISTRY_FONTWEIGHT, SET_FIELD_AND_SIZE(_uFontWeight) },
{ _RegPropertyType::String, CONSOLE_REGISTRY_FACENAME, SET_FIELD_AND_SIZE(_FaceName) },
{ _RegPropertyType::Dword, CONSOLE_REGISTRY_CURSORSIZE, SET_FIELD_AND_SIZE(_uCursorSize) },
{ _RegPropertyType::Dword, CONSOLE_REGISTRY_HISTORYSIZE, SET_FIELD_AND_SIZE(_uHistoryBufferSize) },
{ _RegPropertyType::Dword, CONSOLE_REGISTRY_HISTORYBUFS, SET_FIELD_AND_SIZE(_uNumberOfHistoryBuffers) },
{ _RegPropertyType::Boolean, CONSOLE_REGISTRY_HISTORYNODUP, SET_FIELD_AND_SIZE(_bHistoryNoDup) },
{ _RegPropertyType::Dword, CONSOLE_REGISTRY_SCROLLSCALE, SET_FIELD_AND_SIZE(_uScrollScale) },
{ _RegPropertyType::Word, CONSOLE_REGISTRY_FILLATTR, SET_FIELD_AND_SIZE(_wFillAttribute) },
{ _RegPropertyType::Coordinate, CONSOLE_REGISTRY_BUFFERSIZE, SET_FIELD_AND_SIZE(_dwScreenBufferSize) },
{ _RegPropertyType::Coordinate, CONSOLE_REGISTRY_WINDOWSIZE, SET_FIELD_AND_SIZE(_dwWindowSize) },
{ _RegPropertyType::Boolean, CONSOLE_REGISTRY_TRIMZEROHEADINGS, SET_FIELD_AND_SIZE(_fTrimLeadingZeros) },
{ _RegPropertyType::Boolean, CONSOLE_REGISTRY_ENABLE_COLOR_SELECTION, SET_FIELD_AND_SIZE(_fEnableColorSelection) },
{ _RegPropertyType::Coordinate, CONSOLE_REGISTRY_WINDOWPOS, SET_FIELD_AND_SIZE(_dwWindowOrigin) },
{ _RegPropertyType::Dword, CONSOLE_REGISTRY_CURSORTYPE, SET_FIELD_AND_SIZE(_CursorType) },
{ _RegPropertyType::Boolean, CONSOLE_REGISTRY_INTERCEPTCOPYPASTE, SET_FIELD_AND_SIZE(_fInterceptCopyPaste) },
{ _RegPropertyType::Boolean, CONSOLE_REGISTRY_TERMINALSCROLLING, SET_FIELD_AND_SIZE(_TerminalScrolling) },
Introduce AtlasEngine - A new text rendering prototype (#11623) This commit introduces "AtlasEngine", a new text renderer based on DxEngine. But unlike it, DirectWrite and Direct2D are only used to rasterize glyphs. Blending and placing these glyphs into the target view is being done using Direct3D and a simple HLSL shader. Since this new renderer more aggressively assumes that the text is monospace, it simplifies the implementation: The viewport is divided into cells, and its data is stored as a simple matrix. Modifications to this matrix involve only simple pointer arithmetic and is easy to understand. But just like with DxEngine however, DirectWrite related code remains extremely complex and hard to understand. Supported features: * Basic text rendering with grayscale AA * Foreground and background colors * Emojis, including zero width joiners * Underline, dotted underline, strikethrough * Custom font axes and features * Selections * All cursor styles * Full alpha support for all colors * _Should_ work with Windows 7 Unsupported features: * A more conservative GPU memory usage The backing texture atlas for glyphs is grow-only and will not shrink. After 256MB of memory is used up (~20k glyphs) text output will be broken until the renderer is restarted. * ClearType * Remaining gridlines (left, right, top, bottom, double underline) * Hyperlinks don't get full underlines if hovered in WT * Softfonts * Non-default line renditions Performance: * Runs at up to native display refresh rate Unfortunately the frame rate often drops below refresh rate, due us fighting over the buffer lock with other parts of the application. * CPU consumption is up to halved compared to DxEngine AtlasEngine is still highly unoptimized. Glyph hashing consumes up to a third of the current CPU time. * No regressions in WT performance VT parsing and related buffer management takes up most of the CPU time (~85%), due to which the AtlasEngine can't show any further improvements. * ~2x improvement in raw text throughput in OpenConsole compared to DxEngine running at 144 FPS * ≥10x improvement in colored VT output in WT/OpenConsole compared to DxEngine running at 144 FPS
2021-11-13 01:10:06 +01:00
{ _RegPropertyType::Dword, CONSOLE_REGISTRY_USEDX, SET_FIELD_AND_SIZE(_fUseDx) },
Consolidate the color palette APIs (#11784) This PR merges the default colors and cursor color into the main color table, enabling us to simplify the `ConGetSet` and `ITerminalApi` interfaces, with just two methods required for getting and setting any form of color palette entry. The is a follow-up to the color table standardization in #11602, and a another small step towards de-duplicating `AdaptDispatch` and `TerminalDispatch` for issue #3849. It should also make it easier to support color queries (#3718) and a configurable bold color (#5682) in the future. On the conhost side, default colors could originally be either indexed positions in the 16-color table, or separate standalone RGB values. With the new system, the default colors will always be in the color table, so we just need to track their index positions. To make this work, those positions need to be calculated at startup based on the loaded registry/shortcut settings, and updated when settings are changed (this is handled in `CalculateDefaultColorIndices`). But the plus side is that it's now much easier to lookup the default color values for rendering. For now the default colors in Windows Terminal use hardcoded positions, because it doesn't need indexed default colors like conhost. But in the future I'd like to extend the index handling to both terminals, so we can eventually support the VT525 indexed color operations. As for the cursor color, that was previously stored in the `Cursor` class, which meant that it needed to be copied around in various places where cursors were being instantiated. Now that it's managed separately in the color table, a lot of that code is no longer required. ## Validation Some of the unit test initialization code needed to be updated to setup the color table and default index values as required for the new system. There were also some adjustments needed to account for API changes, in particular for methods that now take index values for the default colors in place of COLORREFs. But for the most part, the essential behavior of the tests remains unchanged. I've also run a variety of manual tests looking at the legacy console APIs as well as the various VT color sequences, and checking that everything works as expected when color schemes are changed, both in Windows Terminal and conhost, and in the latter case with both indexed colors and RGB values. Closes #11768
2021-11-23 19:28:55 +01:00
{ _RegPropertyType::Boolean, CONSOLE_REGISTRY_COPYCOLOR, SET_FIELD_AND_SIZE(_fCopyColor) },
{ _RegPropertyType::Dword, CONSOLE_REGISTRY_DEFAULTFOREGROUND, SET_FIELD_AND_SIZE(_colorTable[TextColor::DEFAULT_FOREGROUND]) },
{ _RegPropertyType::Dword, CONSOLE_REGISTRY_DEFAULTBACKGROUND, SET_FIELD_AND_SIZE(_colorTable[TextColor::DEFAULT_BACKGROUND]) },
{ _RegPropertyType::Dword, CONSOLE_REGISTRY_CURSORCOLOR, SET_FIELD_AND_SIZE(_colorTable[TextColor::CURSOR_COLOR]) }
};
const size_t RegistrySerialization::s_PropertyMappingsSize = ARRAYSIZE(s_PropertyMappings);
// Global registry settings to load
const RegistrySerialization::_RegPropertyMap RegistrySerialization::s_GlobalPropMappings[] =
{
//+---------------------------------+-----------------------------------------------+-----------------------------------------------+
//| Property type | Property Name | Corresponding Settings field |
{ _RegPropertyType::Dword, CONSOLE_REGISTRY_VIRTTERM_LEVEL, SET_FIELD_AND_SIZE(_dwVirtTermLevel), }
};
const size_t RegistrySerialization::s_GlobalPropMappingsSize = ARRAYSIZE(s_GlobalPropMappings);
// clang-format off
// Routine Description:
// - Reads number from the registry and applies it to the given property if the value exists
// Supports: Dword, Word, Byte, Boolean, and Coordinate
// Arguments:
// - hKey - Registry key to read from
// - pPropMap - Contains property information to use in looking up/storing value data
// Return Value:
// - STATUS_SUCCESSFUL or appropriate NTSTATUS reply for registry operations.
[[nodiscard]]
NTSTATUS RegistrySerialization::s_LoadRegDword(const HKEY hKey, const _RegPropertyMap* const pPropMap, _In_ Settings* const pSettings)
{
// find offset into destination structure for this numerical value
PBYTE const pbField = (PBYTE)pSettings + pPropMap->dwFieldOffset;
// attempt to load number into this field
// If we're not successful, it's ok. Just don't fill it.
DWORD dwValue;
NTSTATUS Status = s_QueryValue(hKey,
pPropMap->pwszValueName,
sizeof(dwValue),
ToWin32RegistryType(pPropMap->propertyType),
(PBYTE)& dwValue,
nullptr);
if (NT_SUCCESS(Status))
{
switch (pPropMap->propertyType)
{
case _RegPropertyType::Dword:
{
DWORD* const pdField = (DWORD*)pbField;
*pdField = dwValue;
break;
}
case _RegPropertyType::Word:
{
WORD* const pwField = (WORD*)pbField;
*pwField = (WORD)dwValue;
break;
}
case _RegPropertyType::Boolean:
{
*pbField = !!dwValue;
break;
}
case _RegPropertyType::Byte:
{
*pbField = LOBYTE(dwValue);
break;
}
case _RegPropertyType::Coordinate:
{
PCOORD pcoordField = (PCOORD)pbField;
pcoordField->X = LOWORD(dwValue);
pcoordField->Y = HIWORD(dwValue);
break;
}
}
}
return Status;
}
// Routine Description:
// - Reads string from the registry and applies it to the given property if the value exists
// Arguments:
// - hKey - Registry key to read from
// - pPropMap - Contains property information to use in looking up/storing value data
// Return Value:
// - STATUS_SUCCESSFUL or appropriate NTSTATUS reply for registry operations.
[[nodiscard]]
NTSTATUS RegistrySerialization::s_LoadRegString(const HKEY hKey, const _RegPropertyMap* const pPropMap, _In_ Settings* const pSettings)
{
// find offset into destination structure for this numerical value
PBYTE const pbField = (PBYTE)pSettings + pPropMap->dwFieldOffset;
// number of characters within the field
size_t const cchField = pPropMap->cbFieldSize / sizeof(WCHAR);
PWCHAR pwchString = new(std::nothrow) WCHAR[cchField];
NTSTATUS Status = NT_TESTNULL(pwchString);
if (NT_SUCCESS(Status))
{
Status = s_QueryValue(hKey,
pPropMap->pwszValueName,
(DWORD)(cchField) * sizeof(WCHAR),
ToWin32RegistryType(pPropMap->propertyType),
(PBYTE)pwchString,
nullptr);
if (NT_SUCCESS(Status))
{
// ensure pwchString is null terminated
pwchString[cchField - 1] = UNICODE_NULL;
Status = StringCchCopyW((PWSTR)pbField, cchField, pwchString);
}
delete[] pwchString;
}
return Status;
}
#pragma region Helpers
// Routine Description:
// - Opens the root console key from HKCU
// Arguments:
// - phCurrentUserKey - Returns a handle to the HKCU root
// - phConsoleKey - Returns a handle to the Console subkey
// Return Value:
// - STATUS_SUCCESSFUL or appropriate NTSTATUS reply for registry operations.
[[nodiscard]]
NTSTATUS RegistrySerialization::s_OpenConsoleKey(_Out_ HKEY* phCurrentUserKey, _Out_ HKEY* phConsoleKey)
{
// Always set an output value. It will be made valid before the end if everything succeeds.
*phCurrentUserKey = static_cast<HKEY>(INVALID_HANDLE_VALUE);
*phConsoleKey = static_cast<HKEY>(INVALID_HANDLE_VALUE);
wil::unique_hkey currentUserKey;
wil::unique_hkey consoleKey;
// Open the current user registry key.
NTSTATUS Status = NTSTATUS_FROM_WIN32(RegOpenCurrentUser(KEY_READ | KEY_WRITE, &currentUserKey));
if (NT_SUCCESS(Status))
{
// Open the console registry key.
Status = s_OpenKey(currentUserKey.get(), CONSOLE_REGISTRY_STRING, &consoleKey);
// If we can't open the console registry key, create one and open it.
if (NTSTATUS_FROM_WIN32(ERROR_FILE_NOT_FOUND) == Status)
{
Status = s_CreateKey(currentUserKey.get(), CONSOLE_REGISTRY_STRING, &consoleKey);
}
// If we're successful, give the keys back.
if (NT_SUCCESS(Status))
{
*phCurrentUserKey = currentUserKey.release();
*phConsoleKey = consoleKey.release();
}
}
return Status;
}
// Routine Description:
// - Opens a subkey of the given key. Fails if it doesn't exist.
// - NOTE: To create if it doesn't exist and open otherwise, try `s_CreateKey`.
// Arguments:
// - hKey - Handle to a registry key
// - pwszSubKey - String name of sub key
// - phResult - Filled with handle to the sub key if available. Check return status.
// Return Value:
// - STATUS_SUCCESSFUL or appropriate NTSTATUS reply for registry operations.
[[nodiscard]]
NTSTATUS RegistrySerialization::s_OpenKey(_In_opt_ HKEY const hKey, _In_ PCWSTR const pwszSubKey, _Out_ HKEY* const phResult)
{
return NTSTATUS_FROM_WIN32(RegOpenKeyW(hKey, pwszSubKey, phResult));
}
// Routine Description:
// - Deletes the value under a given key
// Arguments:
// - hKey - Handle to a registry key
// - pwszValueName - String name of value to delete under that key
// Return Value:
// - STATUS_SUCCESSFUL or appropriate NTSTATUS reply for registry operations.
[[nodiscard]]
NTSTATUS RegistrySerialization::s_DeleteValue(const HKEY hKey, _In_ PCWSTR const pwszValueName)
{
Introduce AtlasEngine - A new text rendering prototype (#11623) This commit introduces "AtlasEngine", a new text renderer based on DxEngine. But unlike it, DirectWrite and Direct2D are only used to rasterize glyphs. Blending and placing these glyphs into the target view is being done using Direct3D and a simple HLSL shader. Since this new renderer more aggressively assumes that the text is monospace, it simplifies the implementation: The viewport is divided into cells, and its data is stored as a simple matrix. Modifications to this matrix involve only simple pointer arithmetic and is easy to understand. But just like with DxEngine however, DirectWrite related code remains extremely complex and hard to understand. Supported features: * Basic text rendering with grayscale AA * Foreground and background colors * Emojis, including zero width joiners * Underline, dotted underline, strikethrough * Custom font axes and features * Selections * All cursor styles * Full alpha support for all colors * _Should_ work with Windows 7 Unsupported features: * A more conservative GPU memory usage The backing texture atlas for glyphs is grow-only and will not shrink. After 256MB of memory is used up (~20k glyphs) text output will be broken until the renderer is restarted. * ClearType * Remaining gridlines (left, right, top, bottom, double underline) * Hyperlinks don't get full underlines if hovered in WT * Softfonts * Non-default line renditions Performance: * Runs at up to native display refresh rate Unfortunately the frame rate often drops below refresh rate, due us fighting over the buffer lock with other parts of the application. * CPU consumption is up to halved compared to DxEngine AtlasEngine is still highly unoptimized. Glyph hashing consumes up to a third of the current CPU time. * No regressions in WT performance VT parsing and related buffer management takes up most of the CPU time (~85%), due to which the AtlasEngine can't show any further improvements. * ~2x improvement in raw text throughput in OpenConsole compared to DxEngine running at 144 FPS * ≥10x improvement in colored VT output in WT/OpenConsole compared to DxEngine running at 144 FPS
2021-11-13 01:10:06 +01:00
const auto result = RegDeleteKeyValueW(hKey, nullptr, pwszValueName);
return result == ERROR_FILE_NOT_FOUND ? S_OK : NTSTATUS_FROM_WIN32(result);
}
// Routine Description:
// - Creates a subkey of the given key
// This function creates keys with read/write access.
// - If key already exists, opens existing.
// Arguments:
// - hKey - Handle to a registry key
// - pwszSubKey - String name of sub key
// - phResult - Filled with handle to the sub key if created/opened successfully. Check return status.
// Return Value:
// - STATUS_SUCCESSFUL or appropriate NTSTATUS reply for registry operations.
[[nodiscard]]
NTSTATUS RegistrySerialization::s_CreateKey(const HKEY hKey, _In_ PCWSTR const pwszSubKey, _Out_ HKEY* const phResult)
{
return NTSTATUS_FROM_WIN32(RegCreateKeyW(hKey, pwszSubKey, phResult));
}
// Routine Description:
// - Sets a value for the given key
// Arguments:
// - hKey - Handle to a registry key
// - pwszValueName - Name of the value to set
// - dwType - Type of the value being set (see: http://msdn.microsoft.com/en-us/library/windows/desktop/ms724884(v=vs.85).aspx)
// - pbData - Pointer to byte stream of data to set into this value
// - cbDataLength - The length in bytes of the data provided
// Return Value:
// - STATUS_SUCCESSFUL or appropriate NTSTATUS reply for registry operations.
[[nodiscard]]
NTSTATUS RegistrySerialization::s_SetValue(const HKEY hKey,
_In_ PCWSTR const pValueName,
const DWORD dwType,
_In_reads_bytes_(cbDataLength) BYTE* const pbData,
const DWORD cbDataLength)
{
return NTSTATUS_FROM_WIN32(RegSetKeyValueW(hKey,
nullptr,
pValueName,
dwType,
pbData,
cbDataLength));
}
// Routine Description:
// - Queries a value for the given key
// Arguments:
// - hKey - Handle to a registry key
// - pwszValueName - Name of the value to query
// - cbValueLength - Length of the provided data buffer.
// - regType - the type of the registry key.
// - pbData - Pointer to byte stream of data to fill with the registry value data.
// - pcbDataLength - Number of bytes filled in the given data buffer
// Return Value:
// - STATUS_SUCCESSFUL or appropriate NTSTATUS reply for registry operations.
[[nodiscard]]
NTSTATUS RegistrySerialization::s_QueryValue(const HKEY hKey,
_In_ PCWSTR const pwszValueName,
const DWORD cbValueLength,
const DWORD regType,
_Out_writes_bytes_(cbValueLength) BYTE* const pbData,
_Out_opt_ _Out_range_(0, cbValueLength) DWORD* const pcbDataLength)
{
DWORD cbData = cbValueLength;
DWORD actualRegType = 0;
LONG const Result = RegQueryValueExW(hKey,
pwszValueName,
nullptr,
&actualRegType,
pbData,
&cbData);
if (ERROR_FILE_NOT_FOUND != Result &&
actualRegType != regType)
{
return STATUS_OBJECT_TYPE_MISMATCH;
}
if (nullptr != pcbDataLength)
{
*pcbDataLength = cbData;
}
return NTSTATUS_FROM_WIN32(Result);
}
// Routine Description:
// - Enumerates the values for the given key
// Arguments:
// - hKey - Handle to a registry key
// - dwIndex - Index of value within this key to return
// - cbValueLength - Length of the provided value name buffer.
// - pwszValueName - Value name buffer to be filled with null terminated string name of value.
// - cbDataLength - Length of the provided value data buffer.
// - pbData - Value data buffer to be filled based on data type. Will be null terminated for string types. (REG_SZ, REG_MULTI_SZ, REG_EXPAND_SZ)
// Return Value:
// - STATUS_SUCCESSFUL or appropriate NTSTATUS reply for registry operations.
[[nodiscard]]
NTSTATUS RegistrySerialization::s_EnumValue(const HKEY hKey,
const DWORD dwIndex,
const DWORD cbValueLength,
_Out_writes_bytes_(cbValueLength) PWSTR const pwszValueName,
const DWORD cbDataLength,
_Out_writes_bytes_(cbDataLength) BYTE* const pbData)
{
DWORD cchValueName = cbValueLength / sizeof(WCHAR);
DWORD cbData = cbDataLength;
#pragma prefast(suppress: 26015, "prefast doesn't realize that cbData == cbDataLength and cchValueName == cbValueLength/2")
return NTSTATUS_FROM_WIN32(RegEnumValueW(hKey,
dwIndex,
pwszValueName,
&cchValueName,
nullptr,
nullptr,
pbData,
&cbData));
}
// Routine Description:
// - Updates the value in a given key
// - NOTE: For the console registry, if we're filling a console subkey and the default matches, the subkey copy will be deleted.
// We only store settings in subkeys if they differ from the defaults.
// Arguments:
// - hConsoleKey - Handle to the default console key
// - hKey - Handle to the console subkey for this particular console
// - pwszValueName - Name of the value within the default and subkeys to check/update.
// - dwType - Type of the value being set (see: http://msdn.microsoft.com/en-us/library/windows/desktop/ms724884(v=vs.85).aspx)
// - pbData - Value data buffer to be stored into the registry.
// - cbDataLength - Length of the provided value data buffer.
// Return Value:
// - STATUS_SUCCESSFUL or appropriate NTSTATUS reply for registry operations.
[[nodiscard]]
NTSTATUS RegistrySerialization::s_UpdateValue(const HKEY hConsoleKey,
const HKEY hKey,
_In_ PCWSTR const pwszValueName,
const DWORD dwType,
_In_reads_bytes_(cbDataLength) BYTE* pbData,
const DWORD cbDataLength)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL; // This value won't be used, added to avoid compiler warnings.
BYTE* Data = new(std::nothrow) BYTE[cbDataLength];
if (Data != nullptr)
{
// If this is not the main console key but the value is the same,
// delete it. Otherwise, set it.
bool fDeleteKey = false;
if (hConsoleKey != hKey)
{
Status = s_QueryValue(hConsoleKey, pwszValueName, cbDataLength, dwType, Data, nullptr);
if (NT_SUCCESS(Status))
{
fDeleteKey = (memcmp(pbData, Data, cbDataLength) == 0);
}
}
if (fDeleteKey)
{
Status = s_DeleteValue(hKey, pwszValueName);
}
else
{
Status = s_SetValue(hKey, pwszValueName, dwType, pbData, cbDataLength);
}
delete[] Data;
}
return Status;
}
[[nodiscard]]
NTSTATUS RegistrySerialization::s_OpenCurrentUserConsoleTitleKey(_In_ PCWSTR const title,
_Out_ HKEY* phCurrentUserKey,
_Out_ HKEY* phConsoleKey,
_Out_ HKEY* phTitleKey)
{
NTSTATUS Status = NTSTATUS_FROM_WIN32(RegOpenKeyW(HKEY_CURRENT_USER,
nullptr,
phCurrentUserKey));
if (NT_SUCCESS(Status))
{
Status = RegistrySerialization::s_CreateKey(*phCurrentUserKey,
CONSOLE_REGISTRY_STRING,
phConsoleKey);
if (NT_SUCCESS(Status))
{
Status = RegistrySerialization::s_CreateKey(*phConsoleKey, title, phTitleKey);
if (!NT_SUCCESS(Status))
{
RegCloseKey(*phConsoleKey);
RegCloseKey(*phCurrentUserKey);
}
//else all keys were created/opened successfully, and we'll return success
}
else
{
RegCloseKey(*phCurrentUserKey);
}
}
return Status;
}
#pragma endregion