Repository navigation
OfficeScrubOrchestrator
Relevant source files
The following files were used as context for generating this wiki page:
The OfficeScrubOrchestrator class is the central coordinator for all Office cleanup operations in the OfficeScrubC2R native library. It instantiates and manages the eight specialized helper classes, provides dependency injection for helpers that require shared resources, and exposes two key validation methods (IsC2RPath and IsInScope) used throughout the cleanup workflow.
This page documents the orchestrator's architecture, instantiation pattern, public interface, and dependency management. For documentation on individual helper classes, see Helper Classes. For information on how PowerShell calls into the orchestrator, see PowerShell Module.
Sources: OfficeScrubC2R-Native.cs:1349-1409
The OfficeScrubOrchestrator class serves three primary functions:
- Helper Management: Instantiates and exposes all eight helper classes as public properties
-
Dependency Injection: Provides the
RegistryHelperinstance to helpers that require registry access -
Scope Validation: Implements
IsC2RPath()andIsInScope()to determine which Office artifacts should be cleaned
classDiagram
class OfficeScrubOrchestrator {
+RegistryHelper Registry
+FileHelper Files
+ProcessHelper Processes
+ShellHelper Shell
+WindowsInstallerHelper WindowsInstaller
+TypeLibHelper TypeLib
+LicenseHelper License
+ServiceHelper Services
+OfficeScrubOrchestrator(bool is64Bit)
+bool IsC2RPath(string path)
+bool IsInScope(string productCode)
}
class RegistryHelper {
-bool _is64Bit
+bool KeyExists()
+bool DeleteKey()
+string[] EnumerateKeys()
}
class FileHelper {
-List~string~ _pendingDeletes
+bool DeleteFile()
+bool DeleteDirectory()
}
class ProcessHelper {
+List~int~ TerminateProcesses()
+bool IsProcessRunning()
}
class ShellHelper {
+bool UnpinFromTaskbar()
+bool UnpinFromStartMenu()
}
class WindowsInstallerHelper {
-RegistryHelper _regHelper
+void CleanupUpgradeCodes()
+void CleanupProducts()
}
class TypeLibHelper {
-RegistryHelper _regHelper
+void CleanupKnownTypeLibs()
}
class LicenseHelper {
+void CleanOSPP()
+void ClearVNextLicenseCache()
}
class ServiceHelper {
+bool DeleteService()
}
OfficeScrubOrchestrator --> RegistryHelper : creates
OfficeScrubOrchestrator --> FileHelper : creates
OfficeScrubOrchestrator --> ProcessHelper : creates
OfficeScrubOrchestrator --> ShellHelper : creates
OfficeScrubOrchestrator --> WindowsInstallerHelper : creates
OfficeScrubOrchestrator --> TypeLibHelper : creates
OfficeScrubOrchestrator --> LicenseHelper : creates
OfficeScrubOrchestrator --> ServiceHelper : creates
WindowsInstallerHelper ..> RegistryHelper : depends on
TypeLibHelper ..> RegistryHelper : depends on
Sources: OfficeScrubC2R-Native.cs:1349-1370
The orchestrator has a single constructor that accepts a boolean flag indicating the operating system architecture:
public OfficeScrubOrchestrator(bool is64Bit)Parameters:
-
is64Bit:truefor 64-bit Windows,falsefor 32-bit Windows. This flag determines WOW64 registry handling behavior.
The constructor executes the following initialization sequence:
sequenceDiagram
participant PowerShell as "PowerShell Layer"
participant Orch as "OfficeScrubOrchestrator"
participant RegHelper as "RegistryHelper"
participant WIHelper as "WindowsInstallerHelper"
participant TLHelper as "TypeLibHelper"
PowerShell->>Orch: new(is64Bit)
Note over Orch: Step 1: Create independent helpers
Orch->>RegHelper: new RegistryHelper(is64Bit)
RegHelper-->>Orch: instance
Orch->>Orch: new FileHelper()
Orch->>Orch: new ProcessHelper()
Orch->>Orch: new ShellHelper()
Orch->>Orch: new LicenseHelper()
Orch->>Orch: new ServiceHelper()
Note over Orch: Step 2: Create dependent helpers with injection
Orch->>WIHelper: new WindowsInstallerHelper(Registry)
WIHelper-->>Orch: instance
Orch->>TLHelper: new TypeLibHelper(Registry)
TLHelper-->>Orch: instance
Orch-->>PowerShell: Orchestrator instance
Two helper classes require registry access and receive the RegistryHelper instance via constructor injection:
| Helper Class | Dependencies | Injection Point |
|---|---|---|
WindowsInstallerHelper |
RegistryHelper |
OfficeScrubC2R-Native.cs:1366 |
TypeLibHelper |
RegistryHelper |
OfficeScrubC2R-Native.cs:1367 |
This pattern ensures:
- Single
RegistryHelperinstance with consistent WOW64 configuration - Shared registry operation caching across dependent helpers
- Centralized architecture detection (
_is64Bitflag)
Sources: OfficeScrubC2R-Native.cs:1360-1369, OfficeScrubC2R-Native.cs:980-987, OfficeScrubC2R-Native.cs:1134-1157
The orchestrator exposes eight helper classes as public read-only properties:
| Property Name | Type | Purpose | Location |
|---|---|---|---|
Registry |
RegistryHelper |
Registry operations with WOW64 support | OfficeScrubC2R-Native.cs:1351 |
Files |
FileHelper |
File/directory deletion with reboot scheduling | OfficeScrubC2R-Native.cs:1352 |
Processes |
ProcessHelper |
Process termination with parallel execution | OfficeScrubC2R-Native.cs:1353 |
Shell |
ShellHelper |
Shell integration (taskbar/start menu) | OfficeScrubC2R-Native.cs:1354 |
WindowsInstaller |
WindowsInstallerHelper |
MSI metadata cleanup | OfficeScrubC2R-Native.cs:1355 |
TypeLib |
TypeLibHelper |
COM type library cleanup | OfficeScrubC2R-Native.cs:1356 |
License |
LicenseHelper |
Office license cleanup via SPP/OSPP | OfficeScrubC2R-Native.cs:1357 |
Services |
ServiceHelper |
Windows service deletion | OfficeScrubC2R-Native.cs:1358 |
PowerShell code can access helpers through the orchestrator instance:
$orchestrator = New-OfficeScrubOrchestrator
$orchestrator.Registry.DeleteKey(...)
$orchestrator.Files.DeleteDirectory(...)The Get-OfficeScrubHelpers function provides a convenient wrapper that returns all helpers as a PSCustomObject:
Sources: OfficeScrubC2R.psm1:76-96, OfficeScrubC2R-Native.cs:1351-1358
Determines whether a given file path contains Click-to-Run (C2R) installation markers.
Signature:
public bool IsC2RPath(string path)Parameters:
-
path: File or directory path to check
Returns: true if path contains any C2R pattern, false otherwise
Implementation:
flowchart TD
Start["IsC2RPath(path)"] --> NullCheck{"path null<br/>or empty?"}
NullCheck -->|Yes| ReturnFalse["return false"]
NullCheck -->|No| CheckPatterns["Iterate C2R_PATTERNS array"]
CheckPatterns --> Pattern1["Pattern: \\ROOT\\OFFICE1"]
CheckPatterns --> Pattern2["Pattern: Microsoft Office\\Root\\"]
CheckPatterns --> Pattern3["Pattern: \\microsoft shared\\ClickToRun"]
CheckPatterns --> Pattern4["Pattern: \\Microsoft Office\\PackageManifests"]
CheckPatterns --> Pattern5["Pattern: \\Microsoft Office\\PackageSunrisePolicies"]
CheckPatterns --> Pattern6["Pattern: Microsoft Office 15"]
CheckPatterns --> Pattern7["Pattern: Microsoft Office 16"]
Pattern1 --> AnyMatch{"Any pattern found<br/>using IndexOf<br/>(OrdinalIgnoreCase)?"}
Pattern2 --> AnyMatch
Pattern3 --> AnyMatch
Pattern4 --> AnyMatch
Pattern5 --> AnyMatch
Pattern6 --> AnyMatch
Pattern7 --> AnyMatch
AnyMatch -->|Yes| ReturnTrue["return true"]
AnyMatch -->|No| ReturnFalse
Pattern Source: The method checks against OfficeConstants.C2R_PATTERNS, defined at OfficeScrubC2R-Native.cs:36-45.
Usage: Called by cleanup logic to determine if an installation path represents a Click-to-Run deployment rather than MSI-based Office.
Sources: OfficeScrubC2R-Native.cs:1372-1379, OfficeScrubC2R-Native.cs:36-45
Determines whether a given Office product GUID should be included in the cleanup operation based on version and SKU validation.
Signature:
public bool IsInScope(string productCode)Parameters:
-
productCode: Office product GUID in standard format (38 characters with braces and dashes, e.g.,{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX})
Returns: true if product should be cleaned, false otherwise
Validation Flow:
flowchart TD
Start["IsInScope(productCode)"] --> LengthCheck{"productCode length == 38?"}
LengthCheck -->|No| ReturnFalse["return false"]
LengthCheck -->|Yes| ToUpper["upper = productCode.ToUpperInvariant()"]
ToUpper --> EndCheck{"upper.EndsWith<br/>OFFICE_ID<br/>(0000000FF1CE})?"}
EndCheck -->|No| ReturnFalse
EndCheck -->|Yes| VersionParse["Parse version from<br/>substring(3, 2)"]
VersionParse --> VersionCheck{"version > 14?"}
VersionCheck -->|No| ReturnFalse
VersionCheck -->|Yes| SKUExtract["Extract SKU from<br/>substring(10, 4)"]
SKUExtract --> SKUCheck{"SKU in validSkus<br/>007E, 008F, 008C<br/>24E1, 237A, 00DD?"}
SKUCheck -->|Yes| ReturnTrue["return true"]
SKUCheck -->|No| SpecialCheck{"productCode in<br/>special products list?"}
SpecialCheck -->|Yes| ReturnTrue
SpecialCheck -->|No| ReturnFalse
Validation Rules:
| Check | Rule | Location |
|---|---|---|
| Length | Must be exactly 38 characters | OfficeScrubC2R-Native.cs:1383 |
| Office ID | Must end with 0000000FF1CE}
|
OfficeScrubC2R-Native.cs:1388 |
| Version | Must be > 14 (Office 2010+) | OfficeScrubC2R-Native.cs:1393 |
| SKU | Must match valid SKU codes | OfficeScrubC2R-Native.cs:1397-1398 |
| Special Products | Three hardcoded special GUIDs | OfficeScrubC2R-Native.cs:1401-1403 |
Valid SKU Codes:
-
007E,008F,008C- Standard editions -
24E1,237A,00DD- Volume license editions
Special Product GUIDs:
{6C1ADE97-24E1-4AE4-AEDD-86D3A209CE60}{9520DDEB-237A-41DB-AA20-F2EF2360DCEB}{9AC08E99-230B-47E8-9721-4577B7F124EA}
Usage: Passed as a callback (shouldDelete) to WindowsInstallerHelper methods for filtering MSI metadata.
Sources: OfficeScrubC2R-Native.cs:1381-1404, OfficeScrubC2R-Native.cs:29-56
The PowerShell module provides a factory function for instantiation:
function New-OfficeScrubOrchestrator {
param([bool]$Is64Bit = [Environment]::Is64BitOperatingSystem)
Import-OfficeScrubNative | Out-Null
return [OfficeScrubNative.OfficeScrubOrchestrator]::new($Is64Bit)
}Default Behavior: Automatically detects OS architecture via [Environment]::Is64BitOperatingSystem.
Manual Override: Callers can force 32-bit or 64-bit mode by passing explicit $Is64Bit parameter.
Sources: OfficeScrubC2R.psm1:37-46
PowerShell wrapper for IsC2RPath() method:
function Test-OfficeC2RPath {
param(
[Parameter(Mandatory)][string]$Path,
[OfficeScrubNative.OfficeScrubOrchestrator]$Orchestrator
)
if (-not $Orchestrator) {
$Orchestrator = New-OfficeScrubOrchestrator
}
return $Orchestrator.IsC2RPath($Path)
}Auto-Creation: If no orchestrator is provided, function creates one automatically.
Sources: OfficeScrubC2R.psm1:48-60
PowerShell wrapper for IsInScope() method:
function Test-OfficeProductScope {
param(
[Parameter(Mandatory)][string]$ProductCode,
[OfficeScrubNative.OfficeScrubOrchestrator]$Orchestrator
)
if (-not $Orchestrator) {
$Orchestrator = New-OfficeScrubOrchestrator
}
return $Orchestrator.IsInScope($ProductCode)
}Auto-Creation: If no orchestrator is provided, function creates one automatically.
Sources: OfficeScrubC2R.psm1:62-74
PowerShell function that returns all helpers as a custom object:
function Get-OfficeScrubHelpers {
param([OfficeScrubNative.OfficeScrubOrchestrator]$Orchestrator)
if (-not $Orchestrator) {
$Orchestrator = New-OfficeScrubOrchestrator
}
[pscustomobject]@{
RegistryHelper = $Orchestrator.Registry
FileHelper = $Orchestrator.Files
ProcessHelper = $Orchestrator.Processes
ShellHelper = $Orchestrator.Shell
WindowsInstallerHelper = $Orchestrator.WindowsInstaller
TypeLibHelper = $Orchestrator.TypeLib
LicenseHelper = $Orchestrator.License
ServiceHelper = $Orchestrator.Services
}
}Return Type: PSCustomObject with eight properties, each containing a helper instance.
Sources: OfficeScrubC2R.psm1:76-96
An optimized version of the orchestrator exists in the codebase that improves performance through static collections:
// Static HashSets for O(1) lookup instead of O(n) array operations
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}"
};
public bool IsInScope(string productCode)
{
// Check special products first with O(1) HashSet lookup
if (SpecialProducts.Contains(upper))
return true;
// SKU check uses HashSet instead of array Contains
var sku = upper.Substring(10, 4);
return ValidSkus.Contains(sku);
}Performance Improvement: Changes SKU validation from O(n) array iteration to O(1) HashSet lookup.
Trade-off: Slightly higher memory usage for static collections vs. dynamic array allocation per call.
Sources: docs/OfficeScrubC2R-Native-Optimized-Real.cs:1317-1382
The orchestrator establishes the following dependency relationships:
graph TB
subgraph "OfficeScrubOrchestrator"
Orch["OfficeScrubOrchestrator<br/>Constructor(is64Bit)"]
end
subgraph "Independent Helpers"
RegHelper["RegistryHelper<br/>(requires is64Bit)"]
FileHelper["FileHelper<br/>(no dependencies)"]
ProcHelper["ProcessHelper<br/>(no dependencies)"]
ShellHelper["ShellHelper<br/>(no dependencies)"]
LicHelper["LicenseHelper<br/>(no dependencies)"]
SvcHelper["ServiceHelper<br/>(no dependencies)"]
end
subgraph "Dependent Helpers"
WIHelper["WindowsInstallerHelper<br/>(requires RegistryHelper)"]
TLHelper["TypeLibHelper<br/>(requires RegistryHelper)"]
end
Orch -->|"new(is64Bit)"| RegHelper
Orch -->|"new()"| FileHelper
Orch -->|"new()"| ProcHelper
Orch -->|"new()"| ShellHelper
Orch -->|"new()"| LicHelper
Orch -->|"new()"| SvcHelper
Orch -->|"new(Registry)"| WIHelper
Orch -->|"new(Registry)"| TLHelper
RegHelper -.->|"injected into"| WIHelper
RegHelper -.->|"injected into"| TLHelper
Initialization Order:
-
RegistryHelpercreated first withis64Bitflag - Independent helpers created (no constructor parameters)
- Dependent helpers created with
Registryinjection
Sources: OfficeScrubC2R-Native.cs:1360-1369
$orch = New-OfficeScrubOrchestrator
# Test if path is C2R installation
$isC2R = $orch.IsC2RPath("C:\Program Files\Microsoft Office\root")
# Test if product GUID is in scope
$inScope = $orch.IsInScope("{90160000-008F-0000-1000-0000000FF1CE}")$orch = New-OfficeScrubOrchestrator
# Access helpers through properties
$orch.Registry.DeleteKey([OfficeScrubNative.RegistryHiveType]::LocalMachine, "Software\Microsoft\Office\16.0")
$orch.Files.DeleteDirectory("C:\ProgramData\Microsoft\Office")
$pids = $orch.Processes.TerminateProcesses($processNames, 5000)$orch = New-OfficeScrubOrchestrator
# Pass IsInScope as callback for filtering
$shouldDelete = [Func[string,bool]]{ param($guid) $orch.IsInScope($guid) }
$orch.WindowsInstaller.CleanupProducts($shouldDelete)
$orch.WindowsInstaller.CleanupUpgradeCodes($shouldDelete)Sources: OfficeScrubC2R.psm1:37-96, OfficeScrubC2R-Native.cs:989-1039
The OfficeScrubOrchestrator implements a Facade Pattern with Dependency Injection:
| Design Pattern | Implementation | Benefit |
|---|---|---|
| Facade | Single entry point for 8 helper classes | Simplified API surface |
| Dependency Injection | Constructor injection of RegistryHelper
|
Shared state, consistent WOW64 handling |
| Factory |
New-OfficeScrubOrchestrator in PowerShell |
Architecture auto-detection |
| Composition | Properties expose helper instances | Direct access when needed |
This architecture provides:
- Centralized initialization with consistent configuration
- Simplified PowerShell usage through wrappers
-
Reusable validation logic (
IsC2RPath,IsInScope) - Dependency management for helpers requiring shared resources
Sources: OfficeScrubC2R-Native.cs:1349-1409, OfficeScrubC2R.psm1:37-96