Skip to content

Three‐Stage Execution Pipeline

Calvindd2f edited this page Jan 14, 2026 · 1 revision

Three-Stage Execution Pipeline

Relevant source files

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

Purpose and Scope

This document details the three-stage execution pipeline that performs Office Click-to-Run removal. The pipeline consists of three sequential cleanup stages coordinated by the PowerShell orchestration layer and executed primarily by the native C# library (OfficeScrubNative.dll):

  • Stage 1: Uninstall - License cleanup, process termination, ODT/MSI uninstallation
  • Stage 2: Registry Cleanup - Removal of Office registry keys, Windows Installer metadata, TypeLib registrations
  • Stage 3: File System Cleanup - Deletion of Office services, files, and folders with reboot scheduling for locked resources

The PowerShell layer (OfficeScrubC2R.psm1) provides workflow orchestration, parameter handling, and logging, while delegating performance-critical operations to the native layer through the OfficeScrubOrchestrator class.

For the PowerShell orchestration details, see PowerShell Module. For native layer components, see Native Performance Layer. For WOW64 registry handling, see WOW64 Registry Handling.

Sources: OfficeScrubC2R-Native.cs:1315-1383, direct_conversion.cs:233-304


Pipeline Architecture Overview

The three-stage pipeline is coordinated by the PowerShell layer but executes primarily through the native OfficeScrubOrchestrator class, which wires together eight specialized helper classes for performance-critical operations.

Native Layer Architecture

The OfficeScrubOrchestrator class OfficeScrubC2R-Native.cs:1315-1350 provides access to helper instances:

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; }

Three-Stage Cleanup Workflow

Stage Primary Operations Native Helpers Used Key Registry/File Targets
Stage 1: Uninstall OSPP license cleanup
Process termination
Scheduled task deletion
Shortcut cleanup
ODT/MSI uninstall
LicenseHelper
ProcessHelper
ShellHelper
Office SPP products
vNext license cache
Scheduled tasks
Shell shortcuts
Stage 2: Registry Cleanup Office key removal
Windows Installer metadata
TypeLib registrations
Shell integration cleanup
RegistryHelper
WindowsInstallerHelper
TypeLibHelper
HKCU\Software\Microsoft\Office
HKLM\SOFTWARE\...Installer\
TypeLib registrations
Stage 3: File System Cleanup Service deletion
Office folder removal
Locked file scheduling
ServiceHelper
FileHelper
ClickToRunSvc, OfficeSvc
Program Files\Microsoft Office
ProgramData\Microsoft\ClickToRun

Sources: OfficeScrubC2R-Native.cs:1315-1350, direct_conversion.cs:233-304


Pipeline Execution Flow

The pipeline is initiated by PowerShell orchestration and delegates to native helper classes for each stage's operations.

flowchart TD
    Start["PowerShell Entry:<br/>Invoke-OfficeScrubC2R"]
    
    Init["Initialize:<br/>Load OfficeScrubNative.dll<br/>Create OfficeScrubOrchestrator"]
    
    Detect["Detection Phase:<br/>FindInstalledOProducts<br/>Test-OfficeC2RPath<br/>Test-OfficeProductScope"]
    
    Stage1["Stage 1: Uninstall"]
    
    subgraph S1["Stage 1 Operations"]
        OSPP["LicenseHelper.CleanOSPP<br/>Remove Office SPP licenses"]
        VNext["LicenseHelper.ClearVNextLicenseCache<br/>Delete local license cache"]
        Proc["ProcessHelper.TerminateProcesses<br/>Kill Office processes in parallel"]
        Tasks["Delete Scheduled Tasks<br/>via PowerShell"]
        Shortcuts["ShellHelper.UnpinFromTaskbar<br/>ShellHelper.UnpinFromStartMenu"]
        ODT["ODT/MSI Uninstall<br/>via external executables"]
    end
    
    Stage2["Stage 2: Registry Cleanup"]
    
    subgraph S2["Stage 2 Operations"]
        RegKeys["RegistryHelper.DeleteKey<br/>HKCU/HKLM Office keys<br/>WOW64 dual-path deletion"]
        MSIData["WindowsInstallerHelper:<br/>CleanupUpgradeCodes<br/>CleanupProducts<br/>CleanupComponents<br/>CleanupPublishedComponents"]
        TypeLib["TypeLibHelper.CleanupKnownTypeLibs<br/>Remove COM registrations"]
        Shell["ShellHelper cleanup:<br/>Remove shell extensions<br/>Remove BHO entries"]
    end
    
    Stage3["Stage 3: File System Cleanup"]
    
    subgraph S3["Stage 3 Operations"]
        Services["ServiceHelper.DeleteService<br/>OfficeSvc, ClickToRunSvc"]
        Files["FileHelper.DeleteFile<br/>FileHelper.DeleteDirectory<br/>Remove Office folders"]
        Locked["FileHelper.ScheduleDeleteOnReboot<br/>MoveFileEx + PendingFileRenameOperations"]
    end
    
    Reboot{"Reboot Required?<br/>$script:RebootRequired"}
    
    Schedule["Schedule locked files<br/>for reboot deletion"]
    
    Exit["Return error code"]
    
    Start --> Init
    Init --> Detect
    Detect --> Stage1
    
    Stage1 --> OSPP
    OSPP --> VNext
    VNext --> Proc
    Proc --> Tasks
    Tasks --> Shortcuts
    Shortcuts --> ODT
    
    ODT --> Stage2
    
    Stage2 --> RegKeys
    RegKeys --> MSIData
    MSIData --> TypeLib
    TypeLib --> Shell
    
    Shell --> Stage3
    
    Stage3 --> Services
    Services --> Files
    Files --> Locked
    
    Locked --> Reboot
    Reboot -->|Yes| Schedule
    Reboot -->|No| Exit
    Schedule --> Exit
