Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save eladkarako/730d692364a8626ef5942949c74ab349 to your computer and use it in GitHub Desktop.

Select an option

Save eladkarako/730d692364a8626ef5942949c74ab349 to your computer and use it in GitHub Desktop.
pnp devices. all imaging image capturing capable devices. ps1 powershell windows. class image. wmi object Win32_PnPEntity. properties sorted. json file output.

how about that,
i was trying to install the printer using USB cable,
but it was unable to register scan task in the printer!

after 10 uninstalls in some variations,
where i don't understand why the UI has missing sections,
and trying to run the exe for registring the computer in the printer manually,
nothing worked,

the uninstall was deep, and included also removing all HP related folders using the search programs everything.
and registry cleanup, including old devices.

it was an issue removing scheduled tasks, startup run items (resolved)
and the old hp drivers (i just left them).

after installation from the thin (~20mb) installer instead of the full 300mb one,
letting it download everything,
since it also doing stuff...
"figuring device capabilities"

a 10+ minutes of waiting in front of white screen,
the setup finally managed to look for the printer and install all the drivers.

then i figured that the DHCP of the router wasn't preserving the IP of the printer,
and the installation is HARD CODING THE IP!!! in various registry entries as well as properties of the printer...

so... I was entering asap to the embedded-webpage of the printer, that allows managing it,
in my case http://10.0.0.5
firefox was able to enter it, with warning,

under network i managed to change its HOSTNAME ("שם מארח") - https://10.0.0.5/#hId-pgNetworkSummary
I made sure there - there is no hard-coding of IP, just DHCP... i forced just 5ghz under https://10.0.0.5/#hId-pgWirelessBandFrequency

I used the name from the Bonjour protocol - https://10.0.0.5/#hId-pgBonjour it is kind of random i think 'HPC8AAAAAAAAAAA' but just in case i'll obfuscate its content

and - probably my mistake i made sure everything here was enabled:
https://10.0.0.5/#hId-pgDHCP (the DHCPv4 was unchecked)

you will be notified that the printer will be disconnected and the certificate for the embedded webpage will be re-written..

now that you've set the hostname properly you can access the web-page using "http://HPC8AAAAAAAAAAA"

AND!!!!!

use this instead of IP in your computer,
this is a workaround for hard coded IP.

you'll find a Location value under those values:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers\HP OfficeJet Pro 8020 series PCL-3 (Network)
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers\HP OfficeJet Pro 8020 series PCL-3 (Network)\DsSpooler
HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows NT\CurrentVersion\Print\Printers\HP OfficeJet Pro 8020 series PCL-3 (Network)
HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows NT\CurrentVersion\Print\Printers\HP OfficeJet Pro 8020 series PCL-3 (Network)\DsSpooler

this is where i replaced the ip 10.0.0.1 or 10.0.0.5 with the hostname
"http://HPAAAAAAAAAAA:3911/"
obviously, you can not predict what the model and number will be in your installation, so visit few keys before
"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers"
and try to figure out what your printer is...

I also found this:
"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class{6bdd1fc6-810f-11d0-bec7-08002be2092f}\0001\DeviceData" with "NetworkDeviceID" pointing to "\hostname:10.0.0.1\ipaddr:10.0.0.1\guid:urn:uuid:e13d7e55-3d02-557b-807b-90ff21186e82\macaddr:c85acfce519f\port:1"
as you can see the IP is hard coded by the installation, as hostname,
I had to both fix the hostname as well as change the IP, which I assume sometime when the device will disconnect the router will re-assign a new one...
but I hope the DHCP will allow me to always resolve the HOSTNAME correctly so I updated the hostname and the ip in "NetworkDeviceID"
to "\hostname:HPAAAAAAAAAAA\ipaddr:10.0.0.5\guid:urn:uuid:e13d7e55-3d02-557b-807b-90ff21186e82\macaddr:c85acfce519f\port:1"

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\SWD\PRINTENUM{E67D9B3B-6190-463F-A39D-E3531E87256C} had "LocationInformation"
which I also updated to "http://HPC85ACFCE519E:3911/"

as well as
"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\SWD\PRINTENUM\WSD-17d495ba-e941-4b8b-a443-3baf24e2759a"

eventually I found the registry keys by searching both the current (to the router session)
and the old installation values

(I did found an old 10.0.0.11 when searched 10.0.0.1, so be carefull!!!)

note:
I had to also search (using the everything client)
for any (small) file that might have the ip hard coded..
size:2..100kb content:10.0.0.5
size:2..100kb content:10.0.0.1
the search (the entire computer is very slow).


there is also a program in the UI that handles updating IP address,
for the installation in the computer.

in my case it was using the old 10.0.0.1

its command line (from the main manager application) is
"C:\Program Files\HP\HP OfficeJet Pro 8020 series\Bin\DeviceSetup.exe" /user "ELADCOM\Elad" /changeip "" /prfn "HP OfficeJet Pro 8020 series PCL-3 (Network)"

