Skip to content

GUID Transformation System

Calvindd2f edited this page Jan 14, 2026 · 1 revision

GUID Transformation System

Relevant source files

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

Overview

The GUID Transformation System provides bidirectional conversion between the three GUID encoding formats used by Windows Installer metadata. This system is critical for identifying and cleaning Office-related registry entries during the Click-to-Run uninstallation process.

For information about the broader Windows Installer cleanup operations that consume these transformations, see WindowsInstallerHelper.

Sources: OfficeScrubC2R-Native.cs:169-329


GUID Format Specifications

Windows Installer stores product, component, and upgrade code GUIDs in three distinct formats depending on registry location and data type.

Format Length Example Usage Location
Expanded 38 chars {90140000-0011-0000-0000-0000000FF1CE} Standard GUID representation
Compressed 32 chars 00004109110000000000000F1CE00000 Registry key names (Products, UpgradeCodes, Components)
Encoded 20 chars QD3H=4cB#zG1aD6;R4 Multi-string registry values (PublishedComponents)

Compressed Format Byte-Order Transformation

The compressed format applies byte-pair reversals to specific GUID segments:

flowchart LR
    subgraph Expanded["Expanded Format"]
        E1["Segment 1<br/>8 chars"]
        E2["Segment 2<br/>4 chars"]
        E3["Segment 3<br/>4 chars"]
        E4["Segment 4<br/>4 chars"]
        E5["Segment 5<br/>12 chars"]
    end
    
    subgraph Compressed["Compressed Format"]
        C1["Reversed<br/>8 chars"]
        C2["Reversed<br/>4 chars"]
        C3["Reversed<br/>4 chars"]
        C4["Pair-swapped<br/>4 chars"]
        C5["Pair-swapped<br/>12 chars"]
    end
    
    E1 -->|"Reverse entire segment"| C1
    E2 -->|"Reverse entire segment"| C2
    E3 -->|"Reverse entire segment"| C3
    E4 -->|"Swap each pair"| C4
    E5 -->|"Swap each pair"| C5
Loading

Sources: OfficeScrubC2R-Native.cs:173-214, OfficeScrubC2R-Native.cs:216-253


GuidHelper Class Architecture

The GuidHelper static class in the OfficeScrubNative namespace provides three transformation methods and one helper method.

classDiagram
    class GuidHelper {
        <<static>>
        +GetExpandedGuid(compressedGuid: string) string
        +GetCompressedGuid(expandedGuid: string) string
        +GetDecodedGuid(encodedGuid: string, out decodedGuid: string) bool
        -Reverse(s: string) string
    }
    
    class WindowsInstallerHelper {
        -_regHelper: RegistryHelper
        +CleanupUpgradeCodes(shouldDelete: Func)
        +CleanupProducts(shouldDelete: Func)
        +CleanupComponents(shouldDelete: Func)
        +CleanupPublishedComponents(shouldDelete: Func)
    }
    
    WindowsInstallerHelper ..> GuidHelper : uses
Loading

Sources: OfficeScrubC2R-Native.cs:169-329, OfficeScrubC2R-Native.cs:978-1128


Transformation Methods

GetExpandedGuid

Converts a 32-character compressed GUID to the standard 38-character expanded format with braces and dashes.

Algorithm:

  1. Validate input length is exactly 32 characters
  2. Allocate StringBuilder with capacity 38
  3. Reverse bytes in segments 1, 2, and 3 (positions 0-8, 8-12, 12-16)
  4. Swap byte pairs in segments 4 and 5 (positions 16-20, 20-32)
  5. Insert braces and dashes at positions: {[0]-[1]-[2]-[3]-[4]}
  6. Return uppercase result