Loading

Diagram: Three-Stage Pipeline with Native Helper Invocations

Sources: OfficeScrubC2R-Native.cs:1315-1383, direct_conversion.cs:139-304


Orchestrator Initialization

Before pipeline execution, the PowerShell layer loads the native DLL and instantiates the OfficeScrubOrchestrator class with system architecture detection.

sequenceDiagram
    participant PS as PowerShell Module
    participant Import as Import-OfficeScrubNative
    participant Orch as New-OfficeScrubOrchestrator
    participant Native as OfficeScrubOrchestrator
    participant Helpers as Helper Class Instances
    
    PS->>Import: Load native assembly
    Import->>Import: Detect system architecture<br/>$is64Bit = [Environment]::Is64BitOperatingSystem
    Import->>Import: Add-Type from OfficeScrubNative.dll
    Import-->>PS: Assembly loaded
    
    PS->>Orch: Create orchestrator
    Orch->>Native: new OfficeScrubOrchestrator($is64Bit)
    
    Native->>Helpers: Registry = new RegistryHelper($is64Bit)
    Native->>Helpers: Files = new FileHelper()
    Native->>Helpers: Processes = new ProcessHelper()
    Native->>Helpers: Shell = new ShellHelper()
    Native->>Helpers: WindowsInstaller = new WindowsInstallerHelper(Registry)
    Native->>Helpers: TypeLib = new TypeLibHelper(Registry)
    Native->>Helpers: License = new LicenseHelper()
    Native->>Helpers: Services = new ServiceHelper()
    
    Helpers-->>Native: All helpers initialized
    Native-->>Orch: Orchestrator instance
    Orch-->>PS: $script:Orchestrator
Loading

Diagram: Native Orchestrator Initialization Sequence

The orchestrator constructor OfficeScrubC2R-Native.cs:1339-1349 instantiates all helper classes and passes $is64Bit to RegistryHelper for WOW64 support.

Sources: OfficeScrubC2R-Native.cs:1315-1349


Product Detection and Scope Validation

Before entering the three-stage pipeline, the system performs product detection and scope validation using the orchestrator's helper methods.

Detection Algorithm

Product detection uses two key orchestrator methods that bridge PowerShell detection logic with native scope validation:

IsC2RPath(string path) Method OfficeScrubC2R-Native.cs:1352-1359

public bool IsC2RPath(string path)
{
    if (string.IsNullOrEmpty(path))
        return false;

    return OfficeConstants.C2R_PATTERNS.Any(pattern =>
        path.Contains(pattern, StringComparison.OrdinalIgnoreCase));
}

Checks if a file path matches Click-to-Run patterns defined in OfficeConstants.C2R_PATTERNS OfficeScrubC2R-Native.cs:36-45:

  • \ROOT\OFFICE1
  • Microsoft Office\Root\
  • \microsoft shared\ClickToRun
  • \Microsoft Office\PackageManifests
  • \Microsoft Office\PackageSunrisePolicies
  • Microsoft Office 15
  • Microsoft Office 16

IsInScope(string productCode) Method OfficeScrubC2R-Native.cs:1361-1382

public bool IsInScope(string productCode)
{
    if (string.IsNullOrEmpty(productCode) || productCode.Length != 38)
        return false;

    var upper = productCode.ToUpperInvariant();

    // Check special products first with HashSet O(1) lookup
    if (SpecialProducts.Contains(upper))
        return true;

    if (!upper.EndsWith(OfficeConstants.OFFICE_ID))
        return false;

    // Check version >= 15
    if (!int.TryParse(upper.Substring(3, 2), out int version) || version <= 14)
        return false;

    // Check SKU
    var sku = upper.Substring(10, 4);
    return ValidSkus.Contains(sku);
}

