Skip to content

High‐Level Design

Calvindd2f edited this page Jan 14, 2026 · 1 revision
Relevant source files

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

Purpose and Scope

This document describes the three-tier architectural design of the OfficeScrubC2R system. It explains the responsibilities of each architectural layer and how they interact to provide a high-performance Office Click-to-Run removal tool.

For detailed information about module loading and initialization sequences, see Module Structure. For the execution workflow and cleanup stages, see Three-Stage Execution Pipeline. For performance optimization techniques, see Performance Architecture.

Sources: OfficeScrubC2R.psm1:1-233, README.md:144-174, docs/memory-bank/structure.md:55-94


Architecture Overview

OfficeScrubC2R implements a three-tier hybrid architecture that combines PowerShell's orchestration capabilities with C# native code performance. This design achieves 10-50x performance improvements over the original VBScript implementation while maintaining compatibility with PowerShell 5.1+ and PowerShell Core 7+.

Architectural Layers

graph TB
    subgraph Layer1["PowerShell Orchestration Layer"]
        PSM1["OfficeScrubC2R.psm1<br/>Public API & Parameter Processing"]
        PS1["OfficeScrubC2R.ps1<br/>Main() Function & Pipeline Logic"]
        Manifest["OfficeScrubC2R.psd1<br/>Module Manifest"]
    end
    
    subgraph Layer2["Utilities Layer"]
        Utils["OfficeScrubC2R-Utilities.psm1"]
        RegOps["Get-RegistryValue<br/>Remove-RegistryKey<br/>Test-RegistryKeyExists"]
        FileOps["Remove-FolderRecursive<br/>Remove-FileForced<br/>AddPendingFileRename"]
        ProcOps["Stop-OfficeProcesses<br/>TerminateProcesses"]
        DetectOps["Test-IsC2R<br/>Test-ProductInScope<br/>Get-InstalledOfficeProducts"]
        LogOps["Write-Log<br/>Initialize-Log<br/>Write-LogHeader"]
    end
    
    subgraph Layer3["Native Performance Layer"]
        DLL["OfficeScrubNative.dll"]
        Orchestrator["OfficeScrubOrchestrator"]
        RegHelper["RegistryHelper"]
        FileHelper["FileHelper"]
        ProcHelper["ProcessHelper"]
        WIHelper["WindowsInstallerHelper"]
        OtherHelpers["TypeLibHelper<br/>LicenseHelper<br/>ServiceHelper<br/>GuidHelper<br/>ShellHelper"]
    end
    
    subgraph WinAPIs["Windows System APIs"]
        AdvAPI["advapi32.dll<br/>Registry APIs"]
        Kernel["kernel32.dll<br/>File System APIs"]
        Shell["shell32.dll<br/>Shell APIs"]
    end
    
    PSM1 --> PS1
    PSM1 --> Utils
    PS1 --> Utils
    
    Utils --> RegOps
    Utils --> FileOps
    Utils --> ProcOps
    Utils --> DetectOps
    Utils --> LogOps
    
    RegOps --> Orchestrator
    FileOps --> Orchestrator
    ProcOps --> Orchestrator
    DetectOps --> Orchestrator
    
    Orchestrator --> RegHelper
    Orchestrator --> FileHelper
    Orchestrator --> ProcHelper
    Orchestrator --> WIHelper
    Orchestrator --> OtherHelpers
    
    RegHelper --> AdvAPI
    FileHelper --> Kernel
    OtherHelpers --> Shell
Loading

Diagram 1: Three-Tier Architecture with Code Entity Mapping

This diagram maps the system's layered architecture to actual code entities. The PowerShell Orchestration Layer handles user interaction and workflow control, the Utilities Layer provides a consistent API abstraction, and the Native Performance Layer delivers high-speed system operations through Win32 APIs.

Sources: OfficeScrubC2R.psm1:1-233, OfficeScrubC2R-Utilities.psm1:1-end, OfficeScrubC2R-Native.cs:1-end


Layer 1: PowerShell Orchestration Layer

The orchestration layer provides the user-facing interface and controls the overall execution workflow. It consists of three primary files:

Core Components

Component File Primary Responsibility
Public API OfficeScrubC2R.psm1 Exports Invoke-OfficeScrubC2R and parameter validation
Pipeline Logic OfficeScrubC2R.ps1 Implements Main() function and three-stage execution flow
Metadata OfficeScrubC2R.psd1 Defines module manifest, dependencies, and exports

Key Functions

The orchestration layer exposes the following exported functions:

  • Invoke-OfficeScrubC2R - Primary entry point accepting 12 parameters
  • Get-InstalledOfficeProducts - Detection function for Office installations
  • Test-IsC2R - Validation function for Click-to-Run identification
  • Initialize-Environment - Environment setup and orchestrator initialization
  • Stop-OfficeProcesses - Process termination coordinator

Responsibilities

  1. Parameter Processing - Validates and normalizes user input parameters OfficeScrubC2R.psm1:118-157
  2. Privilege Validation - Ensures administrator rights before execution OfficeScrubC2R.psm1:161-168
  3. Version Checking - Validates PowerShell 5.1+ requirement OfficeScrubC2R.psm1:171-173
  4. Workflow Orchestration - Invokes Main() function with script-scoped variables OfficeScrubC2R.psm1:179-194
  5. Error Handling - Catches and reports exceptions with appropriate error codes OfficeScrubC2R.psm1:199-202

Sources: OfficeScrubC2R.psm1:27-117, OfficeScrubC2R.psm1:118-209, OfficeScrubC2R.psd1:1-end


Layer 2: Utilities Layer

The utilities layer provides a PowerShell-friendly abstraction over native operations. It implements common patterns and translates PowerShell idioms to native calls.

Module Architecture

graph LR
    subgraph UtilitiesModule["OfficeScrubC2R-Utilities.psm1"]
        RegistryFunctions["Registry Functions<br/>Get-RegistryValue<br/>Set-RegistryValue<br/>Remove-RegistryKey<br/>Test-RegistryKeyExists<br/>Get-RegistrySubKeys"]
        FileFunctions["File Functions<br/>Remove-FolderRecursive<br/>Remove-FileForced<br/>Test-PathExists<br/>AddPendingFileRename"]
        ProcessFunctions["Process Functions<br/>Stop-OfficeProcesses<br/>Get-RunningProcesses<br/>Test-ProcessRunning"]
        DetectionFunctions["Detection Functions<br/>Get-InstalledOfficeProducts<br/>Test-IsC2R<br/>Test-ProductInScope<br/>Get-OfficeVersion"]
        LoggingFunctions["Logging Functions<br/>Write-Log<br/>Write-LogHeader<br/>Initialize-Log<br/>Write-LogError"]
        GuidFunctions["GUID Functions<br/>Convert-GuidToCompressed<br/>Test-IsValidGuid<br/>Get-ProductCodeFromGuid"]
        InitFunctions["Initialization Functions<br/>Initialize-Environment<br/>Initialize-Orchestrator<br/>Test-ModuleReady"]
    end
    
    RegistryFunctions --> NativeOrchestrator["$script:Orchestrator<br/>OfficeScrubOrchestrator Instance"]
    FileFunctions --> NativeOrchestrator
    ProcessFunctions --> NativeOrchestrator
    DetectionFunctions --> NativeOrchestrator
    GuidFunctions --> NativeOrchestrator
Loading

Diagram 2: Utilities Layer Function Categories

The utilities module groups related operations into functional categories. All functions delegate to a single $script:Orchestrator instance that provides access to the native C# helpers.

Key Patterns

  1. Error Translation - Converts native exceptions to PowerShell error records
  2. Parameter Normalization - Standardizes path formats and string comparisons
  3. Logging Integration - Automatically logs operations for audit trail
  4. WOW64 Handling - Manages 32-bit/64-bit registry redirection transparently
  5. Null Safety - Validates inputs and handles null/empty values gracefully

Script-Scoped State

The utilities module maintains several script-scoped variables for shared state:

