Public/Get-DllGuidTable.ps1
|
function Get-DllGuidTable { <# .SYNOPSIS Flat GUID/CLSID/IID table extracted from a DLL's embedded TypeLib. .DESCRIPTION Reads the TYPELIB resource of a Windows PE binary (DLL/OCX) via oleaut32!LoadTypeLibEx (REGKIND_NONE - no registration) and emits one PSCustomObject per TypeLib entry with four fields: Type, Name, Guid, RegKey. Suitable for Format-Table or quick eyeballing of every CoClass / interface / dispinterface / enum / record / union / alias / module the binary declares. RegKey is the registry path under which that GUID is registered, or empty when the entry is not registered (or its kind is not normally registered, e.g. enum/record/union/alias/module). CoClasses are looked up under HKCR\CLSID, interfaces and dispinterfaces under HKCR\Interface; both HKLM and HKCU plus 32-bit (Wow6432Node) views are searched. Lookups are read-only - no LoadLibrary, no regsvr32. .PARAMETER Path One or more paths to PE files. Accepts pipeline input (e.g. from Get-ChildItem) and the FullName / PSPath / FilePath property aliases. .PARAMETER Kind Optional filter. One or more of: coclass, interface, dispatch, enum, record, union, alias, module. When omitted, every TypeLib entry is emitted. .PARAMETER RegKey Default Format-Table view shows the columns Type, Name, Guid. With -RegKey it shows Type, Name, RegKey instead - useful to avoid line wrapping in narrow consoles since the RegKey path already contains the GUID. The underlying objects always expose all four properties regardless of this switch, so ConvertTo-Json / Where-Object / etc. keep seeing the full record. Mutually exclusive with -Both. .PARAMETER Both Show both Guid and RegKey columns at once (Type, Name, Guid, RegKey). The combined width is around 130 characters and will wrap on the typical 120-char console. See the wrap-avoidance EXAMPLE below. Mutually exclusive with -RegKey. .EXAMPLE Get-DllGuidTable -Path C:\App\Administrador.dll | Format-Table .EXAMPLE Get-DllGuidTable .\foo.dll -Kind coclass .EXAMPLE Get-ChildItem C:\Legacy -Include *.dll,*.ocx -Recurse | Get-DllGuidTable | Sort-Object Type, Name .EXAMPLE # Show both Guid and RegKey columns without wrapping on a 120-char # console. Format-Table -Wrap would split each row across multiple # visual lines (ugly); piping through Out-String -Width N produces # a single rendered string per row, so each entry stays on one line # even if it exceeds the console width: Get-DllGuidTable .\foo.dll -Both | Format-Table -AutoSize | Out-String -Width 250 | Write-Host # Alternative: enlarge the console buffer once per session and use # Format-Table normally afterwards: $bs = $host.UI.RawUI.BufferSize $bs.Width = 250 $host.UI.RawUI.BufferSize = $bs Get-DllGuidTable .\foo.dll -Both | Format-Table -AutoSize .NOTES PowerShell 5.1+ on Windows. Files without a TypeLib resource emit no output (use -Verbose to see them being skipped). #> [CmdletBinding(DefaultParameterSetName = 'Guid')] param( [Parameter(Mandatory, Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName)] [Alias('FullName', 'PSPath', 'FilePath')] [string[]]$Path, [ValidateSet('coclass','interface','dispatch','enum','record','union','alias','module')] [string[]]$Kind, [Parameter(ParameterSetName = 'RegKey')] [switch]$RegKey, [Parameter(ParameterSetName = 'Both')] [switch]$Both ) begin { # Pre-build the PSStandardMembers used for default Format-Table # display. Output objects always carry all four fields (so JSON / # Where-Object see everything); the parameter set only controls # which columns are shown by default to avoid wrapping. $defaultCols = switch ($PSCmdlet.ParameterSetName) { 'Both' { 'Type','Name','Guid','RegKey' } 'RegKey' { 'Type','Name','RegKey' } default { 'Type','Name','Guid' } } $displaySet = New-Object System.Management.Automation.PSPropertySet( 'DefaultDisplayPropertySet', [string[]]$defaultCols) $stdMembers = [System.Management.Automation.PSMemberInfo[]]@($displaySet) } process { foreach ($p in $Path) { try { $resolved = (Resolve-Path -LiteralPath $p -ErrorAction Stop).ProviderPath } catch { Write-Error "Failed to resolve '$p': $($_.Exception.Message)" continue } $tlib = Get-TypeLibInfoSafe -FilePath $resolved if (-not $tlib) { Write-Verbose "No TypeLib in $resolved (file missing, not a PE, or no TYPELIB resource) - skipped." continue } if (-not $tlib.TypeInfos -or $tlib.TypeInfos.Count -eq 0) { Write-Verbose "TypeLib in $resolved is empty - skipped." continue } $libName = $tlib.Name foreach ($ti in $tlib.TypeInfos) { $type = ($ti.Kind -replace '^TKIND_', '').ToLowerInvariant() if ($Kind -and ($Kind -notcontains $type)) { continue } if ([string]::IsNullOrEmpty($libName)) { $qname = $ti.Name } else { $qname = "$libName.$($ti.Name)" } $guidStr = $ti.Guid.ToString().ToUpperInvariant() $regKeyVal = Resolve-RegistryKeyForGuid -Guid $guidStr -Kind $type $obj = [pscustomobject]@{ Type = $type Name = $qname Guid = $guidStr RegKey = $regKeyVal } $obj | Add-Member -MemberType MemberSet -Name PSStandardMembers ` -Value $stdMembers -Force $obj } } } } |