Skip to content

Development Guidelines

Calvindd2f edited this page Jan 14, 2026 · 1 revision

Development Guidelines

Relevant source files

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

This document provides comprehensive coding standards, architectural patterns, and best practices for contributors to the OfficeScrubC2R project. It covers conventions for both PowerShell and C# code, including naming standards, code organization, error handling, performance optimization, and testing patterns.

For information about building the project from source, see Building from Source. For details about the repository structure, see Code Organization. For testing procedures, see Testing and Validation.


Naming Conventions

The OfficeScrubC2R project follows standard .NET and PowerShell naming conventions to ensure consistency and readability across the codebase.

C# Naming Standards

Element Convention Example
Classes PascalCase with descriptive names RegistryHelper, WindowsInstallerHelper, OfficeScrubOrchestrator
Methods PascalCase with action-oriented names DeleteKey, EnumerateValues, TerminateProcesses
Properties PascalCase for public properties Registry, Files, Processes, PendingDeletes
Constants UPPER_CASE with underscores OFFICE_ID, PROD_LEN, KEY_WOW64_64KEY
Private Fields camelCase with underscore prefix _regHelper, _pendingDeletes, _is64Bit
Parameters camelCase compressedGuid, directoryPath, processNames

PowerShell Naming Standards

Element Convention Example
Functions PascalCase with approved verbs Import-OfficeScrubNative, Test-OfficeC2RPath
Parameters PascalCase $ProductId, $DetectOnly, $KeepLicense
Variables camelCase for local, PascalCase for script-scope $tempPath, $script:LogDir
Constants UPPER_CASE with underscores $OFFICE_15, $C2R_PATTERN

Sources:


Code Organization Patterns

Region-Based Organization

C# source files use #region blocks to group related functionality and improve code navigation. This pattern is extensively used in OfficeScrubC2R-Native.cs.

graph TB
    subgraph "OfficeScrubC2R-Native.cs File Structure"
        File["OfficeScrubC2R-Native.cs"]
        
        File --> RegionEnums["#region Enums and Constants<br/>OfficeConstants class<br/>GUID lengths, paths"]
        File --> RegionPInvoke["#region P/Invoke Declarations<br/>NativeMethods class<br/>Win32 API imports"]
        File --> RegionOrch["#region OfficeScrubOrchestrator<br/>Central coordinator<br/>IsInScope method"]
        File --> RegionReg["#region RegistryHelper<br/>Registry operations<br/>WOW64 support"]
        File --> RegionFile["#region FileHelper<br/>File/directory operations<br/>Reboot scheduling"]
        File --> RegionProc["#region ProcessHelper<br/>Process termination<br/>Parallel execution"]
        File --> RegionWI["#region WindowsInstallerHelper<br/>MSI cleanup<br/>GUID transformations"]
        File --> RegionOther["#region Additional Helpers<br/>TypeLib, License, Service<br/>Shell, Guid helpers"]
    end
    
    style File fill:#f9f
    style RegionOrch fill:#fff4e1
Loading

Diagram: C# Source File Organization with Region Blocks

Logical Grouping by Helper Classes

The native library separates concerns into distinct helper classes, each responsible for a specific Windows subsystem:

graph LR
    subgraph "Helper Class Organization"
        Orchestrator["OfficeScrubOrchestrator<br/>(Central Coordinator)"]
        
        Orchestrator --> Registry["RegistryHelper<br/>KeyExists, DeleteKey<br/>EnumerateKeys, WOW64"]
        Orchestrator --> File["FileHelper<br/>DeleteFile, DeleteDirectory<br/>MoveFileEx, PendingDeletes"]
        Orchestrator --> Process["ProcessHelper<br/>TerminateProcesses<br/>Parallel Task.Run"]
        Orchestrator --> WI["WindowsInstallerHelper<br/>CleanupUpgradeCodes<br/>CleanupProducts"]
        Orchestrator --> TLH["TypeLibHelper<br/>UnregisterTypeLib<br/>COM cleanup"]
        Orchestrator --> LH["LicenseHelper<br/>OSPP operations<br/>SPP cleanup"]
        Orchestrator --> SH["ServiceHelper<br/>StopService<br/>DeleteService"]
        Orchestrator --> Shell["ShellHelper<br/>Pin/Unpin operations<br/>Shortcut cleanup"]
        Orchestrator --> Guid["GuidHelper<br/>GetExpandedGuid<br/>GetCompressedGuid"]
    end
    
    style Orchestrator fill:#fff4e1