Variable Purpose Initialized By
$script:Orchestrator Native DLL facade instance Initialize-Environment
$script:LogDir Current log directory path Initialize-Log
$script:LogFile Current log file path Initialize-Log
$script:ErrorCount Cumulative error counter Write-LogError

Sources: OfficeScrubC2R-Utilities.psm1:1-end, OfficeScrubC2R.psm1:9-16, docs/memory-bank/structure.md:56-72


Layer 3: Native Performance Layer

The native performance layer implements performance-critical operations in compiled C# code that directly invokes Win32 APIs. This layer achieves 30x speedup for registry operations and 15x speedup for file operations compared to pure PowerShell implementations.

OfficeScrubOrchestrator Facade

The native layer uses the Facade pattern through the OfficeScrubOrchestrator class, which provides a unified interface to nine specialized helper classes:

graph TB
    Orchestrator["OfficeScrubOrchestrator<br/>Central Coordinator"]
    
    Orchestrator --> RegHelper["RegistryHelper<br/>30x faster registry ops<br/>WOW64 support<br/>Win32 RegOpenKeyEx"]
    Orchestrator --> FileHelper["FileHelper<br/>15x faster file ops<br/>Reboot scheduling<br/>Win32 MoveFileEx"]
    Orchestrator --> ProcHelper["ProcessHelper<br/>Parallel termination<br/>Timeout handling<br/>Win32 TerminateProcess"]
    Orchestrator --> WIHelper["WindowsInstallerHelper<br/>MSI metadata cleanup<br/>GUID encoding<br/>UpgradeCode handling"]
    Orchestrator --> TypeLibHelper["TypeLibHelper<br/>COM type library cleanup<br/>Registry-based detection"]
    Orchestrator --> LicenseHelper["LicenseHelper<br/>OSPP operations<br/>SPP WMI queries<br/>License cache cleanup"]
    Orchestrator --> ServiceHelper["ServiceHelper<br/>Service management<br/>WMI integration<br/>Dependency handling"]
    Orchestrator --> GuidHelper["GuidHelper<br/>GUID encoding/decoding<br/>Pattern matching<br/>Validation"]
    Orchestrator --> ShellHelper["ShellHelper<br/>Taskbar integration<br/>Start menu cleanup<br/>IShellItem COM"]
Loading

Diagram 3: Native Helper Class Architecture

Each helper class encapsulates a specific domain of system operations. The orchestrator maintains singleton instances of all helpers and exposes their functionality through a unified interface.

Helper Class Specialization

Helper Class Primary Win32 API Key Optimization Performance Gain
RegistryHelper advapi32.dll Span<char> stack allocation 30x faster
FileHelper kernel32.dll Parallel Task.Run operations 15x faster
ProcessHelper kernel32.dll Parallel.ForEach termination 8x faster
WindowsInstallerHelper Registry + WMI Cached GUID encoding table 12x faster
TypeLibHelper Registry enumeration HashSet O(1) lookups 5x faster
LicenseHelper WMI Concurrent query execution 4x faster
ServiceHelper WMI Static service cache 3x faster
GuidHelper String operations StringComparison.OrdinalIgnoreCase 2x faster
ShellHelper shell32.dll Direct COM invocation 6x faster

Native Code Loading Strategy

The system employs a smart fallback mechanism for loading the native DLL:

stateDiagram-v2
    [*] --> CheckDLL
    
    CheckDLL --> LoadDLL: DLL exists
    CheckDLL --> CompileSource: DLL missing
    
    LoadDLL --> CheckBlocked
    CheckBlocked --> UsePrecompiled: Not blocked
    CheckBlocked --> UnblockAttempt: File blocked
    
    UnblockAttempt --> UsePrecompiled: Unblock succeeded
    UnblockAttempt --> CompileSource: Unblock failed
    
    CompileSource --> FindCSC
    FindCSC --> CompileWithCSC: csc.exe found
    FindCSC --> UseFallback: csc.exe not found
    
    CompileWithCSC --> LoadCompiled: Compilation succeeded
    CompileWithCSC --> UseFallback: Compilation failed
    
    LoadCompiled --> Initialize
    UsePrecompiled --> Initialize
    UseFallback --> Initialize
    
    Initialize --> [*]
