Repository navigation
Reference
Relevant source files
The following files were used as context for generating this wiki page:
This page provides comprehensive reference documentation for the OfficeScrubC2R system, including all constants, enums, function signatures, registry paths, error codes, and data structures. This serves as a technical lookup guide for developers working with or extending the codebase.
For usage examples and operational modes, see User Guide. For architectural patterns and design principles, see Architecture. For development guidelines and coding standards, see Developer Guide.
The OfficeConstants class defines core constants used throughout the system for product identification and validation.
| Constant | Type | Value | Purpose |
|---|---|---|---|
OFFICE_ID |
string | "{0000000FF1CE}" |
Office product identifier suffix used in GUID validation |
PROD_LEN |
int | 13 |
Expected length for product identifiers |
SQUISHED |
int | 20 |
Length of Base85-encoded (squished) GUIDs |
COMPRESSED |
int | 32 |
Length of compressed GUIDs without dashes |
Sources: OfficeScrubC2R-Native.cs:29-56
The C2R_PATTERNS array defines string patterns that identify Click-to-Run installations:
public static readonly string[] C2R_PATTERNS = new[]
{
@"\ROOT\OFFICE1",
@"Microsoft Office\Root\",
@"\microsoft shared\ClickToRun",
@"\Microsoft Office\PackageManifests",
@"\Microsoft Office\PackageSunrisePolicies",
@"Microsoft Office 15",
@"Microsoft Office 16"
};These patterns are used by OfficeScrubOrchestrator.IsC2RPath() to validate file paths and identify C2R installations.
Sources: OfficeScrubC2R-Native.cs:36-45
The OFFICE_PROCESSES array defines process names that must be terminated during cleanup:
public static readonly string[] OFFICE_PROCESSES = new[]
{
"appvshnotify.exe", "integratedoffice.exe", "integrator.exe",
"firstrun.exe", "communicator.exe", "msosync.exe", "OneNoteM.exe",
"iexplore.exe", "mavinject32.exe", "werfault.exe", "perfboost.exe",
"roamingoffice.exe", "officeclicktorun.exe", "officeondemand.exe",
"OfficeC2RClient.exe", "winword.exe", "excel.exe", "powerpnt.exe",
"outlook.exe", "onenote.exe", "mspub.exe", "msaccess.exe",
"lync.exe", "skype.exe", "teams.exe"
};Sources: OfficeScrubC2R-Native.cs:47-55
Defines Windows Registry hive identifiers using unchecked integer casting to handle unsigned values:
public enum RegistryHiveType
{
ClassesRoot = unchecked((int)0x80000000),
CurrentUser = unchecked((int)0x80000001),
LocalMachine = unchecked((int)0x80000002),
Users = unchecked((int)0x80000003)
}These values correspond to the Win32 Registry API HKEY constants. The unchecked keyword is required because the hexadecimal values exceed int.MaxValue.
Usage: All RegistryHelper methods accept RegistryHiveType parameters to specify the target registry hive.
Sources: OfficeScrubC2R-Native.cs:21-27
The NativeMethods class defines Win32 API constants for P/Invoke operations:
| Constant | Value | Purpose |
|---|---|---|
KEY_WOW64_64KEY |
0x0100 |
Access 64-bit registry view on WOW64 |
KEY_WOW64_32KEY |
0x0200 |
Access 32-bit registry view on WOW64 |
KEY_READ |
0x20019 |
Read access to registry keys |
KEY_WRITE |
0x20006 |
Write access to registry keys |
KEY_ALL_ACCESS |
0xF003F |
Full access to registry keys |
| Constant | Value | Purpose |
|---|---|---|
MOVEFILE_DELAY_UNTIL_REBOOT |
0x4 |
Schedule file deletion on next reboot |
| Constant | Value | Purpose |
|---|---|---|
ERROR_SUCCESS |
0 |
Operation completed successfully |
ERROR_NO_MORE_ITEMS |
259 |
Registry enumeration complete |
Sources: OfficeScrubC2R-Native.cs:62-153
graph TB
Orch["OfficeScrubOrchestrator"]
RH["RegistryHelper"]
FH["FileHelper"]
PH["ProcessHelper"]
SH["ShellHelper"]
WIH["WindowsInstallerHelper"]
TLH["TypeLibHelper"]
LH["LicenseHelper"]
SVH["ServiceHelper"]
GH["GuidHelper"]
Orch -->|"owns"| RH
Orch -->|"owns"| FH
Orch -->|"owns"| PH
Orch -->|"owns"| SH
Orch -->|"owns"| WIH
Orch -->|"owns"| TLH
Orch -->|"owns"| LH
Orch -->|"owns"| SVH
WIH -->|"uses"| RH
WIH -->|"uses"| GH
TLH -->|"uses"| RH
style Orch fill:#f9f9f9
style GH fill:#e9e9e9
Sources: OfficeScrubC2R-Native.cs:1313-1349
Provides GUID transformation utilities for Windows Installer metadata operations.
public static string GetExpandedGuid(string compressedGuid)Parameters:
-
compressedGuid: 32-character compressed GUID (no dashes, byte-order reversed)
Returns: 38-character standard GUID format {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} or null if invalid
Purpose: Converts Windows Installer compressed GUIDs to standard format. Reverses byte order in first three segments and swaps byte pairs in remaining segments.
Example:
- Input:
D0DF45B14A8C7E47A9AAAAB346B3DBF0 - Output:
{D0498E0A-45B7-42AE-A9AA-ABA463DBD3BF}
Sources: OfficeScrubC2R-Native.cs:173-214
public static string GetCompressedGuid(string expandedGuid)Parameters:
-
expandedGuid: 38-character standard GUID format with braces and dashes
Returns: 32-character compressed GUID or null if invalid
Purpose: Inverse operation of GetExpandedGuid. Converts standard GUIDs to compressed format for Windows Installer registry lookups.
Sources: OfficeScrubC2R-Native.cs:216-253
public static bool GetDecodedGuid(string encodedGuid, out string decodedGuid)Parameters:
-
encodedGuid: 20-character Base85-encoded GUID -
decodedGuid: (out) Decoded standard GUID format
Returns: true if decoding successful, false otherwise
Purpose: Decodes Base85 (ASCII85) encoded GUIDs found in published component registry data. Uses a 128-byte decode table for character-to-value mapping.
Algorithm: Processes input in 5-character chunks, each representing 4 bytes. Each chunk is decoded using base-85 arithmetic: value = Σ(char[i] × 85^i) for i=0 to 4.
Sources: OfficeScrubC2R-Native.cs:255-320
Provides registry operations with automatic WOW64 (32-bit/64-bit) handling.
public RegistryHelper(bool is64Bit)Parameters:
-
is64Bit: System architecture flag (determines WOW64 behavior)
public bool KeyExists(RegistryHiveType hive, string subKey)Behavior: On 64-bit systems, checks both native 64-bit view and WOW64 32-bit view.
Sources: OfficeScrubC2R-Native.cs:343-347
public bool DeleteKey(RegistryHiveType hive, string subKey, bool recursive = true)Parameters:
-
hive: Target registry hive -
subKey: Registry key path -
recursive: Iftrue, deletes all subkeys recursively
Returns: true if any deletion occurred (native or WOW64 view)
Behavior: On 64-bit systems, attempts deletion in both views. Uses DeleteSubKeyTree() for recursive deletion.
Sources: OfficeScrubC2R-Native.cs:349-364
public bool DeleteValue(RegistryHiveType hive, string subKey, string valueName)Purpose: Deletes a named value from a registry key. Handles both registry views on 64-bit systems.
Sources: OfficeScrubC2R-Native.cs:366-400
public string[] EnumerateKeys(RegistryHiveType hive, string subKey)Returns: Distinct subkey names from both registry views (case-insensitive)
Implementation: Uses HashSet<string> with StringComparer.OrdinalIgnoreCase to deduplicate results from both views.
Sources: OfficeScrubC2R-Native.cs:402-436
public string[] EnumerateValues(RegistryHiveType hive, string subKey)Returns: Distinct value names from both registry views
Sources: OfficeScrubC2R-Native.cs:438-472
public object GetValue(RegistryHiveType hive, string subKey, string valueName,
object defaultValue = null)Behavior: Attempts native view first, then WOW64 view on 64-bit systems.
Sources: OfficeScrubC2R-Native.cs:474-502
public bool SetValue(RegistryHiveType hive, string subKey, string valueName,
object value, RegistryValueKind kind)Behavior: Creates key if it doesn't exist. Only writes to native view.
Sources: OfficeScrubC2R-Native.cs:504-521
public void AddPendingFileRenameOperation(string filePath)Purpose: Adds entry to HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\PendingFileRenameOperations for deletion on reboot.
Format: Adds two entries: \\??\{filePath} followed by empty string.
Sources: OfficeScrubC2R-Native.cs:595-615
The private GetWow64Key() method transforms registry paths for 32-bit view access:
| Input Pattern | Transformation | Output Pattern |
|---|---|---|
Software\Classes\X |
Insert after Classes\
|
Software\Classes\Wow6432Node\X |
Software\X |
Insert after first \
|
Software\Wow6432Node\X |
X |
Prepend | Wow6432Node\X |
Sources: OfficeScrubC2R-Native.cs:583-593
Provides file system operations with reboot scheduling for locked files.
public bool DeleteFile(string filePath, bool scheduleOnFail = true)Behavior:
- Returns
trueif file doesn't exist - Removes
ReadOnlyattribute if present - Attempts
File.Delete() - On failure, schedules deletion via
MoveFileExifscheduleOnFailistrue
Sources: OfficeScrubC2R-Native.cs:626-651
public bool DeleteDirectory(string directoryPath, bool recursive = true,
bool scheduleOnFail = true)Performance Optimization: First attempts deletion using cmd.exe /c rd /s /q for 15x performance improvement on large directories. Falls back to Directory.Delete() if command fails.
Reboot Scheduling: If deletion fails and scheduleOnFail is true, schedules all files first, then directories in reverse depth order (deepest first).
Sources: OfficeScrubC2R-Native.cs:653-687
public void ScheduleDeleteOnReboot(string filePath)Implementation: Calls MoveFileEx(filePath, null, MOVEFILE_DELAY_UNTIL_REBOOT) and tracks in _pendingDeletes list.
Sources: OfficeScrubC2R-Native.cs:689-697
public void ScheduleDirectoryDeleteOnReboot(string directoryPath)Algorithm:
- Enumerate all files recursively and schedule each
- Enumerate directories and sort by length descending
- Schedule directories in reverse depth order
- Schedule root directory last
Rationale: Windows processes PendingFileRenameOperations sequentially, so parent directories must be scheduled after their contents.
Sources: OfficeScrubC2R-Native.cs:699-727
public List<string> GetPendingDeletes()Returns: Copy of internal _pendingDeletes list for reporting purposes.
Sources: OfficeScrubC2R-Native.cs:729-732
public bool IsFileLocked(string filePath)Implementation: Attempts to open file with FileShare.None. Returns true if IOException is thrown.
Sources: OfficeScrubC2R-Native.cs:750-766
Provides process management with parallel termination capabilities.
public List<int> TerminateProcesses(string[] processNames, int timeoutMs = 5000)Parameters:
-
processNames: Array of process names (with or without .exe extension) -
timeoutMs: Timeout per process forWaitForExit()
Returns: List of successfully terminated process IDs
Algorithm:
- Enumerate all matching processes
- Create
Task.Run()for each process - Each task: check
!HasExited, callKill(), wait for exit with timeout - Use
lock(terminatedPids)for thread-safe list updates - Call
Task.WaitAll(tasks, timeoutMs * 2)to wait for all tasks
Performance: Parallel execution provides significant speedup when terminating multiple Office applications simultaneously.
Sources: OfficeScrubC2R-Native.cs:775-814
public List<string> GetProcessesUsingPath(string path)Purpose: Identifies processes with executable paths under specified directory.
Implementation: Enumerates all processes, checks MainModule.FileName with case-insensitive prefix match.
Sources: OfficeScrubC2R-Native.cs:816-842
public bool IsProcessRunning(string processName)Returns: true if any instance of the process is running
Sources: OfficeScrubC2R-Native.cs:844-857
Provides cleanup of Windows Installer metadata for Office products.
public WindowsInstallerHelper(RegistryHelper regHelper)Dependency: Requires RegistryHelper for registry operations and WOW64 support.
public void CleanupUpgradeCodes(Func<string, bool> shouldDelete)Purpose: Removes upgrade code entries from HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UpgradeCodes
Algorithm:
- Enumerate 32-character subkeys
- Expand each to standard GUID format using
GuidHelper.GetExpandedGuid() - Pass to
shouldDeletecallback (typicallyOfficeScrubOrchestrator.IsInScope()) - Delete if callback returns
true
Sources: OfficeScrubC2R-Native.cs:989-1011
public void CleanupProducts(Func<string, bool> shouldDelete)Registry Paths:
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\ProductsHKCR\Installer\Products
Sources: OfficeScrubC2R-Native.cs:1013-1039
public void CleanupComponents(Func<string, bool> shouldDelete)Purpose: Removes component value entries where the value name is an Office product GUID.
Registry Path: HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components
Algorithm: Enumerates component keys, then enumerates values within each. Deletes value if its name (32-char compressed GUID) expands to an in-scope product.
Sources: OfficeScrubC2R-Native.cs:1041-1067
public void CleanupPublishedComponents(Func<string, bool> shouldDelete)Purpose: Cleans Base85-encoded GUIDs from multi-string registry values.
Registry Path: HKCR\Installer\Components
Algorithm:
- Enumerate component keys and their values
- Read
REG_MULTI_SZdata - For each string in multi-string data:
- Extract first 20 characters (Base85-encoded GUID)
- Decode using
GuidHelper.GetDecodedGuid() - Test with
shouldDeletecallback
- If modifications made, update or delete value
Sources: OfficeScrubC2R-Native.cs:1069-1094
Manages COM type library registry cleanup.
The class maintains a static array of 73 known Office type library GUIDs:
private static readonly string[] KnownTypeLibs = new[]
{
"{000204EF-0000-0000-C000-000000000046}", // Office Object Library
"{00020813-0000-0000-C000-000000000046}", // Excel Object Library
// ... 71 more entries
};Sources: OfficeScrubC2R-Native.cs:1106-1118
public void CleanupKnownTypeLibs()Registry Path: HKLM\Software\Classes\TypeLib\{GUID}
Algorithm:
- For each known type library GUID:
- Enumerate version subkeys
- For each version, check platform-specific paths:
{version}\0\Win32{version}\9\Win32{version}\0\Win64{version}\9\Win64
- Read default value (file path)
- Check if file exists
- Delete version key if no file exists
- Delete type library key if all versions removed
File Path Handling: Extracts safe file path by taking substring up to LastIndexOf('.') + 4 to avoid issues with embedded paths in registry data.
Sources: OfficeScrubC2R-Native.cs:1126-1176
Provides Office license cleanup operations.
public void CleanOSPP(int versionNT)Parameters:
-
versionNT: OS version number (> 601 usesSoftwareLicensingProduct, elseOfficeSoftwareProtectionProduct)
Purpose: Removes Office licenses from Software Protection Platform (SPP/OSPP).
WMI Query:
SELECT ProductKeyID FROM SoftwareLicensingProduct
WHERE ApplicationId = '0ff1ce15-a989-479d-af46-f275c6370663'
AND PartialProductKey <> NULL
Action: Calls UninstallProductKey() method on each matching license.
Sources: OfficeScrubC2R-Native.cs:1184-1219
public void ClearVNextLicenseCache(string localAppData)Purpose: Deletes local license cache directories for vNext licensing.
Paths Deleted:
{LocalAppData}\Microsoft\Office\Licenses{LocalAppData}\Microsoft\Office\15.0\Licensing{LocalAppData}\Microsoft\Office\16.0\Licensing
Sources: OfficeScrubC2R-Native.cs:1221-1239
Provides Windows service management operations.
public bool DeleteService(string serviceName)Parameters:
-
serviceName: Service name (supports wildcard suffix via LIKE query)
Algorithm:
-
Primary Method: WMI
- Query:
SELECT * FROM Win32_Service WHERE Name LIKE '{serviceName}%' - Stop service if running:
service.InvokeMethod("StopService", null) - Delete:
service.InvokeMethod("Delete", null)
- Query:
-
Fallback Method:
sc.exe- Command:
sc.exe delete {serviceName} - Returns
trueif exit code is 0
- Command:
Sources: OfficeScrubC2R-Native.cs:1247-1309
Provides Windows Shell integration operations.
public bool UnpinFromTaskbar(string shortcutPath)Purpose: Unpins Office shortcuts from Windows taskbar.
Implementation:
- Uses
Shell.ApplicationCOM object - Enumerates verbs for the shortcut file
- Matches verb names against localized strings for "unpin from taskbar"
- Invokes matching verb
Supported Languages: English, German, French, Spanish, Swedish, Danish, Czech, Dutch, Finnish, Italian
Sources: OfficeScrubC2R-Native.cs:866-907
public bool UnpinFromStartMenu(string shortcutPath)Purpose: Unpins Office shortcuts from Start menu.
Implementation: Similar to UnpinFromTaskbar but matches "unpin from start" verb patterns.
Sources: OfficeScrubC2R-Native.cs:909-948
public void RestartExplorer()Purpose: Terminates and restarts Windows Explorer to release file locks.
Algorithm:
- Get all
explorerprocesses - Kill each with 5-second timeout
- Wait 1 second
- Start new
explorer.exeinstance
Sources: OfficeScrubC2R-Native.cs:950-973
Central coordinator class that orchestrates all helper classes and provides high-level cleanup logic.
public RegistryHelper Registry { get; private set; }
public FileHelper Files { get; private set; }
public ProcessHelper Processes { get; private set; }
public ShellHelper Shell { get; private set; }
public WindowsInstallerHelper WindowsInstaller { get; private set; }
public TypeLibHelper TypeLib { get; private set; }
public LicenseHelper License { get; private set; }
public ServiceHelper Services { get; private set; }Sources: OfficeScrubC2R-Native.cs:1330-1337
public OfficeScrubOrchestrator(bool is64Bit)Initialization: Instantiates all helper classes. RegistryHelper is initialized with is64Bit flag and passed to helpers that need registry access.
Sources: OfficeScrubC2R-Native.cs:1339-1349
public bool IsC2RPath(string path)Purpose: Determines if a file path belongs to a Click-to-Run installation.
Implementation: Returns true if path contains any pattern from OfficeConstants.C2R_PATTERNS (case-insensitive).
Sources: OfficeScrubC2R-Native.cs:1352-1359
public bool IsInScope(string productCode)Purpose: Determines if a product GUID is within removal scope.
Validation Steps:
- Check length is exactly 38 characters
- Check if in special products set (3 hard-coded GUIDs)
- Check if ends with
OfficeConstants.OFFICE_ID - Parse version from positions 3-4, reject if ≤ 14
- Extract SKU from positions 10-13, check if in valid SKUs set
Special Products:
{6C1ADE97-24E1-4AE4-AEDD-86D3A209CE60}{9520DDEB-237A-41DB-AA20-F2EF2360DCEB}{9AC08E99-230B-47E8-9721-4577B7F124EA}
Valid SKUs: 007E, 008F, 008C, 24E1, 237A, 00DD
Sources: OfficeScrubC2R-Native.cs:1361-1382, OfficeScrubC2R-Native.cs:1318-1328
| Registry Path | Purpose | Data Format |
|---|---|---|
HKLM\SOFTWARE\Microsoft\Office\15.0\ClickToRun |
Office 2013 C2R configuration | Various |
HKLM\SOFTWARE\Microsoft\Office\16.0\ClickToRun |
Office 2016/2019 C2R configuration | Various |
HKLM\SOFTWARE\Microsoft\Office\ClickToRun |
Modern Office C2R configuration | Various |
HKLM\SOFTWARE\Microsoft\Office\15.0\ClickToRun\Configuration |
O15 product configuration | REG_SZ |
HKLM\SOFTWARE\Microsoft\Office\16.0\ClickToRun\Configuration |
O16 product configuration | REG_SZ |
HKLM\SOFTWARE\Microsoft\Office\ClickToRun\Configuration |
Modern product configuration | REG_SZ |
Key Values:
-
PackageFolder(REG_SZ): Installation directory path -
PackageGUID(REG_SZ): Package identifier GUID -
ProductReleaseIds(REG_SZ): Comma-separated list of installed products
Sources: direct_conversion.cs:881-888
| Registry Path | Purpose |
|---|---|
HKLM\SOFTWARE\Microsoft\Office\15.0\ClickToRun\ProductReleaseIDs\Active\culture |
O15 product versions by culture |
HKLM\SOFTWARE\Microsoft\Office\16.0\ClickToRun\ProductReleaseIDs\Active\culture |
O16 product versions by culture |
HKLM\SOFTWARE\Microsoft\Office\ClickToRun\ProductReleaseIDs |
Modern product release tracking |
HKLM\SOFTWARE\Microsoft\Office\ClickToRun\ProductReleaseIDs\{ActiveConfiguration} |
Active configuration products |
Values: Version strings (e.g., "16.0.12345.67890")
Sources: direct_conversion.cs:883-888
graph LR
subgraph "HKLM Paths"
UC["UpgradeCodes<br/>SOFTWARE\Microsoft\Windows\<br/>CurrentVersion\Installer\<br/>UpgradeCodes"]
PROD["Products<br/>SOFTWARE\Microsoft\Windows\<br/>CurrentVersion\Installer\<br/>UserData\S-1-5-18\Products"]
COMP["Components<br/>SOFTWARE\Microsoft\Windows\<br/>CurrentVersion\Installer\<br/>UserData\S-1-5-18\Components"]
end
subgraph "HKCR Paths"
IPROD["Installer\Products"]
ICOMP["Installer\Components"]
IFEAT["Installer\Features"]
end
UC -->|"32-char compressed GUID keys"| PROD
PROD -->|"32-char compressed GUID keys"| COMP
IPROD -.->|"duplicate metadata"| PROD
ICOMP -->|"20-char Base85 values"| COMP
Sources: OfficeScrubC2R-Native.cs:989-1094
| Registry Path | Purpose | Notes |
|---|---|---|
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall |
64-bit application registry | Native view |
HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall |
32-bit application registry | WOW64 view |
Key Values:
-
DisplayName(REG_SZ): Product display name -
DisplayVersion(REG_SZ): Version string -
UninstallString(REG_SZ): Uninstall command -
SystemComponent(REG_DWORD): If 1, hidden from UI
Sources: direct_conversion.cs:881-882
| Registry Path | Purpose |
|---|---|
HKLM\SOFTWARE\Classes\Protocols\Handler\osf |
Office protocol handler |
HKLM\SOFTWARE\Classes\CLSID\{...} |
Context menu handlers |
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers |
Icon overlays |
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Browser Helper Objects |
Browser extensions |
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved |
Approved extensions |
Sources: direct_conversion.cs:2336-2376
| Registry Path | Purpose | Format |
|---|---|---|
HKLM\Software\Classes\TypeLib\{GUID} |
Type library registration root | GUID key |
HKLM\Software\Classes\TypeLib\{GUID}\{version} |
Version-specific registration | Version string |
HKLM\Software\Classes\TypeLib\{GUID}\{version}\0\Win32 |
32-bit neutral path | File path |
HKLM\Software\Classes\TypeLib\{GUID}\{version}\9\Win32 |
32-bit locale path | File path |
HKLM\Software\Classes\TypeLib\{GUID}\{version}\0\Win64 |
64-bit neutral path | File path |
HKLM\Software\Classes\TypeLib\{GUID}\{version}\9\Win64 |
64-bit locale path | File path |
Sources: OfficeScrubC2R-Native.cs:1126-1176
| Registry Path | Value Name | Type | Purpose |
|---|---|---|---|
HKLM\SYSTEM\CurrentControlSet\Control\Session Manager |
PendingFileRenameOperations |
REG_MULTI_SZ | Files to delete/rename on reboot |
Format: Pairs of strings where first is source path prefixed with \\??\ and second is empty string for deletion.
Sources: OfficeScrubC2R-Native.cs:595-615
Windows Installer uses three GUID encodings for metadata storage:
| Format | Length | Example | Used In |
|---|---|---|---|
| Expanded | 38 chars | {D0498E0A-45B7-42AE-A9AA-ABA463DBD3BF} |
Standard representation |
| Compressed | 32 chars | D0DF45B14A8C7E47A9AAAAB346B3DBF0 |
Registry key names |
| Base85 | 20 chars | "_@5%G'!O75'G4@5%" |
Multi-string values |
Sources: OfficeScrubC2R-Native.cs:29-34
graph TB
EXP["Expanded GUID<br/>{D0498E0A-45B7-42AE-A9AA-ABA463DBD3BF}"]
COMP["Compressed GUID<br/>D0DF45B14A8C7E47A9AAAAB346B3DBF0"]
B85["Base85 Encoded<br/>20-char ASCII string"]
EXP -->|"GetCompressedGuid()<br/>Remove braces/dashes<br/>Reverse byte order"| COMP
COMP -->|"GetExpandedGuid()<br/>Add braces/dashes<br/>Reverse byte order"| EXP
B85 -->|"GetDecodedGuid()<br/>Base85 decode<br/>Reorder segments"| EXP
subgraph "Compression Algorithm"
direction LR
S1["Segment 1: 8 chars"]
S2["Segment 2: 4 chars"]
S3["Segment 3: 4 chars"]
S4["Segment 4: 4 chars"]
S5["Segment 5: 12 chars"]
S1 -.->|"Reverse"| S1
S2 -.->|"Reverse"| S2
S3 -.->|"Reverse"| S3
S4 -.->|"Swap pairs"| S4
S5 -.->|"Swap pairs"| S5
end
subgraph "Base85 Decoding"
direction LR
C1["5 chars"]
C2["5 chars"]
C3["5 chars"]
C4["5 chars"]
C1 -.->|"Σ(char[i] × 85^i)"| B1["4 bytes"]
C2 -.->|"Σ(char[i] × 85^i)"| B2["4 bytes"]
C3 -.->|"Σ(char[i] × 85^i)"| B3["4 bytes"]
C4 -.->|"Σ(char[i] × 85^i)"| B4["4 bytes"]
end
Sources: OfficeScrubC2R-Native.cs:169-328
Input: {D0498E0A-45B7-42AE-A9AA-ABA463DBD3BF}
Step 1: Remove braces and dashes
- Result:
D0498E0A45B742AEA9AAABA463DBD3BF
Step 2: Process segments
- First 8 chars: Reverse →
A0E89804D - Next 4 chars: Reverse →
7B54 - Next 4 chars: Reverse →
EA24 - Next 4 chars: Swap pairs →
A9AA→9AAA - Last 12 chars: Swap pairs →
ABA463DBD3BF→BA4A36BD3DBF
Step 3: Concatenate
- Result:
D0DF45B14A8C7E47A9AAAAB346B3DBF0
Sources: OfficeScrubC2R-Native.cs:216-253
The decode table maps ASCII characters (32-127) to Base85 values (0-84):
| ASCII Range | Mapping |
|---|---|
33 (!) |
0x00 |
34-44 (" to ,) |
0x01-0x0B |
45 (-) |
Invalid (0xFF) |
46-57 (. to 9) |
0x0B-0x15 |
58-61 (: to =) |
Invalid (0xFF) |
62 (>) |
0x16 |
63 (?) |
0x17 |
64-90 (@ to Z) |
0x18-0x32 |
91 ([) |
Invalid (0xFF) |
92-94 (\ to ^) |
0x34-0x36 |
95-122 (_ to z) |
0x37-0x52 |
123 ({) |
Invalid (0xFF) |
| 124-126 (` |
to~`) |
Invalid characters (marked 0xFF) will cause GetDecodedGuid() to return false.
Sources: OfficeScrubC2R-Native.cs:264-274
The original VBScript implementation defined these error codes (documented for reference, not used in C# version):
| Constant | Value | Description |
|---|---|---|
ERROR_SUCCESS |
0 | Operation completed successfully |
ERROR_FAIL |
1 | General failure |
ERROR_REBOOT_REQUIRED |
2 | System reboot required |
ERROR_USERCANCEL |
4 | User canceled operation |
ERROR_STAGE1 |
8 | Stage 1 (Uninstall) failed |
ERROR_STAGE2 |
16 | Stage 2 (Cleanup) failed |
ERROR_INCOMPLETE |
32 | Removal incomplete |
ERROR_DCAF_FAILURE |
64 | Second cleanup attempt failed |
ERROR_ELEVATION_USERDECLINED |
128 | User declined elevation prompt |
ERROR_ELEVATION |
256 | Elevation failed |
ERROR_SCRIPTINIT |
512 | Initialization error |
ERROR_RELAUNCH |
1024 | Relaunch attempt failed |
ERROR_UNKNOWN |
2048 | Unknown error |
ERROR_ALL |
4095 | All error flags set |
ERROR_USER_ABORT |
-1073741510 | Process terminated |
ERROR_SUCCESS_CONFIG_COMPLETE |
1728 | MSI config complete |
ERROR_SUCCESS_REBOOT_REQUIRED |
3010 | MSI success, reboot required |
Note: These are bitwise flags that can be combined using OR operations.
Sources: direct_conversion.cs:45-61
Used in P/Invoke operations:
| Code | Value | Meaning |
|---|---|---|
ERROR_SUCCESS |
0 | Success |
ERROR_NO_MORE_ITEMS |
259 | Registry enumeration complete |
Sources: OfficeScrubC2R-Native.cs:151-152
All P/Invoke declarations are contained in the internal static NativeMethods class.
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool MoveFileEx(
string lpExistingFileName,
string lpNewFileName,
int dwFlags);Purpose: Schedule file deletion on reboot when lpNewFileName is null and dwFlags is MOVEFILE_DELAY_UNTIL_REBOOT.
Sources: OfficeScrubC2R-Native.cs:64-68
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool SetFileAttributes(
string lpFileName,
uint dwFileAttributes);Purpose: Modify file attributes (used to remove ReadOnly before deletion).
Sources: OfficeScrubC2R-Native.cs:138-140
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool RemoveDirectory(string lpPathName);Purpose: Alternative directory deletion method (not currently used).
Sources: OfficeScrubC2R-Native.cs:143
[DllImport("advapi32.dll", SetLastError = true)]
internal static extern int RegOpenKeyEx(
UIntPtr hKey,
string lpSubKey,
uint ulOptions,
int samDesired,
out UIntPtr phkResult);Purpose: Open registry key with specific access rights and view flags.
Sources: OfficeScrubC2R-Native.cs:70-76
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern int RegDeleteKeyEx(
UIntPtr hKey,
string lpSubKey,
int samDesired,
uint Reserved);Purpose: Delete registry key with specific view flag (KEY_WOW64_64KEY or KEY_WOW64_32KEY).
Sources: OfficeScrubC2R-Native.cs:82-86
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern int RegEnumKeyEx(
UIntPtr hKey,
uint dwIndex,
StringBuilder lpName,
ref uint lpcchName,
IntPtr lpReserved,
IntPtr lpClass,
IntPtr lpcchClass,
IntPtr lpftLastWriteTime);Purpose: Enumerate subkeys of a registry key.
Sources: OfficeScrubC2R-Native.cs:94-102
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern int RegEnumValue(
UIntPtr hKey,
uint dwIndex,
StringBuilder lpValueName,
ref uint lpcchValueName,
IntPtr lpReserved,
IntPtr lpType,
IntPtr lpData,
IntPtr lpcbData);Purpose: Enumerate values within a registry key.
Sources: OfficeScrubC2R-Native.cs:105-113
[DllImport("advapi32.dll", SetLastError = true)]
internal static extern int RegQueryValueEx(
UIntPtr hKey,
string lpValueName,
IntPtr lpReserved,
out uint lpType,
IntPtr lpData,
ref uint lpcbData);Purpose: Read registry value data.
Sources: OfficeScrubC2R-Native.cs:116-122
[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
internal static extern IntPtr ILCreateFromPath(string pszPath);Purpose: Create item identifier list from file path.
Sources: OfficeScrubC2R-Native.cs:125
[DllImport("shell32.dll")]
internal static extern void ILFree(IntPtr pidl);Purpose: Free item identifier list memory.
Sources: OfficeScrubC2R-Native.cs:128
[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
internal static extern int SHCreateShellItem(
IntPtr pidlParent,
IntPtr psfParent,
IntPtr pidl,
out IShellItem ppsi);Purpose: Create shell item from item identifier list.
Sources: OfficeScrubC2R-Native.cs:131-135
[ComImport]
[Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IShellItem
{
void BindToHandler(IntPtr pbc, [MarshalAs(UnmanagedType.LPStruct)] Guid bhid,
[MarshalAs(UnmanagedType.LPStruct)] Guid riid, out IntPtr ppv);
void GetParent(out IShellItem ppsi);
void GetDisplayName(uint sigdnName, out IntPtr ppszName);
void GetAttributes(uint sfgaoMask, out uint psfgaoAttribs);
void Compare(IShellItem psi, uint hint, out int piOrder);
}Purpose: Shell item interface for file system operations.
Sources: OfficeScrubC2R-Native.cs:155-165
The orchestrator uses static HashSet collections for O(1) lookup performance:
private static readonly HashSet<string> ValidSkus = new HashSet<string>
{
"007E", "008F", "008C", "24E1", "237A", "00DD"
};
private static readonly HashSet<string> SpecialProducts = new HashSet<string>
{
"{6C1ADE97-24E1-4AE4-AEDD-86D3A209CE60}",
"{9520DDEB-237A-41DB-AA20-F2EF2360DCEB}",
"{9AC08E99-230B-47E8-9721-4577B7F124EA}"
};Performance: These static collections eliminate repeated array traversal in IsInScope() validation logic.
Sources: OfficeScrubC2R-Native.cs:1318-1328
private readonly List<string> _pendingDeletes = new List<string>();Tracks files scheduled for deletion on reboot via PendingFileRenameOperations.
Sources: OfficeScrubC2R-Native.cs:624
lock (terminatedPids)
{
terminatedPids.Add(pid);
}Thread-safe list updated from parallel tasks during process termination.
Sources: OfficeScrubC2R-Native.cs:796-799
| Method | Input | Output | Complexity |
|---|---|---|---|
GetExpandedGuid |
32-char string | 38-char GUID or null | O(1) |
GetCompressedGuid |
38-char GUID | 32-char string or null | O(1) |
GetDecodedGuid |
20-char Base85 | bool + out GUID | O(1) |
| Method | WOW64 Aware | Returns | Purpose |
|---|---|---|---|
KeyExists |
Yes | bool | Check key existence |
DeleteKey |
Yes | bool | Delete key and subkeys |
DeleteValue |
Yes | bool | Delete named value |
EnumerateKeys |
Yes | string[] | List subkeys (merged) |
EnumerateValues |
Yes | string[] | List values (merged) |
GetValue |
Yes | object | Read value data |
SetValue |
No | bool | Write value data |
| Method | Schedules on Fail | Returns | Purpose |
|---|---|---|---|
DeleteFile |
Optional | bool | Delete single file |
DeleteDirectory |
Optional | bool | Delete directory tree |
ScheduleDeleteOnReboot |
N/A | void | Schedule file deletion |
ScheduleDirectoryDeleteOnReboot |
N/A | void | Schedule dir deletion |
GetPendingDeletes |
N/A | List | Get scheduled items |
IsFileLocked |
N/A | bool | Check file lock status |
| Method | Parallel | Returns | Purpose |
|---|---|---|---|
TerminateProcesses |
Yes | List | Kill processes by name |
GetProcessesUsingPath |
No | List | Find processes in path |
IsProcessRunning |
No | bool | Check if process exists |
Sources: OfficeScrubC2R-Native.cs:171-1382
This diagram maps high-level system concepts to specific code entities for easy code navigation:
graph TB
subgraph "Natural Language Concepts"
C2R["Click-to-Run Detection"]
GUID_OPS["GUID Transformation"]
REG_CLEAN["Registry Cleanup"]
FILE_CLEAN["File Cleanup"]
PROC_TERM["Process Termination"]
MSI_META["MSI Metadata Cleanup"]
end
subgraph "Code Entities"
C2R_PATH["OfficeScrubOrchestrator.IsC2RPath()"]
C2R_CONST["OfficeConstants.C2R_PATTERNS"]
GUID_EXP["GuidHelper.GetExpandedGuid()"]
GUID_COMP["GuidHelper.GetCompressedGuid()"]
GUID_DEC["GuidHelper.GetDecodedGuid()"]
REG_DEL["RegistryHelper.DeleteKey()"]
REG_ENUM["RegistryHelper.EnumerateKeys()"]
REG_WOW["RegistryHelper.GetWow64Key()"]
FILE_DEL["FileHelper.DeleteDirectory()"]
FILE_SCHED["FileHelper.ScheduleDeleteOnReboot()"]
FILE_PEND["RegistryHelper.AddPendingFileRenameOperation()"]
PROC_KILL["ProcessHelper.TerminateProcesses()"]
PROC_LIST["OfficeConstants.OFFICE_PROCESSES"]
MSI_UPG["WindowsInstallerHelper.CleanupUpgradeCodes()"]
MSI_PROD["WindowsInstallerHelper.CleanupProducts()"]
MSI_COMP["WindowsInstallerHelper.CleanupComponents()"]
MSI_PUB["WindowsInstallerHelper.CleanupPublishedComponents()"]
end
C2R --> C2R_PATH
C2R --> C2R_CONST
GUID_OPS --> GUID_EXP
GUID_OPS --> GUID_COMP
GUID_OPS --> GUID_DEC
REG_CLEAN --> REG_DEL
REG_CLEAN --> REG_ENUM
REG_CLEAN --> REG_WOW
FILE_CLEAN --> FILE_DEL
FILE_CLEAN --> FILE_SCHED
FILE_CLEAN --> FILE_PEND
PROC_TERM --> PROC_KILL
PROC_TERM --> PROC_LIST
MSI_META --> MSI_UPG
MSI_META --> MSI_PROD
MSI_META --> MSI_COMP
MSI_META --> MSI_PUB
Sources: OfficeScrubC2R-Native.cs:1-1915
This diagram shows the code path for registry operations with WOW64 support:
graph TB
CALL["Client Code:<br/>regHelper.DeleteKey(HKLM, 'SOFTWARE\X', true)"]
DELETE["RegistryHelper.DeleteKey()"]
CHECK64{"_is64Bit == true?"}
NATIVE_EXISTS["KeyExists(hive, subKey)"]
NATIVE_DELETE["DeleteKeyInternal(hive, subKey, recursive)"]
WOW_TRANSFORM["GetWow64Key(subKey)<br/>Returns: 'SOFTWARE\Wow6432Node\X'"]
WOW_EXISTS["KeyExists(hive, wow64Key)"]
WOW_DELETE["DeleteKeyInternal(hive, wow64Key, recursive)"]
OPEN_NATIVE["GetHiveKey(hive).OpenSubKey(subKey, false)"]
OPEN_WOW["GetHiveKey(hive).OpenSubKey(wow64Key, false)"]
DEL_NATIVE["GetHiveKey(hive).DeleteSubKeyTree(subKey, false)"]
DEL_WOW["GetHiveKey(hive).DeleteSubKeyTree(wow64Key, false)"]
RESULT["Return: (native || wow64) result"]
CALL --> DELETE
DELETE --> CHECK64
CHECK64 -->|"Yes"| NATIVE_EXISTS
CHECK64 -->|"No"| NATIVE_EXISTS
NATIVE_EXISTS -->|"Exists"| NATIVE_DELETE
NATIVE_DELETE --> OPEN_NATIVE
OPEN_NATIVE --> DEL_NATIVE
DEL_NATIVE --> CHECK64_POST{"_is64Bit?"}
CHECK64_POST -->|"Yes"| WOW_TRANSFORM
CHECK64_POST -->|"No"| RESULT
WOW_TRANSFORM --> WOW_EXISTS
WOW_EXISTS -->|"Exists"| WOW_DELETE
WOW_EXISTS -->|"Not Exists"| RESULT
WOW_DELETE --> OPEN_WOW
OPEN_WOW --> DEL_WOW
DEL_WOW --> RESULT
Sources: OfficeScrubC2R-Native.cs:349-593
This reference documentation provides comprehensive coverage of all public APIs, constants, data structures, and code entities within the OfficeScrubC2R system. For usage examples and architectural patterns, refer to the related wiki pages listed at the beginning of this document.