Validates product codes against:

  1. Special Products HashSet OfficeScrubC2R-Native.cs:1323-1327: O(1) lookup for known GUIDs
  2. Office ID Suffix OfficeScrubC2R-Native.cs:31: {0000000FF1CE}
  3. Version Check: Must be Office 15 (2013) or later
  4. SKU Validation OfficeScrubC2R-Native.cs:1318-1321: HashSet contains 007E, 008F, 008C, 24E1, 237A, 00DD

Detection Flow Diagram

flowchart TD
    Start["PowerShell: FindInstalledOProducts"]
    
    Metadata["Ensure WI Metadata Integrity<br/>Delete invalid GUID entries"]
    
    ScanO15["Scan Office 15.0 Keys:<br/>HKLM\SOFTWARE\Microsoft\Office\15.0\ClickToRun"]
    ScanO16["Scan Office 16.0 Keys:<br/>HKLM\SOFTWARE\Microsoft\Office\16.0\ClickToRun"]
    ScanC2R["Scan Generic C2R Keys:<br/>HKLM\SOFTWARE\Microsoft\Office\ClickToRun"]
    
    ScanARP["Scan ARP Registry:<br/>HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"]
    
    ExtractGUID["Extract Product GUIDs<br/>from UninstallString"]
    
    TestScope["Call Orchestrator.IsInScope(guid)"]
    
    CheckScope{"In Scope?"}
    
    AddProduct["Add to C2RSuite hashtable"]
    
    TestPath["Call Orchestrator.IsC2RPath(installPath)"]
    
    CheckPath{"Is C2R Path?"}
    
    AddC2R["Mark as C2R installation"]
    
    Complete["Return product collections"]
    
    Start --> Metadata
    Metadata --> ScanO15
    ScanO15 --> ScanO16
    ScanO16 --> ScanC2R
    ScanC2R --> ScanARP
    ScanARP --> ExtractGUID
    ExtractGUID --> TestScope
    TestScope --> CheckScope
    CheckScope -->|Yes| AddProduct
    CheckScope -->|No| Complete
    AddProduct --> TestPath
    TestPath --> CheckPath
    CheckPath -->|Yes| AddC2R
    CheckPath -->|No| Complete
    AddC2R --> Complete
Loading

Diagram: Product Detection with Native Scope Validation

Sources: OfficeScrubC2R-Native.cs:1318-1382, direct_conversion.cs:872-1195


Stage 1: Uninstall Phase

Stage 1 is orchestrated by Uninstall-OfficeProducts OfficeScrubC2R.ps1:187-247 which implements a multi-step uninstallation process.

Stage 1 Execution Order

flowchart TD
    Start["Uninstall-OfficeProducts<br/>[187-247]"]
    
    CheckCancel{"ErrorCode has<br/>ERROR_USERCANCEL?"}
    Return["return"]
    
    Header["Write log header:<br/>Stage # 1 - Uninstall"]
    
    Licenses["Clear-OfficeLicenses<br/>Remove OSPP licenses<br/>unless KeepLicense=true"]
    
    StopProc1["Stop-OfficeProcesses<br/>Terminate Office executables"]
    
    CheckSuite{"C2RSuite.Count == 0<br/>OR<br/>NOT KeepSku?"}
    ClearShell["Clear-ShellIntegration<br/>Remove shell extensions"]
    
    RemoveTasks["Remove-ScheduledTasks<br/>Delete Office scheduled tasks<br/>[602-656]"]
    
    Shortcuts["Clear-Shortcuts<br/>Delete + Unpin shortcuts<br/>AllUsersProfile + C:\Users"]
    
    CheckOSE["Check OSE service state<br/>Fix StartMode + StartName<br/>[217-228]"]
    
    UninstallC2R["Uninstall-OfficeC2R<br/>ODT-based removal<br/>[249-282]"]
    
    RemovePub["Remove-PublishedComponents<br/>Delete manifest files<br/>Run integrator.exe<br/>[315-378]"]
    
    RemoveC2R["Remove-C2RRegistryData<br/>Delete ARP entries<br/>Remove C2R keys<br/>Clean App-V keys<br/>[380-419]"]
    
    UninstallMSI["Uninstall-MSIProducts<br/>MSI-based removal<br/>[421-491]"]
    
    Start --> CheckCancel
    CheckCancel -->|Yes| Return
    CheckCancel -->|No| Header
    Header --> Licenses
    Licenses --> StopProc1
    StopProc1 --> CheckSuite
    CheckSuite -->|Yes| ClearShell
    CheckSuite -->|No| RemoveTasks
    ClearShell --> RemoveTasks
    RemoveTasks --> Shortcuts
    Shortcuts --> CheckOSE
    CheckOSE --> UninstallC2R
    UninstallC2R --> RemovePub
    RemovePub --> RemoveC2R
    RemoveC2R --> UninstallMSI