flowchart TD
    Start["GetExpandedGuid(compressedGuid)"] --> Validate{"Length == 32?"}
    Validate -->|No| ReturnNull["return null"]
    Validate -->|Yes| AllocSB["StringBuilder sb = new(38)"]
    
    AllocSB --> AppendBrace["sb.Append('{')"]
    AppendBrace --> Rev1["Reverse(substring 0-8)"]
    Rev1 --> Dash1["sb.Append('-')"]
    Dash1 --> Rev2["Reverse(substring 8-12)"]
    Rev2 --> Dash2["sb.Append('-')"]
    Dash2 --> Rev3["Reverse(substring 12-16)"]
    Rev3 --> Dash3["sb.Append('-')"]
    
    Dash3 --> Swap1["Swap pairs 16-20"]
    Swap1 --> Dash4["sb.Append('-')"]
    Dash4 --> Swap2["Swap pairs 20-32"]
    Swap2 --> CloseBrace["sb.Append('}')"]
    
    CloseBrace --> Upper["ToUpperInvariant()"]
    Upper --> Return["return result"]
    
    ReturnNull --> End["End"]
    Return --> End
Loading

Sources: OfficeScrubC2R-Native.cs:173-214


GetCompressedGuid

Converts a 38-character expanded GUID to the 32-character compressed format used in registry key names.

Algorithm:

  1. Validate input length is exactly 38 characters
  2. Trim braces and split on dash delimiter (5 segments)
  3. Reverse segments 1, 2, and 3
  4. Swap byte pairs in segments 4 and 5
  5. Concatenate all segments without delimiters
  6. Return uppercase result

Sources: OfficeScrubC2R-Native.cs:216-253


GetDecodedGuid

Decodes a 20-character Base85-encoded GUID to expanded format. This encoding is used exclusively in the PublishedComponents registry location.

Algorithm:

  1. Validate input length is exactly 20 characters
  2. Process in groups of 5 characters (4 groups total)
  3. For each group:
    • Look up each character in the Base85 decode table
    • Accumulate as total += table[char] * (85^position)
    • After 5 characters, append total.ToString("X8") to result
  4. Rearrange decoded hex segments to standard GUID format
  5. Return formatted result with braces and dashes

Base85 Decode Table:

The decode table maps ASCII characters to 5-bit values (0-84). Invalid characters are marked with 0xff.

flowchart TD
    Start["GetDecodedGuid(encodedGuid, out decodedGuid)"] --> Validate{"Length == 20?"}
    Validate -->|No| SetNull["decodedGuid = null<br/>return false"]
    Validate -->|Yes| InitSB["StringBuilder sb = new()"]
    
    InitSB --> LoopStart["For i = 0 to 19"]
    LoopStart --> CheckGroup{"i % 5 == 0?"}
    CheckGroup -->|Yes| ResetAccum["total = 0<br/>pow85 = 1"]
    CheckGroup -->|No| GetChar["Get char at i"]
    ResetAccum --> GetChar
    
    GetChar --> Lookup["chr = table[ascii]"]
    Lookup --> ValidChar{"chr != 0xff?"}
    ValidChar -->|No| SetNull
    ValidChar -->|Yes| Accumulate["total += chr * pow85"]
    
    Accumulate --> CheckEnd{"i % 5 == 4?"}
    CheckEnd -->|Yes| AppendHex["sb.Append(total.ToString('X8'))"]
    CheckEnd -->|No| NextPow["pow85 *= 85"]
    AppendHex --> NextIter
    NextPow --> NextIter["i++"]
    
    NextIter --> LoopEnd{"i < 20?"}
    LoopEnd -->|Yes| LoopStart
    LoopEnd -->|No| Rearrange["Rearrange segments<br/>to GUID format"]
    
    Rearrange --> FormatGuid["Format as<br/>{seg0-seg1-seg2-seg3-seg4}"]
    FormatGuid --> Success["return true"]
    
    SetNull --> End["End"]
    Success --> End
Loading

Sources: OfficeScrubC2R-Native.cs:255-320


Integration with Windows Installer Cleanup

The WindowsInstallerHelper class consumes GUID transformations to identify Office-related metadata across four registry locations.