Loading

Diagram: Separation of Concerns via Helper Classes

Static Utility Classes

Utility functions that don't maintain state are organized as static classes:

Sources:


Error Handling Standards

Silent Failure Pattern

The codebase uses a consistent error handling pattern where operations return false or null on failure rather than throwing exceptions. This allows the orchestrator to continue execution even when individual operations fail.

flowchart TD
    Start["Method Execution"] --> Try["try block<br/>Perform operation"]
    Try -->|"Success"| Return["return true/result"]
    Try -->|"Exception"| Catch["catch block<br/>(no specific handling)"]
    Catch --> ReturnFalse["return false/null"]
    
    subgraph "Example: RegistryHelper.DeleteKey"
        ExStart["DeleteKey(keyPath)"] --> ExTry["try:<br/>RegDeleteKeyEx()"]
        ExTry -->|"Success"| ExTrue["return true"]
        ExTry -->|"Exception"| ExCatch["catch:<br/>silent"]
        ExCatch --> ExFalse["return false"]
    end
    
    style Catch fill:#ffe1e1
    style ExCatch fill:#ffe1e1
Loading

Diagram: Silent Failure Pattern with Try-Catch

Implementation Example: OfficeScrubC2R-Native.cs - RegistryHelper.DeleteKey, FileHelper.DeleteFile, and other operations follow this pattern.

Resource Disposal

Always use using statements or explicit disposal in finally blocks for resources that implement IDisposable:

// Pattern used in ProcessHelper.TerminateProcesses
using (Process process = Process.GetProcessById(pid))
{
    process.Kill();
    process.WaitForExit(timeout);
}

Graceful Degradation

Provide fallback mechanisms when primary operations fail:

Primary Operation Fallback Mechanism
Directory.Delete() cmd.exe rd /s /q
File deletion Schedule on reboot with MoveFileEx
Pre-compiled DLL Source compilation with csc.exe
Native WMI operations PowerShell cmdlets

Sources:


Helper Class Pattern

The helper class pattern is the core architectural design that separates concerns into specialized components.

Pattern Structure

graph TB
    subgraph "Helper Class Pattern"
        Orchestrator["OfficeScrubOrchestrator<br/>(Composition Root)"]
        
        subgraph "Initialization"
            Constructor["Constructor()"]
            Constructor --> CreateHelpers["Create helper instances"]
            CreateHelpers --> RegInstance["_regHelper = new RegistryHelper()"]
            CreateHelpers --> FileInstance["_fileHelper = new FileHelper()"]
            CreateHelpers --> ProcInstance["_processHelper = new ProcessHelper()"]
        end
        
        subgraph "Dependency Injection"
            Orchestrator --> PropRegistry["Registry Property<br/>get => _regHelper"]
            Orchestrator --> PropFiles["Files Property<br/>get => _fileHelper"]
            Orchestrator --> PropProcesses["Processes Property<br/>get => _processHelper"]
        end
        
        subgraph "Helper Interface"
            Helper["Helper Class"]
            Helper --> State["Internal State<br/>_is64Bit, _pendingDeletes"]
            Helper --> Methods["Public Methods<br/>Operation-specific"]
            Helper --> Cache["Caching<br/>HashSet collections"]
        end
        
        Orchestrator --> Helper
    end
    
    style Orchestrator fill:#fff4e1
    style Helper fill:#e1ffe1
Loading

Diagram: Helper Class Pattern with Dependency Injection

Creating a New Helper Class

When adding a new helper class, follow these steps:

  1. Define the helper class with private constructor fields for dependencies
  2. Encapsulate related operations in public methods
  3. Maintain internal state for caching and performance
  4. Use the silent failure pattern for error handling
  5. Add to orchestrator as a property with lazy initialization if needed

Example structure:

public class MyNewHelper
{
    private readonly SomeDependency _dependency;
    private readonly HashSet<string> _cache;
    