Loading

Diagram 4: Native DLL Loading State Machine

The loading strategy ensures the module functions in diverse environments, including restricted systems where pre-compiled DLLs may be blocked by security policies.

Sources: OfficeScrubC2R-Native.cs:1-end, OfficeScrubC2R.psm1:9-16, README.md:161-174


Layer Interaction and Data Flow

Typical Operation Flow

The following sequence demonstrates how a registry operation flows through all three layers:

sequenceDiagram
    participant User
    participant InvokeFunc as "Invoke-OfficeScrubC2R<br/>(OfficeScrubC2R.psm1)"
    participant MainFunc as "Main()<br/>(OfficeScrubC2R.ps1)"
    participant RemoveKey as "Remove-RegistryKey<br/>(Utilities)"
    participant Orch as "$script:Orchestrator"
    participant RegHelper as "RegistryHelper"
    participant WinAPI as "advapi32.dll<br/>RegDeleteKeyEx"
    
    User->>InvokeFunc: Call with -Force parameter
    Note over InvokeFunc: Parameter validation<br/>Privilege check<br/>Version check
    
    InvokeFunc->>MainFunc: Invoke Main() with script vars
    Note over MainFunc: Initialize-Script<br/>Stage 0: Detection<br/>Stage 1: Uninstall
    
    MainFunc->>RemoveKey: Call Remove-RegistryKey("HKLM:\\Software\\Microsoft\\Office")
    Note over RemoveKey: Normalize path<br/>Validate parameters<br/>Log operation
    
    RemoveKey->>Orch: $script:Orchestrator.Registry.DeleteKey()
    Note over Orch: Route to RegistryHelper
    
    Orch->>RegHelper: DeleteKey(hive, subkey, wow64)
    Note over RegHelper: Convert to Win32 format<br/>Handle WOW64 view<br/>Use Span<char> optimization
    
    RegHelper->>WinAPI: P/Invoke RegDeleteKeyEx
    Note over WinAPI: Native Windows<br/>Registry operation
    
    WinAPI-->>RegHelper: Return status code
    RegHelper-->>Orch: Return success/failure
    Orch-->>RemoveKey: Return boolean result
    
    RemoveKey->>RemoveKey: Log result
    RemoveKey-->>MainFunc: Return operation status
    MainFunc-->>InvokeFunc: Return exit code
    InvokeFunc-->>User: Display results or throw error
Loading

Diagram 5: Cross-Layer Interaction Sequence

This sequence demonstrates the typical flow for a registry deletion operation, showing how data and control pass through each architectural layer with appropriate transformations and logging at each level.

Data Transformation Pipeline

Layer Input Format Transformation Output Format
Orchestration PowerShell switch parameters Map to script-scoped variables Normalized boolean flags
Utilities PowerShell paths (HKLM:\Software) Convert to hive + subkey format Win32-compatible strings
Native Managed strings Convert to Span<char> or char arrays Unmanaged pointers
Win32 API Unmanaged memory Native system operations Status codes (HRESULT)

Sources: OfficeScrubC2R.psm1:176-204, OfficeScrubC2R.ps1:1-end, OfficeScrubC2R-Utilities.psm1:1-end


Design Principles

Separation of Concerns

Each layer has distinct responsibilities with minimal overlap:

  • Orchestration Layer: User interaction, parameter validation, workflow control
  • Utilities Layer: PowerShell idioms, error translation, logging integration
  • Native Layer: Performance optimization, Win32 API invocation, low-level operations

Dependency Inversion

Higher layers depend on abstractions, not implementations:

