Repository navigation
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.
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
Sources: OfficeScrubC2R-Native.cs:334-616
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 totrueon 64-bit Windows systems to enable WOW64 redirection handling
Usage Pattern:
var orchestrator = new OfficeScrubOrchestrator(Environment.Is64BitOperatingSystem);
var regHelper = orchestrator.Registry; // Already initializedLocation: OfficeScrubC2R-Native.cs:338-341
Sources: OfficeScrubC2R-Native.cs:338-341
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"]
Sources: OfficeScrubC2R-Native.cs:334-594, docs/memory-bank/guidelines.md:57-62
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:
- If path starts with
Software\Classes\, insertWow6432Node\afterClasses\ - Otherwise, insert
Wow6432Node\after first backslash - If no backslash, prepend
Wow6432Node\
Sources: OfficeScrubC2R-Native.cs:583-593
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
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: Iftrue, deletes all subkeys; iffalse, only deletes empty keys
Returns: true if any key was deleted, false if operation failed
Implementation: OfficeScrubC2R-Native.cs:349-364
Behavior:
- Check if key exists in native view → delete if present
- On 64-bit systems, check WOW64 view → delete if present
- Return
trueif 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
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:
- Open key in native view, check value exists, delete if present
- On 64-bit systems, repeat for WOW64 view
- Return
trueif deleted from either view
Sources: OfficeScrubC2R-Native.cs:366-400
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
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
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
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
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
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
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
Sources: OfficeScrubC2R-Native.cs:980-1128, OfficeScrubC2R-Native.cs:1134-1209
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:
- Enumerate Windows Installer metadata keys (UpgradeCodes, Products, Components)
- Read compressed GUID values
- Delete keys matching Office product codes
Example Flow (CleanupComponents):
-
EnumerateKeys()to list all component GUIDs -
EnumerateValues()to list product codes under each component - Pass compressed GUID to
GuidHelper.GetExpandedGuid() - Call callback function
shouldDelete(guid)to validate -
DeleteValue()to remove matching product code
Sources: OfficeScrubC2R-Native.cs:1041-1067
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:
- Iterate through known Office TypeLib GUIDs
-
KeyExists()to check if TypeLib is registered -
EnumerateKeys()to get version numbers -
GetValue()to read DLL file paths - Verify file existence
-
DeleteKey()if orphaned (file doesn't exist)
Sources: OfficeScrubC2R-Native.cs:1158-1208
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
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
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
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
On 64-bit systems, most operations perform 2x registry access:
- Native 64-bit view
- 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
var regHelper = new RegistryHelper(Environment.Is64BitOperatingSystem);
// Check if Office 16 key exists
bool exists = regHelper.KeyExists(
RegistryHiveType.LocalMachine,
@"SOFTWARE\Microsoft\Office\16.0"
);// Delete C2R configuration recursively
regHelper.DeleteKey(
RegistryHiveType.LocalMachine,
@"SOFTWARE\Microsoft\Office\ClickToRun",
recursive: true
);// List all Office version keys
string[] versions = regHelper.EnumerateKeys(
RegistryHiveType.LocalMachine,
@"SOFTWARE\Microsoft\Office"
);
// Returns: ["15.0", "16.0", "Common", "ClickToRun", ...]// 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
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 systemsSources: OfficeScrubC2R-Native.cs:334-616, docs/memory-bank/guidelines.md:72-76
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
All registry operations require appropriate permissions:
-
Read Operations: Requires
KEY_READaccess -
Delete Operations: Requires
KEY_WRITEandKEY_ALL_ACCESSpermissions - System Keys: Some keys are protected even with admin rights
Elevation: OfficeScrubC2R requires administrator privileges for full registry cleanup.
Certain registry keys are exempt from WOW64 redirection:
HKLM\SOFTWARE\Classes\CLSIDHKLM\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.
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