Skip to content

Instantly share code, notes, and snippets.

@Still34
Created January 23, 2024 06:15
Show Gist options
  • Save Still34/3b6184e1fb70b693a2f36e628fe65268 to your computer and use it in GitHub Desktop.
Save Still34/3b6184e1fb70b693a2f36e628fe65268 to your computer and use it in GitHub Desktop.
Lookup Win32 APIs from pwsh
# Made with <3 by Still/Azaka
# https://links.azaka.fun
#Requires -Module ps-menu
#Requires -Version 7
$msftDocsCacheDir = [System.IO.Path]::Combine(($env:HOME ?? $env:USERPROFILE), '.msftdocs-cache')
function Update-MsDocsCache
{
$win32Categories = "_ad", "_adam", "_rm", "_adsi", "_alljoyn", "_amsi", "_setup", "_recovery", "_audio", "_automat", "_bits", "_backup", "_battery", "_bltooth", "_bluetooth", "_cimfs", "_cimwin32", "_cloudapi", "_cos", "_com", "_cmpapi", "_coreaudio", "_fs", "_dedup", "_dataxchg", "_debug", "_dwm", "_devlic", "_winprog", "_deviceaccess", "_devinst", "_dlgbox", "_directmanipulation", "_direct2d", "_direct3d10", "_direct3d11", "_direct3d12", "_direct3d9", "_directcomp", "_directdraw", "_directml", "_directwrite", "_dxmath", "_display", "_dfs", "_msdtcwmi", "_dns", "_devtest", "_dxcore", "_direct3ddxgi", "_dhcp", "_enstor", "_etw", "_eventlogprov", "_eaphost", "_eap", "_cluswmi", "_clushyperv", "_fax", "_fsrm", "_gamemode", "_gamingdvcinfo", "_gdiplus", "_policy", "_gpmc", "_hcp", "_hidpi", "_direct3dhlsl", "_http", "_hid", "_hyperv", "_virtual", "_imapi", "_indexsrv", "_input_ink", "_input_feedback", "_input_sourceid", "_ifsk", "_input_intcontext", "_intl", "_iphlp", "_iscsidisc", "_iscsitarg", "_kernel", "_inputdev", "_lwef", "_ldap", "_winlocation", "_magapi", "_mib", "_menurc", "_htmlhelp", "_mmc", "_mf", "_mbn", "_mdmreg", "_mdmsettingsprov", "_monitor", "_mschap", "_madcap", "_nwifi", "_netbios", "_netshell", "_nap", "_ndf", "_netxp", "_netvista", "_nla", "_netmgmt", "_nps", "_of", "_windowssetupandmigration", "_opengl", "_oprec", "_ncd", "_appxpkg", "_opc", "_parcon", "_p2p", "_perf", "_pla", "_picacq", "_input_pointerdevice", "_inputmsg", "_powermeter", "_print", "_proc_snap", "_psapi", "_projfs", "_qos", "_input_radial", "_remoteassist", "_termserv", "_rdc", "_rpc", "_rstmgr", "_rras", "_security", "_winsensors", "_sensors", "_serports", "_smi", "_snmp", "_swdevice", "_spellcheck", "_storage", "_vdswmi", "_stream", "_stg", "_sens", "_sysmon", "_sr", "_base", "_tablet", "_tapi2", "_taskschd", "_tapi3", "_tspi", "_tsf", "_shell", "_win32_tile_badge_notif", "_tcui", "_toolhelp", "_input_touchhittest", "_input_touchinjection", "_wintouch", "_tbs", "_tracelogging", "_upnp", "_buses", "_ual", "_vhd", "_vstor", "_w_graph_fx", "_w_ui_comp", "_webdav", "_websock", "_whqlprov", "_wia", "_winstation", "_winauto", "_wab", "_winmsg", "_uianimation", "_secbiomet", "_mscs", "_wcs", "_wcn", "_wcm", "_wincontacts", "_controls", "_wdacwmiprov", "_wds", "_rdp", "_wer", "_wec", "_wes", "_fwp", "_ics", "_gdi", "_winhttp", "_wic", "_edp", "_msiprov", "_wininet", "_machinelearning", "_wmi_v2", "_wmi", "_wmdm", "_mapi", "_winmessenger", "_mixedreality", "_multimedia", "_wnv", "_wnet", "_properties", "_winrm", "_windowsribbon", "_winrt", "_search", "_wsb", "_winsock", "_wsl", "_winsync", "_winsat", "_wua", "_wsw", "_winlocation_com_ref", "_winsensors_com_ref", "_wlbsprov", "_nfswmi", "_wpdsdk", "_wpdauto", "_wibe", "_xaml_diagnostics", "_xaudio2", "_xblidp", "_xinput", "_ixhr2", "_xps", "winsvc", "processthreadsapi", "sysinfoapi", "console", "errhandlingapi",'winhttp', "memoryapi", 'winuser'
New-Item -ItemType Directory $msftDocsCacheDir -Force
$origin = @{}
$configs = [System.Collections.ArrayList]::new()
$jobId = 0
foreach ($category in $win32Categories)
{
$config = [PSCustomObject]@{
Id = $jobId
Progress = @{}
Category = $Category
TargetPath = [System.IO.Path]::Combine($msftDocsCacheDir, "$Category.json")
}
$origin.($config.Id) = $config
$configs += $config
$jobId++;
}
$syncHashtable = [hashtable]::Synchronized($origin)
$jobs = $configs | ForEach-Object -AsJob -Parallel {
$syncConfig = $using:syncHashtable
$config = $PSItem
$process = $syncConfig[$config.Id].Progress
$process.Id = $config.Id
$process.Activity = "Downloading TOC for $($config.Category)"
$process.Status = "Downloading $($config.Category) to $($config.TargetPath)..."
Invoke-RestMethod "https://docs.microsoft.com/en-us/windows/win32/api/$($config.Category)/toc.json" -OutFile $config.TargetPath
$process.PercentComplete = 100
$process.Completed = $true
}
while ($jobs.State -eq 'Running')
{
$syncHashtable.Keys | Foreach-Object {
if (![string]::IsNullOrEmpty($syncHashtable.$_.Progress.Activity))
{
$param = $syncHashtable.$_.Progress
Write-Progress @param
}
}
Start-Sleep 0.1
}
Write-Host "Finished updating MS docs TOC cache."
}
function Get-MsDocs
{
param(
[string]
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty]
$Query,
[switch]
$Raw
)
begin
{
if (-not (Test-Path $msftDocsCacheDir))
{
throw [System.IO.DirectoryNotFoundException]::new("Microsoft Docs cache has not been created yet. Run Update-MsDocsCache first!")
}
$baseUrl = "https://docs.microsoft.com/en-us/windows/win32/api/undefined_category"
}
process
{
$searchMatches = (Get-ChildItem -Filter "*.json" -File -Path $msftDocsCacheDir | ForEach-Object -ThrottleLimit 8 -Parallel {
Write-Debug $_.FullName
$tocList = Get-Content $_.FullName | ConvertFrom-Json
$expandedChildren = $tocList | Select-Object -ExpandProperty items | Select-Object -ExpandProperty children
if ($expandedChildren | Get-Member -Name children -MemberType NoteProperty)
{
$expandedChildren = $expandedChildren | Select-Object -ExpandProperty children
}
$expandedChildren | Where-Object { $_.toc_title -match $using:Query }
})
if ($searchMatches.Length -eq 0)
{
throw [System.Exception]::new("Matching docs not found.")
}
$finalUrl = ''
$path = ''
Write-Debug "Base URL: $baseUrl"
if ($searchMatches.Length -eq 1)
{
$path = ($searchMatches[0] | Select-Object -exp href)
}
else
{
Write-Host "Found multiple matches:"
$targetArticle = menu ($searchMatches | Select-Object -exp toc_title)
$path = $searchMatches | Where-Object { $_.toc_title -eq $targetArticle } | Select-Object -Unique | Select-Object -exp href | Select-Object -First 1
}
if (-not $path.StartsWith('../'))
{
$path = $path.Insert(0, '../../../..')
}
Write-Debug "Target path: $path"
$finalUrl = [System.IO.Path]::Combine($baseUrl, $path)
Write-Debug "Combined URL: $finalUrl"
}
end
{
$finalUrl = $finalUrl.replace('\', '/')
Write-Debug "Formatted URL: $finalUrl"
if ([uri]::IsWellFormedUriString($finalUrl, [System.UriKind]::RelativeOrAbsolute) -and (-not [string]::IsNullOrEmpty($finalUrl)))
{
Write-Debug "Final URL: $finalUrl"
$Raw ? (Write-Host $finalUrl) : (Start-Process $finalUrl)
}
else
{
throw [System.UriFormatException]::new("$finalUrl is not a valid URI.")
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment