Last active
June 21, 2020 04:16
-
-
Save awsr/189a3381b10e8779f33489556b6b4d1e to your computer and use it in GitHub Desktop.
PowerShell script for monitoring USB device disconnections
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<#PSScriptInfo | |
.VERSION 1.2 | |
.GUID a164a9e6-875a-4be8-a7ef-a69a35873a52 | |
.AUTHOR Awsr | |
#> | |
$global:USBScriptBlock = { | |
$TestUSB = (Get-CimInstance Win32_USBControllerDevice).Dependent.DeviceID | |
if ($TestUSB.Count -lt $global:PnpCount) { | |
[void] $global:USBEvents.TryAdd($TestUSB) | |
} | |
} | |
function global:Get-USBEvents { | |
[CmdletBinding()] | |
Param() | |
Write-Host -ForegroundColor Green "`n--------------------------" | |
Write-Host -ForegroundColor Green "USB device event triggered" | |
Write-Host -ForegroundColor Green "--------------------------`n" | |
Write-Host "Waiting..." | |
# Brief pause to make sure we're not stopping too early (extreme edge case) | |
Start-Sleep -Seconds 1 | |
# Pause monitoring | |
$global:Timer.Stop() | |
Unregister-Event USBDebugging | |
# Initialize empty objects | |
$InnerResults = [System.Collections.Generic.Dictionary[string, string]]::new() | |
$global:WorkingData = @() | |
# Begin processing | |
Write-Host -ForegroundColor Green "Processing $($USBEvents.Count) events...`n" | |
while ($USBEvents.TryDequeue([ref]$WorkingData)) { | |
$BaselineDeviceIds | ForEach-Object { | |
# Check for missing devices | |
if (-not $WorkingData.Contains($_) -and -not $InnerResults.ContainsKey($_)) { | |
$InnerResults.Add($_, $PnpDefinitions[$_]) | |
} | |
} | |
} | |
$global:Results.Add($InnerResults) | |
Write-Host -ForegroundColor Green "Results available in `$Results[$ResultsCount]" | |
$global:ResultsCount++ | |
$InnerResults | Out-Host | |
if ($KeepVal) { | |
Register-CimIndicationEvent -ClassName Win32_DeviceChangeEvent -SourceIdentifier USBDebugging -Action $global:USBScriptBlock | Out-Null | |
$global:Timer.Start() | |
} | |
else { | |
Unregister-Event USBEventChecker | |
$global:Timer.Close() | |
Write-Host "(If the prompt isn't showing below this, just press Enter)" | |
} | |
} | |
<# | |
.DESCRIPTION | |
Script to monitor for USB device disconnections. Will probably rewrite this using Runspaces if the response time isn't fast enough. | |
.PARAMETER PnpDetails | |
Open a GridView window showing detailed information for all PnP devices on the system. | |
Not recommended while monitoring is active. | |
.PARAMETER Keep | |
Keep monitoring after getting results. | |
#> | |
function global:Start-USBMonitor { | |
[CmdletBinding()] | |
Param( | |
[Parameter()] | |
[switch]$PnpDetails, | |
[Parameter()] | |
[switch]$Keep | |
) | |
$global:KeepVal = $Keep | |
if ($PnpDetails) { | |
# Warning: Lots of data to render, so vertical scrolling is CPU-heavy | |
Get-PnpDevice | Select-Object -Property * | Out-GridView | |
return | |
} | |
# Cleanup from previous runs in current session if run multiple times | |
if ((Get-Job).Count -gt 0 -or (Get-EventSubscriber).Count -gt 0) { | |
Unregister-Event -SourceIdentifier USBDebugging -ErrorAction Ignore | |
Unregister-Event -SourceIdentifier USBEventChecker -ErrorAction Ignore | |
Remove-Job -Name USBDebugging, USBEventChecker -Force -ErrorAction Ignore | |
if (Test-Path Variable:Global:Timer) { | |
$global:Timer.Close() | |
} | |
} | |
$global:Results = [System.Collections.Generic.List[System.Collections.Generic.Dictionary[string, string]]]::new() | |
$global:ResultsCount = 0 | |
# Get current list of USB devices | |
$BaselineUSB = Get-CimInstance Win32_USBControllerDevice | |
$global:BaselineDeviceIds = $BaselineUSB.Dependent.DeviceID | |
# Initialize hashtable | |
$global:PnpDefinitions = @{} | |
# Get PnP device names | |
Get-PnpDevice | ForEach-Object { | |
if ($_.DeviceID -in $BaselineDeviceIds) { | |
$global:PnpDefinitions[$_.DeviceID] = $_.Name | |
} | |
} | |
if ($BaselineDeviceIds.Count -ne $PnpDefinitions.Count) { | |
Write-Warning "Some names could not be resolved." | |
} | |
# Save to variable for speed | |
[int]$global:PnpCount = $PnpDefinitions.Count | |
# Setup collection using a thread-safe type as a precaution due to potentially multiple events running very quickly | |
$global:USBEvents = [System.Collections.Concurrent.ConcurrentQueue[object]]::new() | |
# Check devices on change event and add to queue for processing if a device is missing | |
Register-CimIndicationEvent -ClassName Win32_DeviceChangeEvent -SourceIdentifier USBDebugging -Action $global:USBScriptBlock | Out-Null | |
# ---------------------------------------- | |
if (-not (Test-Path Variable:Global:Timer) -or -not $global:Timer.Enabled) { | |
# Timer setup (making global to ensure it's always available for the function) | |
$global:Timer = [System.Timers.Timer]::new(10000) | |
$global:Timer.AutoReset = $true | |
} | |
# Check queue on timer interval | |
Register-ObjectEvent -InputObject $global:Timer -EventName Elapsed -SourceIdentifier USBEventChecker -Action { | |
if ($USBEvents.Count -gt 0) { | |
& Get-USBEvents | |
} | |
} | Out-Null | |
# Activate timer | |
$global:Timer.Start() | |
Write-Host -ForegroundColor Green "Waiting for Win32_DeviceChangeEvent" | |
Write-Host "Note: Script should be restarted if new devices are added while this is running (you can just run Start-USBMonitoring again)" | |
Write-Host "Event queue will be checked every 10 seconds." | |
if ($Keep) { | |
Write-Host -ForegroundColor Yellow "Use the command `"stopit`" to stop monitoring" | |
} | |
} | |
function global:stopit { | |
Unregister-Event USBDebugging -ErrorAction Ignore | |
Unregister-Event -SourceIdentifier USBEventChecker -ErrorAction Ignore | |
Remove-Job -Name USBDebugging, USBEventChecker -Force -ErrorAction Ignore | |
if (Test-Path Variable:Global:Timer) { | |
$global:Timer.Close() | |
} | |
Write-Host "USB monitoring stopped" | |
} | |
Write-Host -NoNewLine "To run monitoring, type " | |
Write-Host -NoNewline -ForegroundColor Green "Start-USBMonitor" | |
Write-Host -NoNewline " or " | |
Write-Host -NoNewline -ForegroundColor Green "Start-USBMonitor -Keep" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment