Skip to content

User Guide

Calvindd2f edited this page Jan 14, 2026 · 1 revision

User Guide

Relevant source files

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

This document provides practical guidance for using OfficeScrubC2R to remove Office Click-to-Run installations. It covers module usage, helper class operations, orchestrator patterns, and common scenarios.

Scope: This guide focuses on end-user operation of the PowerShell module and native library. For architectural details, see Architecture. For development information, see Developer Guide. For complete function references, see Reference.

Overview

OfficeScrubC2R is a lightweight PowerShell module that exposes the OfficeScrubNative.dll C# library for Office Click-to-Run removal operations. The module provides five exported functions that load the native assembly and surface specialized helper classes for registry, file system, process, Windows Installer, and shell operations.

The module focuses on providing direct access to the native library's capabilities rather than high-level automation scripts. Users construct custom cleanup workflows by combining the orchestrator and helper classes.

Sources: OfficeScrubC2R.psm1:1-99, README.md:1-71

Module Architecture Overview

The following diagram shows the relationship between PowerShell functions and the native C# classes they expose:

graph TB
    subgraph "PowerShell Module: OfficeScrubC2R.psm1"
        ImportNative["Import-OfficeScrubNative<br/>(lines 10-35)"]
        NewOrch["New-OfficeScrubOrchestrator<br/>(lines 37-46)"]
        TestPath["Test-OfficeC2RPath<br/>(lines 48-60)"]
        TestScope["Test-OfficeProductScope<br/>(lines 62-74)"]
        GetHelpers["Get-OfficeScrubHelpers<br/>(lines 76-96)"]
    end
    
    subgraph "Native Assembly: OfficeScrubNative.dll"
        Assembly["[System.Reflection.Assembly]"]
        Orchestrator["OfficeScrubOrchestrator"]
        
        subgraph "Helper Classes"
            RegHelper["RegistryHelper"]
            FileHelper["FileHelper"]
            ProcHelper["ProcessHelper"]
            ShellHelper["ShellHelper"]
            WIHelper["WindowsInstallerHelper"]
            TypeLibHelper["TypeLibHelper"]
            LicenseHelper["LicenseHelper"]
            ServiceHelper["ServiceHelper"]
        end
    end
    
    ImportNative -->|"Loads"| Assembly
    NewOrch -->|"Instantiates"| Orchestrator
    TestPath -->|"Calls"| Orchestrator
    TestScope -->|"Calls"| Orchestrator
    GetHelpers -->|"Returns"| RegHelper
    GetHelpers -->|"Returns"| FileHelper
    GetHelpers -->|"Returns"| ProcHelper
    GetHelpers -->|"Returns"| ShellHelper
    GetHelpers -->|"Returns"| WIHelper
    GetHelpers -->|"Returns"| TypeLibHelper
    GetHelpers -->|"Returns"| LicenseHelper
    GetHelpers -->|"Returns"| ServiceHelper
    
    Orchestrator -->|"Wires up"| RegHelper
    Orchestrator -->|"Wires up"| FileHelper
    Orchestrator -->|"Wires up"| ProcHelper
    Orchestrator -->|"Wires up"| ShellHelper
    Orchestrator -->|"Wires up"| WIHelper
    Orchestrator -->|"Wires up"| TypeLibHelper
    Orchestrator -->|"Wires up"| LicenseHelper
    Orchestrator -->|"Wires up"| ServiceHelper
Loading

Sources: OfficeScrubC2R.psm1:1-99, README.md:36-46

Basic Module Usage

The module must be imported before any operations:

# Import from module installation path
Import-Module OfficeScrubC2R -Force

# Import from repository root (for development)
Import-Module .\OfficeScrubC2R.psd1 -Force

After import, the native DLL is loaded automatically when calling any exported function. The assembly loading is cached in $script:NativeAssembly to prevent duplicate loads.

Sources: OfficeScrubC2R.psm1:6-8, OfficeScrubC2R.psm1:23-34, README.md:17-19

Function Reference

The module exports five functions:

Function Purpose Returns
Import-OfficeScrubNative Loads OfficeScrubNative.dll into the AppDomain [System.Reflection.Assembly]
New-OfficeScrubOrchestrator Creates orchestrator with specified bitness [OfficeScrubNative.OfficeScrubOrchestrator]
Test-OfficeC2RPath Checks if path matches Click-to-Run patterns [bool]
Test-OfficeProductScope Validates product code against cleanup scope [bool]
Get-OfficeScrubHelpers Surfaces all helper class instances [PSCustomObject]

Sources: OfficeScrubC2R.psm1:98, README.md:38-45

Import-OfficeScrubNative

Loads the native assembly from disk. The function uses [System.Reflection.Assembly]::LoadFrom() and caches the result in $script:NativeAssembly.

Syntax:

Import-OfficeScrubNative [-Path <string>]

Parameters:

  • -Path (optional): Custom path to OfficeScrubNative.dll. Defaults to $script:NativeAssemblyPath (module directory).

Example:

# Load from default location
$assembly = Import-OfficeScrubNative

# Load from custom path
$assembly = Import-OfficeScrubNative -Path "C:\Custom\OfficeScrubNative.dll"

# Verify loaded types
$assembly.GetTypes() | Where-Object { $_.Name -like "*Helper" }

Sources: OfficeScrubC2R.psm1:10-35

New-OfficeScrubOrchestrator

Creates an instance of OfficeScrubOrchestrator with the specified system bitness. The orchestrator wires up all helper classes and provides the IsC2RPath() and IsInScope() validation methods.

Syntax:

New-OfficeScrubOrchestrator [-Is64Bit <bool>]

Parameters:

  • -Is64Bit (optional): Whether to use 64-bit registry/file views. Defaults to [Environment]::Is64BitOperatingSystem.

