Skip to content

RegistryHelper

Calvindd2f edited this page Jan 14, 2026 · 1 revision

RegistryHelper

Relevant source files

The following files were used as context for generating this wiki page:

The RegistryHelper class provides high-performance Windows Registry operations with automatic WOW64 redirection handling. It encapsulates registry read, write, enumerate, and delete operations across both native and 32-bit (WOW6432Node) registry views on 64-bit systems, ensuring complete Office artifact cleanup regardless of installation bitness.

For file system operations with reboot scheduling, see FileHelper. For broader cleanup orchestration, see OfficeScrubOrchestrator.


Class Overview

The RegistryHelper class is instantiated with a system architecture flag and provides CRUD operations for Windows Registry keys and values. All methods automatically handle both registry views when running on 64-bit Windows.

Location: OfficeScrubC2R-Native.cs:334-616

graph TB
    subgraph "RegistryHelper Public API"
        KE["KeyExists(hive, subKey)"]
        DK["DeleteKey(hive, subKey, recursive)"]
        DV["DeleteValue(hive, subKey, valueName)"]
        EK["EnumerateKeys(hive, subKey)"]
        EV["EnumerateValues(hive, subKey)"]
        GV["GetValue(hive, subKey, valueName)"]
        SV["SetValue(hive, subKey, valueName, value)"]
        APFR["AddPendingFileRenameOperation(filePath)"]
    end
    
    subgraph "Internal Implementation"
        GHK["GetHiveKey(RegistryHiveType)"]
        GWOK["GetWow64Key(subKey)"]
        KEI["KeyExistsInternal(hive, subKey)"]
        DKI["DeleteKeyInternal(hive, subKey)"]
        OK["OpenKey(hive, subKey, writable)"]
    end
    
    subgraph "State"
        IS64["_is64Bit field"]
    end
    
    KE --> IS64
    KE --> GHK
    KE --> KEI
    KE --> GWOK
    
    DK --> IS64
    DK --> GHK
    DK --> DKI
    DK --> GWOK
    
    DV --> IS64
    DV --> GHK
    DV --> OK
    DV --> GWOK
    
    EK --> IS64
    EK --> GHK
    EK --> GWOK
    
    GV --> IS64
    GV --> GHK
    GV --> GWOK
    
    style KE fill:#f9f9f9
    style DK fill:#f9f9f9
    style DV fill:#f9f9f9
    style EK fill:#f9f9f9
Loading

Sources: OfficeScrubC2R-Native.cs:334-616


Constructor and Initialization

The RegistryHelper is initialized with a boolean flag indicating 64-bit system architecture. This flag determines whether dual-path registry operations are performed.

public RegistryHelper(bool is64Bit)

Parameters:

  • is64Bit: Set to true on 64-bit Windows systems to enable WOW64 redirection handling

Usage Pattern:

var orchestrator = new OfficeScrubOrchestrator(Environment.Is64BitOperatingSystem);
var regHelper = orchestrator.Registry; // Already initialized

Location: OfficeScrubC2R-Native.cs:338-341

Sources: OfficeScrubC2R-Native.cs:338-341


WOW64 Registry Architecture

On 64-bit Windows systems, the registry is divided into native 64-bit and redirected 32-bit (WOW6432Node) views. Office can be installed in either bitness, leaving artifacts in different registry locations. The RegistryHelper automatically checks both views.

graph LR
    subgraph "Registry Operation Flow"
        REQ["Registry Operation Request"]
        CHECK["Check _is64Bit"]
        
        subgraph "32-bit System"
            SINGLE["Single Path Operation<br/>Native View Only"]
        end
        
        subgraph "64-bit System"
            NATIVE["Native 64-bit View<br/>HKLM\Software\X"]
            WOW["WOW64 32-bit View<br/>HKLM\Software\Wow6432Node\X"]
        end
        
        MERGE["Merge Results<br/>HashSet Deduplication"]
    end
    
    REQ --> CHECK
    CHECK -->|"is64Bit = false"| SINGLE
    CHECK -->|"is64Bit = true"| NATIVE
    CHECK -->|"is64Bit = true"| WOW
    
    NATIVE --> MERGE
    WOW --> MERGE
    SINGLE --> MERGE
    
    MERGE --> RESULT["Return Combined Result"]
