Skip to content

Constants and Error Codes

Calvindd2f edited this page Jan 14, 2026 · 1 revision

Constants and Error Codes

Relevant source files

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

This page documents all constants, error codes, return values, and exit codes used throughout OfficeScrubC2R. The system defines constants in multiple locations: the native C# library (OfficeConstants class), Win32 API P/Invoke declarations (NativeMethods class), and the original VBScript conversion reference (Constants class).

For information about the three-stage pipeline that generates these errors, see Three-Stage Execution Pipeline. For troubleshooting guidance based on error codes, see Troubleshooting.


Overview

OfficeScrubC2R uses three categories of constants:

  1. Office Detection Constants - Product identification strings, GUID patterns, process names, and path patterns defined in OfficeConstants
  2. Win32 API Constants - Registry access flags, file operation flags, and Windows error codes defined in NativeMethods
  3. Error Codes - Bitmask-based error tracking system inherited from the original VBScript implementation

Each constant category serves a specific purpose in the cleanup pipeline. Office detection constants identify what to remove, Win32 API constants control how native operations are performed, and error codes track the success or failure of each stage.

Sources: OfficeScrubC2R-Native.cs:29-56, OfficeScrubC2R-Native.cs:62-153, direct_conversion.cs:27-62


OfficeConstants Class

The OfficeConstants static class defines Office-specific identification constants used throughout the cleanup process. These constants are used by detection logic, path validation, and process termination.

classDiagram
    class OfficeConstants {
        +const string OFFICE_ID
        +const int PROD_LEN
        +const int SQUISHED
        +const int COMPRESSED
        +static readonly string[] C2R_PATTERNS
        +static readonly string[] OFFICE_PROCESSES
    }
    
    class OfficeScrubOrchestrator {
        +bool IsC2RPath(string path)
        +bool IsInScope(string productCode)
    }
    
    class ProcessHelper {
        +List~int~ TerminateProcesses(string[] processNames)
    }
    
    class GuidHelper {
        +string GetExpandedGuid(string compressedGuid)
        +string GetCompressedGuid(string expandedGuid)
        +bool GetDecodedGuid(string encodedGuid, out string decodedGuid)
    }
    
    OfficeConstants --> OfficeScrubOrchestrator : "used by IsInScope()"
    OfficeConstants --> OfficeScrubOrchestrator : "used by IsC2RPath()"
    OfficeConstants --> ProcessHelper : "used by TerminateProcesses()"
    OfficeConstants --> GuidHelper : "COMPRESSED constant"
Loading

OfficeConstants Usage Throughout System

Core Constants

Constant Value Type Purpose
OFFICE_ID "{0000000FF1CE}" string Office product GUID suffix - all Office product codes end with this identifier
PROD_LEN 13 int Expected length of product identifier segment in product codes
SQUISHED 20 int Length of Base85-encoded GUID format (20 characters) used in published components
COMPRESSED 32 int Length of compressed GUID format (32 characters) used in Windows Installer metadata

Sources: OfficeScrubC2R-Native.cs:31-34

C2R_PATTERNS Array

Click-to-Run installation path patterns used by IsC2RPath() method to identify C2R installations:

public static readonly string[] C2R_PATTERNS = new[]
{
    @"\ROOT\OFFICE1",                          // Root Office folder pattern
    @"Microsoft Office\Root\",                 // Modern C2R structure
    @"\microsoft shared\ClickToRun",           // ClickToRun service folder
    @"\Microsoft Office\PackageManifests",     // Package manifest location
    @"\Microsoft Office\PackageSunrisePolicies",// Policy configuration
    @"Microsoft Office 15",                    // Office 2013 C2R
    @"Microsoft Office 16"                     // Office 2016+ C2R
};

Any path containing one of these patterns is identified as a Click-to-Run installation path. The comparison is case-insensitive via StringComparison.OrdinalIgnoreCase.

Sources: OfficeScrubC2R-Native.cs:36-45

OFFICE_PROCESSES Array

Known Office executable names for process termination:

