Get-DbgObject.ps1
param( [Parameter(ValueFromPipelineByPropertyName = $true)] $Address, [Switch] $ExcludeFields, [String] $BreadCrumb ) begin { function unstrict { Set-StrictMode -Off; . $args[0] } Set-StrictMode -Version Latest } process { if(-not $BreadCrumb) { $BreadCrumb = $Address } ## Ensure the address is of the proper width. If not, it's probably ## a field value. if($Address.Length -ne 16) { return } ## Check if the address is 0, some variation of that, or negative. ## In this case, it's probably also a field value. $intAddress = $Address -as [Decimal] if(($intAddress -is [Decimal]) -and ((-not $intAddress) -or ($intAddress -le 0))) { return } ## Prepare the !DumpObject command $command = "!DumpObj" if($ExcludeFields) { $command += " -NoFields" } $command += " $Address" ## Invoke it, and start preparing the results $results = @(Invoke-DbgCommand $command) $returnObject = New-Object PSObject -Property @{ Type = "" Size = 0 Address = $Address Path = $BreadCrumb Fields = @{} } ## Check if there were any errors. if($results.Length -eq 1) { Write-Error ("Could not analyze object ($Address) : " + $results) return } ## Pick out the fields foreach($result in $results) { ## If it was a name:value pair, split it on that if($result -match ": ") { $fields = $result -split ': ' | Foreach-Object Trim } ## Otherwise, do general parsing on columns else { $fields = $result -split '\s+' | Foreach-Object Trim } ## Handle properties we want to remap if($fields[0] -eq "Name") { $type = $fields[1] $returnObject.Type = $type continue } if($fields[0] -eq "Size") { $size = $fields[1] $size = ($size -split '\(')[0] $returnObject.Size = $size continue } ## Handle other generic properties if($fields.Length -eq 2) { $field = ($fields[0] -split ': ')[0].Trim() if($field) { Add-Member -in $returnObject NoteProperty $field $fields[1].Trim() } continue } ## Get information about the fields if(-not $ExcludeFields) { ## This is a field if($fields.Length -eq 8) { $methodTable = $fields[0] $field = $fields[1] $offset = $fields[2] $type = $fields[3] $vt = $fields[4] $attr = $fields[5] $address = $fields[6] $name = $fields[7] ## Fix the name if needed / possible. The default view often ## truncates this. if($type -match "\.\.\.") { $mtResults = Invoke-DbgCommand "!dumpmt $methodTable" $mtName = $mtResults[2] $mtName = ($mtName -split ': ')[1] $type = $mtName } ## Now, if it's an object, follow the reference to see what it ## really is if($type -eq "System.Object") { $actualField = Get-DbgObject -Address $address -ExcludeFields $type = $actualField.Type } ## Start preparing the field to return $returnField = New-Object PSObject -Property @{ MethodTable = $methodTable; Field = $field; Offset = $offset; Type = $type; VirtualTable = $vt; Attribute = $attr; Address = $address; Name = $name } ## Store the rich data in the Fields property of ReturnObject $field = (New-Object PSObject $returnField | Select-Object Name,Address,Type,MethodTable,Field,Offset,VirtualTable,Attribute) $returnObject.Fields[$name] = $field ## Now, process the field to add user-friendly data $visualResult = "{0} {1} @ {2}" -f $field.Attribute,$field.Type,$field.Address ## Try to do some basic type conversions try { $realType = [Type] $type } catch { $realType = [System.Object] } ## If it was a string, follow the reference to get its value if($realType -eq [System.String]) { $actualField = Get-DbgObject -Address $address $visualResult = unstrict { $actualField.String } } ## If it's a value type, try to get its value as well elseif($realType.IsValueType) { ## The value for value types is often buried in the reference, under ## a "value" field. @TODO $result = Get-DbgObject $address if($result) { $valueFields = @($result.Fields.GetEnumerator() | ? { $_.Name -match "value" }) $value = $valueFields[0].Value.Address } ## Otherwise, the value is in the Address field else { $value = $address } ## Many things convert via Int, so try to convert the string to an int if($value -as [Int]) { $value = $value -as [Int] } ## Now try to convert it to the original type if($value -as $realType) { $value = $value -as $realType } ## And remember the visual result $visualResult = $value } ## Store the visual resule Add-Member -in $returnObject NoteProperty $name $visualResult ## Create a navigation breadcrumb so that we can follow references ## around. $newBreadCrumb = $breadCrumb + "." + $name Add-Member -in $returnObject ScriptMethod Get$name { Get-DbgObject -Address $address -BreadCrumb $newBreadCrumb }.GetNewClosure() } } } ## Output the final object $returnObject } |