Loading

Sources: OfficeScrubC2R-Native.cs:334-594, docs/memory-bank/guidelines.md:57-62

WOW64 Path Transformation

The GetWow64Key() method transforms registry paths to access the 32-bit redirected view:

Original Path Transformed Path
Software\Microsoft\Office Software\Wow6432Node\Microsoft\Office
Software\Classes\TypeLib Software\Classes\Wow6432Node\TypeLib
Classes\Installer Wow6432Node\Classes\Installer

Implementation: OfficeScrubC2R-Native.cs:583-593

Logic:

  1. If path starts with Software\Classes\, insert Wow6432Node\ after Classes\
  2. Otherwise, insert Wow6432Node\ after first backslash
  3. If no backslash, prepend Wow6432Node\

Sources: OfficeScrubC2R-Native.cs:583-593


Public API Methods

KeyExists

Checks if a registry key exists in either the native or WOW64 view.

public bool KeyExists(RegistryHiveType hive, string subKey)

Parameters:

  • hive: Registry hive (ClassesRoot, CurrentUser, LocalMachine, Users)
  • subKey: Registry path relative to hive

Returns: true if key exists in any view, false otherwise

Implementation: OfficeScrubC2R-Native.cs:343-347

WOW64 Behavior: On 64-bit systems, checks both native and WOW64 views

Sources: OfficeScrubC2R-Native.cs:343-347


DeleteKey

Deletes a registry key from both native and WOW64 views (if present).

public bool DeleteKey(RegistryHiveType hive, string subKey, bool recursive = true)

Parameters:

  • hive: Registry hive to delete from
  • subKey: Registry path to delete
  • recursive: If true, deletes all subkeys; if false, only deletes empty keys

Returns: true if any key was deleted, false if operation failed

Implementation: OfficeScrubC2R-Native.cs:349-364

Behavior:

  1. Check if key exists in native view → delete if present
  2. On 64-bit systems, check WOW64 view → delete if present
  3. Return true if either deletion succeeded

Error Handling: Uses DeleteSubKeyTree() for recursive, DeleteSubKey() for non-recursive. Catches and silently handles exceptions.

Sources: OfficeScrubC2R-Native.cs:349-364, OfficeScrubC2R-Native.cs:538-552


DeleteValue

Deletes a registry value from both native and WOW64 views.

public bool DeleteValue(RegistryHiveType hive, string subKey, string valueName)

Parameters:

  • hive: Registry hive containing the key
  • subKey: Registry key path
  • valueName: Name of value to delete

Returns: true if value was deleted from any view, false otherwise

Implementation: OfficeScrubC2R-Native.cs:366-400

Behavior:

  1. Open key in native view, check value exists, delete if present
  2. On 64-bit systems, repeat for WOW64 view
  3. Return true if deleted from either view

Sources: OfficeScrubC2R-Native.cs:366-400


EnumerateKeys

Returns all subkey names from both registry views, deduplicated.

public string[] EnumerateKeys(RegistryHiveType hive, string subKey)

Parameters:

  • hive: Registry hive to enumerate
  • subKey: Parent key path

Returns: Array of subkey names (case-insensitive deduplicated)

Implementation: OfficeScrubC2R-Native.cs:402-436

Deduplication Strategy:

var keys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

This ensures keys like Office16 and office16 are treated as the same entry.

Sources: OfficeScrubC2R-Native.cs:402-436


EnumerateValues

Returns all value names within a registry key from both views.

public string[] EnumerateValues(RegistryHiveType hive, string subKey)

Parameters:

  • hive: Registry hive containing the key
  • subKey: Registry key path

Returns: Array of value names (case-insensitive deduplicated)

Implementation: OfficeScrubC2R-Native.cs:438-472

Use Case: Used by WindowsInstallerHelper to enumerate component GUIDs for deletion validation.

Sources: OfficeScrubC2R-Native.cs:438-472


GetValue

Reads a registry value, checking native view first, then WOW64 view.

public object GetValue(RegistryHiveType hive, string subKey, string valueName, object defaultValue = null)