public static readonly string[] OFFICE_PROCESSES = new[]
{
    "appvshnotify.exe",      // App-V notification
    "integratedoffice.exe",  // Integration component
    "integrator.exe",        // C2R integrator
    "firstrun.exe",          // First run experience
    "communicator.exe",      // Lync/Skype for Business
    "msosync.exe",           // OneDrive sync
    "OneNoteM.exe",          // OneNote Modern app
    "iexplore.exe",          // Internet Explorer (Office integration)
    "mavinject32.exe",       // App-V injector
    "werfault.exe",          // Windows Error Reporting
    "perfboost.exe",         // Performance optimization
    "roamingoffice.exe",     // Roaming settings
    "officeclicktorun.exe",  // C2R service
    "officeondemand.exe",    // On-demand streaming
    "OfficeC2RClient.exe",   // C2R client
    "winword.exe",           // Microsoft Word
    "excel.exe",             // Microsoft Excel
    "powerpnt.exe",          // Microsoft PowerPoint
    "outlook.exe",           // Microsoft Outlook
    "onenote.exe",           // Microsoft OneNote
    "mspub.exe",             // Microsoft Publisher
    "msaccess.exe",          // Microsoft Access
    "lync.exe",              // Lync
    "skype.exe",             // Skype for Business
    "teams.exe"              // Microsoft Teams
};

The ProcessHelper.TerminateProcesses() method iterates this array and terminates all matching processes in parallel.

Sources: OfficeScrubC2R-Native.cs:47-55


NativeMethods Constants

The NativeMethods internal static class defines Win32 API constants for P/Invoke declarations. These constants control registry access, file operations, and error handling.

graph TB
    subgraph "Registry Operations"
        RegOpen["RegOpenKeyEx()"]
        RegDelete["RegDeleteKeyEx()"]
        RegEnum["RegEnumKeyEx()"]
        
        KEY_WOW64_64KEY["KEY_WOW64_64KEY<br/>0x0100"]
        KEY_WOW64_32KEY["KEY_WOW64_32KEY<br/>0x0200"]
        KEY_READ["KEY_READ<br/>0x20019"]
        KEY_WRITE["KEY_WRITE<br/>0x20006"]
        KEY_ALL_ACCESS["KEY_ALL_ACCESS<br/>0xF003F"]
    end
    
    subgraph "File Operations"
        MoveFileEx["MoveFileEx()"]
        
        MOVEFILE_DELAY_UNTIL_REBOOT["MOVEFILE_DELAY_UNTIL_REBOOT<br/>0x4"]
    end
    
    subgraph "Error Codes"
        ERROR_SUCCESS["ERROR_SUCCESS<br/>0"]
        ERROR_NO_MORE_ITEMS["ERROR_NO_MORE_ITEMS<br/>259"]
    end
    
    RegOpen --> KEY_WOW64_64KEY
    RegOpen --> KEY_WOW64_32KEY
    RegOpen --> KEY_READ
    RegDelete --> KEY_WOW64_64KEY
    RegDelete --> KEY_WOW64_32KEY
    RegEnum --> KEY_READ
    
    MoveFileEx --> MOVEFILE_DELAY_UNTIL_REBOOT
    
    RegOpen --> ERROR_SUCCESS
    RegEnum --> ERROR_NO_MORE_ITEMS
Loading

Win32 API Constants Usage

Registry Access Flags

Constant Hex Value Decimal Purpose
KEY_WOW64_64KEY 0x0100 256 Access 64-bit registry view on 64-bit Windows
KEY_WOW64_32KEY 0x0200 512 Access 32-bit registry view (WOW64 redirected) on 64-bit Windows
KEY_READ 0x20019 131097 Read-only access (STANDARD_RIGHTS_READ | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY)
KEY_WRITE 0x20006 131078 Write access (STANDARD_RIGHTS_WRITE | KEY_SET_VALUE | KEY_CREATE_SUB_KEY)
KEY_ALL_ACCESS 0xF003F 983103 Full access (STANDARD_RIGHTS_ALL | KEY_QUERY_VALUE | KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY | KEY_CREATE_LINK)

These flags are combined with bitwise OR when opening registry keys. For example, KEY_READ | KEY_WOW64_64KEY opens a key for reading from the 64-bit registry view.

Sources: OfficeScrubC2R-Native.cs:145-149

File Operation Flags

Constant Hex Value Decimal Purpose
MOVEFILE_DELAY_UNTIL_REBOOT 0x4 4 Schedules file/directory deletion on next system restart via MoveFileEx()

