Skip to content

OfficeScrubOrchestrator

Calvindd2f edited this page Jan 14, 2026 · 1 revision

OfficeScrubOrchestrator

Relevant source files

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

Purpose

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


Class Structure and Responsibilities

The OfficeScrubOrchestrator class serves three primary functions:

  1. Helper Management: Instantiates and exposes all eight helper classes as public properties
  2. Dependency Injection: Provides the RegistryHelper instance to helpers that require registry access
  3. Scope Validation: Implements IsC2RPath() and IsInScope() to determine which Office artifacts should be cleaned

Orchestrator Class Diagram

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
Loading

Sources: OfficeScrubC2R-Native.cs:1349-1370


Instantiation and Initialization

Constructor Signature

The orchestrator has a single constructor that accepts a boolean flag indicating the operating system architecture:

public OfficeScrubOrchestrator(bool is64Bit)

Parameters:

  • is64Bit: true for 64-bit Windows, false for 32-bit Windows. This flag determines WOW64 registry handling behavior.

Initialization Sequence

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
Loading

Dependency Injection Pattern

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 RegistryHelper instance with consistent WOW64 configuration
  • Shared registry operation caching across dependent helpers
  • Centralized architecture detection (_is64Bit flag)

Sources: OfficeScrubC2R-Native.cs:1360-1369, OfficeScrubC2R-Native.cs:980-987, OfficeScrubC2R-Native.cs:1134-1157


Public Properties

The orchestrator exposes eight helper classes as public read-only properties:

Helper Properties Table

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

Property Access from PowerShell

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


Public Methods

IsC2RPath Method

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
Loading

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


IsInScope Method

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
Loading

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


PowerShell Integration

Creation via New-OfficeScrubOrchestrator

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


Usage via Test-OfficeC2RPath

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


Usage via Test-OfficeProductScope

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


Helper Access via Get-OfficeScrubHelpers

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


Optimized Implementation Variant

An optimized version of the orchestrator exists in the codebase that improves performance through static collections:

Performance Optimizations

// 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


Dependency Graph

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
Loading

Initialization Order:

  1. RegistryHelper created first with is64Bit flag
  2. Independent helpers created (no constructor parameters)
  3. Dependent helpers created with Registry injection

Sources: OfficeScrubC2R-Native.cs:1360-1369


Usage Patterns

Pattern 1: Direct Method Access

$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}")

Pattern 2: Helper Property Access

$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)

Pattern 3: Callback Usage with WindowsInstallerHelper

$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


Architecture Summary

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

Clone this wiki locally