Example:

# Create orchestrator with automatic bitness detection
$orchestrator = New-OfficeScrubOrchestrator

# Force 64-bit mode
$orchestrator = New-OfficeScrubOrchestrator -Is64Bit $true

# Access wired-up helpers
$orchestrator.Registry
$orchestrator.Files
$orchestrator.Processes

Sources: OfficeScrubC2R.psm1:37-46, README.md:21-22

Test-OfficeC2RPath

Validates whether a file system path matches known Click-to-Run installation patterns. Calls the orchestrator's IsC2RPath() method.

Syntax:

Test-OfficeC2RPath -Path <string> [-Orchestrator <OfficeScrubOrchestrator>]

Parameters:

  • -Path (required): File system path to test
  • -Orchestrator (optional): Existing orchestrator instance. If not provided, creates a new one.

Example:

# Test typical C2R paths
Test-OfficeC2RPath -Path "C:\Program Files\Microsoft Office"
Test-OfficeC2RPath -Path "C:\Program Files (x86)\Microsoft Office"

# Test with custom orchestrator
$orch = New-OfficeScrubOrchestrator
Test-OfficeC2RPath -Path "C:\Custom\Office" -Orchestrator $orch

Sources: OfficeScrubC2R.psm1:48-60, README.md:24-25

Test-OfficeProductScope

Determines whether a product code (GUID) is in scope for Click-to-Run removal. Calls the orchestrator's IsInScope() method.

Syntax:

Test-OfficeProductScope -ProductCode <string> [-Orchestrator <OfficeScrubOrchestrator>]

Parameters:

  • -ProductCode (required): Product GUID in standard format {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}
  • -Orchestrator (optional): Existing orchestrator instance. If not provided, creates a new one.

Example:

# Test Office product codes
Test-OfficeProductScope -ProductCode '{9AC08E99-230B-47E8-9721-4577B7F124EA}'

# Batch test multiple products
$products = @('{9AC08E99-230B-47E8-9721-4577B7F124EA}', '{90160000-008C-0000-0000-0000000FF1CE}')
$orch = New-OfficeScrubOrchestrator
foreach ($product in $products) {
    $inScope = Test-OfficeProductScope -ProductCode $product -Orchestrator $orch
    Write-Host "$product : $inScope"
}

Sources: OfficeScrubC2R.psm1:62-74, README.md:27-28

Get-OfficeScrubHelpers

Returns a PSCustomObject containing all eight helper class instances from the orchestrator. This provides direct access to native methods for custom operations.

Syntax:

Get-OfficeScrubHelpers [-Orchestrator <OfficeScrubOrchestrator>]

Parameters:

  • -Orchestrator (optional): Existing orchestrator instance. If not provided, creates a new one.

Returns: A PSCustomObject with these properties:

  • RegistryHelper → OfficeScrubNative.RegistryHelper
  • FileHelper → OfficeScrubNative.FileHelper
  • ProcessHelper → OfficeScrubNative.ProcessHelper
  • ShellHelper → OfficeScrubNative.ShellHelper
  • WindowsInstallerHelper → OfficeScrubNative.WindowsInstallerHelper
  • TypeLibHelper → OfficeScrubNative.TypeLibHelper
  • LicenseHelper → OfficeScrubNative.LicenseHelper
  • ServiceHelper → OfficeScrubNative.ServiceHelper

Example:

# Get all helpers
$helpers = Get-OfficeScrubHelpers

# Use RegistryHelper directly
$helpers.RegistryHelper.KeyExists("HKLM:\Software\Microsoft\Office")
$helpers.RegistryHelper.EnumerateKeys("HKLM:\Software\Microsoft\Office\ClickToRun")

# Use FileHelper for deletion with reboot fallback
$helpers.FileHelper.DeleteDirectory("C:\Program Files\Microsoft Office", $true)

# Use ProcessHelper for parallel termination
$helpers.ProcessHelper.TerminateProcesses(@("WINWORD", "EXCEL", "OUTLOOK"), 5000)

Sources: OfficeScrubC2R.psm1:76-96, README.md:30-33

Typical Usage Workflow

The following sequence diagram shows a typical cleanup operation using the module's exported functions:

sequenceDiagram
    participant User as PowerShell User
    participant Module as OfficeScrubC2R.psm1
    participant Orchestrator as OfficeScrubOrchestrator
    participant RegHelper as RegistryHelper
    participant FileHelper as FileHelper
    participant ProcHelper as ProcessHelper
    
    User->>Module: Import-Module OfficeScrubC2R
    Module->>Module: Set $script:NativeAssemblyPath
    
    User->>Module: New-OfficeScrubOrchestrator
    Module->>Module: Import-OfficeScrubNative (line 44)
    Module->>Orchestrator: new($Is64Bit)
    Orchestrator->>RegHelper: Instantiate with bitness
    Orchestrator->>FileHelper: Instantiate
    Orchestrator->>ProcHelper: Instantiate
    Orchestrator-->>Module: Return orchestrator instance
    Module-->>User: $orchestrator
    
    User->>Module: Test-OfficeC2RPath -Path "C:\Program Files\Microsoft Office"
    Module->>Orchestrator: IsC2RPath(path)
    Orchestrator-->>Module: $true
    Module-->>User: $true
    
    User->>Module: Get-OfficeScrubHelpers -Orchestrator $orchestrator
    Module->>Orchestrator: Access .Registry property
    Module->>Orchestrator: Access .Files property
    Module->>Orchestrator: Access .Processes property
    Module-->>User: PSCustomObject with helpers
    
    User->>ProcHelper: TerminateProcesses(@("WINWORD", "EXCEL"))
    ProcHelper->>ProcHelper: Parallel Task.Run() execution
    ProcHelper-->>User: List<int> terminated PIDs
    
    User->>RegHelper: DeleteKey("HKLM:\Software\Microsoft\Office\16.0")
    RegHelper->>RegHelper: Delete native + WOW64 views
    RegHelper-->>User: $true
    
    User->>FileHelper: DeleteDirectory("C:\Program Files\Microsoft Office", $true)
    FileHelper->>FileHelper: Try cmd.exe rd /s /q
    FileHelper->>FileHelper: Schedule locked files via MoveFileEx
    FileHelper-->>User: $false (reboot required)
