Registry Transactions

Leveraging Registry Transactions for Error-Resilient Operations

Registry transactions offer a robust mechanism for combining and managing multiple independent operations within the Windows Registry. They ensure that all operations succeed together or none at all, reducing the risk of leaving the registry in an inconsistent state. In this article, we will explore a practical use case of registry transactions and their ability to mitigate potential issues.

Understanding the Power of Registry Transactions

Registry transactions are a critical technology when making changes to the Windows Registry. When performing a series of modifications, it’s essential to either complete all of them successfully or roll them all back if an error occurs. In a previous article, we introduced the concept of transactions in registry and file access. Here, we present a real-world scenario to demonstrate their effectiveness.

Real-World Scenario

Recently, I was tasked with developing code for a COM server to unregister itself from the list of registered classes. However, a typographical error led to unexpected consequences. The process involved two parts: the first part aimed to delete a registry subtree, and the second part was a separate action dependent on the success of the first.

Due to the typo, I accidentally deleted HKEY_CURRENT_USER, which surprisingly worked. However, the second part failed due to the same typo. If I had used transactions, only a minor inconvenience would have occurred – a quick typo fix. Instead, I had to invest considerable time in restoring my user profile, which could have been avoided.

The Code in Action

The code focuses on deleting all registry keys and values beneath a specified subkey, including the subkey itself. The key function is w32_RegDeleteTreeTransacted.


LSTATUS w32_RegDeleteTreeTransacted(
    HKEY hKeyRoot, LPCTSTR subKey, bool deleteSubKey, HANDLE transaction = INVALID_HANDLE_VALUE);
    
  • hKeyRoot: The root registry key.
  • subKey: The subkey under which all keys and values will be deleted.
  • deleteSubKey: Indicates whether the subkey itself should be deleted.
  • transaction: An optional parameter for integrating this change into an overall transaction.

Input Validation


if (hKeyRoot == NULL)
    return ERROR_INVALID_PARAMETER;

if ((hKeyRoot == HKEY_CLASSES_ROOT ||
    hKeyRoot == HKEY_CURRENT_CONFIG ||
    hKeyRoot == HKEY_CURRENT_USER ||
    hKeyRoot == HKEY_LOCAL_MACHINE ||
    hKeyRoot == HKEY_USERS) && subKey == NULL)
    return ERROR_INVALID_PARAMETER;

if (deleteSubKey && subKey == NULL)
    return ERROR_INVALID_PARAMETER;
    

Input validation ensures that the root and subkey parameters are valid. It prevents cases where deleting everything under a root key would lead to unintended consequences.

Transaction Management


HANDLE localTransaction = INVALID_HANDLE_VALUE;
if (transaction == INVALID_HANDLE_VALUE)
    localTransaction = w32_CreateTransaction();
if (localTransaction == INVALID_HANDLE_VALUE)
    return GetLastError();
else
    localTransaction = transaction;

if (transaction == INVALID_HANDLE_VALUE)
    if (status != ERROR_SUCCESS)
    RollbackTransaction(localTransaction);
    CloseHandle(localTransaction);
    return GetLastError();

else
    CommitTransaction(localTransaction);
    CloseHandle(localTransaction);
    return NO_ERROR;
    

Transaction management handles the use of an optional transaction parameter. If none is provided, a local transaction is created for these changes. The decision to commit or roll back is based on the error status.

Performing the Work


HKEY hKey = NULL;
DWORD status = ERROR_SUCCESS;

if (status == ERROR_SUCCESS)
    status = RegOpenKeyTransacted(
        hKeyRoot, subKey, 0, KEY_WRITE | KEY_READ, &hKey, localTransaction, NULL);
if (status == ERROR_SUCCESS)
    status = RegDeleteTree(hKey, NULL);

if (status == ERROR_SUCCESS)
    if (deleteSubKey)
        status = RegDeleteKeyTransacted(
            hKeyRoot, subKey, KEY_WRITE);
    
if (hKey != NULL)
    CloseHandle(hKey);
    

The primary functionality involves opening a registry key, deleting everything underneath it, and deleting the subkey itself. The RegDeleteTree API, even though not transaction-aware, behaves as expected when operations are within a transacted handle.

Registry transactions offer a powerful tool for managing complex registry operations with resilience against errors. They ensure that changes either fully succeed or leave the registry in its original state. By creating a helper function like w32_RegDeleteTreeTransacted, you can simplify and safeguard your registry operations. This function combines actions, showcases transaction capabilities, and enhances code robustness. Registry transactions empower developers to handle complex registry operations with confidence.

Feel free to use the provided helper functions in your projects to streamline registry operations and minimize risks. This article is licensed under the MIT license.

History

  • V1. 17AUG2022: Initial version