Loading

Diagram: Stage 1 Uninstall Sequence

Sources: OfficeScrubC2R.ps1:187-247

ODT-Based C2R Uninstall

The Uninstall-OfficeC2R function OfficeScrubC2R.ps1:249-282 implements Office Deployment Tool-based removal for Click-to-Run products.

ODT Uninstall Process:

  1. XML Configuration Generation OfficeScrubC2R.ps1:252-257

    • Calls Build-RemoveXml 284-294 to create removal configuration
    • XML structure 285-292:
      <Configuration>
        <Remove All="True" />
        <Display Level="None" AcceptEULA="True" />
        <Property Name="FORCEAPPSHUTDOWN" Value="True" />
      </Configuration>
    • Saves to $script:ScrubDir\RemoveAll.xml
  2. ODT Download OfficeScrubC2R.ps1:260-261

    • Calls Download-ODT 296-313 unless -Offline mode
    • Downloads from Microsoft CDN: OfficeDeploymentTool_12325-20288.exe
    • Saves to $script:ScrubDir\setup.exe
  3. ODT Execution OfficeScrubC2R.ps1:262-279

    • Builds arguments: /configure "RemoveAll.xml"
    • Adds /quiet if $script:Quiet is true 263-268
    • Executes via Start-Process -Wait -PassThru 272
    • Checks exit code 273-278:
      • 3010: Sets $script:RebootRequired = $true and ERROR_REBOOT_REQUIRED
      • Other codes: Logged but not treated as errors

Sources: OfficeScrubC2R.ps1:249-313

Published Components Removal

The Remove-PublishedComponents function OfficeScrubC2R.ps1:315-378 removes Click-to-Run integration manifests and runs the integrator to unregister COM components.

Algorithm Optimization:

The function employs a batch collection strategy to minimize file system operations 323-368:

  1. Collection Phase 326-348

    • Queries registry for PackageFolder and PackageGUID from three locations:
      • SOFTWARE\Microsoft\Office\15.0\ClickToRun
      • SOFTWARE\Microsoft\Office\16.0\ClickToRun
      • SOFTWARE\Microsoft\Office\ClickToRun
    • Scans $packageFolder\root\Integration for C2RManifest*.xml files
    • Appends all manifests to $allManifestFiles array
    • Prepares integrator tasks for later execution 340-347
  2. Batch Deletion Phase 351-368

    • Processes all collected manifest files in one operation
    • Uses [System.IO.File]::Delete() for performance 359-366
    • Handles deletion errors gracefully 363-365
  3. Integrator Execution Phase 370-377

    • Runs each integrator with arguments: /U /Extension PackageRoot="..." PackageGUID=...
    • Waits for completion and logs exit codes

Sources: OfficeScrubC2R.ps1:315-378

MSI Product Uninstall

The Uninstall-MSIProducts function OfficeScrubC2R.ps1:421-491 handles MSI-based Office installations using Windows Installer automation.

MSI Uninstall Algorithm:

  1. Product Discovery OfficeScrubC2R.ps1:425-439

    • Creates COM object: WindowsInstaller.Installer
    • Enumerates all installed products via $msi.Products
    • Partitions products using Test-ProductInScope:
      • $inScopeProducts: Office products to remove
      • $outOfScopeProducts: Non-Office products (logged only)
  2. Command Preparation OfficeScrubC2R.ps1:446-462

    • Pre-builds all msiexec.exe command arguments
    • For each product:
      • Arguments: /x{ProductGUID} REBOOT=ReallySuppress NOREMOVESPAWN=True
      • User interface: /qb-! (reduced UI) or /q (silent if $script:Quiet)
      • Logging: /l*v "Uninstall_{ProductGUID}.log"
    • Stores in $msiexecArgsList for batch processing
  3. Process Termination OfficeScrubC2R.ps1:464

    • Calls Stop-OfficeProcesses to prevent file locking
  4. Batch Execution OfficeScrubC2R.ps1:466-479

    • Skipped if -DetectOnly mode
    • Sequentially executes each prepared msiexec command
    • Monitors exit codes:
      • 3010: Sets reboot flag 474-477
      • Other codes: Logged but not treated as fatal
  5. Cleanup OfficeScrubC2R.ps1:483-484

    • Stops Windows Installer service: net stop msiserver