sadly, it told me "we currently have issues updating the ip in your device, try to reboot"....


alright what i did was, under tools, choose to connect "new device",
which i choose wireless (first option),
next, i chose to manually set the device, and entered exactly the hostname,
which seems to use the same installation of the previous device.
it doesn't seems to be fully via registry, so I started looking around,

and C:\ProgramData\HP\HP OfficeJet Pro 8020 series\NetworkDevices
has a single ini file with the device name there.
not sure what it does,
but the details there seems to be the most recent, i.e. including the hostname and the ip.

# what would normally take couple of hours, was very efficiently programmed under 20 minutes, with the help of https://duck.ai/chat
# initially I was looking for the information I could have got for the combined device HP Officeset.
# to figure out if I could have registered the "notify to device" TWAIN message,
#
# placed in the registry, under:
# - key: HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run
# - value: HP OfficeJet Pro xxxx series
# - data: "C:\Program Files\HP\HP OfficeJet Pro xxxx series\Bin\ScanToPCActivationApp.exe" -deviceID "XXXXXXXXXX:XX" -scfn "HP OfficeJet Pro XXXX series" -AutoStart 1
#
# both the human readable title of the printer as well as the device id are somewhat manually enumeratable
# you have to access all devices and printers under control panel
# then your printer/scanner/imager
# its properties will detail multiple entries, from which you need to click the actual imager device,
# in the properties dialog you have to select the "PnP-X serial number" from the drop down combo-box, to view the value
# to this you add :NW for some reason
# so if the value is "TH1234S56K" you should use "TH1234S56K:NW"
# I guess it is a suffix for something, or I did not populate this propertie.
#
# I suggest you'll keep, export the registry entry, and make it into a cmd, and launch it when your device does not show your computer.
# as well as export the JSON using this code as soon as possible after you'll finish uninstalling the generic Microsoft Store HP app
# and install the (originally meant for Windows 10 but fully compatible with Windows 11) full driver and programs from hp support page.
# I also suggest you would not change the name or title or anything related to naming the printer in your devices.
#
# hp combined devices includes printer, scanner (both feeder and "glass"), fax machine and 2 side print
# it is a little hassle to set up.
# and the thing you've just noticed is that each installation is unique,
# it is not unlikely that some of the worst installation cases will leave a computer so broken that you'll need to re-install windows.
#
# at least you can tweak their web-based "manager" (i.e. foce change the language of the ui) since it is hta, some html and javascript.
#
# 202604070235 EladKarako
#
#
# -----------------------------------------------------------------------------
# Natural comparer implemented as a PowerShell class.
# Implements System.Collections.IComparer so we can pass an object to ArrayList.Sort
# The comparer performs "natural" / "numeric-aware" comparison so that:
# "item1" < "item2" < "item10" < "item20"
# -----------------------------------------------------------------------------
class NaturalComparer : System.Collections.IComparer {
# constructor (no-op)
NaturalComparer() { }
# Compare method required by IComparer
[int] Compare([object] $x, [object] $y) {
# Convert inputs to strings
$a = [string]$x
$b = [string]$y
# quick equality check
if ($a -eq $b) { return 0 }
# tokenization: split string into alternating numeric and non-numeric segments
$tokenize = {
param($s)
[System.Text.RegularExpressions.Regex]::Matches(
$s,
'\d+|\D+'
) | ForEach-Object { $_.Value }
}
# get token arrays for both values
$ta = $tokenize.Invoke($a)
$tb = $tokenize.Invoke($b)
# compare segment by segment
$len = [Math]::Max($ta.Count, $tb.Count)
for ($i = 0; $i -lt $len; $i++) {
# segment or empty if beyond length
$sa = if ($i -lt $ta.Count) { $ta[$i] } else { '' }
$sb = if ($i -lt $tb.Count) { $tb[$i] } else { '' }
# identical segment -> continue
if ($sa -eq $sb) { continue }
# detect numeric segments
$isNumA = [System.Text.RegularExpressions.Regex]::IsMatch($sa, '^\d+$')
$isNumB = [System.Text.RegularExpressions.Regex]::IsMatch($sb, '^\d+$')
if ($isNumA -and $isNumB) {
# both numeric: compare as integers (use Int64 for safety)
$na = [int64]$sa
$nb = [int64]$sb
if ($na -lt $nb) { return -1 }
elseif ($na -gt $nb) { return 1 }
else { continue } # numerically equal (rare if padded zeros etc.)
} elseif ($isNumA) {
# numeric segments sort before non-numeric segments
return -1
} elseif ($isNumB) {
return 1
} else {
# both non-numeric: do a case-insensitive culture-invariant compare
$cmp = [string]::Compare($sa, $sb, [System.StringComparison]::InvariantCultureIgnoreCase)
if ($cmp -ne 0) { return $cmp }
}
}
# if we fell through, treat as equal
return 0
}
}
# -----------------------------------------------------------------------------
# Sort-Natural: helper function that sorts an array of strings using NaturalComparer
# Returns an ordered array (ArrayList converted back to pipeline-safe output)
# -----------------------------------------------------------------------------
function Sort-Natural([string[]] $arr) {
# handle empty / null input
if (-not $arr) { return @() }
# use ArrayList to get a Sort method that accepts an IComparer instance
$alist = [System.Collections.ArrayList]::new()
$null = $alist.AddRange($arr)
# sort using our NaturalComparer
$alist.Sort([NaturalComparer]::new())
# return as array (comma to ensure array not enumerated as items in pipeline)
return ,$alist
}
# -----------------------------------------------------------------------------
# Main: enumerate PnP imaging devices, preserve arrays as JSON arrays,
# sort all property keys naturally, and export to JSON.
#
# Notes:
# - Excludes heavy CIM/WMI blob properties from PSObject.Properties
# (CimClass, CimInstance, CimSystemProperties, CimInstanceProperties)
# - Preserves array-typed property values as arrays in the resulting JSON
# - Collects PnP properties via Get-PnpDeviceProperty and includes them under
# "PnpProperties" as an array of objects: { Key, Data }
# - Writes full JSON to: $env:USERPROFILE\Desktop\ImagingDevices_Full.json
# -----------------------------------------------------------------------------
Get-PnpDevice -Class Image -ErrorAction SilentlyContinue | ForEach-Object {
# current device object
$device = $_
# collect PSObject properties, filtering out known CIM/WMI blobs
$props = $device.PSObject.Properties |
Where-Object {
$_.Name -notin @(
'CimClass',
'CimInstance',
'CimSystemProperties',
'CimInstanceProperties'
)
}
# extract property names and sort them naturally
$sortedNames = Sort-Natural ($props.Name)
# create an ordered hashtable to preserve property order in JSON output
$base = [ordered]@{}
# iterate sorted property names and add them to the ordered hashtable
foreach ($name in $sortedNames) {
# find the matching property object (first match)
$prop = $props | Where-Object { $_.Name -eq $name } | Select-Object -First 1
# raw value
$val = $prop.Value
# preserve arrays as arrays for JSON output
if ($val -is [System.Array]) {
# build a new array while converting complex objects to strings
$arr = @()
foreach ($item in $val) {
if (
$item -is [System.Management.ManagementBaseObject] -or
$item -is [Microsoft.Management.Infrastructure.CimInstance]
) {
# convert complex management/CIM objects to their string representations
$arr += ($item.ToString())
} else {
# keep primitive items unchanged
$arr += $item
}
}
# assign the array to the ordered hashtable
$base[$name] = $arr
} elseif (
$val -is [System.Management.ManagementBaseObject] -or
$val -is [Microsoft.Management.Infrastructure.CimInstance]
) {
# convert single complex management/CIM object to string
$base[$name] = $val.ToString()
} else {
# primitive value (string/number/bool/null) — keep as-is
$base[$name] = $val
}
}
# -----------------------------------------------------------------------------
# Collect PnP properties (DEVPKEYs) for this device.
# Each PnP property is represented as an object: { Key = <key>, Data = <value> }
# If Data is an array, we preserve it as an array.
# -----------------------------------------------------------------------------
$pnpProps = Get-PnpDeviceProperty -InstanceId $device.InstanceId -ErrorAction SilentlyContinue | ForEach-Object {
$data = $_.Data
if ($data -is [System.Array]) {
# preserve the array items directly
$arr = @()
foreach ($i in $data) {
$arr += $i
}
$d = $arr
} else {
# single value
$d = $data
}
# return an object with Key/Data shape
[PSCustomObject]@{
Key = $_.Key
Data = $d
}
}
# -----------------------------------------------------------------------------
# If any PnP properties exist, sort them by Key using natural sorting,
# then reorder the objects to match the sorted keys.
# -----------------------------------------------------------------------------
if ($pnpProps) {
# get sorted list of keys
$sortedPnpKeys = Sort-Natural ($pnpProps | Select-Object -ExpandProperty Key)
# reorder PnP property objects in the same order as sorted keys
$pnpOrdered = foreach ($k in $sortedPnpKeys) {
$pnpProps | Where-Object { $_.Key -eq $k } | Select-Object -First 1
}
} else {
# no pnp properties found for this device
$pnpOrdered = @()
}
# add the ordered PnP properties array under the "PnpProperties" key
$base['PnpProperties'] = $pnpOrdered
# output the device as a PSCustomObject (ordered) so ConvertTo-Json preserves order
[PSCustomObject]$base
} | ConvertTo-Json -Depth 20 | Out-File -FilePath "$env:USERPROFILE\Desktop\ImagingDevices_Full.json" -Encoding UTF8
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment