Repository navigation
Three‐Stage Execution Pipeline
Relevant source files
The following files were used as context for generating this wiki page:
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
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.
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; }| 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 |
LicenseHelperProcessHelperShellHelper
|
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 |
RegistryHelperWindowsInstallerHelperTypeLibHelper
|
HKCU\Software\Microsoft\OfficeHKLM\SOFTWARE\...Installer\TypeLib registrations |
| Stage 3: File System Cleanup | Service deletion Office folder removal Locked file scheduling |
ServiceHelperFileHelper
|
ClickToRunSvc, OfficeSvc Program Files\Microsoft Office ProgramData\Microsoft\ClickToRun |
Sources: OfficeScrubC2R-Native.cs:1315-1350, direct_conversion.cs:233-304
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
Diagram: Three-Stage Pipeline with Native Helper Invocations
Sources: OfficeScrubC2R-Native.cs:1315-1383, direct_conversion.cs:139-304
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
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
Before entering the three-stage pipeline, the system performs product detection and scope validation using the orchestrator's helper methods.
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\OFFICE1Microsoft Office\Root\\microsoft shared\ClickToRun\Microsoft Office\PackageManifests\Microsoft Office\PackageSunrisePoliciesMicrosoft Office 15Microsoft 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:
- Special Products HashSet OfficeScrubC2R-Native.cs:1323-1327: O(1) lookup for known GUIDs
-
Office ID Suffix OfficeScrubC2R-Native.cs:31:
{0000000FF1CE} - Version Check: Must be Office 15 (2013) or later
-
SKU Validation OfficeScrubC2R-Native.cs:1318-1321: HashSet contains
007E,008F,008C,24E1,237A,00DD
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
Diagram: Product Detection with Native Scope Validation
Sources: OfficeScrubC2R-Native.cs:1318-1382, direct_conversion.cs:872-1195
Stage 1 is orchestrated by Uninstall-OfficeProducts OfficeScrubC2R.ps1:187-247 which implements a multi-step uninstallation process.
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
Diagram: Stage 1 Uninstall Sequence
Sources: OfficeScrubC2R.ps1:187-247
The Uninstall-OfficeC2R function OfficeScrubC2R.ps1:249-282 implements Office Deployment Tool-based removal for Click-to-Run products.
ODT Uninstall Process:
-
XML Configuration Generation OfficeScrubC2R.ps1:252-257
-
ODT Download OfficeScrubC2R.ps1:260-261
- Calls
Download-ODT296-313 unless-Offlinemode - Downloads from Microsoft CDN:
OfficeDeploymentTool_12325-20288.exe - Saves to
$script:ScrubDir\setup.exe
- Calls
-
ODT Execution OfficeScrubC2R.ps1:262-279
Sources: OfficeScrubC2R.ps1:249-313
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:
-
Collection Phase 326-348
- Queries registry for
PackageFolderandPackageGUIDfrom three locations:SOFTWARE\Microsoft\Office\15.0\ClickToRunSOFTWARE\Microsoft\Office\16.0\ClickToRunSOFTWARE\Microsoft\Office\ClickToRun
- Scans
$packageFolder\root\IntegrationforC2RManifest*.xmlfiles - Appends all manifests to
$allManifestFilesarray - Prepares integrator tasks for later execution 340-347
- Queries registry for
-
Batch Deletion Phase 351-368
-
Integrator Execution Phase 370-377
- Runs each integrator with arguments:
/U /Extension PackageRoot="..." PackageGUID=... - Waits for completion and logs exit codes
- Runs each integrator with arguments:
Sources: OfficeScrubC2R.ps1:315-378
The Uninstall-MSIProducts function OfficeScrubC2R.ps1:421-491 handles MSI-based Office installations using Windows Installer automation.
MSI Uninstall Algorithm:
-
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)
-
- Creates COM object:
-
Command Preparation OfficeScrubC2R.ps1:446-462
- Pre-builds all
msiexec.execommand 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"
- Arguments:
- Stores in
$msiexecArgsListfor batch processing
- Pre-builds all
-
Process Termination OfficeScrubC2R.ps1:464
- Calls
Stop-OfficeProcessesto prevent file locking
- Calls
-
Batch Execution OfficeScrubC2R.ps1:466-479
- Skipped if
-DetectOnlymode - Sequentially executes each prepared msiexec command
- Monitors exit codes:
-
3010: Sets reboot flag 474-477 - Other codes: Logged but not treated as fatal
-
- Skipped if
-
Cleanup OfficeScrubC2R.ps1:483-484
- Stops Windows Installer service:
net stop msiserver
- Stops Windows Installer service:
Sources: OfficeScrubC2R.ps1:421-491
Stage 2 consists of two major functions that perform exhaustive cleanup of remaining Office artifacts.
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:
-
Descend 552-555
- Gets all subkeys via
Get-RegistryKeys - Recursively calls itself for each subkey
- Gets all subkeys via
-
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
-
Prune Empty Keys 567-569
- If key has no subkeys AND no values AND
KeepSku.Count == 0 - Removes the empty key
- If key has no subkeys AND no values AND
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-WindowsInstallerMetadatafrom utilities -
TypeLib Registrations 545: Calls
Clear-TypeLibRegistrationsfor COM cleanup
Sources: OfficeScrubC2R.ps1:497-598
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
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
The Show-Summary function OfficeScrubC2R.ps1:867-918 provides comprehensive results reporting and handles reboot prompts.
-
Error Code Update OfficeScrubC2R.ps1:871
- Calls
Set-ReturnValue $script:ErrorCode
- Calls
-
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" -
Detailed Error Logging OfficeScrubC2R.ps1:885-897
- If
ERROR_FAILflag 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"
-
- If
-
Reboot Handling OfficeScrubC2R.ps1:902-915
- If
$script:RebootRequired == $true:- Displays prominent reboot notification
- If NOT
-Quietmode:- Prompts user: "Do you want to reboot now? (Y/N)"
- If user confirms: Executes
Restart-Computer -Force
- If
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
The pipeline maintains state through script-scoped variables that control execution flow and track operation status.
| 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 |
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 --> [*]
Diagram: Pipeline State Transitions
Sources: OfficeScrubC2R.ps1:924-1001
The pipeline contains multiple decision points that alter execution paths based on parameters and runtime conditions.
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
Diagram: Complete Decision Point Flow
Sources: OfficeScrubC2R.ps1:87-1001
The pipeline implements comprehensive error handling with graceful degradation and detailed error reporting.
-
Early Exit on Critical Errors
- Initialization failure OfficeScrubC2R.ps1:927-929 returns
ERROR_SCRIPTINIT - Elevation failure OfficeScrubC2R.ps1:113-117 sets
ERROR_ELEVATIONand returns - User cancellation OfficeScrubC2R.ps1:956-960 sets
ERROR_USERCANCELand exits
- Initialization failure OfficeScrubC2R.ps1:927-929 returns
-
Non-Fatal Error Tracking
- Stage 1 MSI failures OfficeScrubC2R.ps1:488-490 set
ERROR_STAGE1but continue - Locked file errors accumulate in
$script:DelInUsefor reboot handling - Individual operation failures are logged but don't halt pipeline
- Stage 1 MSI failures OfficeScrubC2R.ps1:488-490 set
-
Try-Catch in Main OfficeScrubC2R.ps1:990-996
- Catches unhandled exceptions
- Logs error message and stack trace
- Sets
ERROR_UNKNOWN - Ensures
Show-Summaryexecutes
-
Finally Block OfficeScrubC2R.ps1:997-1000
- Guarantees
Close-Logalways executes - Ensures log file is flushed and closed properly
- Guarantees
When reboot is required OfficeScrubC2R.ps1:837-843:
- Locked files are added to
PendingFileRenameOperationsregistry key - Windows deletes these files during boot sequence before file system is locked
-
$script:RebootRequiredflag triggers user notification - Exit code includes
ERROR_REBOOT_REQUIRED(3010) - Script can be re-run after reboot to complete cleanup
Sources: OfficeScrubC2R.ps1:924-1001
The pipeline implements several performance optimizations to minimize execution time:
- Manifest Deletion OfficeScrubC2R.ps1:351-368: Collects all files first, then deletes in batch
- MSI Command Preparation OfficeScrubC2R.ps1:447-462: Pre-builds all commands before execution
- Process Termination OfficeScrubC2R.ps1:679-685: Uses native orchestrator's parallel termination
-
Registry Enumeration: Functions like
Clear-OfficeHKLMenumerate keys once and process recursively -
Metadata Validation:
Ensure-ValidWIMetadatavalidates multiple locations but only one pass per location
Critical operations delegate to OfficeScrubNative.dll:
-
$script:Orchestrator.Registry.*for 30x faster registry operations -
$script:Orchestrator.Processes.TerminateProcessesfor parallel termination -
$script:Orchestrator.Shell.RestartExplorerfor Explorer restart
See Performance Architecture for detailed analysis of native optimizations.
Sources: OfficeScrubC2R.ps1:1-1009, docs/CHANGELOG.md:46-102
| 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