Sources: OfficeScrubC2R.ps1:421-491


Stage 2: Cleanup Phase

Stage 2 consists of two major functions that perform exhaustive cleanup of remaining Office artifacts.

Stage 2a: Registry Cleanup

The Clean-OfficeRegistry function OfficeScrubC2R.ps1:497-546 removes all Office-related registry keys across three hives.

Registry Cleanup Targets:

Registry Location Purpose
HKCU\Software\Microsoft\Office\{15.0,16.0}\Registration User registration data
HKLM\SOFTWARE\Microsoft\Office\{15.0,16.0}\Common\InstallRoot\Virtual Virtual installation roots
HKLM\SOFTWARE\Classes\CLSID\{2027FC3B-CF9D-4ec7-A823-38BA308625CC} MAPI search registration
HKC{U,LM}\SOFTWARE\Microsoft\Office\{15.0,16.0}\ClickToRun C2R configuration keys
HKLM\SOFTWARE\Microsoft\Office\{15.0,16.0}\ClickToRunStore C2R package store
HKLM\Software\Microsoft\Office\{15.0,16.0} Root Office keys (if no KeepSku)

Recursive Cleanup:

The function calls Clear-OfficeHKLM OfficeScrubC2R.ps1:548-570 which implements a recursive descent algorithm:

  1. Descend 552-555

    • Gets all subkeys via Get-RegistryKeys
    • Recursively calls itself for each subkey
  2. Clean Values 558-564

    • Enumerates all values in current key
    • Tests each value with Test-IsC2R (checks for C2R-related paths/GUIDs)
    • Removes C2R-related values
  3. Prune Empty Keys 567-569

    • If key has no subkeys AND no values AND KeepSku.Count == 0
    • Removes the empty key

Additional Cleanup Operations:

  • Run Keys OfficeScrubC2R.ps1:572-585: Removes C2R entries from HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
  • ARP Entries OfficeScrubC2R.ps1:587-598: Removes Add/Remove Programs entries for in-scope products
  • Windows Installer Metadata 543: Calls Clear-WindowsInstallerMetadata from utilities
  • TypeLib Registrations 545: Calls Clear-TypeLibRegistrations for COM cleanup

Sources: OfficeScrubC2R.ps1:497-598

Stage 2b: File System Cleanup

The Complete-Cleanup function OfficeScrubC2R.ps1:660-844 performs comprehensive file and folder deletion with special handling for locked files.

flowchart TD
    Start["Complete-Cleanup<br/>[660-844]"]
    
    CheckCancel{"ErrorCode has<br/>ERROR_USERCANCEL?"}
    Return["return"]
    
    Header["Write log header:<br/>Stage # 2 - CleanUp - Files"]
    
    StopProc["Stop-OfficeProcesses"]
    RemoveTasks["Remove-ScheduledTasks"]
    
    DeleteServices["Delete Services<br/>OfficeSvc<br/>ClickToRunSvc<br/>[671-676]"]
    
    TermProc["Terminate additional processes<br/>explorer.exe, msiexec.exe, ose.exe<br/>via Orchestrator.Processes<br/>[679-685]"]
    
    CheckPaths{"Any Office folders exist?<br/>[691-710]"}
    
    WarnExplorer["Log: Closing Explorer.exe<br/>Will restart automatically"]
    StopProc2["Stop-OfficeProcesses"]
    
    DeleteOffice15_16["Delete Office 15/16 folders<br/>[713-730]"]
    
    DeleteRoot["Delete root Office files/folders<br/>PackageManifests, root, etc<br/>[732-775]"]
    
    DeleteAdditional["Delete additional paths<br/>ClickToRun, FFPackageLocker<br/>[777-789]"]
    
    DeleteFiles["Delete file-based entries<br/>FFPackageLocker, FFStatePBLocker<br/>[792-801]"]
    
    DeleteHeartbeat["Delete Heartbeat folder<br/>if KeepSku.Count == 0<br/>[803-805]"]
    
    DeleteUserProfile["Delete user profile folders<br/>[808-818]"]
    
    RestoreExplorer["Orchestrator.Shell.RestartExplorer<br/>[821-824]"]
    
    ClearShortcuts["Clear-Shortcuts<br/>Delete from AllUsersProfile<br/>and C:\Users<br/>[827-831]"]
    
    RemoveEmpty["Remove-EmptyFolders<br/>[834]"]
    
    CheckDelInUse{"$script:DelInUse.Count > 0?"}
    
    ScheduleReboot["Add PendingFileRenameOperations<br/>for locked files<br/>[837-843]"]
    
    End["End"]
    
    Start --> CheckCancel
    CheckCancel -->|Yes| Return
    CheckCancel -->|No| Header
    Header --> StopProc
    StopProc --> RemoveTasks
    RemoveTasks --> DeleteServices
    DeleteServices --> TermProc
    TermProc --> CheckPaths
    CheckPaths -->|Yes| WarnExplorer
    CheckPaths -->|No| DeleteOffice15_16
    WarnExplorer --> StopProc2
    StopProc2 --> DeleteOffice15_16
    DeleteOffice15_16 --> DeleteRoot
    DeleteRoot --> DeleteAdditional
    DeleteAdditional --> DeleteFiles
    DeleteFiles --> DeleteHeartbeat
    DeleteHeartbeat --> DeleteUserProfile
    DeleteUserProfile --> RestoreExplorer
    RestoreExplorer --> ClearShortcuts
    ClearShortcuts --> RemoveEmpty
    RemoveEmpty --> CheckDelInUse
    CheckDelInUse -->|Yes| ScheduleReboot
    CheckDelInUse -->|No| End
    ScheduleReboot --> End