This flag is used by FileHelper.ScheduleDeleteOnReboot() to delete files that are currently locked by running processes. The file path is added to the PendingFileRenameOperations registry value.

Sources: OfficeScrubC2R-Native.cs:150

Win32 Error Codes

Constant Decimal Value Meaning
ERROR_SUCCESS 0 Operation completed successfully
ERROR_NO_MORE_ITEMS 259 No more registry keys or values to enumerate

These are return values from Win32 API functions, not to be confused with the OfficeScrubC2R error codes documented later on this page.

Sources: OfficeScrubC2R-Native.cs:151-152


GUID Format Constants

GUID transformation operations use constants from OfficeConstants to validate and convert between different GUID encodings.

graph LR
    subgraph "GUID Formats"
        Expanded["Expanded GUID<br/>38 characters<br/>{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}"]
        Compressed["Compressed GUID<br/>32 characters<br/>XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX<br/>(COMPRESSED constant)"]
        Encoded["Encoded GUID<br/>20 characters<br/>Base85 format<br/>(SQUISHED constant)"]
    end
    
    subgraph "Validation"
        CheckLength38["Length == 38?"]
        CheckLength32["Length == 32?<br/>(COMPRESSED)"]
        CheckLength20["Length == 20?<br/>(SQUISHED)"]
    end
    
    Expanded --> CheckLength38
    Compressed --> CheckLength32
    Encoded --> CheckLength20
    
    CheckLength38 --> GetCompressedGuid["GetCompressedGuid()"]
    CheckLength32 --> GetExpandedGuid["GetExpandedGuid()"]
    CheckLength20 --> GetDecodedGuid["GetDecodedGuid()"]
Loading

GUID Format Validation Using Constants

Constant Value Used By Validates
COMPRESSED 32 GetExpandedGuid(), Windows Installer helpers Compressed GUID format (registry key names)
SQUISHED 20 GetDecodedGuid() Base85-encoded GUID format (published components)
N/A (literal) 38 GetCompressedGuid() Standard GUID format with braces and dashes

Sources: OfficeScrubC2R-Native.cs:33-34, OfficeScrubC2R-Native.cs:173-328


Error Code System


The error code system implements a bitmask-based architecture where multiple error conditions can be combined into a single exit code. Each error condition is represented by a unique power of 2, allowing multiple errors to be combined using bitwise OR operations.

The error code system serves three primary purposes:

  1. Precise Failure Tracking - Multiple error conditions can be tracked simultaneously in a single integer value
  2. Automated Error Cascading - Critical errors automatically set the ERROR_FAIL flag
  3. Reboot State Management - The system tracks when system restart is required to complete cleanup

Error Code Values

Error codes are defined in the original VBScript Constants class and maintained for compatibility. The system uses 13 distinct error flags.

Constant Decimal Value Binary Value Hex Value Description
ERROR_SUCCESS 0 0b0000000000000 0x0000 No errors - operation completed successfully
ERROR_FAIL 1 0b0000000000001 0x0001 Generic failure flag - set automatically for critical errors
ERROR_REBOOT_REQUIRED 2 0b0000000000010 0x0002 System restart required to complete uninstall
ERROR_USERCANCEL 4 0b0000000000100 0x0004 User cancelled the operation
ERROR_STAGE1 8 0b0000000001000 0x0008 Stage 1 (Uninstall) failed - msiexec or ODT error
ERROR_STAGE2 16 0b0000000010000 0x0010 Stage 2 (Cleanup) failed - registry or file cleanup error
ERROR_INCOMPLETE 32 0b0000000100000 0x0020 Removal incomplete - rerun after reboot needed
ERROR_DCAF_FAILURE 64 0b0000001000000 0x0040 Second attempt cleanup still incomplete (DCAF = Delete Content And Folders)
ERROR_ELEVATION_USERDECLINED 128 0b0000010000000 0x0080 User declined UAC elevation prompt
ERROR_ELEVATION 256 0b0000100000000 0x0100 Elevation failed - insufficient privileges
ERROR_SCRIPTINIT 512 0b0001000000000 0x0200 Script initialization failed
ERROR_RELAUNCH 1024 0b0010000000000 0x0400 Unhandled error during relaunch attempt
ERROR_UNKNOWN 2048 0b0100000000000 0x0800 Unknown error occurred