graph TB
    subgraph High["High-Level Modules"]
        MainPS["OfficeScrubC2R.ps1<br/>Main() Function"]
    end
    
    subgraph Abstraction["Abstraction Layer"]
        UtilsInterface["Utility Functions<br/>Remove-RegistryKey()<br/>Remove-FolderRecursive()<br/>Stop-OfficeProcesses()"]
    end
    
    subgraph Low["Low-Level Modules"]
        OrchestratorImpl["OfficeScrubOrchestrator<br/>Implementation"]
        HelpersImpl["Helper Classes<br/>RegistryHelper<br/>FileHelper<br/>ProcessHelper"]
    end
    
    MainPS --> UtilsInterface
    UtilsInterface --> OrchestratorImpl
    OrchestratorImpl --> HelpersImpl
    
    Note1["Depends on abstraction,<br/>not implementation"]
    Note2["Defines contract"]
    Note3["Implements contract"]
    
    MainPS -.-> Note1
    UtilsInterface -.-> Note2
    OrchestratorImpl -.-> Note3
Loading

Diagram 6: Dependency Inversion Architecture

The main script depends on utility function signatures, not their implementations. This allows the utilities layer to change internal implementation (e.g., switching from one native method to another) without affecting the orchestration layer.

Single Responsibility Principle

Each helper class handles a single domain:

  • RegistryHelper - Only registry operations
  • FileHelper - Only file system operations
  • ProcessHelper - Only process management
  • WindowsInstallerHelper - Only MSI metadata
  • LicenseHelper - Only licensing operations

This focused design enables independent optimization and testing of each component.

Open/Closed Principle

The architecture supports extension without modification:

  1. New Detection Logic - Add functions to utilities layer without changing native layer
  2. New Cleanup Operations - Extend helper classes without modifying orchestrator
  3. New Parameters - Add to Invoke-OfficeScrubC2R without changing pipeline logic

Sources: docs/memory-bank/structure.md:73-94, docs/memory-bank/tech.md:1-136, README.md:7-20


Module Dependencies and Load Order

The three-tier architecture requires careful initialization sequencing:

graph LR
    Start[["Module Import<br/>Import-Module OfficeScrubC2R"]]
    
    Start --> LoadManifest["1. Load Manifest<br/>OfficeScrubC2R.psd1"]
    LoadManifest --> LoadRoot["2. Load RootModule<br/>OfficeScrubC2R.psm1"]
    
    LoadRoot --> ImportUtils["3. Import-Module<br/>OfficeScrubC2R-Utilities.psm1<br/>-Force -Global"]
    ImportUtils --> LoadNative["4. Load Native DLL<br/>OfficeScrubNative.dll<br/>(with fallback)"]
    
    LoadNative --> DotSource["5. Dot-Source Main Script<br/>. OfficeScrubC2R.ps1"]
    DotSource --> InitEnv["6. Initialize-Environment<br/>Create $script:Orchestrator"]
    
    InitEnv --> ExportMembers["7. Export-ModuleMember<br/>Public Functions + Aliases"]
    ExportMembers --> Ready[["Module Ready"]]
Loading

Diagram 7: Module Initialization Sequence

The initialization sequence ensures that all dependencies are available before the module exports its public functions. The utilities module is imported globally, while the main script is dot-sourced into the module scope.

Critical Load Order Requirements

  1. Utilities Before Native - Utility functions must be available before native DLL loading attempts
  2. Native Before Main - The $script:Orchestrator must be created before Main() function definition
  3. Main Before Export - Functions must be defined before Export-ModuleMember executes
  4. Export Before Initialize - Public API must be available before Initialize-Environment runs

Sources: OfficeScrubC2R.psm1:9-16, OfficeScrubC2R.psm1:18-25, OfficeScrubC2R.psm1:223-231


Summary

The OfficeScrubC2R three-tier architecture provides:

  • Clear separation between user interaction (orchestration), business logic (utilities), and performance optimization (native)
  • Modularity enabling independent development and testing of each layer
  • Performance through strategic use of compiled C# code for system operations
  • Maintainability via dependency inversion and single-responsibility design
  • Flexibility supporting extension without modification of core components

The hybrid PowerShell/C# design combines the rapid development and scripting capabilities of PowerShell with the performance and Win32 API access of native code, achieving 10-50x performance improvements over pure interpreted script implementations.

Sources: OfficeScrubC2R.psm1:1-233, OfficeScrubC2R-Utilities.psm1:1-end, OfficeScrubC2R-Native.cs:1-end, docs/memory-bank/structure.md:73-118, README.md:144-174

Clone this wiki locally