Loading

Diagram: Stage 2b File Cleanup Flow

Locked File Handling:

When files cannot be deleted due to locks, the utilities module adds them to $script:DelInUse hashtable. At the end of cleanup OfficeScrubC2R.ps1:837-843, all locked files are scheduled for deletion on next reboot using AddPendingFileRenameOperation from the native orchestrator.

Key File Locations:

  • $script:ProgramFiles\Microsoft Office {15,16}
  • $script:ProgramFiles\Microsoft Office\{PackageManifests,root,Office15,Office16}
  • $script:ProgramData\Microsoft\ClickToRun
  • $script:CommonProgramFiles\microsoft shared\ClickToRun
  • $env:USERPROFILE\Microsoft Office {15,16}

Sources: OfficeScrubC2R.ps1:660-863


Stage 3: Summary and Exit

The Show-Summary function OfficeScrubC2R.ps1:867-918 provides comprehensive results reporting and handles reboot prompts.

Summary Operations

  1. Error Code Update OfficeScrubC2R.ps1:871

    • Calls Set-ReturnValue $script:ErrorCode
  2. Status Determination OfficeScrubC2R.ps1:874-882

    If ERROR_INCOMPLETE flag: "INCOMPLETE. Uninstall requires system reboot"
    Else:
      - Default: "SUCCESS"
      - If ERROR_USERCANCEL: "USER CANCELED"
      - If ERROR_FAIL: "FAIL"
    
  3. Detailed Error Logging OfficeScrubC2R.ps1:885-897

    • If ERROR_FAIL flag is set, logs specific error components:
      • ERROR_REBOOT_REQUIRED: "Reboot required"
      • ERROR_USERCANCEL: "User cancel"
      • ERROR_STAGE1: "Msiexec failed"
      • ERROR_STAGE2: "Cleanup failed"
      • ERROR_INCOMPLETE: "Removal incomplete. Rerun after reboot needed"
      • ERROR_DCAF_FAILURE: "Second attempt cleanup still incomplete"
      • ERROR_ELEVATION_USERDECLINED: "User declined elevation"
      • ERROR_ELEVATION: "Elevation failed"
      • ERROR_SCRIPTINIT: "Initialization error"
      • ERROR_RELAUNCH: "Unhandled error during relaunch attempt"
      • ERROR_UNKNOWN: "Unknown error"
  4. Reboot Handling OfficeScrubC2R.ps1:902-915

    • If $script:RebootRequired == $true:
      • Displays prominent reboot notification
      • If NOT -Quiet mode:
        • Prompts user: "Do you want to reboot now? (Y/N)"
        • If user confirms: Executes Restart-Computer -Force

Error Code Bitmask Architecture

The error handling system uses a bitmask pattern where multiple error conditions can be tracked simultaneously:

Error Constant Value Description
ERROR_SUCCESS 0 No errors
ERROR_REBOOT_REQUIRED 3010 Reboot needed for completion
ERROR_FAIL 4095 Generic failure
ERROR_USERCANCEL 1 User cancelled operation
ERROR_STAGE1 2 Stage 1 (msiexec) failure
ERROR_STAGE2 4 Stage 2 (cleanup) failure
ERROR_INCOMPLETE 8 Incomplete removal
ERROR_DCAF_FAILURE 16 Persistent cleanup failure
ERROR_ELEVATION_USERDECLINED 32 User declined UAC prompt
ERROR_ELEVATION 64 Elevation check failed
ERROR_SCRIPTINIT 128 Script initialization error
ERROR_RELAUNCH 256 Relaunch error
ERROR_UNKNOWN 512 Unknown error

Error codes are tested using bitwise AND: if ($script:ErrorCode -band $script:ERROR_USERCANCEL) checks if the cancel flag is set regardless of other flags.