Maximum Possible Error Code: 4095 (0xFFF) - all error flags set simultaneously

Sources: direct_conversion.cs:45-61


Bitmask Architecture

The error code system uses bitwise operations to combine multiple error states into a single integer value. This allows the script to track multiple failure conditions simultaneously while returning only a single exit code.

Setting Error Codes

Error codes are set using the bitwise OR operation (-bor in PowerShell), which adds the error flag to the existing error code without clearing other flags.

Example:
  Current ErrorCode: 0  (binary: 0000000000000)
  Set ERROR_REBOOT_REQUIRED (2)
  New ErrorCode: 2  (binary: 0000000000010)
  
  Then set ERROR_STAGE1 (8)
  New ErrorCode: 10  (binary: 0000000001010)  [Both flags are preserved]

Clearing Error Codes

Error codes are cleared using the bitwise AND operation with the NOT of the error flag (-band (-bnot $ErrorBit)), which removes only the specified flag while preserving others.

Example:
  Current ErrorCode: 10  (binary: 0000000001010)  [REBOOT_REQUIRED + STAGE1]
  Clear ERROR_STAGE1 (8)
  New ErrorCode: 2  (binary: 0000000000010)  [Only REBOOT_REQUIRED remains]

Sources: OfficeScrubC2R-Utilities.psm1:333-346, OfficeScrubC2R-Utilities.psm1:349-362


Error Code Setting Flow

flowchart TD
    Start["Operation encounters error"]
    SetError["Call Set-ErrorCode($ErrorBit)"]
    BitwiseOR["$script:ErrorCode = $script:ErrorCode -bor $ErrorBit"]
    CheckCritical{"Is error bit in<br/>critical set?"}
    CriticalSet["$criticalErrors =<br/>DCAF_FAILURE | STAGE2 |<br/>ELEVATION_USERDECLINED |<br/>ELEVATION | SCRIPTINIT"]
    SetFail["$script:ErrorCode =<br/>$script:ErrorCode -bor ERROR_FAIL"]
    Done["Error code updated"]
    
    Start --> SetError
    SetError --> BitwiseOR
    BitwiseOR --> CheckCritical
    CheckCritical -->|Yes| CriticalSet
    CheckCritical -->|No| Done
    CriticalSet --> SetFail
    SetFail --> Done
Loading

Error Code Setting Process

This diagram shows how the Set-ErrorCode function adds error flags to the global error code. Critical errors automatically trigger the ERROR_FAIL flag through cascading logic.

Sources: OfficeScrubC2R-Utilities.psm1:333-346


Error Code Clearing Flow

flowchart TD
    Start["Operation succeeds after retry"]
    ClearError["Call Clear-ErrorCode($ErrorBit)"]
    BitwiseAND["$script:ErrorCode = $script:ErrorCode -band (-bnot $ErrorBit)"]
    CheckClearable{"Is error bit in<br/>clearable set?"}
    ClearableSet["$clearableErrors =<br/>ELEVATION_USERDECLINED |<br/>ELEVATION | SCRIPTINIT"]
    ClearFail["$script:ErrorCode =<br/>$script:ErrorCode -band (-bnot ERROR_FAIL)"]
    Done["Error code updated"]
    
    Start --> ClearError
    ClearError --> BitwiseAND
    BitwiseAND --> CheckClearable
    CheckClearable -->|Yes| ClearableSet
    CheckClearable -->|No| Done
    ClearableSet --> ClearFail
    ClearFail --> Done
Loading

Error Code Clearing Process

This diagram shows how the Clear-ErrorCode function removes error flags. Clearing certain clearable errors (elevation/initialization) also clears the ERROR_FAIL flag if no other critical errors remain.

Sources: OfficeScrubC2R-Utilities.psm1:349-362


Error Cascading Logic

The system implements automatic error cascading where critical errors automatically set the ERROR_FAIL flag. This ensures that severe failures are always reflected in the exit code.

Critical Errors

The following errors are considered critical and automatically set ERROR_FAIL:

Error Code Reason for Criticality
ERROR_DCAF_FAILURE Second cleanup attempt failed - persistent system state corruption
ERROR_STAGE2 Registry/file cleanup failed - incomplete removal
ERROR_ELEVATION_USERDECLINED User denied admin access - cannot proceed
ERROR_ELEVATION Failed to obtain admin privileges - insufficient permissions
ERROR_SCRIPTINIT Initialization failed - script cannot operate

Clearable Errors

Only elevation and initialization errors can be cleared (typically after successful retry):

Error Code When Cleared
ERROR_SCRIPTINIT After successful initialization on retry
ERROR_ELEVATION After successful elevation on retry
ERROR_ELEVATION_USERDECLINED After user accepts UAC on retry

Sources: OfficeScrubC2R-Utilities.psm1:340-346, OfficeScrubC2R-Utilities.psm1:356-361


Error Code Usage in Execution Pipeline

stateDiagram-v2
    [*] --> Initialize_Script
    
    Initialize_Script --> Check_ERROR_SUCCESS: Success
    Initialize_Script --> Set_ERROR_SCRIPTINIT: Init failure
    
    Check_ERROR_SUCCESS --> Stage0_Detection
    Set_ERROR_SCRIPTINIT --> Show_Summary
    
    Stage0_Detection --> Stage1_Uninstall: Products found
    Stage0_Detection --> Show_Summary: No products
    
    Stage1_Uninstall --> Set_ERROR_REBOOT_REQUIRED: ODT exit 3010
    Stage1_Uninstall --> Set_ERROR_STAGE1: Msiexec failure
    Stage1_Uninstall --> Stage2_Cleanup: Success
    
    Set_ERROR_REBOOT_REQUIRED --> Stage2_Cleanup
    Set_ERROR_STAGE1 --> Stage2_Cleanup
    
    Stage2_Cleanup --> Set_ERROR_REBOOT_REQUIRED2: Files locked
    Stage2_Cleanup --> Set_ERROR_STAGE2: Cleanup failure
    Stage2_Cleanup --> Stage3_Summary: Success
    
    Set_ERROR_REBOOT_REQUIRED2 --> Stage3_Summary
    Set_ERROR_STAGE2 --> Stage3_Summary
    
    Stage3_Summary --> Show_Summary
    Show_Summary --> [*]
    
    note right of Set_ERROR_SCRIPTINIT
        Line 928:
        ERROR_SCRIPTINIT (512)
        Auto-sets ERROR_FAIL (1)
    end note
    
    note right of Set_ERROR_REBOOT_REQUIRED
        Line 276-278:
        ERROR_REBOOT_REQUIRED (2)
        $script:RebootRequired = $true
    end note
    
    note right of Set_ERROR_STAGE1
        Line 489:
        ERROR_STAGE1 (8)
    end note
    
    note right of Set_ERROR_REBOOT_REQUIRED2
        Lines 494-496, 522-525:
        ERROR_REBOOT_REQUIRED (2)
        Files scheduled for reboot
    end note
    
    note right of Set_ERROR_STAGE2
        ERROR_STAGE2 (16)
        Auto-sets ERROR_FAIL (1)
    end note
Loading

Error Code Generation Through Pipeline Stages

This diagram shows where error codes are set during the three-stage execution pipeline. Each error condition has specific trigger points in the code.

Sources: OfficeScrubC2R.ps1:115-117, OfficeScrubC2R.ps1:276-278, OfficeScrubC2R.ps1:474-477, OfficeScrubC2R.ps1:489, OfficeScrubC2R.ps1:494-496, OfficeScrubC2R.ps1:522-525, OfficeScrubC2R.ps1:928


Interpreting Combined Error Codes

Since multiple error flags can be set simultaneously, the final error code must be decomposed using bitwise AND operations to determine which individual errors occurred.

Error Code Decomposition

Given an error code value, use bitwise AND (-band) to test for individual error flags:

# Example: Error code 11 (0x0B)
$errorCode = 11

# Test for individual flags
if ($errorCode -band $ERROR_FAIL) { 
    # True: 11 & 1 = 1
    Write-Host "Generic failure occurred" 
}

if ($errorCode -band $ERROR_REBOOT_REQUIRED) { 
    # True: 11 & 2 = 2
    Write-Host "Reboot required" 
}

if ($errorCode -band $ERROR_STAGE1) { 
    # True: 11 & 8 = 8
    Write-Host "Stage 1 failed" 
}