    public MyNewHelper(SomeDependency dependency)
    {
        _dependency = dependency;
        _cache = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
    }
    
    public bool PerformOperation(string input)
    {
        try
        {
            // Operation logic
            _cache.Add(input);
            return true;
        }
        catch
        {
            return false;
        }
    }
}

Sources:


P/Invoke Integration Pattern

P/Invoke (Platform Invoke) is used to call Win32 APIs directly for performance-critical operations. All P/Invoke declarations are centralized in the NativeMethods internal static class.

P/Invoke Architecture

graph TB
    subgraph "P/Invoke Pattern"
        NativeMethods["internal static class NativeMethods"]
        
        subgraph "Win32 API Declarations"
            NativeMethods --> RegAPI["advapi32.dll APIs<br/>RegOpenKeyEx<br/>RegDeleteKeyEx<br/>RegEnumKeyEx"]
            NativeMethods --> FileAPI["kernel32.dll APIs<br/>MoveFileEx<br/>GetFileAttributes<br/>SetFileAttributes"]
            NativeMethods --> ShellAPI["shell32.dll APIs<br/>SHGetKnownFolderPath<br/>IShellLink"]
        end
        
        subgraph "Constants and Enums"
            NativeMethods --> Flags["API Flags<br/>KEY_WOW64_64KEY<br/>KEY_WOW64_32KEY<br/>MOVEFILE_DELAY_UNTIL_REBOOT"]
            NativeMethods --> Access["Access Rights<br/>KEY_READ<br/>KEY_WRITE<br/>KEY_ALL_ACCESS"]
        end
        
        subgraph "Helper Class Usage"
            Registry["RegistryHelper"] --> RegAPI
            FileHelper["FileHelper"] --> FileAPI
            ShellHelper["ShellHelper"] --> ShellAPI
        end
    end
    
    style NativeMethods fill:#fff4e1
Loading

Diagram: P/Invoke Pattern with NativeMethods Class

P/Invoke Declaration Pattern

All Win32 API imports follow this pattern:

[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern int RegOpenKeyEx(
    IntPtr hKey,
    string lpSubKey,
    int ulOptions,
    int samDesired,
    out IntPtr phkResult);

Key elements:

  • [DllImport] attribute specifying the DLL name
  • CharSet = CharSet.Unicode for proper string marshaling
  • SetLastError = true to enable Marshal.GetLastWin32Error()
  • internal static extern visibility and signature
  • Proper parameter marshaling with IntPtr, string, and out parameters

Win32 API Constants

Constants for Win32 API flags are defined in the NativeMethods class:

Category Constants Usage
Registry Views KEY_WOW64_64KEY = 0x0100
KEY_WOW64_32KEY = 0x0200
WOW64 registry redirection
Access Rights KEY_READ = 0x20019
KEY_WRITE = 0x20006
KEY_ALL_ACCESS = 0xF003F
Registry key access
File Operations MOVEFILE_DELAY_UNTIL_REBOOT = 0x4 Schedule file deletion on reboot

Sources:


WOW64 Support Pattern

On 64-bit Windows systems, the registry is split into two views: the native 64-bit view and the redirected 32-bit view (WOW64). Office installations can leave artifacts in both views, so cleanup operations must handle both.

WOW64 Registry Handling

flowchart TD
    Start["Registry Operation"] --> CheckBit{"_is64Bit?"}
    
    CheckBit -->|"32-bit System"| SinglePath["Single Path<br/>HKLM\Software\X"]
    CheckBit -->|"64-bit System"| DualPath["Dual Path Operations"]
    
    DualPath --> Native["Native 64-bit View<br/>HKLM\Software\X<br/>KEY_WOW64_64KEY flag"]
    DualPath --> WOW["WOW64 32-bit View<br/>HKLM\Software\Wow6432Node\X<br/>KEY_WOW64_32KEY flag"]
    
    Native --> NativeOp["RegDeleteKeyEx(..., KEY_WOW64_64KEY)"]
    WOW --> WOWOp["RegDeleteKeyEx(..., KEY_WOW64_32KEY)"]
    
    SinglePath --> Cache["Check Cache"]
    NativeOp --> Cache
    WOWOp --> Cache
    
    Cache -->|"Not in cache"| Execute["Execute + Add to Cache"]
    Cache -->|"In cache"| Skip["Skip (already processed)"]
    
    subgraph "GetWow64Key Method"
        Transform["Transform Path"]
        Transform --> Replace["Replace 'Software\' with<br/>'Software\Wow6432Node\'"]
    end
    
    WOW --> Transform
    
    style DualPath fill:#fff4e1
    style Cache fill:#e1ffe1
Loading

Diagram: WOW64 Registry Dual-Path Handling

Implementation Details

The RegistryHelper class implements WOW64 support through:

  1. _is64Bit field - Determines system architecture via Environment.Is64BitOperatingSystem
  2. GetWow64Key() method - Transforms registry paths for 32-bit view by injecting Wow6432Node
  3. Dual API calls - Each registry operation calls Win32 API twice with different flags:
    • KEY_WOW64_64KEY for native 64-bit view
    • KEY_WOW64_32KEY for redirected 32-bit view
  4. Cache deduplication - HashSet<string> with StringComparer.OrdinalIgnoreCase prevents redundant operations

Example from OfficeScrubC2R-Native.cs:

private string GetWow64Key(string keyPath)
{
    if (!keyPath.Contains(@"Software\", StringComparison.OrdinalIgnoreCase))
        return keyPath;
    
    return keyPath.Replace(@"Software\", @"Software\Wow6432Node\", 
        StringComparison.OrdinalIgnoreCase);
}

Sources:


Performance Optimization Patterns

The OfficeScrubC2R project achieves 10-50x performance improvements over the original VBScript through several optimization techniques.

Parallel Processing Pattern

CPU-intensive operations that can be parallelized use Task.Run() with Task.WaitAll() to execute concurrently:

sequenceDiagram
    participant Caller
    participant ProcessHelper
    participant Tasks["Task.Run() Pool"]
    participant Lock["Thread-Safe Collection"]
    
    Caller->>ProcessHelper: TerminateProcesses(processNames)
    ProcessHelper->>ProcessHelper: Enumerate processes
    
    loop For each process
        ProcessHelper->>Tasks: Task.Run(() => KillProcess(pid))
    end
    
    par Parallel Execution
        Tasks->>Tasks: Process 1: Kill + WaitForExit
        Tasks->>Tasks: Process 2: Kill + WaitForExit
        Tasks->>Tasks: Process N: Kill + WaitForExit
    end
    
    Tasks->>Lock: lock(terminatedPids) { Add(pid) }
    
    ProcessHelper->>Tasks: Task.WaitAll(allTasks, timeout)
    Tasks-->>ProcessHelper: All complete
    ProcessHelper-->>Caller: Return terminated PIDs
Loading

Diagram: Parallel Process Termination with Task.Run()

Implementation location: OfficeScrubC2R-Native.cs in ProcessHelper.TerminateProcesses

StringBuilder Usage

Use StringBuilder with pre-allocated capacity for string building operations:

// Pattern for GUID manipulation
StringBuilder sb = new StringBuilder(capacity: 38);
sb.Append('{');
sb.Append(segment1);
sb.Append('-');
// ...
return sb.ToString();

Lazy Evaluation

Use LINQ methods like Any() for early termination in collection searches:

// Early termination when any match found
if (officeProcessNames.Any(name => 
    process.ProcessName.Equals(name, StringComparison.OrdinalIgnoreCase)))
{
    // Found match, no need to continue enumeration
}

Resource Pooling and Caching

The system caches frequently accessed data to avoid redundant operations:

Component Cached Data Purpose
RegistryHelper HashSet<string> of processed keys Prevent duplicate registry operations
FileHelper List<string> of pending deletes Track reboot-scheduled deletions
WindowsInstallerHelper Expanded GUIDs Avoid repeated GUID transformations

Batch Operations

Related operations are grouped together for efficiency:

  • Registry enumeration - Enumerate all keys once, then filter
  • Process termination - Collect all PIDs, then terminate in parallel
  • File deletion - Use cmd.exe rd /s /q for large directories (15x faster)

Sources:


Security and Safety Patterns

Input Validation

All public methods validate inputs at entry points:

flowchart TD
    MethodEntry["Method Entry"] --> NullCheck{"Input == null?"}
    NullCheck -->|"Yes"| ReturnEarly["return false/null"]
    NullCheck -->|"No"| EmptyCheck{"string.IsNullOrEmpty?"}
    
    EmptyCheck -->|"Yes"| ReturnEarly
    EmptyCheck -->|"No"| LengthCheck{"Valid length?"}
    
    LengthCheck -->|"No"| ReturnEarly
    LengthCheck -->|"Yes"| FormatCheck{"Valid format?"}
    
    FormatCheck -->|"No"| ReturnEarly
    FormatCheck -->|"Yes"| ProcessInput["Process Input"]
    
    subgraph "GUID Validation Example"
        GUIDEntry["GetExpandedGuid(guid)"]
        GUIDEntry --> GUIDLen{"guid.Length == 32?"}
        GUIDLen -->|"No"| GUIDFail["return null"]
        GUIDLen -->|"Yes"| GUIDProcess["Transform GUID"]
    end
    
    style ReturnEarly fill:#ffe1e1
    style GUIDFail fill:#ffe1e1
Loading

Diagram: Input Validation Flow with Early Returns

Validation checklist:

  • Check for null or empty inputs
  • Validate string lengths and formats
  • Validate GUID formats before transformation
  • Check array bounds before access
  • Validate registry key paths

Privilege Handling

The system assumes administrator privileges are available but implements graceful degradation:

// Pattern: Attempt operation with appropriate access rights
int samDesired = KEY_ALL_ACCESS; // Requires admin
if (RegOpenKeyEx(hive, subKey, 0, samDesired, out IntPtr hKey) != 0)
{
    // Fall back to read-only if write fails
    samDesired = KEY_READ;
    RegOpenKeyEx(hive, subKey, 0, samDesired, out hKey);
}

Registry access rights used:

  • KEY_READ = 0x20019 - Read-only access
  • KEY_WRITE = 0x20006 - Write access
  • KEY_ALL_ACCESS = 0xF003F - Full access (requires admin)

Safe String Operations

Use framework methods for safe string manipulation:

Operation Safe Method Purpose
Process name extraction Path.GetFileNameWithoutExtension() Handles invalid path chars
Substring operations Bounds checking before substring Prevent IndexOutOfRangeException
Case-insensitive compare StringComparison.OrdinalIgnoreCase Culture-independent comparison
Path combination Path.Combine() Handles separators correctly

Sources:


Testing and Debugging Patterns

Defensive Programming

The codebase uses defensive programming techniques to prevent errors:

// Return early on invalid inputs
if (string.IsNullOrEmpty(keyPath))
    return false;

// Use null-conditional operators
process?.Kill();

// Implement bounds checking
if (index < 0 || index >= array.Length)
    return null;

Logging Integration Points

Helper methods are designed to return success/failure indicators that enable comprehensive logging by the PowerShell layer:

graph LR
    subgraph "Logging Architecture"
        Helper["Helper Method"] --> Return["Returns bool/null"]
        Return --> PS["PowerShell Layer"]
        PS --> Check{"Success?"}
        Check -->|"true"| LogSuccess["Write-Verbose: Success"]
        Check -->|"false"| LogError["Write-Warning: Failed"]
        
        Helper --> State["Exposes State"]
        State --> Pending["PendingDeletes property"]
        State --> Lists["Collection properties"]
        
        PS --> Query["Query state after operation"]
        Query --> Report["Generate detailed report"]
    end
    
    style Helper fill:#fff4e1
    style LogError fill:#ffe1e1
Loading

Diagram: Logging Integration Points

Logging pattern:

  1. Helper methods return bool for success/failure
  2. Helper classes expose collections for post-operation querying
  3. PowerShell layer logs detailed operation results
  4. Error details available via return values and state properties

Testability Design

The architecture enables unit testing through:

  • Single Responsibility Principle - Each helper class has one focus
  • Dependency Injection - Orchestrator composes helpers
  • Stateless utility methods - GuidHelper methods are pure functions
  • Mockable dependencies - Interfaces can be added for testing

Sources:


Documentation Standards

XML Documentation Comments

All public classes and methods require XML documentation:

/// <summary>
/// Deletes a registry key and all its subkeys.
/// </summary>
/// <param name="keyPath">Full path to the registry key (e.g., "HKLM\Software\Microsoft\Office")</param>
/// <returns>True if the key was successfully deleted or doesn't exist; false on error</returns>
public bool DeleteKey(string keyPath)
{
    // Implementation
}

Required XML tags:

  • <summary> - Brief description of the class/method
  • <param> - Description of each parameter
  • <returns> - Description of return value
  • <remarks> - Additional usage notes (optional)
  • <example> - Usage examples for complex operations (optional)

Code Comments

Inline comments should be used sparingly, only for:

  • Complex algorithms - Explain non-obvious logic
  • Win32 API usage - Document parameter meanings and error codes
  • Business logic - Explain Office-specific requirements
  • Known limitations - Document edge cases or assumptions

Good comment:

// WOW64 redirection: On 64-bit systems, Office can install in either
// the native 64-bit view or the 32-bit redirected view. We must check both.
if (_is64Bit)
{
    // Check native view with KEY_WOW64_64KEY flag
    // Check WOW64 view with KEY_WOW64_32KEY flag
}

Poor comment (self-documenting code is better):

// Increment counter
counter++; // DON'T DO THIS

Constant Documentation

Magic numbers and constants must be explained:

// Office product ID length is always 4 characters (e.g., "O365", "2019")
public const int PROD_LEN = 4;

// Registry key for pending file renames on reboot
private const string PENDING_FILE_RENAME_KEY = 
    @"SYSTEM\CurrentControlSet\Control\Session Manager";

Documentation for:

  • Purpose and source of magic numbers
  • Significance of registry paths
  • Known GUID patterns and their meanings
  • Win32 API constants and their values

Sources:


PowerShell Coding Standards

Function Structure

PowerShell functions follow this template:

function Verb-Noun {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$RequiredParameter,
        
        [Parameter()]
        [int]$OptionalParameter = 0,
        
        [switch]$SwitchParameter
    )
    
    begin {
        # Initialization
        Write-Verbose "Starting Verb-Noun"
    }
    
    process {
        # Main logic
        try {
            # Operation
        }
        catch {
            Write-Error "Error: $_"
        }
    }
    
    end {
        # Cleanup
        Write-Verbose "Completed Verb-Noun"
    }
}

Comment-Based Help

All exported functions require comment-based help:

<#
.SYNOPSIS
    Brief description of the function
    
.DESCRIPTION
    Detailed description of what the function does
    
.PARAMETER ParameterName
    Description of what the parameter does
    
.EXAMPLE
    Verb-Noun -ParameterName "Value"
    
    Description of what this example does
    
.NOTES
    Additional information (author, version, etc.)
#>

Approved Verbs

Use only approved PowerShell verbs (check with Get-Verb):

Category Common Verbs Usage
Common Get, Set, New, Remove Basic CRUD operations
Data Import, Export, Convert Data transformation
Diagnostic Test, Trace, Measure Testing and diagnostics
Lifecycle Start, Stop, Restart Process/service control

Sources:


PSScriptAnalyzer Configuration

The project uses PSScriptAnalyzer for code quality enforcement. Configuration is defined in PSScriptAnalyzerSettings.psd1.

Current Configuration

@{
    'ExcludeRules' = @('*')  # All rules excluded by default
    'Rules'        = @{
        'PSAvoidUsingCmdletAliases' = @{
            'allowlist' = @('set', 'get', 'nv', 'Add-Type')
        }
    }
}

Note: The current configuration excludes most rules. Contributors should follow best practices even if not enforced:

  • Avoid using cmdlet aliases (except those allowlisted)
  • Use proper parameter validation
  • Include error handling
  • Follow naming conventions
  • Add comment-based help

Running PSScriptAnalyzer

# Install PSScriptAnalyzer
Install-Module -Name PSScriptAnalyzer -Scope CurrentUser

# Analyze module
Invoke-ScriptAnalyzer -Path . -Recurse -Settings .\PSScriptAnalyzerSettings.psd1

# Analyze specific file
Invoke-ScriptAnalyzer -Path .\OfficeScrubC2R.psm1

Sources:


Version Control and Commit Standards

Commit Message Format

Follow this structure for commit messages:

Add feature to detect Office 2021

- Implement detection for Office 2021 C2R
- Add registry key checks for Office 2021
- Update tests to cover Office 2021

Fixes #123

Format:

  • First line - Short summary (50 characters max)
  • Blank line
  • Body - Detailed explanation (wrap at 72 characters)
  • Blank line
  • Footer - Reference issues with Fixes #123 or Closes #456

Semantic Versioning

The project follows Semantic Versioning 2.0.0:

graph LR
    Version["Version: MAJOR.MINOR.PATCH"]
    
    Version --> Major["MAJOR<br/>Breaking changes<br/>Non-backward compatible"]
    Version --> Minor["MINOR<br/>New features<br/>Backward compatible"]
    Version --> Patch["PATCH<br/>Bug fixes<br/>Backward compatible"]
    
    subgraph "Example Version History"
        V2190["2.19.0<br/>Initial release"]
        V2191["2.19.1<br/>Bug fixes"]
        V2192["2.19.2<br/>More fixes"]
        V220["2.20.0<br/>New features"]
        V300["3.0.0<br/>Breaking changes"]
        
        V2190 --> V2191
        V2191 --> V2192
        V2192 --> V220
        V220 --> V300
    end
    
    style Major fill:#ffe1e1
    style Minor fill:#fff4e1
    style Patch fill:#e1ffe1
Loading

Diagram: Semantic Versioning Strategy

Version update guidelines:

Change Type Example Version Update
Breaking change Remove public function 2.19.0 → 3.0.0
New feature Add new helper class 2.19.0 → 2.20.0
Bug fix Fix registry deletion 2.19.0 → 2.19.1
Documentation Update wiki No version change

Sources:


Common Patterns Reference

Complete Helper Class Template

#region MyHelper
public class MyHelper
{
    // Private fields with underscore prefix
    private readonly SomeDependency _dependency;
    private readonly HashSet<string> _cache;
    private bool _is64Bit;
    
    // Constructor with dependency injection
    public MyHelper(SomeDependency dependency)
    {
        _dependency = dependency;
        _cache = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
        _is64Bit = Environment.Is64BitOperatingSystem;
    }
    
    // Public operation method with validation and error handling
    public bool PerformOperation(string input)
    {
        // Input validation
        if (string.IsNullOrEmpty(input))
            return false;
        
        // Check cache to avoid redundant operations
        if (_cache.Contains(input))
            return true;
        
        // Try-catch with silent failure pattern
        try
        {
            // Perform operation
            DoWork(input);
            
            // Update cache
            _cache.Add(input);
            
            return true;
        }
        catch
        {
            return false;
        }
    }
    
    // Private helper methods
    private void DoWork(string input)
    {
        // Implementation
    }
    
    // Public property to expose state
    public IReadOnlyCollection<string> ProcessedItems => _cache;
}
#endregion

Registry Operation Pattern with WOW64

public bool DeleteKey(string keyPath)
{
    if (string.IsNullOrEmpty(keyPath))
        return false;
    
    bool success = false;
    
    // Attempt deletion in native view
    success |= DeleteKeyInternal(keyPath, isWow64: false);
    
    // On 64-bit systems, also delete in WOW64 view
    if (_is64Bit)
    {
        string wow64Key = GetWow64Key(keyPath);
        success |= DeleteKeyInternal(wow64Key, isWow64: true);
    }
    
    return success;
}

private bool DeleteKeyInternal(string keyPath, bool isWow64)
{
    int samDesired = isWow64 ? KEY_WOW64_32KEY : KEY_WOW64_64KEY;
    
    try
    {
        // P/Invoke call with appropriate flags
        IntPtr hKey = GetHiveKey(keyPath);
        string subKey = GetSubKeyPath(keyPath);
        
        int result = RegDeleteKeyEx(hKey, subKey, samDesired, 0);
        return result == 0;
    }
    catch
    {
        return false;
    }
}

Sources:


This development guidelines document provides the foundation for maintaining code quality and consistency across the OfficeScrubC2R project. Contributors should refer to these patterns and standards when adding new features or modifying existing code. For specific implementation details, refer to the source files cited throughout this document.

Clone this wiki locally