Repository navigation
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.
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
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
Sources: OfficeScrubC2R.psm1:1-99, README.md:36-46
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 -ForceAfter 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
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
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 toOfficeScrubNative.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
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.ProcessesSources: OfficeScrubC2R.psm1:37-46, README.md:21-22
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 $orchSources: OfficeScrubC2R.psm1:48-60, README.md:24-25
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
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
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)
Sources: OfficeScrubC2R.psm1:10-96, README.md:17-33
Each helper class provides specialized operations for a specific Windows subsystem. The following sections detail common operations for each helper.
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
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
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
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
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
# 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
# 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
# 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
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]::Is64BitOperatingSystemSources: docs/memory-bank/product.md:58-65
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
Sources: OfficeScrubC2R.psm1:6-8, OfficeScrubC2R.psm1:10-35
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
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:
-
Reuse Orchestrator Instances: Create once, use multiple times
$orchestrator = New-OfficeScrubOrchestrator $helpers = Get-OfficeScrubHelpers -Orchestrator $orchestrator # Use $helpers repeatedly without recreating
-
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") }
-
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
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
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
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
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
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
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
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-JsonSources: docs/memory-bank/product.md:42-56
For additional information on using OfficeScrubC2R:
- Command-Line Parameters - Parameter reference (Note: The module exports functions, not a command-line tool)
- Operational Modes - Usage patterns and workflows
- Use Cases and Examples - Detailed real-world scenarios
- Troubleshooting - Common problems and solutions
- Architecture - System design and component interactions
- Native Performance Layer - Details on C# helper classes and Win32 API integration
- Developer Guide - Building from source and contributing
Sources: README.md:1-71
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 -DetectOnlyThe 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
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
Sources: OfficeScrubC2R.ps1:924-1001, OfficeScrubC2R.psm1:176-204, OfficeScrubC2R.ps1:867-918
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
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
Sources: OfficeScrubC2R.ps1:87-101, OfficeScrubC2R.psm1:179-191, OfficeScrubC2R.ps1:952-961
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
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
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
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
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 50Sources: OfficeScrubC2R.ps1:120-126, README.md:236-245
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
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
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