# Binary breakdown of 11:
# 11 = 0b00000001011 = ERROR_FAIL (1) + ERROR_REBOOT_REQUIRED (2) + ERROR_STAGE1 (8)

Common Error Code Combinations

Exit Code Binary Flags Set Meaning
0 0b0000000000000 ERROR_SUCCESS Complete success
1 0b0000000000001 ERROR_FAIL Generic failure (may indicate code bug)
2 0b0000000000010 ERROR_REBOOT_REQUIRED Reboot needed but otherwise successful
3 0b0000000000011 ERROR_FAIL + ERROR_REBOOT_REQUIRED Failed with reboot required
4 0b0000000000100 ERROR_USERCANCEL User cancelled operation
10 0b0000000001010 ERROR_REBOOT_REQUIRED + ERROR_STAGE1 Stage 1 had issues, reboot needed
11 0b0000000001011 ERROR_FAIL + ERROR_REBOOT_REQUIRED + ERROR_STAGE1 Stage 1 failed, reboot needed
257 0b0000100000001 ERROR_FAIL + ERROR_ELEVATION Elevation failed
513 0b0001000000001 ERROR_FAIL + ERROR_SCRIPTINIT Initialization failed
2049 0b0100000000001 ERROR_FAIL + ERROR_UNKNOWN Unknown error occurred

Sources: OfficeScrubC2R.ps1:874-896


Error Reporting in Show-Summary

The Show-Summary function decodes the final error code and logs individual error conditions. This provides detailed diagnostics in the log file.

flowchart TD
    Start["Show-Summary function"]
    CheckIncomplete{"ErrorCode -band<br/>ERROR_INCOMPLETE?"}
    LogIncomplete["Log: INCOMPLETE<br/>Reboot required"]
    CheckUserCancel{"ErrorCode -band<br/>ERROR_USERCANCEL?"}
    CheckFail{"ErrorCode -band<br/>ERROR_FAIL?"}
    LogSuccess["Log: SUCCESS"]
    LogUserCancel["Log: USER CANCELED"]
    LogFail["Log: FAIL"]
    
    TestFlags["Test each flag<br/>with -band"]
    LogReboot{"REBOOT_REQUIRED?"}
    LogCancel{"USERCANCEL?"}
    LogStage1{"STAGE1?"}
    LogStage2{"STAGE2?"}
    LogDCAF{"DCAF_FAILURE?"}
    LogElevDeclined{"ELEVATION_USERDECLINED?"}
    LogElev{"ELEVATION?"}
    LogInit{"SCRIPTINIT?"}
    LogRelaunch{"RELAUNCH?"}
    LogUnknown{"UNKNOWN?"}
    
    LogRebootMsg["Write-Log 'Reboot required'"]
    LogCancelMsg["Write-Log 'User cancel'"]
    LogStage1Msg["Write-Log 'Msiexec failed'"]
    LogStage2Msg["Write-Log 'Cleanup failed'"]
    LogDCAFMsg["Write-Log 'Second attempt cleanup<br/>still incomplete'"]
    LogElevDeclinedMsg["Write-Log 'User declined elevation'"]
    LogElevMsg["Write-Log 'Elevation failed'"]
    LogInitMsg["Write-Log 'Initialization error'"]
    LogRelaunchMsg["Write-Log 'Unhandled error<br/>during relaunch'"]
    LogUnknownMsg["Write-Log 'Unknown error'"]
    
    Done["Return final ErrorCode"]
    
    Start --> CheckIncomplete
    CheckIncomplete -->|Yes| LogIncomplete
    CheckIncomplete -->|No| CheckUserCancel
    LogIncomplete --> Done
    
    CheckUserCancel -->|Yes| LogUserCancel
    CheckUserCancel -->|No| CheckFail
    
    CheckFail -->|Yes| LogFail
    CheckFail -->|No| LogSuccess
    
    LogSuccess --> Done
    LogUserCancel --> Done
    LogFail --> TestFlags
    
    TestFlags --> LogReboot
    LogReboot -->|Yes| LogRebootMsg
    LogReboot -->|No| LogCancel
    LogRebootMsg --> LogCancel
    
    LogCancel -->|Yes| LogCancelMsg
    LogCancel -->|No| LogStage1
    LogCancelMsg --> LogStage1
    
    LogStage1 -->|Yes| LogStage1Msg
    LogStage1 -->|No| LogStage2
    LogStage1Msg --> LogStage2
    
    LogStage2 -->|Yes| LogStage2Msg
    LogStage2 -->|No| LogDCAF
    LogStage2Msg --> LogDCAF
    
    LogDCAF -->|Yes| LogDCAFMsg
    LogDCAF -->|No| LogElevDeclined
    LogDCAFMsg --> LogElevDeclined
    
    LogElevDeclined -->|Yes| LogElevDeclinedMsg
    LogElevDeclined -->|No| LogElev
    LogElevDeclinedMsg --> LogElev
    
    LogElev -->|Yes| LogElevMsg
    LogElev -->|No| LogInit
    LogElevMsg --> LogInit
    
    LogInit -->|Yes| LogInitMsg
    LogInit -->|No| LogRelaunch
    LogInitMsg --> LogRelaunch
    
    LogRelaunch -->|Yes| LogRelaunchMsg
    LogRelaunch -->|No| LogUnknown
    LogRelaunchMsg --> LogUnknown
    
    LogUnknown -->|Yes| LogUnknownMsg
    LogUnknown -->|No| Done
    LogUnknownMsg --> Done