Parameters:

  • hive: Registry hive to read from
  • subKey: Key path
  • valueName: Value name to read
  • defaultValue: Value to return if not found (default: null)

Returns: Value data (string, int, byte[], etc.) or defaultValue if not found

Implementation: OfficeScrubC2R-Native.cs:474-502

Priority: Native view is checked first; WOW64 view is only checked if native returns null.

Sources: OfficeScrubC2R-Native.cs:474-502


SetValue

Writes a registry value to the specified key.

public bool SetValue(RegistryHiveType hive, string subKey, string valueName, object value, RegistryValueKind kind)

Parameters:

  • hive: Registry hive to write to
  • subKey: Key path (created if doesn't exist)
  • valueName: Value name
  • value: Data to write
  • kind: Registry value type (String, DWord, MultiString, etc.)

Returns: true if write succeeded, false otherwise

Implementation: OfficeScrubC2R-Native.cs:504-521

Note: This method does NOT write to WOW64 view. It only writes to the native view. This is used for creating PendingFileRenameOperations entries.

Sources: OfficeScrubC2R-Native.cs:504-521


AddPendingFileRenameOperation

Adds file path to PendingFileRenameOperations for deletion on next reboot.

public void AddPendingFileRenameOperation(string filePath)

Parameters:

  • filePath: Absolute file path to delete on reboot

Registry Location: HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\PendingFileRenameOperations

Implementation: OfficeScrubC2R-Native.cs:595-615

Format:

\\??\C:\Path\To\File.ext
<empty string>

The \\??\ prefix is the NT namespace prefix required for reboot operations.

Use Case: Called by FileHelper when file deletion fails due to locks. See FileHelper for more details.

Sources: OfficeScrubC2R-Native.cs:595-615


RegistryHiveType Enumeration

The RegistryHiveType enum maps to Windows registry hive values for P/Invoke operations.

Enum Value Hex Value Registry Hive
ClassesRoot 0x80000000 HKEY_CLASSES_ROOT
CurrentUser 0x80000001 HKEY_CURRENT_USER
LocalMachine 0x80000002 HKEY_LOCAL_MACHINE
Users 0x80000003 HKEY_USERS

Location: OfficeScrubC2R-Native.cs:21-27

Mapping Implementation: OfficeScrubC2R-Native.cs:566-581

The GetHiveKey() method converts enum values to Microsoft.Win32.Registry static properties:

  • RegistryHiveType.LocalMachine → Registry.LocalMachine
  • RegistryHiveType.CurrentUser → Registry.CurrentUser
  • etc.

Sources: OfficeScrubC2R-Native.cs:21-27, OfficeScrubC2R-Native.cs:566-581


Integration with Helper Classes

The RegistryHelper is used extensively by other helper classes in the native library.

graph TB
    subgraph "Consumers"
        ORCH["OfficeScrubOrchestrator"]
        WIH["WindowsInstallerHelper"]
        TLH["TypeLibHelper"]
    end
    
    subgraph "RegistryHelper Operations"
        RH["RegistryHelper"]
        KE["KeyExists()"]
        DK["DeleteKey()"]
        DV["DeleteValue()"]
        EK["EnumerateKeys()"]
        EV["EnumerateValues()"]
        GV["GetValue()"]
    end
    
    ORCH -->|"Direct Usage"| RH
    
    WIH -->|"Dependency Injection"| RH
    WIH -->|"CleanupUpgradeCodes()"| EK
    WIH -->|"CleanupProducts()"| EK
    WIH -->|"CleanupComponents()"| EK
    WIH -->|"CleanupComponents()"| EV
    WIH -->|"CleanupComponents()"| DV
    WIH -->|"CleanupPublishedComponents()"| EK
    WIH -->|"CleanupPublishedComponents()"| EV
    WIH -->|"CleanupPublishedComponents()"| GV
    WIH -->|"CleanupPublishedComponents()"| DV
    
    TLH -->|"Dependency Injection"| RH
    TLH -->|"CleanupKnownTypeLibs()"| KE
    TLH -->|"CleanupKnownTypeLibs()"| EK
    TLH -->|"CleanupKnownTypeLibs()"| GV
    TLH -->|"CleanupKnownTypeLibs()"| DK
Loading

Sources: OfficeScrubC2R-Native.cs:980-1128, OfficeScrubC2R-Native.cs:1134-1209

WindowsInstallerHelper Usage

The WindowsInstallerHelper receives a RegistryHelper instance via constructor injection:

public WindowsInstallerHelper(RegistryHelper regHelper)
{
    _regHelper = regHelper;
}

Location: OfficeScrubC2R-Native.cs:984-987

It uses the registry helper to:

  1. Enumerate Windows Installer metadata keys (UpgradeCodes, Products, Components)
  2. Read compressed GUID values
  3. Delete keys matching Office product codes

Example Flow (CleanupComponents):

  1. EnumerateKeys() to list all component GUIDs
  2. EnumerateValues() to list product codes under each component
  3. Pass compressed GUID to GuidHelper.GetExpandedGuid()
  4. Call callback function shouldDelete(guid) to validate
  5. DeleteValue() to remove matching product code

Sources: OfficeScrubC2R-Native.cs:1041-1067

TypeLibHelper Usage

The TypeLibHelper uses registry operations to clean up orphaned COM type library registrations:

public TypeLibHelper(RegistryHelper regHelper)
{
    _regHelper = regHelper;
}

Location: OfficeScrubC2R-Native.cs:1153-1156

Cleanup Process:

  1. Iterate through known Office TypeLib GUIDs
  2. KeyExists() to check if TypeLib is registered
  3. EnumerateKeys() to get version numbers
  4. GetValue() to read DLL file paths
  5. Verify file existence
  6. DeleteKey() if orphaned (file doesn't exist)

Sources: OfficeScrubC2R-Native.cs:1158-1208


Error Handling and Silent Failure Pattern

All registry methods follow a "silent failure" pattern to ensure cleanup continues even when individual operations fail.

graph TD
    START["Method Entry"]
    TRY["try { Operation }"]
    CATCH["catch { }"]
    RETURN["Return false/null"]
    SUCCESS["Return true/result"]
    
    START --> TRY
    TRY -->|"Exception"| CATCH
    TRY -->|"Success"| SUCCESS
    CATCH --> RETURN
Loading

Pattern Details:

  • All public methods are wrapped in try-catch blocks
  • Exceptions are caught but not logged or thrown
  • Methods return false, null, or empty arrays on failure
  • This allows cleanup operations to continue despite access denied, missing keys, etc.

Example - KeyExistsInternal:

private bool KeyExistsInternal(RegistryKey hive, string subKey, bool writable)
{
    try
    {
        using (var key = hive.OpenSubKey(subKey, writable))
        {
            return key != null;
        }
    }
    catch
    {
        return false;  // Silent failure
    }
}

Location: OfficeScrubC2R-Native.cs:523-536

Rationale: During Office cleanup, some registry keys may be inaccessible due to:

  • Insufficient permissions
  • Keys already deleted by concurrent operations
  • System-protected registry locations

Silent failures ensure the cleanup process continues to completion.

Sources: OfficeScrubC2R-Native.cs:523-536, docs/memory-bank/guidelines.md:26-42


Performance Characteristics

Caching Strategy

The RegistryHelper does NOT implement internal caching. Each operation opens, performs, and closes registry keys independently.

Rationale:

  • Registry operations are relatively fast (microseconds)
  • Caching would add complexity for minimal benefit
  • Most cleanup operations are one-time deletions

HashSet Deduplication

Enumeration methods use HashSet<string> with case-insensitive comparison:

var keys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

Performance: O(1) insert and lookup, preventing duplicate entries when combining native and WOW64 results.

Memory: Typically stores 10-100 registry key names (tens of KB)

Sources: OfficeScrubC2R-Native.cs:404, OfficeScrubC2R-Native.cs:440

WOW64 Overhead

On 64-bit systems, most operations perform 2x registry access:

  1. Native 64-bit view
  2. WOW64 32-bit view

Mitigation: Both operations run sequentially in the same thread. For typical Office cleanup (100-500 keys), total overhead is <50ms.

Sources: OfficeScrubC2R-Native.cs:343-400


Usage Examples

Checking for Office Registry Keys

var regHelper = new RegistryHelper(Environment.Is64BitOperatingSystem);

// Check if Office 16 key exists
bool exists = regHelper.KeyExists(
    RegistryHiveType.LocalMachine, 
    @"SOFTWARE\Microsoft\Office\16.0"
);

Deleting Office Click-to-Run Keys

// Delete C2R configuration recursively
regHelper.DeleteKey(
    RegistryHiveType.LocalMachine,
    @"SOFTWARE\Microsoft\Office\ClickToRun",
    recursive: true
);

Enumerating Office Products

// List all Office version keys
string[] versions = regHelper.EnumerateKeys(
    RegistryHiveType.LocalMachine,
    @"SOFTWARE\Microsoft\Office"
);
// Returns: ["15.0", "16.0", "Common", "ClickToRun", ...]

Reading Configuration Values

// Get Office installation path
string installPath = regHelper.GetValue(
    RegistryHiveType.LocalMachine,
    @"SOFTWARE\Microsoft\Office\ClickToRun\Configuration",
    "InstallationPath",
    defaultValue: ""
) as string;

Sources: OfficeScrubC2R-Native.cs:334-616


Comparison with .NET Registry Classes

The RegistryHelper wraps Microsoft.Win32.Registry classes with automatic WOW64 handling:

Feature RegistryHelper Microsoft.Win32.RegistryKey
WOW64 Support Automatic dual-path Manual with RegistryView parameter
Error Handling Silent failures Throws exceptions
Enumeration Returns deduplicated arrays Returns arrays per view
Key Existence Single method call Open + null check
Delete Operations Tries both views Single view only

Direct .NET Equivalent (without WOW64):

using (var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Office"))
{
    if (key != null)
    {
        string[] subkeys = key.GetSubKeyNames();
    }
}

RegistryHelper Equivalent (with WOW64):

string[] subkeys = regHelper.EnumerateKeys(
    RegistryHiveType.LocalMachine,
    @"SOFTWARE\Microsoft\Office"
);
// Automatically includes WOW6432Node subkeys on 64-bit systems

Sources: OfficeScrubC2R-Native.cs:334-616, docs/memory-bank/guidelines.md:72-76


Thread Safety

The RegistryHelper class is not thread-safe. Each instance should be used by a single thread.

Rationale:

  • Registry operations in OfficeScrubC2R are sequential within the orchestrator
  • No shared state between instances
  • Creating new instances per thread is lightweight

Multi-threading Pattern (if needed):

// Create separate instance per thread
var tasks = new List<Task>();
foreach (var registryPath in pathsToClean)
{
    tasks.Add(Task.Run(() => 
    {
        var regHelper = new RegistryHelper(is64Bit);
        regHelper.DeleteKey(hive, registryPath, true);
    }));
}
Task.WaitAll(tasks.ToArray());

Sources: OfficeScrubC2R-Native.cs:334-341


Limitations and Considerations

Permission Requirements

All registry operations require appropriate permissions:

  • Read Operations: Requires KEY_READ access
  • Delete Operations: Requires KEY_WRITE and KEY_ALL_ACCESS permissions
  • System Keys: Some keys are protected even with admin rights

Elevation: OfficeScrubC2R requires administrator privileges for full registry cleanup.

Registry Redirection

Certain registry keys are exempt from WOW64 redirection:

  • HKLM\SOFTWARE\Classes\CLSID
  • HKLM\SOFTWARE\Classes\Interface
  • Shared keys where 32-bit and 64-bit apps must see same data

The RegistryHelper applies WOW64 transformation to all keys, which is appropriate for Office cleanup but may not be correct for all registry paths.

Performance Considerations

Registry operations are synchronous and blocking:

  • Enumerating large keys (1000+ subkeys) can take 10-50ms
  • Recursive deletion of deep hierarchies can take 100-500ms
  • WOW64 operations double these times on 64-bit systems

Recommendation: For large-scale cleanup, consider batching operations or using progress reporting.

Sources: OfficeScrubC2R-Native.cs:334-616, docs/memory-bank/guidelines.md:57-76

Clone this wiki locally