flowchart TD
    subgraph Registry["Windows Registry Locations"]
        UC["UpgradeCodes<br/>HKLM\SOFTWARE\...\UpgradeCodes<br/>32-char compressed keys"]
        PR["Products<br/>HKLM\...\Products<br/>HKCR\Installer\Products<br/>32-char compressed keys"]
        CM["Components<br/>HKLM\...\Components<br/>32-char keys + 32-char values"]
        PC["PublishedComponents<br/>HKCR\Installer\Components<br/>32-char keys + 20-char encoded in MultiString"]
    end
    
    subgraph GuidHelper["GuidHelper Methods"]
        GE["GetExpandedGuid()"]
        GD["GetDecodedGuid()"]
    end
    
    subgraph WindowsInstallerHelper["WindowsInstallerHelper Methods"]
        CUC["CleanupUpgradeCodes()"]
        CP["CleanupProducts()"]
        CC["CleanupComponents()"]
        CPC["CleanupPublishedComponents()"]
    end
    
    UC --> CUC
    PR --> CP
    CM --> CC
    PC --> CPC
    
    CUC --> GE
    CP --> GE
    CC --> GE
    CPC --> GD
    
    GE --> IsInScope{"OfficeScrubOrchestrator<br/>IsInScope()"}
    GD --> IsInScope
    
    IsInScope -->|"true"| Delete["Delete registry entry"]
    IsInScope -->|"false"| Keep["Preserve entry"]
Loading

Sources: OfficeScrubC2R-Native.cs:978-1128


Cleanup Method GUID Flow

Each cleanup method follows a similar pattern: enumerate keys, transform GUIDs, validate scope, and delete if matched.

CleanupUpgradeCodes Flow

sequenceDiagram
    participant WIH as WindowsInstallerHelper
    participant RH as RegistryHelper
    participant GH as GuidHelper
    participant Orch as OfficeScrubOrchestrator
    
    WIH->>RH: EnumerateKeys(HKLM, "...\UpgradeCodes")
    RH-->>WIH: string[] keys (32-char compressed)
    
    loop For each key
        WIH->>WIH: Check key.Length == 32
        WIH->>GH: GetExpandedGuid(key)
        GH-->>WIH: expandedGuid (38-char)
        
        alt expandedGuid != null
            WIH->>Orch: shouldDelete(expandedGuid)
            Orch-->>WIH: bool result
            
            alt result == true
                WIH->>RH: DeleteKey(HKLM, "...\UpgradeCodes\{key}")
            end
        end
    end
Loading

Sources: OfficeScrubC2R-Native.cs:989-1011


CleanupPublishedComponents Flow

This method handles the most complex encoding: Base85-encoded GUIDs embedded in the first 20 characters of multi-string registry values.

sequenceDiagram
    participant WIH as WindowsInstallerHelper
    participant RH as RegistryHelper
    participant GH as GuidHelper
    participant Orch as OfficeScrubOrchestrator
    
    WIH->>RH: EnumerateKeys(HKCR, "Installer\Components")
    RH-->>WIH: string[] componentKeys
    
    loop For each componentKey
        WIH->>RH: EnumerateValues(HKCR, "Installer\Components\{key}")
        RH-->>WIH: string[] valueNames
        
        loop For each valueName
            WIH->>RH: GetValue(HKCR, path, valueName)
            RH-->>WIH: string[] data (MultiString)
            
            loop For each item in data
                alt item.Length > 20
                    WIH->>WIH: encoded = item.Substring(0, 20)
                    WIH->>GH: GetDecodedGuid(encoded, out guid)
                    GH-->>WIH: bool success + decodedGuid
                    
                    alt success == true
                        WIH->>Orch: shouldDelete(decodedGuid)
                        Orch-->>WIH: bool result
                        
                        alt result == true
                            WIH->>WIH: Mark item for removal
                        else result == false
                            WIH->>WIH: Keep item in newData
                        end
                    end
                end
            end
            
            alt modified == true
                alt newData.Count == 0
                    WIH->>RH: DeleteValue(HKCR, path, valueName)
                else
                    WIH->>RH: SetValue(HKCR, path, valueName, newData)
                end
            end
        end
    end
