Skip to content

Module Structure

Calvindd2f edited this page Jan 14, 2026 · 1 revision

Module Structure

Relevant source files

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

This document describes the PowerShell module file organization and component interactions in OfficeScrubC2R. The module implements a lightweight wrapper pattern where PowerShell provides the user interface while delegating all performance-critical operations to the native C# library.

For details on the native C# performance layer that this module wraps, see Native Performance Layer. For information on the orchestrator pattern used to coordinate cleanup operations, see OfficeScrubOrchestrator.

Overview

The OfficeScrubC2R module v3.0.0 consists of two primary components with a simplified architecture:

File Type Role
OfficeScrubC2R.psd1 Module Manifest Defines module metadata, version, and exports
OfficeScrubC2R.psm1 Root Module Lightweight wrapper exposing native DLL functions
OfficeScrubNative.dll Native Assembly C# library with all cleanup logic

The module uses a wrapper pattern where the PowerShell layer is intentionally minimal. The module loads the native DLL and exposes five helper functions that create and interact with the OfficeScrubOrchestrator class. This design achieves 10-50x performance improvements by implementing all cleanup operations in compiled C# code with direct Win32 API access.

The repository also includes reference files (OfficeScrubC2R-Native.cs, direct_conversion.cs, docs/source/OfficeScrubC2R.vbs) for documentation and comparison purposes, but these are not loaded by the module.

Sources: OfficeScrubC2R.psd1:1-158, OfficeScrubC2R.psm1:1-99, docs/memory-bank/structure.md:1-133

Module Manifest (OfficeScrubC2R.psd1)

Module Metadata and Configuration

The manifest defines the module's identity, requirements, and structure:

RootModule             = 'OfficeScrubC2R.psm1'
ModuleVersion          = '3.0.0'
CompatiblePSEditions   = @('Desktop', 'Core')
GUID                   = 'f8e7c4d1-5a3b-4e2d-9c8f-1b6a4d7e9c3a'
PowerShellVersion      = '5.1'
DotNetFrameworkVersion = '4.5'
ProcessorArchitecture  = 'None'

The manifest establishes version 3.0.0, which represents a major architectural change from previous versions. The module is now a lightweight wrapper focusing on the native DLL rather than script-based implementations. Support for both PowerShell Desktop and Core editions OfficeScrubC2R.psd1:9 ensures compatibility across Windows PowerShell 5.1 and PowerShell 7+.

Sources: OfficeScrubC2R.psd1:3-35

Dependency Declarations

The manifest declares minimal dependencies:

RequiredModules        = @()
RequiredAssemblies     = @()
NestedModules          = @()

Unlike typical PowerShell modules, this manifest does not declare OfficeScrubNative.dll in RequiredAssemblies OfficeScrubC2R.psd1:43. The DLL is loaded dynamically by the Import-OfficeScrubNative function to avoid conflicts when the assembly is already loaded in the current AppDomain. This approach provides better control over assembly loading and error handling.

Sources: OfficeScrubC2R.psd1:38-55

Export Configuration

The manifest explicitly defines the public API surface:

Export Type Count Members
Functions 5 Import-OfficeScrubNative, New-OfficeScrubOrchestrator, Test-OfficeC2RPath, Test-OfficeProductScope, Get-OfficeScrubHelpers
Aliases 0 None exported
Cmdlets 0 None (pure PowerShell wrapper)
Variables 0 None exported

The explicit export list OfficeScrubC2R.psd1:58-64 ensures only intended functions are visible to module consumers. All five functions are thin wrappers around the native OfficeScrubOrchestrator class and its helper components.

Sources: OfficeScrubC2R.psd1:58-76

FileList and Reference Sources

The manifest includes both runtime and reference files:

FileList = @(
    'OfficeScrubC2R.psd1',
    'OfficeScrubC2R.psm1',
    'OfficeScrubNative.dll',
    'OfficeScrubC2R-Native.cs',
    'direct_conversion.cs',
    'docs\source\OfficeScrubC2R.vbs',
    'LICENSE',
    'README.md'
)

The FileList OfficeScrubC2R.psd1:82-91 includes the original VBScript implementation (OfficeScrubC2R.vbs) and reference C# conversions (OfficeScrubC2R-Native.cs, direct_conversion.cs) for documentation and comparison purposes. These files are not loaded or executed by the module; they serve as historical reference and demonstrate the evolution from VBScript to optimized C#.

Sources: OfficeScrubC2R.psd1:82-91

Root Module (OfficeScrubC2R.psm1)

Module Structure and Initialization

The root module is intentionally minimal, containing only wrapper functions that expose the native DLL functionality. The module defines script-level variables and five exported functions:

$script:ModuleRoot = $PSScriptRoot
$script:NativeAssemblyPath = Join-Path $script:ModuleRoot 'OfficeScrubNative.dll'
$script:NativeAssembly = $null

These script variables OfficeScrubC2R.psm1:6-8 track the module location, DLL path, and loaded assembly reference. The $script:NativeAssembly variable is cached to avoid reloading the assembly if already present in the AppDomain.

Sources: OfficeScrubC2R.psm1:1-9

Module Loading Sequence

sequenceDiagram
    participant User["User"]
    participant Manifest["OfficeScrubC2R.psd1"]
    participant RootModule["OfficeScrubC2R.psm1"]
    participant NativeDLL["OfficeScrubNative.dll"]
    
    User->>Manifest: "Import-Module OfficeScrubC2R"
    Note over Manifest: "Parse manifest v3.0.0<br/>No RequiredAssemblies"
    
    Manifest->>RootModule: "Execute RootModule"
    
    RootModule->>RootModule: "Lines 6-8: Initialize variables<br/>$script:ModuleRoot<br/>$script:NativeAssemblyPath<br/>$script:NativeAssembly"
    
    RootModule->>RootModule: "Lines 10-35: Define<br/>Import-OfficeScrubNative"
    
    RootModule->>RootModule: "Lines 37-46: Define<br/>New-OfficeScrubOrchestrator"
    
    RootModule->>RootModule: "Lines 48-60: Define<br/>Test-OfficeC2RPath"
    
    RootModule->>RootModule: "Lines 62-74: Define<br/>Test-OfficeProductScope"
    
    RootModule->>RootModule: "Lines 76-96: Define<br/>Get-OfficeScrubHelpers"
    
    RootModule->>RootModule: "Line 98: Export-ModuleMember"
    Note over RootModule: "Export 5 functions"
    
    RootModule-->>User: "Module ready<br/>DLL not loaded yet"
    
    Note over User,NativeDLL: "DLL loads on first function call"
    
    User->>RootModule: "New-OfficeScrubOrchestrator"
    RootModule->>RootModule: "Line 44: Call Import-OfficeScrubNative"
    RootModule->>NativeDLL: "Assembly.LoadFrom()"
    NativeDLL-->>RootModule: "Assembly loaded"
    RootModule->>NativeDLL: "[OfficeScrubNative.OfficeScrubOrchestrator]::new()"
    NativeDLL-->>User: "Orchestrator instance"
Loading

Module Loading and Lazy Assembly Loading

This diagram shows the loading sequence with lazy DLL loading. Unlike previous versions, the native assembly is not loaded during module import. The DLL loads on the first function call that requires it, typically New-OfficeScrubOrchestrator. This lazy loading pattern improves module import performance and avoids assembly conflicts.

Sources: OfficeScrubC2R.psm1:1-99, OfficeScrubC2R.psd1:1-158

Native Assembly Loading: Import-OfficeScrubNative

The Import-OfficeScrubNative function implements dynamic assembly loading with caching:

function Import-OfficeScrubNative {
    [CmdletBinding()]
    param(
        [string]$Path = $script:NativeAssemblyPath
    )
    
    $resolvedPath = (Resolve-Path -Path $Path -ErrorAction Stop).ProviderPath
    
    if (-not (Test-Path -Path $resolvedPath -PathType Leaf)) {
        throw "Native assembly not found at '$resolvedPath'..."
    }
    
    if ($script:NativeAssembly -and $script:NativeAssembly.Location -eq $resolvedPath) {
        return $script:NativeAssembly
    }
    
    $script:NativeAssembly = [AppDomain]::CurrentDomain.GetAssemblies() |
        Where-Object { $_.Location -eq $resolvedPath }
    
    if (-not $script:NativeAssembly) {
        $script:NativeAssembly = [System.Reflection.Assembly]::LoadFrom($resolvedPath)
    }
    
    return $script:NativeAssembly
}

The function implements three-level loading strategy OfficeScrubC2R.psm1:10-35:

  1. Cache check: Return $script:NativeAssembly if already loaded at the specified path OfficeScrubC2R.psm1:23-25
  2. AppDomain search: Check if assembly is already loaded in current AppDomain OfficeScrubC2R.psm1:27-28
  3. LoadFrom: Load assembly from disk using Assembly.LoadFrom() OfficeScrubC2R.psm1:31

This pattern avoids the "assembly already loaded" exceptions that would occur if OfficeScrubNative.dll were declared in RequiredAssemblies.

Sources: OfficeScrubC2R.psm1:10-35

Exported Functions and Native Integration

Function Overview and Architecture

graph TB
    subgraph ModuleScope["Module Scope (OfficeScrubC2R.psm1)"]
        ImportFunc["Import-OfficeScrubNative<br/>Lines 10-35<br/>Assembly loading"]
        
        NewOrch["New-OfficeScrubOrchestrator<br/>Lines 37-46<br/>Creates orchestrator instance"]
        
        TestPath["Test-OfficeC2RPath<br/>Lines 48-60<br/>Validates C2R paths"]
        
        TestScope["Test-OfficeProductScope<br/>Lines 62-74<br/>Checks product scope"]
        
        GetHelpers["Get-OfficeScrubHelpers<br/>Lines 76-96<br/>Returns helper objects"]
    end
    
    subgraph NativeScope["Native DLL (OfficeScrubNative.dll)"]
        Orchestrator["OfficeScrubOrchestrator<br/>.new(Is64Bit)"]
        
        Methods["Methods:<br/>IsC2RPath()<br/>IsInScope()"]
        
        Helpers["Properties:<br/>Registry<br/>Files<br/>Processes<br/>Shell<br/>WindowsInstaller<br/>TypeLib<br/>License<br/>Services"]
    end
    
    ImportFunc -->|"Assembly.LoadFrom()"| Orchestrator
    NewOrch -->|"Line 45"| Orchestrator
    TestPath -->|"Line 59"| Methods
    TestScope -->|"Line 73"| Methods
    GetHelpers -->|"Lines 86-95"| Helpers
Loading

PowerShell Wrapper to Native Integration

This diagram shows how the five exported PowerShell functions map to native C# classes and methods. Each function is a thin wrapper that either loads the assembly, creates instances, or invokes methods on the OfficeScrubOrchestrator class.

Sources: OfficeScrubC2R.psm1:10-96

New-OfficeScrubOrchestrator Function

The primary function for creating the orchestrator instance:

function New-OfficeScrubOrchestrator {
    [CmdletBinding()]
    param(
        [bool]$Is64Bit = [Environment]::Is64BitOperatingSystem
    )
    
    Import-OfficeScrubNative | Out-Null
    return [OfficeScrubNative.OfficeScrubOrchestrator]::new($Is64Bit)
}

This function OfficeScrubC2R.psm1:37-46 implements the factory pattern for creating orchestrator instances:

  1. Lazy load assembly: Calls Import-OfficeScrubNative to ensure DLL is loaded OfficeScrubC2R.psm1:44
  2. Detect architecture: Defaults $Is64Bit to system architecture OfficeScrubC2R.psm1:41
  3. Instantiate orchestrator: Creates OfficeScrubOrchestrator with architecture flag OfficeScrubC2R.psm1:45

The $Is64Bit parameter determines whether registry operations use WOW64 redirection. See WOW64 Registry Handling for details.

Sources: OfficeScrubC2R.psm1:37-46

Test-OfficeC2RPath Function

Validates whether a path matches Click-to-Run patterns:

function Test-OfficeC2RPath {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][string]$Path,
        [OfficeScrubNative.OfficeScrubOrchestrator]$Orchestrator
    )
    
    if (-not $PSBoundParameters.ContainsKey('Orchestrator') -or -not $Orchestrator) {
        $Orchestrator = New-OfficeScrubOrchestrator
    }
    
    return $Orchestrator.IsC2RPath($Path)
}

The function OfficeScrubC2R.psm1:48-60 accepts an optional $Orchestrator parameter for performance optimization. If not provided, it creates a new orchestrator instance OfficeScrubC2R.psm1:55-57. The actual path validation logic resides in the native IsC2RPath() method OfficeScrubC2R.psm1:59.

Sources: OfficeScrubC2R.psm1:48-60

Test-OfficeProductScope Function

Determines whether a product code should be cleaned:

function Test-OfficeProductScope {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][string]$ProductCode,
        [OfficeScrubNative.OfficeScrubOrchestrator]$Orchestrator
    )
    
    if (-not $PSBoundParameters.ContainsKey('Orchestrator') -or -not $Orchestrator) {
        $Orchestrator = New-OfficeScrubOrchestrator
    }
    
    return $Orchestrator.IsInScope($ProductCode)
}

This function OfficeScrubC2R.psm1:62-74 wraps the IsInScope() method which evaluates product codes against Office version patterns. The orchestrator maintains internal lists of Office product identifiers for scope determination.

Sources: OfficeScrubC2R.psm1:62-74

Get-OfficeScrubHelpers Function

Provides access to all helper class instances:

function Get-OfficeScrubHelpers {
    [CmdletBinding()]
    param(
        [OfficeScrubNative.OfficeScrubOrchestrator]$Orchestrator
    )
    
    if (-not $PSBoundParameters.ContainsKey('Orchestrator') -or -not $Orchestrator) {
        $Orchestrator = New-OfficeScrubOrchestrator
    }
    
    [pscustomobject]@{
        RegistryHelper         = $Orchestrator.Registry
        FileHelper             = $Orchestrator.Files
        ProcessHelper          = $Orchestrator.Processes
        ShellHelper            = $Orchestrator.Shell
        WindowsInstallerHelper = $Orchestrator.WindowsInstaller
        TypeLibHelper          = $Orchestrator.TypeLib
        LicenseHelper          = $Orchestrator.License
        ServiceHelper          = $Orchestrator.Services
    }
}

The function OfficeScrubC2R.psm1:76-96 returns a PSCustomObject exposing all eight helper class properties from the orchestrator OfficeScrubC2R.psm1:86-95. This provides direct access to specialized helper classes for advanced scenarios. See Helper Classes for detailed documentation of each helper.

Sources: OfficeScrubC2R.psm1:76-96

Module Member Exports

Export-ModuleMember Declaration

The module explicitly exports its public API:

Export-ModuleMember -Function Import-OfficeScrubNative, New-OfficeScrubOrchestrator, Test-OfficeC2RPath, Test-OfficeProductScope, Get-OfficeScrubHelpers

The Export-ModuleMember statement OfficeScrubC2R.psm1:98 lists all five wrapper functions. This declaration must match the manifest's FunctionsToExport OfficeScrubC2R.psd1:58-64 to ensure PowerShell Gallery compatibility and proper function visibility.

Sources: OfficeScrubC2R.psm1:98, OfficeScrubC2R.psd1:58-64

Exported Function Summary

Function Purpose Returns
Import-OfficeScrubNative Loads native DLL with caching System.Reflection.Assembly
New-OfficeScrubOrchestrator Creates orchestrator instance OfficeScrubOrchestrator
Test-OfficeC2RPath Validates C2R path patterns Boolean
Test-OfficeProductScope Checks product code scope Boolean
Get-OfficeScrubHelpers Exposes helper class instances PSCustomObject

All five functions are defined in the root module OfficeScrubC2R.psm1:10-96. The module does not export any aliases, cmdlets, or variables. This minimal API surface reflects the wrapper pattern design.

Sources: OfficeScrubC2R.psm1:10-98, OfficeScrubC2R.psd1:58-73

Module Usage Patterns

Typical Workflow

sequenceDiagram
    participant User["User Script"]
    participant Module["OfficeScrubC2R.psm1"]
    participant ImportFunc["Import-OfficeScrubNative"]
    participant NewOrch["New-OfficeScrubOrchestrator"]
    participant Orchestrator["OfficeScrubOrchestrator"]
    participant RegistryHelper["RegistryHelper"]
    
    User->>Module: "Import-Module OfficeScrubC2R"
    Note over Module: "Module loads<br/>DLL not loaded yet"
    Module-->>User: "Module ready"
    
    User->>NewOrch: "New-OfficeScrubOrchestrator"
    NewOrch->>ImportFunc: "Import-OfficeScrubNative"
    ImportFunc->>ImportFunc: "Check cache, AppDomain<br/>LoadFrom() if needed"
    ImportFunc-->>NewOrch: "Assembly"
    NewOrch->>Orchestrator: "[OfficeScrubOrchestrator]::new($true)"
    Orchestrator-->>User: "Orchestrator instance"
    
    User->>Orchestrator: "$orch.Registry.KeyExists($key)"
    Orchestrator->>RegistryHelper: "KeyExists($key)"
    RegistryHelper-->>User: "$true or $false"
    
    User->>Orchestrator: "$orch.Files.DeleteDirectory($path)"
    Note over Orchestrator: "Helper methods execute<br/>native C# code"
Loading

Typical Module Usage Workflow

This diagram shows the standard usage pattern where users import the module, create an orchestrator, and call methods directly on the helper classes. The lazy loading pattern defers assembly loading until the first orchestrator creation.

Sources: OfficeScrubC2R.psm1:1-99

Advanced Usage: Direct Helper Access

Users can access helper classes directly:

# Create orchestrator
$orch = New-OfficeScrubOrchestrator

# Access individual helpers
$orch.Registry.KeyExists("HKLM:\Software\Microsoft\Office\16.0\Common")
$orch.Files.DeleteDirectory("C:\Program Files\Microsoft Office", $true)
$orch.Processes.TerminateProcesses(@("WINWORD", "EXCEL"), 5000)

Alternatively, use Get-OfficeScrubHelpers to retrieve all helpers as properties:

$helpers = Get-OfficeScrubHelpers
$helpers.RegistryHelper.EnumerateKeys("HKLM:\Software\Microsoft\Office")
$helpers.ProcessHelper.IsProcessRunning("OUTLOOK")

The Get-OfficeScrubHelpers function OfficeScrubC2R.psm1:76-96 wraps the helper properties in a PSCustomObject for cleaner access patterns.

Sources: OfficeScrubC2R.psm1:76-96

Component Relationships

graph TB
    User["User Command"]
    
    Manifest["OfficeScrubC2R.psd1<br/>ModuleVersion: 3.0.0<br/>RootModule: Line 3<br/>RequiredAssemblies: empty<br/>NestedModules: empty"]
    
    RootModule["OfficeScrubC2R.psm1<br/>Lines 6-8: Script variables<br/>Lines 10-35: Import-OfficeScrubNative<br/>Lines 37-46: New-OfficeScrubOrchestrator<br/>Lines 48-96: Wrapper functions<br/>Line 98: Export-ModuleMember"]
    
    NativeDLL["OfficeScrubNative.dll<br/>Lazy loaded by Import-OfficeScrubNative<br/>OfficeScrubOrchestrator<br/>8 Helper Classes"]
    
    SourceFiles["Reference Files<br/>(not loaded)<br/>OfficeScrubC2R-Native.cs<br/>direct_conversion.cs<br/>docs/source/OfficeScrubC2R.vbs"]
    
    User -->|"Import-Module"| Manifest
    Manifest -->|"RootModule"| RootModule
    RootModule -->|"Lazy load on first call"| NativeDLL
    SourceFiles -.->|"Documentation only"| NativeDLL
    
    style SourceFiles fill:#f5f5f5,stroke-dasharray: 5 5
Loading

Module Component Dependencies