Loading

Sources: OfficeScrubC2R.psm1:10-96, README.md:17-33

Helper Class Operations

Each helper class provides specialized operations for a specific Windows subsystem. The following sections detail common operations for each helper.

RegistryHelper Operations

The RegistryHelper class provides WOW64-aware registry operations with caching to prevent redundant operations.

Common Methods:

$helpers = Get-OfficeScrubHelpers
$reg = $helpers.RegistryHelper

# Check key existence (both native and WOW64 views on 64-bit systems)
$exists = $reg.KeyExists("HKLM:\Software\Microsoft\Office\ClickToRun")

# Enumerate subkeys
$subkeys = $reg.EnumerateKeys("HKLM:\Software\Microsoft\Office")

# Get registry value
$version = $reg.GetValue("HKLM:\Software\Microsoft\Office\ClickToRun\Configuration", "VersionToReport")

# Delete key recursively (both views)
$deleted = $reg.DeleteKey("HKLM:\Software\Microsoft\Office\16.0")

# Delete specific value
$deleted = $reg.DeleteValue("HKLM:\Software\Microsoft\Office\ClickToRun\Configuration", "UpdateUrl")

On 64-bit systems, registry operations automatically target both the native 64-bit view and the WOW64 32-bit view. The helper maintains a HashSet cache to avoid processing the same key twice.

Sources: OfficeScrubC2R.psm1:87, docs/memory-bank/guidelines.md:59-62, docs/memory-bank/guidelines.md:73-76

FileHelper Operations

The FileHelper class provides high-performance file deletion with automatic reboot scheduling for locked files.

Common Methods:

$helpers = Get-OfficeScrubHelpers
$files = $helpers.FileHelper

# Delete file with optional reboot fallback
$deleted = $files.DeleteFile("C:\Program Files\Microsoft Office\root\Office16\WINWORD.EXE", $true)

# Delete directory (uses cmd.exe rd /s /q for performance)
$deleted = $files.DeleteDirectory("C:\Program Files\Microsoft Office", $true)

# Check if reboot is required
$rebootNeeded = $files.GetPendingDeletes().Count -gt 0

# Get list of files scheduled for deletion on next reboot
$pending = $files.GetPendingDeletes()
foreach ($path in $pending) {
    Write-Host "Pending deletion: $path"
}

The DeleteDirectory() method uses cmd.exe with rd /s /q for directories with many files, falling back to Directory.Delete() if that fails. Locked files are scheduled for deletion on next reboot using MoveFileEx with MOVEFILE_DELAY_UNTIL_REBOOT.

Sources: OfficeScrubC2R.psm1:88, docs/memory-bank/guidelines.md:78-83

ProcessHelper Operations

The ProcessHelper class provides parallel process termination with timeout support.

Common Methods:

$helpers = Get-OfficeScrubHelpers
$proc = $helpers.ProcessHelper

# Terminate Office processes in parallel (5 second timeout per process)
$terminated = $proc.TerminateProcesses(@("WINWORD", "EXCEL", "OUTLOOK", "ONENOTE"), 5000)
Write-Host "Terminated PIDs: $($terminated -join ', ')"

# Check if specific process is running
$isRunning = $proc.IsProcessRunning("WINWORD")

# Get processes using a specific file path
$processes = $proc.GetProcessesUsingPath("C:\Program Files\Microsoft Office\root\Office16")

The TerminateProcesses() method creates a Task.Run() for each process instance, allowing parallel termination. Terminated process IDs are collected in a thread-safe HashSet<int> using lock statements.

Sources: OfficeScrubC2R.psm1:89, docs/memory-bank/guidelines.md:85-90

WindowsInstallerHelper Operations

The WindowsInstallerHelper class provides Windows Installer metadata cleanup using GUID transformations.

Common Methods:

$helpers = Get-OfficeScrubHelpers
$msi = $helpers.WindowsInstallerHelper

# Clean UpgradeCodes with callback predicate
$callback = { param($guid) Test-OfficeProductScope -ProductCode $guid }
$msi.CleanupUpgradeCodes($callback)

# Clean Products
$msi.CleanupProducts($callback)

# Clean Components
$msi.CleanupComponents($callback)

# Clean PublishedComponents
$msi.CleanupPublishedComponents($callback)

These methods enumerate Windows Installer registry metadata, transform GUIDs between compressed/expanded/encoded formats using GuidHelper, and delete entries where the callback returns $true.

Sources: OfficeScrubC2R.psm1:91, docs/memory-bank/guidelines.md:92-96

Additional Helpers

ShellHelper - Manages shell integration:

$shell = $helpers.ShellHelper
# Unpin items from taskbar (Windows 10/11)
$shell.UnpinFromTaskbar("Microsoft Word")

TypeLibHelper - Cleans COM type library registrations:

$typelib = $helpers.TypeLibHelper
# Remove type library by GUID
$typelib.CleanupTypeLib("{2DF8D04C-5BFA-101B-BDE5-00AA0044DE52}")

LicenseHelper - Manages Office licensing via SPP:

$license = $helpers.LicenseHelper
# Clear Office licenses
$license.ClearOfficeLicenses()