Loading

Sources: OfficeScrubC2R-Native.cs:1069-1127


Performance Optimizations

The optimized implementation in the reference file demonstrates several performance enhancements for high-volume GUID transformations.

Optimizations Applied

Optimization Original Approach Optimized Approach Benefit
Decode Table Allocates array per call Static readonly byte[128] Eliminates 255+ allocations per call
String Building StringBuilder heap allocation Span<char> stack allocation Zero heap allocations
Character Reversal Array.Reverse() + new string() ReverseToSpan() in-place Reduces allocations by 50%
Format Conversion StringBuilder.Append() TryFormat() to Span<char> Reduces string concatenations

Optimized GetExpandedGuid

flowchart LR
    subgraph Original["Original Implementation"]
        O1["StringBuilder sb = new(38)"]
        O2["Multiple Append() calls"]
        O3["String concatenation"]
        O4["Array.Reverse()"]
    end
    
    subgraph Optimized["Optimized Implementation"]
        P1["Span<char> result = stackalloc char[38]"]
        P2["Direct index assignment"]
        P3["ReverseToSpan()"]
        P4["new string(result)"]
    end
    
    Original -->|"Heap allocations<br/>StringBuilder overhead"| Perf1["~150ns per call"]
    Optimized -->|"Stack only<br/>Zero allocations"| Perf2["~80ns per call"]
Loading

Sources: docs/OfficeScrubC2R-Native-Optimized-Real.cs:174-225, docs/OfficeScrubC2R-Native-Optimized-Real.cs:263-318


Error Handling and Validation

All transformation methods implement defensive validation with graceful failure modes.

Validation Rules

Method Input Validation Error Behavior
GetExpandedGuid length == 32 Returns null
GetCompressedGuid length == 38 Returns null
GetDecodedGuid length == 20 + valid ASCII Sets out param to null, returns false

Exception Handling Pattern

All methods wrap transformation logic in try-catch blocks to handle unexpected input data without crashing the cleanup process.

flowchart TD
    Input["Receive GUID string"] --> TryBlock["try {"]
    TryBlock --> Validate["Validate length"]
    Validate --> Transform["Apply transformation"]
    Transform --> Return["Return result"]
    
    Return --> End["End"]
    
    TryBlock -.->|"Any exception"| CatchBlock["catch { }"]
    CatchBlock --> ReturnNull["Return null/false"]
    ReturnNull --> End
Loading

Sources: OfficeScrubC2R-Native.cs:178-213, OfficeScrubC2R-Native.cs:221-252, OfficeScrubC2R-Native.cs:262-319


Registry Location Reference

The following table maps GUID formats to their storage locations in the Windows Registry.

Registry Path Hive Key Format Value Format Used By
SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UpgradeCodes HKLM 32-char compressed - CleanupUpgradeCodes
SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products HKLM 32-char compressed - CleanupProducts
Installer\Products HKCR 32-char compressed - CleanupProducts
SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components HKLM 32-char compressed 32-char compressed CleanupComponents
Installer\Components HKCR 32-char compressed 20-char encoded (MultiString) CleanupPublishedComponents

Sources: OfficeScrubC2R-Native.cs:989-1127


Constants and Magic Values

The OfficeConstants class defines GUID length constants used throughout the transformation system.

classDiagram
    class OfficeConstants {
        <<static>>
        +OFFICE_ID: string = "{0000000FF1CE}"
        +PROD_LEN: int = 13
        +SQUISHED: int = 20
        +COMPRESSED: int = 32
    }
    
    class GuidHelper {
        <<static>>
    }
    
    GuidHelper ..> OfficeConstants : uses SQUISHED, COMPRESSED
Loading
Constant Value Purpose
SQUISHED 20 Length of Base85-encoded GUIDs
COMPRESSED 32 Length of compressed (no dashes) GUIDs
Standard Expanded 38 Length of formatted GUIDs with braces and dashes

Sources: OfficeScrubC2R-Native.cs:29-56

Clone this wiki locally