Private/cliHelper.Core.help/cliHelper.Core.help.psm1

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."
  # }
  # }

  $HelpUrl = Get-HelpUri $Type
  $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 = $HelpUrl }
    )
  }
  $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 (!$_.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)
  }

  $DownloadOnlineHelp = $true
  if ($Property) {
    $PropertyObject = $HelpObject.Properties[$Property]

    if ($PropertyObject) {
      $PropertyHelpUrl = Get-HelpUri $Type -Member $Property
      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
      Add-Member -InputObject $PropertyObject -Name RelatedLinks -Value @(New-Object PSObject -Property @{Title = "Online Version"; Link = $PropertyHelpUrl }) -MemberType NoteProperty
      $PropertyObject.PSObject.TypeNames.Insert(0, "ObjectHelpInfo#Net#PropertyDetail")

      if ($DownloadOnlineHelp) {
        $OnlineHelp = Import-OnlineHelp $PropertyHelpUrl
        if ($OnlineHelp) {
          Add-Member -InputObject $PropertyObject -Name Summary -Value $OnlineHelp.Summary -MemberType NoteProperty
        }
      }

      return $PropertyObject
    } else {
      throw "Property named '$Property' not found."
    }
  } elseif ($Method) {
    $MethodObject = $HelpObject.Methods[$Method]

    if ($MethodObject) {
      $MethodHelpUrl = Get-HelpUri $Type -Member $Method
      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
      Add-Member -InputObject $MethodObject -Name RelatedLinks -Value @(New-Object PSObject -Property @{Title = "Online Version"; Link = $MethodHelpUrl }) -MemberType NoteProperty
      $MethodObject.PSObject.TypeNames.Insert(0, "ObjectHelpInfo#Net#MethodDetail")

      if ($DownloadOnlineHelp) {
        $OnlineHelp = Import-OnlineHelp $MethodHelpUrl
        if ($OnlineHelp) {
          Add-Member -InputObject $MethodObject -Name Summary -Value $OnlineHelp.Summary -MemberType NoteProperty
        }
      }

      return $MethodObject
    } else {
      throw "Method named '$Method' not found."
    }
  } else {
    if ($DownloadOnlineHelp) {
      $OnlineHelp = Import-OnlineHelp $HelpUrl
      if ($OnlineHelp) {
        Add-Member -InputObject $HelpObject.Details -Name Summary -Value $OnlineHelp.Summary -MemberType NoteProperty
      }
    }
    return $HelpObject
  }
}
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
  )

  $HelpUrl = Get-CimUri -Type $Type -Method $Method -Property $Property

  $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      = @{}
    RelatedLinks = @(
      New-Object PSObject -Property @{Title = "Online Version"; Link = $HelpUrl }
    )
  }
  $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
        })
      RelatedLinks = @(
        New-Object PSObject -Property @{Title = "Online Version"; Link = $HelpUrl }
      )
    }
    $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   = @{}
      RelatedLinks = @(
        New-Object PSObject -Property @{Title = "Online Version"; Link = $HelpUrl }
      )
    }
    $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 { !$_.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-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-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 (!$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-CimUri {
  [CmdletBinding()]
  param(
    [Microsoft.Management.Infrastructure.CimClass]$Type,
    [String]$Method,
    [String]$Property
  )

  $Culture = $Host.CurrentCulture.Name
  $TypeName = $Type.CimClassName -replace "_", "-"
  if ($Method) {
    # $Page = "$TypeName#methods"
    $Page = "$Method-method-in-class-$TypeName"
  } elseif ($Property) {
    $Page = "$TypeName#properties"
  } else {
    $Page = $TypeName
  }
  New-Object System.Uri "https://docs.microsoft.com/$Culture/windows/desktop/CIMWin32Prov/$Page"
}
function Get-LocalizedNamespace {
  param(
    $NameSpace
    ,
    [int]$cultureID = (Get-Culture).LCID
  )

  #First, get a list of all localized namespaces under the current namespace
  $localizedNamespaces = Get-CimInstance -NameSpace $NameSpace -Class "__Namespace" | Where-Object { $_.Name -like "ms_*" }
  if ($null -eq $localizedNamespaces) {
    if (!$quiet) {
      Write-Warning "Could not get a list of localized namespaces"
    }
    return
  }

  return ("$namespace\ms_{0:x}" -f $cultureID)
}
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 ($null -eq $localizedNamespace) {
      Write-Verbose "Could not get a list of localized namespaces"
      return
    }

    $localizedClasses = Get-CimInstance -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 ($null -ne $classInfo) {
        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
  }
}