Sources: OfficeScrubC2R.ps1:867-918


Pipeline State Management

The pipeline maintains state through script-scoped variables that control execution flow and track operation status.

Key State Variables

Variable Type Purpose Set By
$script:ErrorCode int Bitmask of error flags Set-ErrorCode, Clear-ErrorCode
$script:RebootRequired bool Tracks if reboot needed ODT/MSI exit codes
$script:InstalledSku hashtable Detected Office products Get-InstalledOfficeProducts
$script:C2RSuite hashtable C2R product registry keys Get-InstalledOfficeProducts
$script:KeepSku hashtable Products to preserve Detection logic
$script:DelInUse hashtable Locked files for reboot deletion Remove-FileForced
$script:DetectOnly bool Detection-only mode flag Parameter
$script:Force bool Skip confirmation prompt Parameter
$script:Quiet bool Silent execution mode Parameter
$script:Offline bool Skip ODT download Parameter
$script:KeepLicense bool Preserve license data Parameter

State Flow Diagram

stateDiagram-v2
    [*] --> Initialized: Initialize-Script
    
    state Initialized {
        [*] --> ErrorCode_0
        ErrorCode_0 --> ErrorCode_128: Init fails
        ErrorCode_0 --> Continue: Init succeeds
    }
    
    state Stage0_Detection {
        [*] --> Validating_Metadata
        Validating_Metadata --> Scanning_Products
        Scanning_Products --> InstalledSku_Populated
        InstalledSku_Populated --> C2RSuite_Populated
    }
    
    state DecisionPoint1 {
        [*] --> Check_DetectOnly
        Check_DetectOnly --> Exit_DetectOnly: DetectOnly=true
        Check_DetectOnly --> Check_UserConfirm: DetectOnly=false
        Check_UserConfirm --> Exit_Cancelled: User declines
        Check_UserConfirm --> Proceed: User confirms/Force
    }
    
    state Stage1_Uninstall {
        [*] --> Licenses_Cleared
        Licenses_Cleared --> Processes_Stopped
        Processes_Stopped --> Tasks_Removed
        Tasks_Removed --> ODT_Executed
        ODT_Executed --> MSI_Executed
        MSI_Executed --> RebootRequired_Set: Exit code 3010
        MSI_Executed --> Continue: Exit code 0
    }
    
    state Stage2_Cleanup {
        [*] --> Registry_Cleaned
        Registry_Cleaned --> Files_Deleted
        Files_Deleted --> DelInUse_Populated: Locked files
        Files_Deleted --> Complete: All deleted
    }
    
    state Stage3_Summary {
        [*] --> Calculate_Status
        Calculate_Status --> Log_Results
        Log_Results --> Check_Reboot
        Check_Reboot --> Prompt_Reboot: RebootRequired=true
        Check_Reboot --> Exit: RebootRequired=false
        Prompt_Reboot --> Execute_Reboot: User confirms
        Prompt_Reboot --> Exit: User declines
    }
    
    Initialized --> Stage0_Detection: Continue
    Initialized --> [*]: ErrorCode_128
    
    Stage0_Detection --> DecisionPoint1
    
    DecisionPoint1 --> Stage3_Summary: Exit_DetectOnly
    DecisionPoint1 --> Stage3_Summary: Exit_Cancelled
    DecisionPoint1 --> Stage1_Uninstall: Proceed
    
    Stage1_Uninstall --> Stage2_Cleanup
    Stage2_Cleanup --> Stage3_Summary
    Stage3_Summary --> [*]
Loading

Diagram: Pipeline State Transitions

Sources: OfficeScrubC2R.ps1:924-1001


Control Flow Decision Points

The pipeline contains multiple decision points that alter execution paths based on parameters and runtime conditions.

Decision Point Summary