ServiceHelper - Manages Windows services:

$services = $helpers.ServiceHelper
# Delete service
$services.DeleteService("ClickToRunSvc")

Sources: OfficeScrubC2R.psm1:90-95, docs/memory-bank/guidelines.md:92-96

Common Usage Patterns

Pattern 1: Complete Office Removal

# Import module
Import-Module OfficeScrubC2R -Force

# Create orchestrator and get helpers
$orchestrator = New-OfficeScrubOrchestrator
$helpers = Get-OfficeScrubHelpers -Orchestrator $orchestrator

# Stage 1: Terminate processes
$helpers.ProcessHelper.TerminateProcesses(@("WINWORD", "EXCEL", "OUTLOOK", "POWERPNT", "ONENOTE", "ONENOTEM", "LYNC", "MSOUC", "MSOSYNC"), 5000)

# Stage 2: Clean registry
$helpers.RegistryHelper.DeleteKey("HKLM:\Software\Microsoft\Office\ClickToRun")
$helpers.RegistryHelper.DeleteKey("HKLM:\Software\Microsoft\Office\16.0")
$helpers.WindowsInstallerHelper.CleanupProducts({ param($guid) $orchestrator.IsInScope($guid) })

# Stage 3: Delete files (with reboot fallback)
$deleted = $helpers.FileHelper.DeleteDirectory("C:\Program Files\Microsoft Office", $true)
if (-not $deleted) {
    Write-Host "Reboot required to complete file deletion"
}

Sources: README.md:17-33, docs/memory-bank/product.md:16-24

Pattern 2: Detection Only

# Check for Click-to-Run installations
$c2rPaths = @(
    "C:\Program Files\Microsoft Office",
    "C:\Program Files (x86)\Microsoft Office"
)

$orchestrator = New-OfficeScrubOrchestrator

foreach ($path in $c2rPaths) {
    if (Test-Path $path) {
        $isC2R = Test-OfficeC2RPath -Path $path -Orchestrator $orchestrator
        Write-Host "${path}: C2R=$isC2R"
    }
}

# Enumerate installed products
$helpers = Get-OfficeScrubHelpers -Orchestrator $orchestrator
$products = $helpers.RegistryHelper.EnumerateKeys("HKLM:\Software\Microsoft\Office\ClickToRun\Configuration")
foreach ($product in $products) {
    Write-Host "Found: $product"
}

Sources: OfficeScrubC2R.psm1:48-74, README.md:24-28

Pattern 3: Targeted Registry Cleanup

# Clean specific Office version registry keys
$orchestrator = New-OfficeScrubOrchestrator
$helpers = Get-OfficeScrubHelpers -Orchestrator $orchestrator
$reg = $helpers.RegistryHelper

# Office 2016/2019/365 (version 16.0)
$reg.DeleteKey("HKLM:\Software\Microsoft\Office\16.0")
$reg.DeleteKey("HKCU:\Software\Microsoft\Office\16.0")

# Click-to-Run configuration
$reg.DeleteKey("HKLM:\Software\Microsoft\Office\ClickToRun")

# Windows Installer metadata
$callback = { param($guid) $orchestrator.IsInScope($guid) }
$helpers.WindowsInstallerHelper.CleanupUpgradeCodes($callback)
$helpers.WindowsInstallerHelper.CleanupProducts($callback)

Sources: OfficeScrubC2R.psm1:86-96, docs/memory-bank/guidelines.md:71-76

System Requirements

Before using the module, verify these prerequisites:

Requirement Minimum Recommended
Operating System Windows 7 SP1 Windows 10/11
PowerShell Version 5.1 7.x
.NET Framework 4.5 4.8
Privileges Administrator Administrator
Architecture x86 or x64 x64

Verification Commands:

# Check PowerShell version
$PSVersionTable.PSVersion

# Check .NET Framework version
[System.Environment]::Version

