Show-Object1.ps1
#[void][System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") #[void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") add-type -AssemblyName System.Windows.Forms -Verbose add-type -AssemblyName System.Drawing -verbose [void][System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") [void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") class Form3 : System.Windows.Forms.Form { # <summary> # Required designer variable. # </summary> [System.ComponentModel.IContainer]$components; #region Windows Form Designer generated code # <summary> # Required method for Designer support - do not modify # the contents of $this method with the code editor. # </summary> [void] InitializeComponent() { $this.ObjectPath = [System.Windows.Forms.TextBox]::new(); $this.menuStrip1 = [System.Windows.Forms.MenuStrip]::New(); $this.splitContainer1 = [System.Windows.Forms.SplitContainer]::New(); $this.splitContainer2 = [System.Windows.Forms.SplitContainer]::New(); $this.treeView1 = [System.Windows.Forms.TreeView]::New(); Add-Member -InputObject $this.treeView1 -MemberType NoteProperty -Name Form -Value $this $this.TypeDetailsTextBox = [System.Windows.Forms.TextBox]::new(); $this.PropertyGrid = [System.Windows.Forms.DataGridView]::New(); $this.findToolStripMenuItem = [System.Windows.Forms.ToolStripMenuItem]::New(); $this.exitToolStripMenuItem = [System.Windows.Forms.ToolStripMenuItem]::New(); $this.Edit = [System.Windows.Forms.ToolStripMenuItem]::New(); $this.findToolStripMenuItem1 = [System.Windows.Forms.ToolStripMenuItem]::New(); $this.findNextToolStripMenuItem = [System.Windows.Forms.ToolStripMenuItem]::New(); $this.PropertyDataTable = [System.Data.DataTable]::New(); $this.menuStrip1.SuspendLayout(); ([System.ComponentModel.ISupportInitialize]($this.splitContainer1)).BeginInit(); $this.splitContainer1.Panel1.SuspendLayout(); $this.splitContainer1.Panel2.SuspendLayout(); $this.splitContainer1.SuspendLayout(); ([System.ComponentModel.ISupportInitialize]($this.splitContainer2)).BeginInit(); $this.splitContainer2.Panel1.SuspendLayout(); $this.splitContainer2.Panel2.SuspendLayout(); $this.splitContainer2.SuspendLayout(); ([System.ComponentModel.ISupportInitialize]($this.PropertyGrid)).BeginInit(); $this.SuspendLayout(); # # ObjectPath # $this.ObjectPath.Dock = "Bottom"; $this.ObjectPath.Location = [System.Drawing.Point]::New(0, 430); $this.ObjectPath.Name = "ObjectPath"; $this.ObjectPath.Size = [System.Drawing.Size]::New(889, 20); $this.ObjectPath.Font = $this.CurrentFont; $this.ObjectPath.TabIndex = 0; # # menuStrip1 # $this.menuStrip1.Items.Add($this.findToolStripMenuItem) $this.menuStrip1.Items.Add($this.Edit) $this.menuStrip1.Location = [System.Drawing.Point]::New(0, 0); $this.menuStrip1.Name = "menuStrip1"; $this.menuStrip1.Size = [System.Drawing.Size]::New(889, 24); $this.menuStrip1.TabIndex = 1; $this.menuStrip1.Text = "menuStrip1"; # # splitContainer1 # $this.splitContainer1.Dock = "Fill"; $this.splitContainer1.Location = [System.Drawing.Point]::New(0, 24); $this.splitContainer1.Name = "splitContainer1"; $this.splitContainer1.Orientation = "Horizontal"; # # splitContainer1.Panel1 # $this.splitContainer1.Panel1.Controls.Add($this.splitContainer2); # # splitContainer1.Panel2 # $this.splitContainer1.Panel2.Controls.Add($this.TypeDetailsTextBox); $this.splitContainer1.Size = [System.Drawing.Size]::New(889, 406); $this.splitContainer1.SplitterDistance = 296; $this.splitContainer1.TabIndex = 2; # # splitContainer2 # $this.splitContainer2.Dock = "Fill"; $this.splitContainer2.Location = [System.Drawing.Point]::New(0, 0); $this.splitContainer2.Name = "splitContainer2"; # # splitContainer2.Panel1 # $this.splitContainer2.Panel1.Controls.Add($this.treeView1); # # splitContainer2.Panel2 # $this.splitContainer2.Panel2.Controls.Add($this.PropertyGrid); $this.splitContainer2.Size = [System.Drawing.Size]::New(889, 296); $this.splitContainer2.SplitterDistance = 445; $this.splitContainer2.TabIndex = 0; # # treeView1 # $this.treeView1.Dock = "Fill"; $this.treeView1.Location = [System.Drawing.Point]::New(0, 0); $this.treeView1.Name = "treeView1"; $this.treeView1.Size = [System.Drawing.Size]::New(445, 296); $this.treeView1.TabIndex = 0; $this.treeView1.Font = $this.currentFont; # # TypeDetailsTextBox # $this.TypeDetailsTextBox.Dock = "Fill"; $this.TypeDetailsTextBox.Location = [System.Drawing.Point]::New(0, 0); $this.TypeDetailsTextBox.Multiline = $true; $this.TypeDetailsTextBox.Name = "TypeDetailsTextBox"; $this.TypeDetailsTextBox.ScrollBars = "Both" $this.TypeDetailsTextBox.Size = [System.Drawing.Size]::New(889, 106); $this.TypeDetailsTextBox.TabIndex = 0; $this.TypeDetailsTextBox.Font = $this.currentFont; # # PropertyGrid # $this.PropertyGrid.ColumnHeadersHeightSizeMode = "AutoSize"; $this.PropertyGrid.Dock = "Fill"; $this.PropertyGrid.Location = [System.Drawing.Point]::New(0, 0); $this.PropertyGrid.Name = "PropertyGrid"; $this.PropertyGrid.SelectionMode = "CellSelect"; $this.PropertyGrid.Size = [System.Drawing.Size]::New(440, 296); $this.PropertyGrid.TabIndex = 0; $this.PropertyGrid.AutoSizeColumnsMode = "Fill"; $this.PropertyGrid.Font = $this.currentFont; $this.PropertyDataTable.Columns.Add([System.Data.DataColumn]::new("Type",[string])); $this.PropertyDataTable.Columns.Add([System.Data.DataColumn]::new("Name",[string])); $this.PropertyDataTable.Columns.Add([System.Data.DataColumn]::new("Value",[Object])); $this.PropertyGrid.DataSource = $this.PropertyDataTable; # # findToolStripMenuItem # $this.findToolStripMenuItem.DropDownItems.Add($this.exitToolStripMenuItem); $this.findToolStripMenuItem.Name = "findToolStripMenuItem"; $this.findToolStripMenuItem.Size = [System.Drawing.Size]::New(37, 20); $this.findToolStripMenuItem.Text = "File"; # # exitToolStripMenuItem # $this.exitToolStripMenuItem.Name = "exitToolStripMenuItem"; $this.exitToolStripMenuItem.Size = [System.Drawing.Size]::New(152, 22); $this.exitToolStripMenuItem.Text = "Exit"; # # Edit # $this.Edit.DropDownItems.Add($this.findToolStripMenuItem1); $this.Edit.DropDownItems.Add($this.findNextToolStripMenuItem); $this.Edit.Name = "Edit"; $this.Edit.Size = [System.Drawing.Size]::New(39, 20); $this.Edit.Text = "Edit"; # # findToolStripMenuItem1 # $this.findToolStripMenuItem1.Name = "findToolStripMenuItem1"; $this.findToolStripMenuItem1.ShortcutKeys = ([System.Windows.Forms.Keys]"Control" -bor [System.Windows.Forms.Keys]"F"); $this.findToolStripMenuItem1.Size = [System.Drawing.Size]::New(152, 22); $this.findToolStripMenuItem1.Text = "Find..."; # # findNextToolStripMenuItem # $this.findNextToolStripMenuItem.Name = "findNextToolStripMenuItem"; $this.findNextToolStripMenuItem.ShortcutKeys = [System.Windows.Forms.Keys]"F3"; $this.findNextToolStripMenuItem.Size = [System.Drawing.Size]::New(152, 22); $this.findNextToolStripMenuItem.Text = "Find Next"; # # Form3 # $this.AutoScaleDimensions = [System.Drawing.SizeF]::New(6, 13); $this.AutoScaleMode = "Font"; $this.ClientSize = [System.Drawing.Size]::New(889, 450); $this.Controls.Add($this.splitContainer1); $this.Controls.Add($this.ObjectPath); $this.Controls.Add($this.menuStrip1); $this.MainMenuStrip = $this.menuStrip1; $this.Name = "Form3"; $this.Text = "Show-Object"; $this.menuStrip1.ResumeLayout($false); $this.menuStrip1.PerformLayout(); $this.splitContainer1.Panel1.ResumeLayout($false); $this.splitContainer1.Panel2.ResumeLayout($false); $this.splitContainer1.Panel2.PerformLayout(); ([System.ComponentModel.ISupportInitialize]($this.splitContainer1)).EndInit(); $this.splitContainer1.ResumeLayout($false); $this.splitContainer2.Panel1.ResumeLayout($false); $this.splitContainer2.Panel2.ResumeLayout($false); ([System.ComponentModel.ISupportInitialize]($this.splitContainer2)).EndInit(); $this.splitContainer2.ResumeLayout($false); ([System.ComponentModel.ISupportInitialize]($this.PropertyGrid)).EndInit(); $this.ResumeLayout($false); $this.PerformLayout(); $this.treeView1.PathSeparator = "." $this.treeView1.ShowNodeToolTips = $true $this.treeView1.Add_AfterSelect( {$this.Form.OnAfterSelect($args[0], $args[1]) } ) $this.treeView1.Add_BeforeExpand( { $this.Form.OnBeforeExpand($args[0], $args[1]) } ) $this.treeView1.Add_KeyPress( {$this.Form.OnTreeViewKeyPress($args[0], $args[1])} ) $this.Add_MouseWheel( { $this.OnMouseWheel($args[0], $args[1]) } ) $this.Add_KeyUp( { $this.OnKeyUp($args[0], $args[1]) } ) } #endregion [System.Windows.Forms.TextBox]$ObjectPath; [System.Windows.Forms.MenuStrip]$menuStrip1; [System.Windows.Forms.SplitContainer]$splitContainer1; [System.Windows.Forms.ToolStripMenuItem]$findToolStripMenuItem; [System.Windows.Forms.ToolStripMenuItem]$exitToolStripMenuItem; [System.Windows.Forms.ToolStripMenuItem]$Edit; [System.Windows.Forms.ToolStripMenuItem]$findToolStripMenuItem1; [System.Windows.Forms.ToolStripMenuItem]$findNextToolStripMenuItem; [System.Windows.Forms.SplitContainer]$splitContainer2; [System.Windows.Forms.TreeView]$treeView1; [System.Windows.Forms.DataGridView]$PropertyGrid; [System.Data.DataTable]$PropertyDataTable; [System.Windows.Forms.TextBox]$TypeDetailsTextBox; [int]$currentFontSize = 12; [System.Drawing.Font]$currentFont = [System.Drawing.Font]::new("Consolas", $this.CurrentFontSize); [String]GetValueString($o) { if ($o -ne $null) { switch ($o.GetType().Fullname) { "System.String" {return "`"$($o.tostring())`""} "System.Boolean"{ if ($o) {return '$True'} else {return '$False'} } default { return $o.ToString()} } } return "" } ## A function to add a placeholder if required to a node. ## If there are any properties or children for this object, make a temporary ## node with the text "..." so that the node shows a '+' sign to be ## expanded. [void]AddPlaceholderIfRequired($node, $object) { if(-not $object -or $object -is [String]) { return } if([System.Management.Automation.LanguagePrimitives]::GetEnumerator($object) -or @($object.PSObject.Properties)) { $null = $node.Nodes.Add( (New-Object Windows.Forms.TreeNode "...") ) } } ## A function invoked when a node is selected. [void]OnAfterSelect($Sender, [System.Windows.Forms.TreeViewEventArgs]$TreeViewEventArgs) { [System.Windows.Forms.TreeNode]$nodeSelected = $treeViewEventArgs.Node ## Walk through its parents, creating the virtual ## PowerShell syntax to access this property. [String]$nodePath = $this.GetPathForNode($nodeSelected) ## Now, invoke that PowerShell syntax to retrieve ## the value of the property. $resultObject = Invoke-Expression $nodePath $this.ObjectPath.Text = $nodePath ## If we got some output, put the object's member ## information in the text box. if($resultObject) { $this.TypeDetailsTextBox.Text = Get-Member -InputObject $resultObject | sort-object MemberType,Name | Out-String } $this.UpdatePropertyGrid($resultObject) } ## A function invoked when a node is selected. [void]UpdatePropertyGrid([Object]$Item) { [System.Windows.Forms.DataGridView]$grid = $this.PropertyGrid; [System.Data.DataTable]$datatable = $this.PropertyDataTable; $datatable.Rows.Clear() if ($item -ne $null) { foreach ($p in Get-Member -input $item -MemberType Properties |sort Name) { $row = $datatable.NewRow(); $row["Name"] = $p.name; $value = $item.$($p.Name); $row["Value"] = $value; if ($value -ne $null) { $row["Type"] = $value.GetType().Fullname; } $datatable.Rows.Add($row) } } } ## A function invoked when the user is about to expand a node [void]OnBeforeExpand($Sender, $TreeViewCancelEventArgs) { ## Determine the selected node $selectedNode = $TreeViewCancelEventArgs.Node ## If it has a child node that is the placeholder, clear ## the placehoder node. if($selectedNode.FirstNode -and ($selectedNode.FirstNode.Text -eq "...")) { $selectedNode.Nodes.Clear() } else { return } ## Walk through its parents, creating the virtual ## PowerShell syntax to access this property. $nodePath = $this.GetPathForNode($selectedNode) $resultObject = $null ## Now, invoke that PowerShell syntax to retrieve ## the value of the property. Invoke-Expression "`$resultObject = $nodePath" ## And populate the node with the result object. $this.PopulateNode($selectedNode, $resultObject) } ## A function to handle key presses on the tree view. ## In this case, we capture ^C to copy the path of ## the object property that we're currently viewing. [void]OnTreeViewKeyPress($Sender, [System.Windows.Forms.KeyEventArgs]$KeyPressEventArgs) { ## [Char] 3 = Control-C if($KeyPressEventArgs.KeyChar -eq 3) { $KeyPressEventArgs.Handled = $true ## Get the object path, and set it on the clipboard $node = $Sender.SelectedNode $nodePath = $this.GetPathForNode($node) [System.Windows.Forms.Clipboard]::SetText($nodePath) $this.Close() } elseif([System.Windows.Forms.Control]::ModifierKeys -eq "Control") { if($KeyPressEventArgs.KeyChar -eq '+') { $this.CurrentFontSize++ $this.UpdateFonts() $KeyPressEventArgs.Handled = $true } elseif($KeyPressEventArgs.KeyChar -eq '-') { $this.CurrentFontSize-- if($this.CurrentFontSize -lt 1) { $this.CurrentFontSize = 1 } $this.UpdateFonts() $KeyPressEventArgs.Handled = $true } } } ## A function to handle key presses on the form. ## In this case, we handle Ctrl-Plus and Ctrl-Minus ## to adjust font size. [void]OnKeyUp($Sender, [System.Windows.Forms.KeyEventArgs]$KeyUpEventArgs) { if([System.Windows.Forms.Control]::ModifierKeys -eq "Control") { if($KeyUpEventArgs.KeyCode -in 'Add','OemPlus') { $this.CurrentFontSize++ $this.UpdateFonts() $KeyUpEventArgs.Handled = $true } elseif($KeyUpEventArgs.KeyCode -in 'Subtract','OemMinus') { $this.CurrentFontSize-- if($this.CurrentFontSize -lt 1) { $this.CurrentFontSize = 1 } $this.UpdateFonts() $KeyUpEventArgs.Handled = $true } elseif($KeyUpEventArgs.KeyCode -eq 'D0') { $this.CurrentFontSize = 12 $this.UpdateFonts() $KeyUpEventArgs.Handled = $true } } } ## A function to handle mouse wheel scrolling. ## In this case, we translate Ctrl-Wheel to zoom. [void]OnMouseWheel($Sender, [System.Windows.Forms.MouseEventArgs]$MouseEventArgs) { if( ([System.Windows.Forms.Control]::ModifierKeys -eq "Control") -and ($MouseEventArgs.Delta -ne 0)) { $this.CurrentFontSize += ($MouseEventArgs.Delta / 120) if($this.CurrentFontSize -lt 1) { $this.CurrentFontSize = 1 } $this.UpdateFonts() $MouseEventArgs.Handled = $true } } [void]OnClick($Sender, [System.Windows.Forms.MouseEventArgs]$MouseClickEventArgs) { if($MouseClickEventArgs.Button -eq "Right") { $this.menuStrip1.show() } } ## A function to walk through the parents of a node, ## creating virtual PowerShell syntax to access this property. [String]GetPathForNode([System.Windows.Forms.TreeNode]$Node) { $nodeElements = @() ## Go through all the parents, adding them so that ## $nodeElements is in order. while($Node) { $nodeElements = ,$Node + $nodeElements $Node = $Node.Parent } ## Now go through the node elements $nodePath = "" foreach($Node in $nodeElements) { $nodeName = $Node.Name ## If it was a node that PowerShell is able to enumerate ## (but not index), wrap it in the array cast operator. if($nodeName.StartsWith('@')) { $nodeName = $nodeName.Substring(1) $nodePath = "@(" + $nodePath + ")" } elseif($nodeName.StartsWith('[')) { ## If it's a child index, we don't need to ## add the dot for property access } elseif($nodePath) { ## Otherwise, we're accessing a property. Add a dot. $nodePath += "." } ## Append the node name to the path $nodePath += $nodeName } ## And return the result return $nodePath } [Void]UpdateFonts() { $this.Currentfont = [System.Drawing.Font]::new("Consolas", $this.currentFontSize) $this.treeView1.Font = ` $this.TypeDetailsTextBox.Font = ` $this.PropertyGrid.Font = ` $this.ObjectPath.Font = $this.Currentfont; } [void]ShowObject($inputObject) { ## Figure out the variable name to use when displaying the ## object navigation syntax. To do this, we look through all ## of the variables for the one with the same object identifier. $rootVariableName = dir variable:\* -Exclude InputObject,Args | Where-Object { $_.Value -and ($_.Value.GetType() -eq $InputObject.GetType()) -and ($_.Value.GetHashCode() -eq $InputObject.GetHashCode()) } ## If we got multiple, pick the first $rootVariableName = $rootVariableName| % Name | Select -First 1 ## If we didn't find one, use a default name if(-not $rootVariableName) { $rootVariableName = "InputObject" } ## Create the root node, which represents the object ## we are trying to show. $root = New-Object Windows.Forms.TreeNode $root.ToolTipText = "[" + $InputObject.GetType() + "]" $root.Text = $InputObject $root.Name = '$' + $rootVariableName $root.Expand() $null = $this.treeView1.Nodes.Add($root) $this.PopulateNode($root, $InputObject) } ## A function to add an object to the display tree [void]PopulateNode($node, $object) { ## If we've been asked to add a NULL object, just return if(-not $object) { return } ## If the object is a collection, then we need to add multiple ## children to the node if([System.Management.Automation.LanguagePrimitives]::GetEnumerator($object)) { ## Some very rare collections don't support indexing (i.e.: $foo[0]). ## In this situation, PowerShell returns the parent object back when you ## try to access the [0] property. $isOnlyEnumerable = $object.GetHashCode() -eq $object[0].GetHashCode() ## Go through all the items $count = 0 foreach($childObjectValue in $object) { ## Create the new node to add, with the node text of the item and ## value, along with its type $newChildNode = New-Object Windows.Forms.TreeNode $newChildNode.Text = "$($node.Name)[$count] = $childObjectValue" $newChildNode.ToolTipText = "[" + $childObjectValue.GetType() + "]" ## Use the node name to keep track of the actual property name ## and syntax to access that property. ## If we can't use the index operator to access children, add ## a special tag that we'll handle specially when displaying ## the node names. if($isOnlyEnumerable) { $newChildNode.Name = "@" } $newChildNode.Name += "[$count]" $null = $node.Nodes.Add($newChildNode) ## If this node has children or properties, add a placeholder ## node underneath so that the node shows a '+' sign to be ## expanded. $this.AddPlaceholderIfRequired($newChildNode, $childObjectValue) $count++ } } else { ## If the item was not a collection, then go through its ## properties foreach($child in $object.PSObject.Properties |Sort-Object Name) { ## Figure out the value of the property, along with ## its type. $childObject = $child.Value $childObjectType = $null if($childObject -ne $null) { $childObjectType = $childObject.GetType() }else { $childObjectType = $Object.gettype().getproperty($($child.Name)).PropertyType.Fullname } ## Create the new node to add, with the node text of the item and ## value, along with its type $childNode = New-Object Windows.Forms.TreeNode $childNode.Text = $child.Name + " = $($this.GetValueString($childObject))" $childNode.ToolTipText = "[" + $childObjectType + "]" if([System.Management.Automation.LanguagePrimitives]::GetEnumerator($childObject)) { $childNode.ToolTipText += "[]" } $childNode.Name = $child.Name $null = $node.Nodes.Add($childNode) ## If this node has children or properties, add a placeholder ## node underneath so that the node shows a '+' sign to be ## expanded. $this.AddPlaceholderIfRequired($childNode, $childObject) } } } } $f = [form3]::new() $f.InitializeComponent(); $x = $args[0] $f.ShowObject($x) $null = $f.ShowDialog() $f.Dispose() |