#Requires -RunAsAdministrator <# .SYNOPSIS Tests COM registration for the OpenDecal project. .DESCRIPTION Builds the Decal solution (optionally), registers 10 COM server DLLs using regasm /codebase, and verifies that every expected CLSID appears in the registry under HKCR:\CLSID\{GUID}\InprocServer32. .PARAMETER Build If specified, builds Decal.sln (Release) before registration. .PARAMETER Unregister If specified, unregisters all DLLs instead of registering them. .EXAMPLE .\Test-ComRegistration.ps1 -Build Builds the solution, registers all DLLs, and verifies CLSIDs. .EXAMPLE .\Test-ComRegistration.ps1 -Unregister Unregisters all DLLs. #> [CmdletBinding()] param( [switch]$Build, [switch]$Unregister ) Set-StrictMode -Version Latest $ErrorActionPreference = 'Continue' # ── Paths ──────────────────────────────────────────────────────────────────── $RegAsm = 'C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe' $SolutionRoot = Split-Path -Parent $PSScriptRoot # reconstructed\ $ManagedRoot = Join-Path $SolutionRoot 'Managed' $SolutionFile = Join-Path $ManagedRoot 'Decal.sln' # ── Verify regasm exists ───────────────────────────────────────────────────── if (-not (Test-Path $RegAsm)) { Write-Error "RegAsm.exe not found at $RegAsm" exit 1 } # ── DLL → CLSID map ───────────────────────────────────────────────────────── # Each key is the relative path from $ManagedRoot to the built DLL. # Each value is an ordered list of [CLSID, FriendlyName] pairs. $DllMap = [ordered]@{ 'Decal.Core\bin\Release\Decal.Core.dll' = @( ,@('CB8875CD-ABC2-42AD-8175-8908706EED37', 'ACHooks') ,@('4557D5A1-00DB-48F6-ACB3-4FEF30E2F358', 'DecalCore') ,@('6DE65A82-C451-46E6-A82D-92137BE851AD', 'DecalEnum') ,@('E2284FC7-17E5-4846-ADAB-07953273C5FB', 'PluginSite2') ) 'Decal.DecalControls\bin\Release\Decal.DecalControls.dll' = @( ,@('5AE37451-F79C-478A-834E-EDCF95F01B0E', 'Checkbox') ,@('E099DC60-0F19-4690-AB7C-8B5834DA286E', 'Choice') ,@('3035299A-C5FB-4CC7-A63C-66400B80DCA4', 'DerethMap') ,@('8E8F88D2-AA47-474E-9DB7-912D49C8BE9D', 'Edit') ,@('CD6556CD-F8D9-4CB0-AB7C-7C33058294E4', 'FixedLayout') ,@('CA121762-31BB-4073-8597-33BAB4BDCAA3', 'BorderLayout') ,@('5AD04D45-D4BF-4729-8A2E-5D37CF726CAA', 'PageLayout') ,@('3839008F-AF5B-43FC-9F6A-3376B99E3DAF', 'List') ,@('864DEABF-D079-4B61-A8CF-081418179239', 'TextColumn') ,@('F12A2C4C-3B78-46EB-8722-68B27A75AE55', 'IconColumn') ,@('48E444F1-8E30-4E4C-B203-4C87FC901586', 'CheckColumn') ,@('ED14E7C5-11BE-4DFD-9829-873AB6BE3CFC', 'Notebook') ,@('FE361225-6BB7-4AE0-A10C-8A6420621680', 'Progress') ,@('AE4525BE-81D1-40FB-9170-77172077EB49', 'PushButton') ,@('8FC80D21-1731-4816-9AD3-B0364D5F2C27', 'Scroller') ,@('5D14557A-1268-43C6-A283-3B5B271359AA', 'Slider') ,@('4887101C-A9F9-495B-921B-EF22822CFB97', 'StaticText') ) 'Decal.DecalDat\bin\Release\Decal.DecalDat.dll' = @( ,@('37B083F0-276E-43AD-8D26-3F7449B519DC', 'DatService') ,@('9F7F6CD9-D164-418D-8CB5-3B9ACD70BEAF', 'DatStream') ,@('6FA05FDA-B4B5-4386-AB45-92D7E6A5D698', 'DatLibrary') ) 'Decal.DecalFilters\bin\Release\Decal.DecalFilters.dll' = @( ,@('50A7E9EC-AB12-4484-9C28-C2A39274A636', 'WorldObject') ,@('2681B113-294E-4ABF-B543-624194846BE1', 'WorldIterator') ,@('443D4A68-5422-4E0C-9460-973F8FBDB190', 'Prefilter') ,@('53092D1B-F0B0-46FF-BF11-8F031EC9B137', 'World') ,@('8C2FA400-315D-41DE-B063-D6EF04F12E1F', 'EchoFilter') ,@('34239EAD-6317-4C40-A405-193BA5232DD8', 'EchoFilter2') ,@('4540C969-08D1-46BF-97AD-6B19D3C10BEE', 'CharacterStats') ) 'Decal.DecalInput\bin\Release\Decal.DecalInput.dll' = @( ,@('F183506A-3664-49D6-8CA4-CFD295F2811D', 'Hotkey') ,@('85AB0296-124E-4E68-A6A8-FCF5721AC09B', 'WndMsg') ,@('B33307BA-706D-474A-80B9-70BB8D13EF3E', 'InputService') ,@('F3170E85-517E-43A4-B7B4-6F006A7B1B85', 'WinMsgHook') ,@('79497C87-92E1-416B-AE5C-9D6C4C59133C', 'Timer') ,@('F0A17A04-7F8F-4A17-A41D-8C297A1E929B', 'InputBuffer') ) 'Decal.DecalNet\bin\Release\Decal.DecalNet.dll' = @( ,@('15631E36-55CB-4D16-ADE7-74D9F8A5F4B6', 'WebRequest') ,@('C8C406F8-BA2E-4964-8B04-FF38394A8E0E', 'NetService') ) 'Decal.DecalRender\bin\Release\Decal.DecalRender.dll' = @( ,@('FB3C8286-88ED-4B4D-B413-94B40F346239', 'RenderService') ,@('218432A3-8960-4D91-BBC6-8CDC105C191E', 'HUDBackground') ,@('EF810EF1-C843-4563-A3DE-19CE8BEEEEC0', 'HUDView') ,@('175D6713-E942-41C0-8BC9-90A565024F76', 'RenderTargetWrapper') ) 'Decal.D3DService\bin\Release\Decal.D3DService.dll' = @( ,@('F0CC07A0-2C89-4FA4-9356-714665BC2F8B', 'D3DService') ,@('81E79859-2783-4B9A-ADC4-308073F5BB3F', 'D3DObj') ) 'Decal.DHS\bin\Release\Decal.DHS.dll' = @( ,@('6B6B9FA8-37DE-4FA3-8C60-52BD6A2F9855', 'HotkeySystem') ,@('F5AA853D-CCF3-4562-A654-3A68AB583BCC', 'Hotkey') ) 'Decal.SpellFilter\bin\Release\Decal.SpellFilter.dll' = @( ,@('C2D43735-BE7E-4829-AF73-F2E7E820EB16', 'Spells') ,@('3040368A-B77A-4FE1-B8CC-D77FA3F36A6D', 'Spell') ,@('20425F2F-54FF-4EAE-9538-36699E3DFB56', 'Component') ) } # ── Helper: coloured write ─────────────────────────────────────────────────── function Write-Status { param( [string]$Label, [string]$Message, [ConsoleColor]$Color = 'White' ) Write-Host " [$Label] " -ForegroundColor $Color -NoNewline Write-Host $Message } # ── Administrator check ────────────────────────────────────────────────────── $identity = [Security.Principal.WindowsIdentity]::GetCurrent() $principal = New-Object Security.Principal.WindowsPrincipal $identity if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { Write-Error 'This script must be run as Administrator (regasm requires elevated privileges).' exit 1 } # ── Optional build step ────────────────────────────────────────────────────── if ($Build) { Write-Host "`n=== Building Decal.sln (Release) ===" -ForegroundColor Cyan $buildArgs = @('build', $SolutionFile, '-c', 'Release') & dotnet @buildArgs if ($LASTEXITCODE -ne 0) { Write-Error "Build failed with exit code $LASTEXITCODE" exit 1 } Write-Host '' } # ── Counters ───────────────────────────────────────────────────────────────── $totalCLSIDs = 0 $passedCLSIDs = 0 $failedCLSIDs = 0 # ── Process each DLL ───────────────────────────────────────────────────────── foreach ($relPath in $DllMap.Keys) { $dllPath = Join-Path $ManagedRoot $relPath $dllName = Split-Path -Leaf $dllPath Write-Host "`n=== $dllName ===" -ForegroundColor Cyan # Check the DLL exists if (-not (Test-Path $dllPath)) { Write-Status 'SKIP' "$dllPath not found. Build the solution first (-Build)." Yellow $clsids = $DllMap[$relPath] $totalCLSIDs += $clsids.Count $failedCLSIDs += $clsids.Count continue } # ── Register or unregister ─────────────────────────────────────────── if ($Unregister) { Write-Host " Unregistering..." -ForegroundColor Yellow $output = & $RegAsm /unregister /nologo $dllPath 2>&1 $regExitCode = $LASTEXITCODE } else { Write-Host " Registering..." -ForegroundColor Yellow $output = & $RegAsm /codebase /nologo $dllPath 2>&1 $regExitCode = $LASTEXITCODE } # Show regasm output if ($output) { foreach ($line in $output) { Write-Host " $line" -ForegroundColor Gray } } if ($regExitCode -ne 0) { Write-Status 'WARN' "regasm exited with code $regExitCode" Yellow } # ── Verify CLSIDs ──────────────────────────────────────────────────── if (-not $Unregister) { $clsids = $DllMap[$relPath] foreach ($entry in $clsids) { $guid = $entry[0] $name = $entry[1] $totalCLSIDs++ # 32-bit regasm registers under WOW6432Node on 64-bit Windows $regKey64 = "Registry::HKEY_CLASSES_ROOT\CLSID\{$guid}\InprocServer32" $regKey32 = "Registry::HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{$guid}\InprocServer32" if ((Test-Path $regKey64) -or (Test-Path $regKey32)) { Write-Status 'PASS' "{$guid} $name" Green $passedCLSIDs++ } else { Write-Status 'FAIL' "{$guid} $name" Red $failedCLSIDs++ } } } } # ── Summary ────────────────────────────────────────────────────────────────── Write-Host "`n============================================" -ForegroundColor Cyan if ($Unregister) { Write-Host " Unregistration complete." -ForegroundColor Cyan } else { $summaryColor = if ($failedCLSIDs -eq 0) { 'Green' } else { 'Red' } Write-Host " Total CLSIDs checked : $totalCLSIDs" -ForegroundColor $summaryColor Write-Host " Passed : $passedCLSIDs" -ForegroundColor Green Write-Host " Failed : $failedCLSIDs" -ForegroundColor $(if ($failedCLSIDs -eq 0) { 'Green' } else { 'Red' }) } Write-Host "============================================`n" -ForegroundColor Cyan if ($failedCLSIDs -gt 0) { exit 1 } exit 0