flowchart TD
    Start["Pipeline Entry"]
    
    D1{"NoElevate=false<br/>AND<br/>NOT IsElevated?"}
    E1["Set ERROR_ELEVATION<br/>Exit immediately"]
    
    D2{"No products found?"}
    E2["Skip to Stage 3<br/>Return SUCCESS"]
    
    D3{"DetectOnly=true?"}
    E3["Skip to Stage 3<br/>Return SUCCESS"]
    
    D4{"NOT Force<br/>AND<br/>NOT Quiet?"}
    Prompt["Prompt user"]
    D5{"User confirms?"}
    E4["Set ERROR_USERCANCEL<br/>Skip to Stage 3"]
    
    D6{"ErrorCode has<br/>USER_CANCEL?"}
    E5["Skip remaining stage<br/>operations"]
    
    D7{"Offline=true?"}
    E6["Skip ODT download<br/>Skip ODT execution"]
    
    D8{"KeepLicense=true?"}
    E7["Skip license cleanup"]
    
    D9{"C2RSuite.Count == 0<br/>OR<br/>NOT KeepSku?"}
    E8["Execute<br/>Clear-ShellIntegration"]
    
    ProceedStage1["Stage 1: Uninstall"]
    ProceedStage2["Stage 2: Cleanup"]
    ProceedStage3["Stage 3: Summary"]
    
    Start --> D1
    D1 -->|Yes| E1
    D1 -->|No| D2
    D2 -->|Yes| E2
    D2 -->|No| D3
    D3 -->|Yes| E3
    D3 -->|No| D4
    D4 -->|Yes| Prompt
    D4 -->|No| D7
    Prompt --> D5
    D5 -->|No| E4
    D5 -->|Yes| D6
    D6 -->|Yes| E5
    D6 -->|No| D7
    D7 -->|Yes| E6
    D7 -->|No| D8
    D8 -->|Yes| E7
    D8 -->|No| D9
    D9 -->|Yes| E8
    D9 -->|No| ProceedStage1
    E6 --> ProceedStage1
    E7 --> ProceedStage1
    E8 --> ProceedStage1
    ProceedStage1 --> ProceedStage2
    ProceedStage2 --> ProceedStage3
    E2 --> ProceedStage3
    E3 --> ProceedStage3
    E4 --> ProceedStage3
    E5 --> ProceedStage3
Loading

Diagram: Complete Decision Point Flow

Sources: OfficeScrubC2R.ps1:87-1001


Error Handling and Recovery

The pipeline implements comprehensive error handling with graceful degradation and detailed error reporting.

Error Handling Patterns

  1. Early Exit on Critical Errors

  2. Non-Fatal Error Tracking

    • Stage 1 MSI failures OfficeScrubC2R.ps1:488-490 set ERROR_STAGE1 but continue
    • Locked file errors accumulate in $script:DelInUse for reboot handling
    • Individual operation failures are logged but don't halt pipeline
  3. Try-Catch in Main OfficeScrubC2R.ps1:990-996

    • Catches unhandled exceptions
    • Logs error message and stack trace
    • Sets ERROR_UNKNOWN
    • Ensures Show-Summary executes
  4. Finally Block OfficeScrubC2R.ps1:997-1000

    • Guarantees Close-Log always executes
    • Ensures log file is flushed and closed properly

Reboot Recovery Mechanism

When reboot is required OfficeScrubC2R.ps1:837-843:

  1. Locked files are added to PendingFileRenameOperations registry key
  2. Windows deletes these files during boot sequence before file system is locked
  3. $script:RebootRequired flag triggers user notification
  4. Exit code includes ERROR_REBOOT_REQUIRED (3010)
  5. Script can be re-run after reboot to complete cleanup

Sources: OfficeScrubC2R.ps1:924-1001


Performance Considerations

The pipeline implements several performance optimizations to minimize execution time:

Batch Processing

Single-Pass Operations

  • Registry Enumeration: Functions like Clear-OfficeHKLM enumerate keys once and process recursively
  • Metadata Validation: Ensure-ValidWIMetadata validates multiple locations but only one pass per location

Native Code Delegation

Critical operations delegate to OfficeScrubNative.dll:

  • $script:Orchestrator.Registry.* for 30x faster registry operations
  • $script:Orchestrator.Processes.TerminateProcesses for parallel termination
  • $script:Orchestrator.Shell.RestartExplorer for Explorer restart

See Performance Architecture for detailed analysis of native optimizations.

Sources: OfficeScrubC2R.ps1:1-1009, docs/CHANGELOG.md:46-102


Stage Execution Summary

Stage Entry Point Conditional Execution Typical Duration Critical Operations
Init Initialize-Script Never skipped 1-2 seconds Environment setup, logging init
Stage 0 Find-InstalledOfficeProducts Never skipped 5-10 seconds Metadata validation, product detection
Decision Main 946-961 Based on DetectOnly/Force/Quiet <1 second User prompt, mode selection
Stage 1 Uninstall-OfficeProducts Skipped if DetectOnly or cancelled 60-180 seconds ODT uninstall, MSI removal
Stage 2a Clean-OfficeRegistry Skipped if DetectOnly or cancelled 10-30 seconds Registry cleanup
Stage 2b Complete-Cleanup Skipped if DetectOnly or cancelled 20-60 seconds File/folder deletion
Stage 3 Show-Summary Always executes 1-2 seconds Results reporting, reboot prompt

Total execution time (full removal): 90-280 seconds depending on Office installation size and system performance.

Sources: OfficeScrubC2R.ps1:1-1009

Clone this wiki locally