Loading

Error Code Reporting Logic

The Show-Summary function at OfficeScrubC2R.ps1:867-918 systematically tests each error flag and logs a human-readable description. This provides detailed diagnostics in the log file for troubleshooting.

Sources: OfficeScrubC2R.ps1:867-918


Return Value Handling

The error code is returned through two mechanisms:

1. Script Exit Code

The main script returns the error code as its exit code, which can be captured by the calling process:

# From Main function
return $script:ErrorCode  # Line 988

# Script execution
$exitCode = Main
exit $exitCode  # Lines 1005-1006

2. Return Value File

For compatibility with scheduled tasks and other automation scenarios, the error code is also written to a file:

# File path: %TEMP%\OfficeScrubC2R\ScrubRetValFile.txt
$retValFile = Join-Path $script:ScrubDir "ScrubRetValFile.txt"
[System.IO.File]::WriteAllText($retValFile, $Value.ToString())

This allows external processes to read the error code even if they cannot capture the script's exit code directly.

Sources: OfficeScrubC2R.ps1:988, OfficeScrubC2R.ps1:1005-1006, OfficeScrubC2R-Utilities.psm1:364-376


Error Code Function Reference

classDiagram
    class Set_ErrorCode {
        +int ErrorBit
        -BitwiseOR()
        -CheckCritical()
        -SetFailIfCritical()
    }
    
    class Clear_ErrorCode {
        +int ErrorBit
        -BitwiseAND_NOT()
        -CheckClearable()
        -ClearFailIfClearable()
    }
    
    class Set_ReturnValue {
        +int Value
        -WriteToFile()
    }
    
    class Show_Summary {
        -LogErrorCode()
        -TestAllFlags()
        -LogIndividualErrors()
        -HandleReboot()
    }
    
    class Initialize_Script {
        -InitErrorCode()
        -SetERROR_SCRIPTINIT()
        -ClearERROR_SCRIPTINIT()
    }
    
    class Main {
        -CallInitialize()
        -ExecutePipeline()
        -ReturnErrorCode()
    }
    
    Set_ErrorCode --> script_ErrorCode : updates
    Clear_ErrorCode --> script_ErrorCode : updates
    Set_ReturnValue --> ScrubRetValFile_txt : writes
    Show_Summary --> script_ErrorCode : reads
    Initialize_Script --> Set_ErrorCode : calls
    Initialize_Script --> Clear_ErrorCode : calls
    Main --> Set_ErrorCode : calls
    Main --> Show_Summary : calls
    Main --> script_ErrorCode : returns
Loading

Error Code Functions and Their Relationships

This diagram shows the key functions involved in error code management and how they interact with the global $script:ErrorCode variable.

Function Locations

