ObjectHelp.psm1
param() @" NAME ObjectHelp Extensions Module 0.2 for PowerShell 2.0 CTP3 SYNOPSIS Get-Help -Object allows you to display usage and summary help for .NET Types and Members. DETAILED DESCRIPTION Get-Help -Object allows you to display usage and summary help for .NET Types and Members. If local documentation is not found and the object vendor is Microsoft, you will be directed to MSDN online to the correct page. If the vendor is not Microsoft and vendor information exists on the owning assembly, you will be prompted to search for information using Microsoft Live Search. TODO * localize strings into PSD1 file * Implement caching in hashtables. XMLDocuments are fat pigs. * Support getting property/field help * PowerTab integration * Test with Strict Parser EXAMPLES # get help on a type PS> get-help -obj [int] # get help against live instances PS> $obj = new-object system.xml.xmldocument PS> get-help -obj `$obj or even: PS> get-help -obj 42 # get help against methods PS> get-help -obj `$obj.Load # explictly try msdn PS> get-help -obj [regex] -online # go to msdn for regex's members PS> get-help -obj [regex] -online -members # pipe support PS> 1,[int],[string]::format | get-help -verbose CREDITS Author: Oisin Grehan (MVP) Blog : http://www.nivot.org/ Have fun! "@ function Get-LocalizedNamespace { param( $NameSpace , [int]$cultureID = (Get-Culture).LCID ) #First, get a list of all localized namespaces under the current namespace $localizedNamespaces = Get-WmiObject -NameSpace $NameSpace -Class "__Namespace" | Where-Object {$_.Name -like "ms_*"} if ($null -eq $localizedNamespaces) { if (-not $quiet) { Write-Warning "Could not get a list of localized namespaces" } return } return ("$namespace\ms_{0:x}" -f $cultureID) } function Get-WmiClassInfo { param( [Parameter(Position = 0)] [string]$Class , [string]$Namespace = "ROOT\cimv2" , [int]$CultureID = (Get-Culture).LCID ) $LocalizedNamespace = Get-LocalizedNamespace $Namespace $CultureID $ClassLocation = $LocalizedNamespace + ':' + $Class $Options = New-Object System.Management.ObjectGetOptions $Options.UseAmendedQualifiers = $true ## Return New-Object System.Management.ManagementClass $ClassLocation,$Options } function Search-WmiHelp { param( [ScriptBlock]$DescriptionExpression = {} , [ScriptBlock]$MethodExpression = {} , [ScriptBlock]$PropertyExpression = {} , $Namespaces = "root\cimv2" , $CultureID = (Get-Culture).LCID , [switch]$List ) $resultWmiClasses = @{} foreach ($namespace in $Namespaces) { #First, get a list of all localized namespaces under the current namespace $localizedNamespace = Get-LocalizedNamespace $namespace if ($localizedNamespace -eq $null) { Write-Verbose "Could not get a list of localized namespaces" return } $localizedClasses = Get-WmiObject -NameSpace $localizedNamespace -Query "select * from meta_class" $count = 0 foreach ($WmiClass in $localizedClasses) { $count++ Write-Progress "Searching Wmi Classes" "$count of $($localizedClasses.Count)" -PercentComplete ($count*100/$localizedClasses.Count) $classLocation= $localizedNamespace + ':' + $WmiClass.__Class $classInfo = Get-WmiClassInfo $classLocation [bool]$found = $false if ($classInfo -ne $null) { if (! $resultWmiClasses.ContainsKey($classLocation)) { $resultWmiClasses.Add($wmiClass.__Class, $classInfo) } $descriptionMatch = [bool]($classInfo.Description | Where-Object $DescriptionExpression) $methodMatch = [bool]($classInfo.Methods.GetEnumerator() | Where-Object $MethodExpression) $propertyMatch = [bool]($classInfo.Properties.GetEnumerator() | Where-Object $PropertyExpression) $found = $descriptionMatch -or $methodMatch -or $propertyMatch if (! $found) { $resultWmiClasses.Remove($WmiClass.__Class) } } } } if ($List) { $resultWmiClasses.Keys | Sort-Object } else { $resultWmiClasses.GetEnumerator() | Sort-Object Key } } function Get-HelpLocation { [CmdletBinding()] param( [System.Type]$Type ) # get documentation filename, assembly location and assembly codebase $DocFilename = [System.IO.Path]::ChangeExtension([System.IO.Path]::GetFileName($Type.Assembly.Location), ".xml") $Location = [System.IO.Path]::GetDirectoryName($Type.Assembly.Location) $CodeBase = (New-Object System.Uri $Type.Assembly.CodeBase).LocalPath $PSCmdlet.WriteVerbose("Documentation file is '$DocFilename.'") ## try localized location (typically newer than base framework dir) $FrameworkDir = "${env:windir}\Microsoft.NET\framework\v2.0.50727" $Language = [System.Globalization.CultureInfo]::CurrentUICulture.Parent.Name foreach ($Path in "$FrameworkDir\$Language\$DocFilename", "$FrameworkDir\$DocFilename", "$Location\$DocFilename", "$CodeBase\$DocFilename") { if (Test-Path $Path) { return $Path } } # if (-not $Online.IsPresent) # { # # try localized location (typically newer than base framework dir) # $frameworkDir = "${env:windir}\Microsoft.NET\framework\v2.0.50727" # $lang = [system.globalization.cultureinfo]::CurrentUICulture.parent.name # # I love looking at this. A Duff's Device for PowerShell.. well, maybe not. # switch # ( # "${frameworkdir}\${lang}\$docFilename", # "${frameworkdir}\$docFilename", # "$location\$docFilename", # "$codebase\$docFilename" # ) # { # { test-path $_ } { $_; return; } # default # { # # try next path # continue; # } # } # } # # failed to find local docs, is it from MS? # if ((Get-ObjectVendor $type) -like "*Microsoft*") # { # # drop locale - site will redirect to correct variation based on browser accept-lang # $suffix = "" # if ($Members.IsPresent) # { # $suffix = "_members" # } # new-object uri ("http://msdn.microsoft.com/library/{0}{1}.aspx" -f $type.fullname,$suffix) # return # } } function Get-HelpUri { [CmdletBinding()] param( [System.Type]$Type , [String]$Member ) ## Needed for UrlEncode() Add-Type -AssemblyName System.Web $Vendor = Get-ObjectVendor $Type if ($Vendor -like "*Microsoft*") { ## drop locale - site will redirect to correct variation based on browser accept-lang $Suffix = "" if ($Member -eq "_members") { $Suffix = "_members" } elseif ($Member) { $Suffix = ".$Member" } $Query = [System.Web.HttpUtility]::UrlEncode(("{0}{1}" -f $Type.FullName,$Suffix)) New-Object System.Uri "http://msdn.microsoft.com/library/$Query.aspx" } else { $Suffix = "" if ($Member -eq "_members") { $Suffix = " members" } elseif ($Member) { $Suffix = ".$Member" } if ($Vendor) { $Query = [System.Web.HttpUtility]::UrlEncode(("`"{0}`" {1}{2}" -f $Vendor,$Type.FullName,$Suffix)) } else { $Query = [System.Web.HttpUtility]::UrlEncode(("{0}{1}" -f $Type.FullName,$Suffix)) } New-Object System.Uri "http://www.bing.com/results.aspx?q=$Query" } } function Get-ObjectVendor { [CmdletBinding()] param( [System.Type]$Type , [switch]$CompanyOnly ) $Assembly = $Type.Assembly $attrib = $Assembly.GetCustomAttributes([Reflection.AssemblyCompanyAttribute], $false) | Select-Object -First 1 if ($attrib.Company) { return $attrib.Company } else { if ($CompanyOnly) { return } # try copyright $attrib = $Assembly.GetCustomAttributes([Reflection.AssemblyCopyrightAttribute], $false) | Select-Object -First 1 if ($attrib.Copyright) { return $attrib.Copyright } } $PSCmdlet.WriteVerbose("Assembly has no [AssemblyCompany] or [AssemblyCopyright] attributes.") } function Import-LocalNetHelp { [CmdletBinding()] param( [string]$File , [string]$Selector ) try { $FileStream = New-Object System.IO.FileStream $File, ([System.IO.FileMode]::Open), ([System.IO.FileAccess]::Read) $Reader = New-Object System.Xml.XmlTextReader $FileStream $Reader.EntityHandling = [System.Xml.EntityHandling]"ExpandEntities" $Document = New-Object System.Xml.XPath.XPathDocument $Reader $Navigator = $Document.CreateNavigator() # TODO: support overloads $Navigator.Select("//member[@name='$Selector' or starts-with(@name,'$Selector(')]") | ForEach-Object {[Xml]$_.OuterXml} } finally { if ($Reader) { $Reader.Close() } } } function Resolve-MemberOwnerType { [CmdletBinding()] param( [Parameter(Position = 0)] [System.Management.Automation.PSMethod]$Method ) # TODO: support overloads, support interface definitions $PSCmdlet.WriteVerbose("Resolving owning type of '$($Method.Name)'.") # hackety-hack - this is prone to breaking in the future $TargetType = [System.Management.Automation.PSMethod].GetField("baseObject", "Instance,NonPublic").GetValue($Method) if (($TargetType -isnot [System.Type]) -and (-not $TargetType.__CLASS)) { $TargetType = $TargetType.GetType() } if ($TargetType -is [System.Management.ManagementObject]) { $DeclaringType = Get-CimClass $TargetType.__CLASS -Namespace $TargetType.__NAMESPACE } else { if ($Method.OverloadDefinitions -match "static") { $Flags = "Static,Public" } else { $Flags = "Instance,Public" } # FIXME: support overloads $MethodInfo = $TargetType.GetMethods($Flags) | Where-Object {$_.Name -eq $Method.Name} | Select-Object -First 1 if (-not $MethodInfo) { # this shouldn't happen. throw "Could not resolve owning type." } $DeclaringType = $MethodInfo.DeclaringType } $PSCmdlet.WriteVerbose("Owning type is $($TargetType.FullName). Method declared on $($DeclaringType.FullName).") $DeclaringType } function Get-CimHelp { [CmdletBinding(DefaultParameterSetName = "Class")] param( [Parameter(Position = 0)] [ValidateNotNullOrEmpty()] [string]$Class , [string]$Namespace = "ROOT\cimv2" , [Parameter(ParameterSetName = "Class")] [switch]$Detailed , [Parameter(ParameterSetName = "Property")] [string]$Property , [Parameter(ParameterSetName = "Method")] [string]$Method ) $CimClass = Get-CimClass $Class -Namespace $Namespace $LocalizedClass = Get-WmiClassInfo $Class -Namespace $Namespace $HelpObject = New-Object PSObject -Property @{ Details = New-Object PSObject -Property @{ Name = $CimClass.CimClassName Namespace = $CimClass.CimSystemProperties.Namespace SuperClass = $CimClass.CimSuperClass.ToString() Description = @($LocalizedClass.Qualifiers["Description"].Value -split "`n" | ForEach-Object { $Paragraph = New-Object PSObject -Property @{Text=$_.Trim()} $Paragraph.PSObject.TypeNames.Insert(0, "CimParaTextItem") $Paragraph }) } Properties = @{} Methods = @{} } $HelpObject.Details.PSObject.TypeNames.Insert(0, "ObjectHelpInfo#Details") $HelpObject.PSObject.TypeNames.Insert(0, "ObjectHelpInfo") $HelpObject.PSObject.TypeNames.Insert(0, "ObjectHelpInfo#Cim") if ($Detailed) { $HelpObject.PSObject.TypeNames.Insert(0, "ObjectHelpInfo#Cim#DetailedView") } foreach ($CimProperty in $LocalizedClass.Properties) { $PropertyObject = New-Object PSObject -Property @{ Name = $CimProperty.Name Type = $CimProperty.Type Description = @($CimProperty.Qualifiers["Description"].Value -split "`n" | ForEach-Object { $Paragraph = New-Object PSObject -Property @{Text=$_.Trim()} $Paragraph.PSObject.TypeNames.Insert(0, "CimParaTextItem") $Paragraph }) } $PropertyObject.PSObject.TypeNames.Insert(0, "ObjectHelpInfo#Cim#Property") $HelpObject.Properties.Add($CimProperty.Name, $PropertyObject) } foreach ($CimMethod in $CimClass.CimClassMethods) { $MethodHelp = $LocalizedClass.Methods[$CimMethod.Name] $MethodObject = New-Object PSObject -Property @{ Name = $CimMethod.Name Static = $CimMethod.Qualifiers["Static"].Value Constructor = $CimMethod.Qualifiers["Constructor"].Value Description = $null Parameters = @{} } $MethodObject.PSObject.TypeNames.Insert(0, "ObjectHelpInfo#Cim#Method") $MethodObject.Description = @($MethodHelp.Qualifiers["Description"].Value -split "`n" | ForEach-Object { $Paragraph = New-Object PSObject -Property @{Text=$_.Trim()} $Paragraph.PSObject.TypeNames.Insert(0, "CimParaTextItem") $Paragraph }) $CimMethod.Parameters | ForEach-Object { if ($_.Qualifiers["In"]) { $MethodObject.Parameters[$_.Name] = New-Object PSObject -Property @{ Name = $_.Name Type = $_.CimType ID = [int]$_.Qualifiers["ID"].Value Description = $null In = $true } } if ($_.Qualifiers["Out"]) { $MethodObject.Parameters[$_.Name] = New-Object PSObject -Property @{ Name = $_.Name Type = $_.CimType ID = [int]$_.Qualifiers["ID"].Value Description = $null In = $false } } } $HelpObject.Methods.Add($CimMethod.Name, $MethodObject) } if ($Property) { $PropertyObject = $HelpObject.Properties[$Property] if ($PropertyObject) { Add-Member -InputObject $PropertyObject -Name Class -Value $HelpObject.Details.Name -MemberType NoteProperty Add-Member -InputObject $PropertyObject -Name Namespace -Value $HelpObject.Details.Namespace -MemberType NoteProperty Add-Member -InputObject $PropertyObject -Name SuperClass -Value $HelpObject.Details.SuperClass -MemberType NoteProperty $PropertyObject.PSObject.TypeNames.Insert(0, "ObjectHelpInfo#Cim#PropertyDetail") return $PropertyObject } else { throw "Property named '$Property' not found." } } elseif ($Method) { $MethodObject = $HelpObject.Methods[$Method] if ($MethodObject) { Write-Progress "Retrieving Parameter Descriptions" $i, $total = 0, $MethodObject.Parameters.Values.Count $MethodHelp = $LocalizedClass.Methods[$Method] $MethodObject.Parameters.Values | Where-Object {$_.In} | ForEach-Object { Write-Progress "Retrieving Parameter Descriptions" -PercentComplete ($i/$total*100); $i++ $ParameterHelp = $MethodHelp.InParameters.Properties | Where-Object Name -eq $_.Name $_.Description = @($ParameterHelp.Qualifiers["Description"].Value -split "`n" | ForEach-Object { $Paragraph = New-Object PSObject -Property @{Text=$_.Trim()} $Paragraph.PSObject.TypeNames.Insert(0, "CimParaTextItem") if ($Paragraph.Text) {$Paragraph} }) } $MethodObject.Parameters.Values | Where-Object {-not $_.In} | ForEach-Object { Write-Progress "Retrieving Parameter Descriptions" -PercentComplete ($i/$total*100); $i++ $ParameterHelp = $MethodHelp.OutParameters.Properties | Where-Object Name -eq $_.Name $_.Description = @($ParameterHelp.Qualifiers["Description"].Value -split "`n" | ForEach-Object { $Paragraph = New-Object PSObject -Property @{Text=$_.Trim()} $Paragraph.PSObject.TypeNames.Insert(0, "CimParaTextItem") if ($Paragraph.Text) {$Paragraph} }) } Add-Member -InputObject $MethodObject -Name Class -Value $HelpObject.Details.Name -MemberType NoteProperty Add-Member -InputObject $MethodObject -Name Namespace -Value $HelpObject.Details.Namespace -MemberType NoteProperty Add-Member -InputObject $PropertyObject -Name SuperClass -Value $HelpObject.Details.SuperClass -MemberType NoteProperty $MethodObject.PSObject.TypeNames.Insert(0, "ObjectHelpInfo#Cim#MethodDetail") Write-Progress "Retrieving Parameter Descriptions" -Completed return $MethodObject } else { throw "Method named '$Method' not found." } } else { return $HelpObject } } function Get-NetHelp { [CmdletBinding(DefaultParameterSetName = "Class")] param( [Parameter(Position = 0)] [ValidateNotNull()] [System.Type]$Type , [Parameter(ParameterSetName = "Class")] [switch]$Detailed , [Parameter(ParameterSetName = "Property")] [string]$Property , [Parameter(ParameterSetName = "Method")] [string]$Method ) # if ($Docs = Get-HelpLocation $Type) { # $PSCmdlet.WriteVerbose("Found '$Docs'.") # $TypeName = $Type.FullName # if ($Method) { # $Selector = "M:$TypeName.$Method" # } else { ## TODO: Property? # $Selector = "T:$TypeName" # } # ## get summary, if possible # $Help = Import-LocalNetHelp $Docs $Selector # if ($Help) { # $Help #| Format-AssemblyHelp # } else { # Write-Warning "While some local documentation was found, it was incomplete." # } # } $HelpObject = New-Object PSObject -Property @{ Details = New-Object PSObject -Property @{ Name = $Type.Name Namespace = $Type.Namespace SuperClass = $Type.BaseType } Properties = @{} Constructors = @() Methods = @{} RelatedLinks = @( New-Object PSObject -Property @{Title="Online Version"; Link = Get-HelpUri $Type} ) } $HelpObject.Details.PSObject.TypeNames.Insert(0, "ObjectHelpInfo#Details") $HelpObject.PSObject.TypeNames.Insert(0, "ObjectHelpInfo") if ($Detailed) { $HelpObject.PSObject.TypeNames.Insert(0, "ObjectHelpInfo#Net#DetailedView") # Write-Error "Local detailed help not available for type '$Type'." } else { $HelpObject.PSObject.TypeNames.Insert(0, "ObjectHelpInfo#Net") } foreach ($NetProperty in $Type.DeclaredProperties) { $PropertyObject = New-Object PSObject -Property @{ Name = $NetProperty.Name Type = $NetProperty.PropertyType } $PropertyObject.PSObject.TypeNames.Insert(0, "ObjectHelpInfo#Net#Property") $HelpObject.Properties.Add($NetProperty.Name, $PropertyObject) } foreach ($NetConstructor in $Type.DeclaredConstructors | Where-Object {$_.IsPublic}) { $ConstructorObject = New-Object PSObject -Property @{ Name = $Type.Name Namespace = $Type.Namespace Parameters = $NetConstructor.GetParameters() } $ConstructorObject.PSObject.TypeNames.Insert(0, "ObjectHelpInfo#Net#Constructor") $HelpObject.Constructors += $ConstructorObject } foreach ($NetMethod in $Type.DeclaredMethods | Where-Object {$_.IsPublic -and (-not $_.IsSpecialName)} | Group-Object Name) { $MethodObject = New-Object PSObject -Property @{ Name = $NetMethod.Name Static = $NetMethod.Group[0].IsStatic Constructor = $NetMethod.Group[0].IsConstructor ReturnType = $NetMethod.Group[0].ReturnType Overloads = @( $NetMethod.Group | ForEach-Object { $MethodOverload = New-Object PSObject -Property @{ Name = $NetMethod.Name Static = $_.IsStatic ReturnType = $_.ReturnType Parameters = @( $_.GetParameters() | ForEach-Object { New-Object PSObject -Property @{ Name = $_.Name ParameterType = $_.ParameterType } } ) Class = $HelpObject.Details.Name Namespace = $HelpObject.Details.Namespace } $MethodOverload.PSObject.TypeNames.Insert(0, "ObjectHelpInfo#Net#MethodOverload") $MethodOverload } ) } $MethodObject.PSObject.TypeNames.Insert(0, "ObjectHelpInfo#Net#Method") $HelpObject.Methods.Add($NetMethod.Name, $MethodObject) } if ($Property) { $PropertyObject = $HelpObject.Properties[$Property] if ($PropertyObject) { Add-Member -InputObject $PropertyObject -Name Class -Value $HelpObject.Details.Name -MemberType NoteProperty Add-Member -InputObject $PropertyObject -Name Namespace -Value $HelpObject.Details.Namespace -MemberType NoteProperty Add-Member -InputObject $PropertyObject -Name SuperClass -Value $HelpObject.Details.SuperClass -MemberType NoteProperty $PropertyObject.PSObject.TypeNames.Insert(0, "ObjectHelpInfo#Net#PropertyDetail") return $PropertyObject } else { throw "Property named '$Property' not found." } } elseif ($Method) { $MethodObject = $HelpObject.Methods[$Method] if ($MethodObject) { Add-Member -InputObject $MethodObject -Name Class -Value $HelpObject.Details.Name -MemberType NoteProperty Add-Member -InputObject $MethodObject -Name Namespace -Value $HelpObject.Details.Namespace -MemberType NoteProperty Add-Member -InputObject $MethodObject -Name SuperClass -Value $HelpObject.Details.SuperClass -MemberType NoteProperty $MethodObject.PSObject.TypeNames.Insert(0, "ObjectHelpInfo#Net#MethodDetail") return $MethodObject } else { throw "Method named '$Method' not found." } } else { return $HelpObject } } function Get-ObjectHelp { [CmdletBinding(DefaultParameterSetName = "Class")] param( [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)] [ValidateNotNull()] [PSObject]$Object , [Parameter(ParameterSetName = "Class")] [switch]$Detailed , [Parameter(ParameterSetName = "Method")] [string]$Method , [Parameter(ParameterSetName = "Property")] [string]$Property , [Parameter()] [switch]$Online ) begin { $PSCmdlet.WriteVerbose("Begin") } process { $Type = $null $TypeName = $null # $Selector = $null Write-Verbose "Start processing..." Write-Verbose ("Input object (Type:" + $Object.GetType() + ", IsType:" + ($Object -is [System.Type]) + ")") if ($Object -is [Management.Automation.PSMemberInfo]) { if ($Object -is [System.Management.Automation.PSMethod]) { $Method = $Object.Name $Type = Resolve-MemberOwnerType $Object } else { Write-Error "Unable to identify owning time of PSMembers." return } } elseif ($Object -is [Microsoft.PowerShell.Commands.MemberDefinition]) { if ($Object.MemberType -eq "Method") { $Method = $Object.Name } else { $Property = $Object.Name } if ($Object.TypeName -match '^System.Management.ManagementObject#(.+)') { $Type = $Object.TypeName } else { $Type = "$($Object.TypeName)" -as [System.Type] } } elseif ($Object -is [Microsoft.Management.Infrastructure.CimClass]) { $Type = $Object } elseif ($Object -is [Microsoft.Management.Infrastructure.CimInstance]) { $Type = $Object.PSBase.CimClass } elseif ($Object -is [System.Management.ManagementObject]) { $Type = Get-CimClass $Object.__CLASS -Namespace $Object.__NAMESPACE } elseif ($Object -is [System.__ComObject]) { $Type = $Object } elseif ($Object -is [System.String]) { switch -regex ($Object) { '^\[[^\[\]]+\]$' { ## .NET Type (ex: [System.String]) try { $Type = Invoke-Expression $Object } catch { } break } '^(Win32|CIM)_[\w]+' { $Type = Get-CimClass $Object } ## TODO: WMI / CIM Default {} } } elseif ($Object -as [System.Type]) { $Type = $Object -as [System.Type] } if (-not $Type) { Write-Error "Could not identify object" return } Write-Verbose ("Object (Type:" + $Object.GetType() + ", IsType:" + ($Object -is [System.Type]) + ")") Write-Verbose ("Method is: $Method") Write-Verbose ("Property is: $Property") $Culture = $Host.CurrentCulture.Name ## TODO: Support culture parameter? if ($Type -is [Microsoft.Management.Infrastructure.CimClass]) { if ($Online) { $TypeName = $Type.CimClassName -replace "_","-" if ($Method) { # $Page = "$TypeName#methods" $Page = "$Method-method-in-class-$TypeName" } elseif ($Property) { $Page = "$TypeName#properties" } else { $Page = $TypeName } $Uri = "https://docs.microsoft.com/$Culture/windows/desktop/CIMWin32Prov/$Page" [System.Diagnostics.Process]::Start($uri) | Out-Null } else { if ($Method) { Get-CimHelp -Class $Type.CimClassName -Namespace $Type.CimSystemProperties.Namespace -Method $Method } elseif ($Property) { Get-CimHelp -Class $Type.CimClassName -Namespace $Type.CimSystemProperties.Namespace -Property $Property } else { Get-CimHelp -Class $Type.CimClassName -Namespace $Type.CimSystemProperties.Namespace -Detailed:$Detailed } } } elseif ($Type -is [System.Type]) { if ($Online) { $Member = if ($Method) { $Method } elseif ($Property) { $Property } else { $null } if ($Uri = Get-HelpUri $Type -Member $Member) { [System.Diagnostics.Process]::Start($Uri.ToString()) | Out-Null } } else { if ($Method) { Get-NetHelp -Type $Type -Method $Method } elseif ($Property) { Get-NetHelp -Type $Type -Property $Property } else { Get-NetHelp -Type $Type -Detailed:$Detailed } } } elseif ($Type -is [System.__ComObject]) { if ($Online) { if ($Type.PSTypeNames[0] -match 'System\.__ComObject#(.*)$') { if (Test-Path "HKLM:\SOFTWARE\Classes\Interface\$($Matches[1])") { $TypeKey = (Get-ItemProperty "HKLM:\SOFTWARE\Classes\Interface\$($Matches[1])").'(default)' if ('_Application' -contains $TypeKey) { $TypeName = (Get-ItemProperty "HKLM:\SOFTWARE\Classes\TypeLib\$TypeLib\$Version").'(default)' } else { $TypeName = $TypeKey } } } $Uri = "http://social.msdn.microsoft.com/Search/$Culture/?query=$TypeName" [System.Diagnostics.Process]::Start($uri) | Out-Null } else { Write-Error "Local help not supported for COM objects." return } } } } New-Alias -Name "ohelp" -Value "Get-ObjectHelp" |