# Verify administrator privileges
([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)

# Check system architecture
[Environment]::Is64BitOperatingSystem

Sources: docs/memory-bank/product.md:58-65

Module Loading and Initialization

The module initialization process follows this sequence:

stateDiagram-v2
    [*] --> ImportModule
    
    state "Import-Module OfficeScrubC2R" as ImportModule {
        [*] --> SetModuleRoot
        SetModuleRoot --> SetNativeAssemblyPath
        SetNativeAssemblyPath --> InitScriptVariables
        InitScriptVariables --> ExportFunctions
        ExportFunctions --> [*]
    }
    
    ImportModule --> UserCall
    
    state "User calls exported function" as UserCall {
        [*] --> CheckNativeLoaded
        CheckNativeLoaded --> LoadNative: Not loaded
        CheckNativeLoaded --> UseCached: Already loaded
        
        LoadNative --> ResolvePathLine17
        ResolvePathLine17 --> TestPathLine19
        TestPathLine19 --> LoadFromLine31
        LoadFromLine31 --> CacheInScriptVar
        
        CacheInScriptVar --> [*]
        UseCached --> [*]
    }
    
    UserCall --> [*]
    
    note right of SetModuleRoot
        $script:ModuleRoot = $PSScriptRoot
        Line 6
    end note
    
    note right of SetNativeAssemblyPath
        $script:NativeAssemblyPath = 
        Join-Path $script:ModuleRoot 'OfficeScrubNative.dll'
        Line 7
    end note
    
    note right of LoadFromLine31
        [System.Reflection.Assembly]::LoadFrom($resolvedPath)
        Line 31
    end note
Loading

Sources: OfficeScrubC2R.psm1:6-8, OfficeScrubC2R.psm1:10-35

Error Handling and Return Values

All helper methods use a silent failure pattern: operations return $false or $null on failure rather than throwing exceptions. This allows callers to implement custom error handling.

Return Value Patterns:

Operation Type Success Return Failure Return Example
Boolean operations $true $false KeyExists(), DeleteKey()
Collection operations List<T> with items Empty List<T> EnumerateKeys(), TerminateProcesses()
Object operations Object instance $null GetValue()
Void operations N/A Silently fails SetValue()

Example Error Handling:

$helpers = Get-OfficeScrubHelpers
$reg = $helpers.RegistryHelper

# Check return values
if ($reg.KeyExists("HKLM:\Software\Microsoft\Office\16.0")) {
    $deleted = $reg.DeleteKey("HKLM:\Software\Microsoft\Office\16.0")
    if (-not $deleted) {
        Write-Warning "Failed to delete registry key"
    }
}

# Handle empty collections
$subkeys = $reg.EnumerateKeys("HKLM:\Software\Microsoft\Office")
if ($subkeys.Count -eq 0) {
    Write-Host "No Office registry keys found"
}

Sources: docs/memory-bank/guidelines.md:27-39

Performance Considerations

The native library provides significant performance improvements over pure PowerShell implementations:

Operation PowerShell Equivalent Native Performance Improvement
Registry enumeration Get-ChildItem -Recurse Parallel enumeration with caching 10-20x faster
File deletion Remove-Item -Recurse cmd.exe rd /s /q + MoveFileEx 15x faster
Process termination Stop-Process sequential Parallel Task.Run() 5-10x faster
GUID transformation String manipulation Byte-level operations 50x faster

Optimization Patterns:

  1. Reuse Orchestrator Instances: Create once, use multiple times

    $orchestrator = New-OfficeScrubOrchestrator
    $helpers = Get-OfficeScrubHelpers -Orchestrator $orchestrator
    # Use $helpers repeatedly without recreating
  2. Batch Registry Operations: Group related operations together

    $reg = $helpers.RegistryHelper
    $keys = @("16.0", "15.0", "14.0")
    foreach ($key in $keys) {
        $reg.DeleteKey("HKLM:\Software\Microsoft\Office\$key")
    }
  3. Parallel Processing: Use helper methods designed for parallelism

    # This terminates all processes in parallel
    $helpers.ProcessHelper.TerminateProcesses(@("WINWORD", "EXCEL", "OUTLOOK"), 5000)

Sources: docs/memory-bank/product.md:8-12, docs/memory-bank/guidelines.md:98-119

Troubleshooting Common Issues

Issue: Assembly Load Failures

Symptom: Import-OfficeScrubNative throws "Native assembly not found" error.

Resolution:

# Verify DLL exists
$dllPath = Join-Path $PSScriptRoot 'OfficeScrubNative.dll'
Test-Path $dllPath

# Check if DLL is blocked
Get-Item $dllPath | Unblock-File

# Verify assembly can be loaded
[System.Reflection.Assembly]::LoadFrom((Resolve-Path $dllPath))

Sources: OfficeScrubC2R.psm1:17-21

Issue: WOW64 Registry Keys Not Deleted

Symptom: Registry keys remain in HKLM:\Software\Wow6432Node after cleanup.

Resolution: Ensure the orchestrator was created with correct bitness detection:

# Verify bitness detection
[Environment]::Is64BitOperatingSystem

# Create orchestrator with explicit 64-bit mode
$orchestrator = New-OfficeScrubOrchestrator -Is64Bit $true
$helpers = Get-OfficeScrubHelpers -Orchestrator $orchestrator

# Both views will be processed automatically
$helpers.RegistryHelper.DeleteKey("HKLM:\Software\Microsoft\Office\16.0")

Sources: OfficeScrubC2R.psm1:41, docs/memory-bank/guidelines.md:59-62

Issue: Files Remain After Deletion

Symptom: DeleteDirectory() returns $false and files remain.

Resolution: Files are locked by running processes. Schedule for deletion on next reboot:

$helpers = Get-OfficeScrubHelpers

# Terminate processes first
$helpers.ProcessHelper.TerminateProcesses(@("WINWORD", "EXCEL"), 5000)

# Delete with reboot fallback ($true = scheduleOnFail)
$deleted = $helpers.FileHelper.DeleteDirectory("C:\Program Files\Microsoft Office", $true)

if (-not $deleted) {
    # Check what's pending deletion
    $pending = $helpers.FileHelper.GetPendingDeletes()
    Write-Host "Files scheduled for deletion on reboot: $($pending.Count)"
    Write-Host "Restart required to complete cleanup"
}

Sources: docs/memory-bank/guidelines.md:78-83

Issue: Insufficient Privileges

Symptom: Registry or file operations fail silently.

Resolution: Verify administrator privileges and elevation:

# Check current privileges
$isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)

if (-not $isAdmin) {
    Write-Error "Administrator privileges required"
    # Restart PowerShell as administrator
    Start-Process powershell -Verb RunAs -ArgumentList "-NoExit", "-Command", "Set-Location '$PWD'"
}

Sources: docs/memory-bank/guidelines.md:128-132

Advanced Scenarios

Custom Cleanup Predicates

Define custom logic for determining what to remove:

$orchestrator = New-OfficeScrubOrchestrator
$helpers = Get-OfficeScrubHelpers -Orchestrator $orchestrator

# Custom predicate: only remove Office 2016
$isOffice2016 = {
    param($productGuid)
    # Office 2016 product codes typically start with {90160000-...}
    return $productGuid -like "{90160000-*"
}

# Apply to Windows Installer cleanup
$helpers.WindowsInstallerHelper.CleanupProducts($isOffice2016)
$helpers.WindowsInstallerHelper.CleanupComponents($isOffice2016)

Sources: OfficeScrubC2R.psm1:62-74

Offline Environment Usage

When internet access is unavailable, skip ODT downloads and use local uninstall only:

# The module itself doesn't download ODT
# This is handled by higher-level scripts
# Use the module's helpers for direct operations

$helpers = Get-OfficeScrubHelpers

# Direct registry cleanup without ODT
$helpers.RegistryHelper.DeleteKey("HKLM:\Software\Microsoft\Office\ClickToRun")

# Direct file cleanup without ODT
$helpers.FileHelper.DeleteDirectory("C:\Program Files\Microsoft Office", $true)

Sources: docs/memory-bank/product.md:39, README.md:1-14

Integration with Configuration Management

Example integration with configuration management systems:

# DSC Resource Script Block
$RemoveOfficeScript = {
    Import-Module OfficeScrubC2R -Force
    
    $orchestrator = New-OfficeScrubOrchestrator
    $helpers = Get-OfficeScrubHelpers -Orchestrator $orchestrator
    
    # Terminate processes
    $terminated = $helpers.ProcessHelper.TerminateProcesses(@("WINWORD", "EXCEL", "OUTLOOK"), 5000)
    
    # Clean registry
    $regClean = $helpers.RegistryHelper.DeleteKey("HKLM:\Software\Microsoft\Office\16.0")
    
    # Clean files with reboot fallback
    $fileClean = $helpers.FileHelper.DeleteDirectory("C:\Program Files\Microsoft Office", $true)
    
    return @{
        ProcessesTerminated = $terminated.Count
        RegistryClean = $regClean
        FileClean = $fileClean
        RebootRequired = -not $fileClean
    }
}

# Execute and capture results
$result = & $RemoveOfficeScript
$result | ConvertTo-Json

Sources: docs/memory-bank/product.md:42-56

Related Documentation

For additional information on using OfficeScrubC2R:

Sources: README.md:1-71

Basic Invocation

The simplest usage patterns are:

# Interactive mode (prompts for confirmation)
Invoke-OfficeScrubC2R

# Silent mode (no prompts, immediate execution)
Invoke-OfficeScrubC2R -Quiet -Force

# Detection only (scan without removal)
Invoke-OfficeScrubC2R -DetectOnly

The tool validates elevation and PowerShell version requirements during initialization. If not running as Administrator, it will exit with ERROR_ELEVATION (256) unless -NoElevate is specified.

Sources: OfficeScrubC2R.psm1:159-174, OfficeScrubC2R.ps1:87-136

User Interaction Flow

The following diagram illustrates the complete user workflow from invocation through completion:

flowchart TD
    User["User executes<br/>Invoke-OfficeScrubC2R"]
    
    ValidateElev["Validate Elevation<br/>(unless -NoElevate)"]
    ElevError["Exit with<br/>ERROR_ELEVATION (256)"]
    
    ValidatePSVersion["Validate PowerShell 5.1+"]
    PSError["Exit with version error"]
    
    InitScript["Initialize-Script<br/>(lines 87-136)"]
    InitLog["Initialize-Log<br/>$script:LogDir"]
    
    Stage0["Stage 0: Detection<br/>Find-InstalledOfficeProducts"]
    NoProducts["No products found<br/>Exit SUCCESS (0)"]
    
    DetectMode{"DetectOnly<br/>parameter?"}
    ShowDetect["Display detected products<br/>Exit SUCCESS (0)"]
    
    ForceOrQuiet{"Force or Quiet<br/>parameter?"}
    PromptUser["Prompt user:<br/>'Remove all Office C2R products?'"]
    UserDecline["User declines<br/>Exit ERROR_USERCANCEL (4)"]
    
    Stage1["Stage 1: Uninstall<br/>Uninstall-OfficeProducts"]
    Stage2Reg["Stage 2: Registry Cleanup<br/>Clean-OfficeRegistry"]
    Stage2File["Stage 2: File Cleanup<br/>Complete-Cleanup"]
    
    Stage3["Stage 3: Summary<br/>Show-Summary"]
    
    RebootCheck{"RebootRequired<br/>flag set?"}
    QuietMode{"Quiet<br/>parameter?"}
    PromptReboot["Prompt:<br/>'Reboot now? (Y/N)'"]
    UserReboot["User confirms<br/>Restart-Computer"]
    
    Complete["Exit with<br/>$script:ErrorCode"]
    
    User --> ValidateElev
    ValidateElev -->|"Not admin"| ElevError
    ValidateElev -->|"Is admin"| ValidatePSVersion
    ValidatePSVersion -->|"< 5.1"| PSError
    ValidatePSVersion -->|">= 5.1"| InitScript
    
    InitScript --> InitLog
    InitLog --> Stage0
    
    Stage0 -->|"No products"| NoProducts
    Stage0 -->|"Products found"| DetectMode
    
    DetectMode -->|"Yes"| ShowDetect
    DetectMode -->|"No"| ForceOrQuiet
    
    ForceOrQuiet -->|"Yes"| Stage1
    ForceOrQuiet -->|"No"| PromptUser
    
    PromptUser -->|"User accepts"| Stage1
    PromptUser -->|"User declines"| UserDecline
    
    Stage1 --> Stage2Reg
    Stage2Reg --> Stage2File
    Stage2File --> Stage3
    
    Stage3 --> RebootCheck
    
    RebootCheck -->|"Yes"| QuietMode
    RebootCheck -->|"No"| Complete
    
    QuietMode -->|"Yes"| Complete
    QuietMode -->|"No"| PromptReboot
    
    PromptReboot -->|"User accepts"| UserReboot
    PromptReboot -->|"User declines"| Complete
    UserReboot --> Complete
Loading

Sources: OfficeScrubC2R.ps1:924-1001, OfficeScrubC2R.psm1:176-204, OfficeScrubC2R.ps1:867-918

Parameter Categories