This diagram shows the simplified component structure. The manifest references only the root module, with no RequiredAssemblies or NestedModules declarations. The native DLL loads lazily when first accessed. Reference source files are included in the package but not loaded by the module.

Sources: OfficeScrubC2R.psd1:1-158, OfficeScrubC2R.psm1:1-99

Native Code Bridge

graph LR
    subgraph PowerShell["PowerShell Layer"]
        ImportFunc["Import-OfficeScrubNative<br/>Assembly.LoadFrom()"]
        NewOrch["New-OfficeScrubOrchestrator<br/>Instantiation wrapper"]
        TestPath["Test-OfficeC2RPath<br/>IsC2RPath() wrapper"]
        TestScope["Test-OfficeProductScope<br/>IsInScope() wrapper"]
        GetHelpers["Get-OfficeScrubHelpers<br/>Property accessor"]
    end
    
    subgraph NativeCode["C# Native Code"]
        Assembly["OfficeScrubNative.dll<br/>Assembly"]
        Orch["OfficeScrubOrchestrator<br/>Constructor & Methods"]
        Helpers["8 Helper Classes<br/>Registry, Files, Processes,<br/>Shell, WindowsInstaller,<br/>TypeLib, License, Services"]
    end
    
    ImportFunc -->|"LoadFrom()"| Assembly
    NewOrch -->|"::new(Is64Bit)"| Orch
    TestPath -->|".IsC2RPath()"| Orch
    TestScope -->|".IsInScope()"| Orch
    GetHelpers -->|"Properties"| Helpers
    Orch --> Helpers
Loading

PowerShell to Native Code Bridge

This diagram maps each PowerShell function to its corresponding C# code entity. The five wrapper functions provide minimal translation between PowerShell calling conventions and native C# methods. All business logic resides in the native layer.

Sources: OfficeScrubC2R.psm1:10-96

Design Philosophy: Wrapper Pattern

Minimal PowerShell Layer

The v3.0.0 architecture adopts a wrapper pattern where PowerShell serves solely as the user interface. This design philosophy differs fundamentally from traditional PowerShell modules:

Aspect Traditional Module OfficeScrubC2R v3.0.0
Business logic PowerShell scripts Native C# DLL
Performance-critical ops Cmdlets or external tools Direct Win32 API calls
Module complexity High (many .ps1/.psm1 files) Low (2 files: .psd1 + .psm1)
Code lines in PowerShell Hundreds to thousands <100 lines
Assembly loading RequiredAssemblies Lazy dynamic loading

The wrapper pattern achieves 10-50x performance improvements by delegating all operations to compiled C# code OfficeScrubC2R.psd1:24-26. The PowerShell layer contains only function definitions and basic parameter handling.

Sources: OfficeScrubC2R.psd1:24-26, OfficeScrubC2R.psm1:1-99

Reference Sources

The module includes reference implementations for documentation:

File Purpose Loaded by Module?
OfficeScrubNative.dll Runtime library Yes (lazy loaded)
OfficeScrubC2R-Native.cs C# source for DLL No
direct_conversion.cs Direct VBScript conversion No
docs/source/OfficeScrubC2R.vbs Original Microsoft VBScript No

The reference files OfficeScrubC2R.psd1:86-88 demonstrate the evolution from VBScript to optimized C# but are not executed. They serve developers analyzing the codebase or comparing implementations.

Sources: OfficeScrubC2R.psd1:82-91

Module Loading and Assembly Lifecycle

Lazy Loading Benefits

The module implements lazy assembly loading for several reasons:

Benefit Implementation Detail
Fast module import Import-Module completes without loading DLL OfficeScrubC2R.psm1:1-9
Avoid conflicts Check AppDomain before loading OfficeScrubC2R.psm1:27-28
Conditional loading DLL loads only if wrapper functions are called
Error isolation Assembly load errors don't prevent module import
Version flexibility Different PowerShell sessions can load different DLL versions

The lazy loading pattern defers Assembly.LoadFrom() OfficeScrubC2R.psm1:31 until Import-OfficeScrubNative is called, typically by New-OfficeScrubOrchestrator. This contrasts with RequiredAssemblies which loads immediately during module import.