Function File Lines Purpose
Set-ErrorCode OfficeScrubC2R-Utilities.psm1 333-346 Sets error flags using bitwise OR with automatic cascading
Clear-ErrorCode OfficeScrubC2R-Utilities.psm1 349-362 Clears error flags using bitwise AND NOT
Set-ReturnValue OfficeScrubC2R-Utilities.psm1 364-376 Writes error code to return value file
Initialize-Script OfficeScrubC2R.ps1 87-136 Initializes error code, sets/clears ERROR_SCRIPTINIT
Show-Summary OfficeScrubC2R.ps1 867-918 Decodes and logs all error flags
Main OfficeScrubC2R.ps1 924-1001 Orchestrates pipeline, catches errors, returns final code

Sources: OfficeScrubC2R-Utilities.psm1:333-376, OfficeScrubC2R.ps1:87-136, OfficeScrubC2R.ps1:867-918, OfficeScrubC2R.ps1:924-1001


Reboot Required Handling

The ERROR_REBOOT_REQUIRED flag has special handling because it indicates a non-fatal but important condition: files are locked and will be deleted on next system restart.

Reboot Flag Setting Conditions

Condition Code Location Description
ODT returns exit code 3010 OfficeScrubC2R.ps1:276-278 Office Deployment Tool requires reboot
Msiexec returns exit code 3010 OfficeScrubC2R.ps1:474-477 Windows Installer requires reboot
Directory deletion fails OfficeScrubC2R.ps1:494-496 Folder is locked, scheduled for reboot deletion
File deletion fails OfficeScrubC2R.ps1:522-525 File is locked, scheduled for reboot deletion
Pending file operations added OfficeScrubC2R-Utilities.psm1:541-543 PendingFileRenameOperations registry updated

Reboot Prompt Logic

When $script:RebootRequired is $true, the user is prompted to restart (unless running in quiet mode):

# From Show-Summary function (lines 902-914)
if ($script:RebootRequired) {
    Write-Log "REBOOT REQUIRED - System restart needed to complete uninstall"
    
    if (-not $script:Quiet) {
        $response = Read-Host "Do you want to reboot now? (Y/N)"
        if ($response -match "^[Yy]") {
            Restart-Computer -Force
        }
    }
}

Sources: OfficeScrubC2R.ps1:276-278, OfficeScrubC2R.ps1:474-477, OfficeScrubC2R.ps1:494-496, OfficeScrubC2R.ps1:522-525, OfficeScrubC2R.ps1:902-914, OfficeScrubC2R-Utilities.psm1:541-543


Error Code Limitations

Maximum Error Code Value

The current system supports 13 distinct error flags (ERROR_SUCCESS through ERROR_UNKNOWN), yielding a maximum theoretical error code of 4095 (0xFFF) if all flags are set simultaneously. This is an edge case that should never occur in normal operation.

No Error Severity Levels

The system does not implement severity levels (warning vs. error vs. critical). All error codes represent actual failures, with the exception of ERROR_SUCCESS (0) and ERROR_REBOOT_REQUIRED (2), which can indicate successful completion with required follow-up action.

Bitwise Operation Requirements

Interpreting error codes requires bitwise AND operations. Simple integer equality checks will fail for combined error codes:

# WRONG: This will fail if multiple errors are set
if ($errorCode -eq $ERROR_REBOOT_REQUIRED) { }

# CORRECT: Use bitwise AND to test flags
if ($errorCode -band $ERROR_REBOOT_REQUIRED) { }

Sources: OfficeScrubC2R-Utilities.psm1:9-22


Error Code Constants Export

All error code constants are exported from the utilities module for use in the main script and by external callers:

# From OfficeScrubC2R-Utilities.psm1 lines 1047-1062
Export-ModuleMember -Variable @(
    'ERROR_SUCCESS',
    'ERROR_FAIL',
    'ERROR_REBOOT_REQUIRED',
    'ERROR_USERCANCEL',
    'ERROR_STAGE1',
    'ERROR_STAGE2',
    'ERROR_INCOMPLETE',
    'ERROR_DCAF_FAILURE',
    'ERROR_ELEVATION_USERDECLINED',
    'ERROR_ELEVATION',
    'ERROR_SCRIPTINIT',
    'ERROR_RELAUNCH',
    'ERROR_UNKNOWN',
    'ErrorCode'  # Current error code value
)

The $script:ErrorCode variable is also exported, allowing external scripts to read (but not modify) the current error state.

Sources: OfficeScrubC2R-Utilities.psm1:1047-1088

Clone this wiki locally