The tool provides 12 command-line parameters organized into five functional categories:

Category Parameters Purpose
Execution Mode -Quiet, -DetectOnly, -Force Control interaction level and confirmation behavior
Scope Control -RemoveAll, -KeepLicense, -KeepSku Define what gets removed
Network Mode -Offline Skip Office Deployment Tool (ODT) download
Advanced Options -ForceArpUninstall, -ClearTaskBand, -UnpinMode, -SkipSD Fine-tune specific operations
System Control -NoElevate, -LogPath Override elevation checks and logging location

For detailed parameter documentation, see Command-Line Parameters.

Sources: OfficeScrubC2R.ps1:62-75, OfficeScrubC2R.psm1:121-157

Parameter Decision Matrix

This diagram shows how parameter combinations determine execution behavior:

graph TB
    Params["Command-line Parameters"]
    
    subgraph "Interaction Level"
        Quiet["-Quiet<br/>Minimal output"]
        DetectOnly["-DetectOnly<br/>Scan only"]
        Force["-Force<br/>No confirmation"]
    end
    
    subgraph "Scope Decisions"
        RemoveAll["-RemoveAll<br/>All products"]
        KeepLicense["-KeepLicense<br/>Preserve licenses"]
        UnpinMode["-UnpinMode<br/>Unpin only"]
    end
    
    subgraph "Network Behavior"
        Offline["-Offline<br/>Skip ODT download"]
    end
    
    subgraph "Script-Level Variables"
        ScriptQuiet["$script:Quiet"]
        ScriptDetectOnly["$script:DetectOnly"]
        ScriptForce["$script:Force"]
        ScriptRemoveAll["$script:RemoveAll"]
        ScriptKeepLicense["$script:KeepLicense"]
        ScriptOffline["$script:Offline"]
    end
    
    subgraph "Execution Behavior"
        NoPrompt["Skip user confirmation<br/>Initialize-Script"]
        NoRemoval["Skip Stage 1 & 2<br/>Main:946-950"]
        SkipODT["Skip Download-ODT<br/>Uninstall-OfficeC2R:304"]
        PreserveSPP["Skip Clear-OfficeLicenses<br/>Uninstall-OfficeProducts:195"]
        OnlyUnpin["Clear-Shortcuts only<br/>Complete-Cleanup:827-831"]
    end
    
    Params --> Quiet
    Params --> DetectOnly
    Params --> Force
    Params --> RemoveAll
    Params --> KeepLicense
    Params --> Offline
    Params --> UnpinMode
    
    Quiet --> ScriptQuiet
    DetectOnly --> ScriptDetectOnly
    Force --> ScriptForce
    RemoveAll --> ScriptRemoveAll
    KeepLicense --> ScriptKeepLicense
    Offline --> ScriptOffline
    
    ScriptForce --> NoPrompt
    ScriptQuiet --> NoPrompt
    ScriptDetectOnly --> NoRemoval
    ScriptOffline --> SkipODT
    ScriptKeepLicense --> PreserveSPP
    UnpinMode --> OnlyUnpin
Loading

Sources: OfficeScrubC2R.ps1:87-101, OfficeScrubC2R.psm1:179-191, OfficeScrubC2R.ps1:952-961

Operational Mode Summary

The tool operates in six distinct modes based on parameter combinations:

Mode Parameters Behavior Exit Code
Interactive None Prompts for confirmation, displays progress 0 on success
Silent -Quiet -Force No prompts, minimal output 0 on success
Detection -DetectOnly Scans registry/files, no removal 0 (always)
Offline -Offline Skips ODT download, uses local uninstall 0 or 2 (reboot)
License Preservation -KeepLicense Removes Office but preserves activation 0 or 2 (reboot)
Unpin Only -UnpinMode -SkipSD Removes shortcuts without full uninstall 0

For detailed mode descriptions and use cases, see Operational Modes.

Sources: OfficeScrubC2R.psm1:84-101, README.md:83-95

Stage Pipeline Overview

All operational modes (except Detection and Unpin-only) execute the three-stage pipeline:

stateDiagram-v2
    [*] --> Stage0
    
    state "Stage 0: Detection" as Stage0 {
        [*] --> FindInstalledOfficeProducts
        FindInstalledOfficeProducts --> EnsureValidWIMetadata
        EnsureValidWIMetadata --> GetInstalledOfficeProducts
        GetInstalledOfficeProducts --> BuildC2RSuite
        BuildC2RSuite --> [*]
    }
    
    Stage0 --> Stage1: Products found
    Stage0 --> Stage3: No products
    
    state "Stage 1: Uninstall" as Stage1 {
        [*] --> ClearOfficeLicenses
        ClearOfficeLicenses --> StopOfficeProcesses
        StopOfficeProcesses --> RemoveScheduledTasks
        RemoveScheduledTasks --> ClearShortcuts
        ClearShortcuts --> UninstallOfficeC2R
        UninstallOfficeC2R --> RemovePublishedComponents
        RemovePublishedComponents --> RemoveC2RRegistryData
        RemoveC2RRegistryData --> UninstallMSIProducts
        UninstallMSIProducts --> [*]
    }
    
    Stage1 --> Stage2
    
    state "Stage 2: Cleanup" as Stage2 {
        [*] --> CleanOfficeRegistry
        CleanOfficeRegistry --> CompleteCleanup
        CompleteCleanup --> DeleteServices
        DeleteServices --> DeleteFiles
        DeleteFiles --> RemoveEmptyFolders
        RemoveEmptyFolders --> RestoreExplorer
        RestoreExplorer --> [*]
    }
    
    Stage2 --> Stage3
    
    state "Stage 3: Summary" as Stage3 {
        [*] --> ShowSummary
        ShowSummary --> CheckRebootRequired
        CheckRebootRequired --> PromptReboot: RebootRequired=true
        CheckRebootRequired --> Exit: RebootRequired=false
        PromptReboot --> Exit
    }
    
    Stage3 --> [*]
    
    note right of Stage0
        Find-InstalledOfficeProducts
        Lines 138-163
    end note
    
    note right of Stage1
        Uninstall-OfficeProducts
        Lines 187-247
    end note
    
    note right of Stage2
        Clean-OfficeRegistry: 497-546
        Complete-Cleanup: 660-844
    end note
    
    note right of Stage3
        Show-Summary
        Lines 867-918
    end note
Loading

Sources: OfficeScrubC2R.ps1:924-1001, OfficeScrubC2R.ps1:138-163, OfficeScrubC2R.ps1:187-247, OfficeScrubC2R.ps1:497-546, OfficeScrubC2R.ps1:660-844, OfficeScrubC2R.ps1:867-918

Exit Codes and Error Handling

The tool uses a bitmask-based error code system defined by constants in the utilities module. Each bit represents a specific failure condition:

Exit Code Constant Name Meaning Recovery Action
0 ERROR_SUCCESS Successful completion None
1 ERROR_FAIL General failure Check logs
2 ERROR_REBOOT_REQUIRED Reboot needed to complete Reboot and rerun if needed
4 ERROR_USERCANCEL User declined confirmation Rerun with -Force
8 ERROR_STAGE1 MSI uninstall failed Check MSI logs
16 ERROR_STAGE2 Cleanup failed Check file locks
32 ERROR_INCOMPLETE Removal incomplete Reboot and rerun
64 ERROR_DCAF_FAILURE Second attempt failed Manual intervention
128 ERROR_ELEVATION_USERDECLINED User declined elevation Rerun as Administrator
256 ERROR_ELEVATION Elevation failed Verify Administrator rights
512 ERROR_SCRIPTINIT Initialization failed Check PowerShell version
1024 ERROR_RELAUNCH Relaunch error Check system state
2048 ERROR_UNKNOWN Unknown error Review stack trace in logs

Multiple error conditions can be combined using bitwise OR operations. For example, exit code 34 (32 + 2) indicates both ERROR_INCOMPLETE and ERROR_REBOOT_REQUIRED.

The Show-Summary function decodes and displays all active error flags at OfficeScrubC2R.ps1:885-896.

Sources: OfficeScrubC2R.ps1:867-918, README.md:189-206

Log File Locations

All operations are logged to disk. The default log path is:

%TEMP%\OfficeScrubC2R\<ComputerName>_<Timestamp>_ScrubLog.txt

You can specify a custom location using the -LogPath parameter:

Invoke-OfficeScrubC2R -LogPath "C:\AdminLogs"

The logging system is initialized by Initialize-Log in the utilities module and creates structured output including:

  • Operation timestamps
  • Registry key changes
  • File deletion attempts
  • Process terminations
  • Error conditions with stack traces

To view the most recent log:

$logPath = "$env:TEMP\OfficeScrubC2R"
Get-ChildItem $logPath -Filter "*ScrubLog.txt" | 
    Sort-Object LastWriteTime -Descending | 
    Select-Object -First 1 | 
    Get-Content -Tail 50

Sources: OfficeScrubC2R.ps1:120-126, README.md:236-245

Quick Reference: Common Commands

The following table provides command templates for typical usage scenarios:

Scenario Command Notes
First-time removal Invoke-OfficeScrubC2R Interactive with confirmation
Automated deployment Invoke-OfficeScrubC2R -Quiet -Force Silent operation
Pre-installation scan Invoke-OfficeScrubC2R -DetectOnly No changes made
Clean Office but keep activation Invoke-OfficeScrubC2R -KeepLicense Preserves SPP licenses
Airgapped environment Invoke-OfficeScrubC2R -Offline -Force No ODT download
Remove taskbar pins only Invoke-OfficeScrubC2R -UnpinMode -SkipSD Shortcuts only
Troubleshooting incomplete removal Invoke-OfficeScrubC2R -Force -LogPath "C:\Temp" Detailed logging
Check what would be removed Invoke-OfficeScrubC2R -DetectOnly -Verbose Verbose detection

For complete examples with explanations, see Use Cases and Examples.

Sources: README.md:60-106, OfficeScrubC2R.psm1:83-101

Prerequisites Checklist

Before executing the tool, verify these requirements:

Requirement Check Command Required Value
PowerShell Version $PSVersionTable.PSVersion 5.1 or later
Administrator Rights ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) True
.NET Framework [System.Environment]::Version 4.5 or later
Module Loaded Get-Module OfficeScrubC2R Present
Orchestrator Available $script:Orchestrator -ne $null True (after Initialize-Environment)

The tool performs these checks automatically during initialization at OfficeScrubC2R.ps1:87-136. If prerequisites are not met, it exits with ERROR_SCRIPTINIT (512) or ERROR_ELEVATION (256).

Sources: OfficeScrubC2R.psm1:159-174, OfficeScrubC2R.ps1:106-117, README.md:21-27

Related Documentation

For specific operational details, refer to these related sections:

  • Command-Line Parameters - Complete reference for all 12 parameters with syntax, examples, and interactions
  • Operational Modes - Detailed descriptions of Interactive, Silent, Detection, Offline, License Preservation, and Unpin-only modes
  • Use Cases and Examples - Practical scenarios including pre-installation cleanup, offline environments, and troubleshooting stuck installations
  • Troubleshooting - Common issues including elevation errors, locked files, incomplete removal, and log interpretation

For architectural understanding, see Architecture. For development and contribution guidelines, see Developer Guide.

Sources: JSON table of contents structure

Clone this wiki locally