Sources: OfficeScrubC2R.psm1:10-35

Assembly Caching Strategy

flowchart TD
    Start["Import-OfficeScrubNative called"]
    
    CheckCache{"$script:NativeAssembly<br/>cached & matches path?"}
    CheckCache -->|Yes| ReturnCache["Return cached assembly"]
    
    CheckCache -->|No| CheckDomain{"Assembly in<br/>AppDomain already?"}
    CheckDomain -->|Yes| CacheDomain["Cache from AppDomain<br/>Return"]
    
    CheckDomain -->|No| Load["Assembly.LoadFrom()<br/>from disk"]
    Load --> CacheNew["Cache new assembly<br/>Return"]
    
    ReturnCache --> End["Return Assembly"]
    CacheDomain --> End
    CacheNew --> End
Loading

Assembly Loading Strategy with Three-Level Caching

This flowchart shows the three-level caching strategy implemented in Import-OfficeScrubNative OfficeScrubC2R.psm1:10-35. The function checks the module-level cache first OfficeScrubC2R.psm1:23-25, then the AppDomain OfficeScrubC2R.psm1:27-28, and finally loads from disk OfficeScrubC2R.psm1:31 as a last resort.

Sources: OfficeScrubC2R.psm1:10-35

Module Import and Function Availability

Simplified Import Workflow

When a user executes Import-Module OfficeScrubC2R, PowerShell performs these steps:

  1. Locate manifest: Find OfficeScrubC2R.psd1 in module path
  2. Parse manifest: Read ModuleVersion, RootModule, and configuration OfficeScrubC2R.psd1:1-158
  3. Skip assembly loading: Empty RequiredAssemblies OfficeScrubC2R.psd1:43
  4. Execute RootModule: Run OfficeScrubC2R.psm1 OfficeScrubC2R.psd1:3
  5. Module ready: Functions available, DLL not loaded yet

The module import completes quickly because no native code is loaded. Assembly loading is deferred to the first function call that requires it.

Sources: OfficeScrubC2R.psd1:1-158, OfficeScrubC2R.psm1:1-99

Error Handling: Assembly Load Failures

The Import-OfficeScrubNative function includes defensive error handling:

if (-not (Test-Path -Path $resolvedPath -PathType Leaf)) {
    throw "Native assembly not found at '$resolvedPath'. Ensure OfficeScrubNative.dll is present alongside the module."
}

The function validates DLL existence OfficeScrubC2R.psm1:19-21 before attempting to load, providing clear error messages. If the assembly file is missing, the module still imports successfully, but calls to New-OfficeScrubOrchestrator fail with a descriptive exception.

The Resolve-Path call OfficeScrubC2R.psm1:17 converts relative paths to absolute paths and validates path syntax, using -ErrorAction Stop to halt on invalid paths.

Sources: OfficeScrubC2R.psm1:10-35

Best Practices Demonstrated

The module structure demonstrates several PowerShell best practices:

  1. Explicit exports: Both manifest and module declare exports OfficeScrubC2R.psd1:65-80, OfficeScrubC2R.psm1:212-221
  2. Scope management: Deliberate use of global vs. module scope for different components
  3. Fail-fast validation: Path checks with immediate exceptions OfficeScrubC2R.psm1:15, 24
  4. Version tracking: Manifest version OfficeScrubC2R.psd1:6 enables update management
  5. CmdletBinding: Advanced function features with proper parameter attributes OfficeScrubC2R.psm1:119
  6. ShouldProcess support: Enables -WhatIf and -Confirm for destructive operations OfficeScrubC2R.psm1:119
  7. Comprehensive metadata: Detailed manifest for PowerShell Gallery compatibility OfficeScrubC2R.psd1:103-168
  8. Auto-initialization: Environment setup on module load OfficeScrubC2R.psm1:225-231

These patterns create a robust, maintainable module structure that follows PowerShell conventions while optimizing for performance and usability.

Sources: OfficeScrubC2R.psm1:1-233, OfficeScrubC2R.psd1:1-176

Clone this wiki locally