Small app/script that will warn if an (external) drive is not connected


Was that last post a mistype?

The batch script's normal window appears and gets minimised to the Taskbar in a fraction of a second where it stays for as long as the script is running.

When I get around to it, I will resume my experiments on this subject.
I like to use Task scheduler to run batch scripts minimised without any normal window appearing.
- VBS does this successfully. I run a VBS from Task scheduler that calls my batch script minimised so there is not even a flash of a normal sized window.
- I think NirCmd can also do this. So I'll try running a NirCmd command from Task scheduler that calls my batch script minimised. I think I have already tried this but cannot find any records of the experiment.
- I don't think PS can do it.


Denis
 

My Computer

System One

  • OS
    Windows 11 Home x64 Version 23H2 Build 22631.3447
Was that last post a mistype?
The minimized icon = the minimized Powershell window's icon on the taskbar of course. (And yes, the actual window itself isn't invisible either, as has already been correctly explained in the post I quoted.)
 

My Computers

System One System Two

  • OS
    11 Home
    Computer type
    Laptop
    Manufacturer/Model
    Asus TUF Gaming (2024)
    CPU
    i7 13650HX
    Memory
    16GB DDR5
    Graphics Card(s)
    GeForce RTX 4060 Mobile
    Sound Card
    Eastern Electric MiniMax DAC Supreme; Emotiva UMC-200; Astell & Kern AK240
    Monitor(s) Displays
    Sony Bravia XR-55X90J
    Screen Resolution
    3840×2160
    Hard Drives
    512GB SSD internal
    37TB external
    PSU
    Li-ion
    Cooling
    2× Arc Flow Fans, 4× exhaust vents, 5× heatpipes
    Keyboard
    Logitech K800
    Mouse
    Logitech G402
    Internet Speed
    20Mbit/s up, 250Mbit/s down
    Browser
    FF
  • Operating System
    11 Home
    Computer type
    Laptop
    Manufacturer/Model
    Medion S15450
    CPU
    i5 1135G7
    Memory
    16GB DDR4
    Graphics card(s)
    Intel Iris Xe
    Sound Card
    Eastern Electric MiniMax DAC Supreme; Emotiva UMC-200; Astell & Kern AK240
    Monitor(s) Displays
    Sony Bravia XR-55X90J
    Screen Resolution
    3840×2160
    Hard Drives
    2TB SSD internal
    37TB external
    PSU
    Li-ion
    Mouse
    Logitech G402
    Keyboard
    Logitech K800
    Internet Speed
    20Mbit/s up, 250Mbit/s down
    Browser
    FF
When you create a new task in Task Scheduler, you can go to the Triggers tab and follow the steps in this article: Trigger Windows Scheduled Task to Run Upon the Successful Completion of Another Scheduled Task
(For our intended purpose, you can just ignore the part about FirstJob/SecondJob and everything before that at the beginning of the article.)
So when you get to the point where you need to enter the XPath of the event filter, you can ignore the rest of the article all the way to the end and just enter this one instead:
XML:
<QueryList>
  <Query Id="0" Path="Microsoft-Windows-Partition/Diagnostic">
    <Select Path="Microsoft-Windows-Partition/Diagnostic">*[System[Provider[@Name='Microsoft-Windows-Partition'] and EventID=1006]] and
      *[EventData[Data[@Name='ParentId']='USB\VID_13FE&amp;PID_6023\07077314B7A1F221' and Data[@Name='Capacity']=0]]</Select>
  </Query>
</QueryList>
...where USB\VID_13FE&amp;PID_6023\07077314B7A1F221 is the Parent ID of the USB Mass Storage Device in question, but the & is replaced with &amp; in it. (I am assuming that the removable drive you use is a USB Mass Storage Device.) I haven't tested this yet BTW... normally it should work.

To find the Parent ID of the device, you can go in Event Viewer | Applications and Services Logs | Microsoft | Windows | Partition | Diagnostic, and, in there, use the Search option (under Actions in the right pane) to search for anything related to USB. Choose the Details tab to inspect the data stored in the event; just continue to search again if needed until you find the USB device that you want, and copy its Parent ID to clipboard.
 
Last edited:

My Computers

System One System Two

  • OS
    11 Home
    Computer type
    Laptop
    Manufacturer/Model
    Asus TUF Gaming (2024)
    CPU
    i7 13650HX
    Memory
    16GB DDR5
    Graphics Card(s)
    GeForce RTX 4060 Mobile
    Sound Card
    Eastern Electric MiniMax DAC Supreme; Emotiva UMC-200; Astell & Kern AK240
    Monitor(s) Displays
    Sony Bravia XR-55X90J
    Screen Resolution
    3840×2160
    Hard Drives
    512GB SSD internal
    37TB external
    PSU
    Li-ion
    Cooling
    2× Arc Flow Fans, 4× exhaust vents, 5× heatpipes
    Keyboard
    Logitech K800
    Mouse
    Logitech G402
    Internet Speed
    20Mbit/s up, 250Mbit/s down
    Browser
    FF
  • Operating System
    11 Home
    Computer type
    Laptop
    Manufacturer/Model
    Medion S15450
    CPU
    i5 1135G7
    Memory
    16GB DDR4
    Graphics card(s)
    Intel Iris Xe
    Sound Card
    Eastern Electric MiniMax DAC Supreme; Emotiva UMC-200; Astell & Kern AK240
    Monitor(s) Displays
    Sony Bravia XR-55X90J
    Screen Resolution
    3840×2160
    Hard Drives
    2TB SSD internal
    37TB external
    PSU
    Li-ion
    Mouse
    Logitech G402
    Keyboard
    Logitech K800
    Internet Speed
    20Mbit/s up, 250Mbit/s down
    Browser
    FF
Code:
Get-CimInstance -ClassName 'Win32_DiskDrive' -Namespace 'root\cimv2' -Filter 'InterfaceType = ''USB''' |
ForEach-Object {
    $diskDrive = $_
    [PSCustomObject]@{
        Drive = [string](@(Get-CimAssociatedInstance -ResultClassName 'Win32_DiskPartition' -InputObject $diskDrive |
                Get-CimAssociatedInstance -ResultClassName 'Win32_LogicalDisk' |
                Sort-Object -Property 'DeviceID' | Select-Object -ExpandProperty 'DeviceID') -join ', ')
        Model = [string]$_.Model
        DeviceID = [string]$_.PNPDeviceID
    }
} | Format-Table -AutoSize
I modified your code to add the Parent ID as an additional column.
Powershell:
Get-CimInstance -ClassName 'Win32_DiskDrive' -Namespace 'root\cimv2' -Filter 'InterfaceType = ''USB''' |
ForEach-Object {
    $diskDrive = $_
    Get-PnpDevice -PresentOnly |? {$_.Class -match 'DiskDrive' -and $_.InstanceId -match $diskDrive.InstanceId} |Get-PnpDeviceProperty -KeyName 'DEVPKEY_Device_Parent' |% {$parent=$_.Data}
    [PSCustomObject]@{
        Drive = [string](@(Get-CimAssociatedInstance -ResultClassName 'Win32_DiskPartition' -InputObject $diskDrive |
                Get-CimAssociatedInstance -ResultClassName 'Win32_LogicalDisk' |
                Sort-Object -Property 'DeviceID' | Select-Object -ExpandProperty 'DeviceID') -join ', ')
        Model = [string]$_.Model
        DeviceID = [string]$_.PNPDeviceID
        ParentID = [string]$parent
    }
} | Out-GridView
 
Last edited:

My Computers

System One System Two

  • OS
    11 Home
    Computer type
    Laptop
    Manufacturer/Model
    Asus TUF Gaming (2024)
    CPU
    i7 13650HX
    Memory
    16GB DDR5
    Graphics Card(s)
    GeForce RTX 4060 Mobile
    Sound Card
    Eastern Electric MiniMax DAC Supreme; Emotiva UMC-200; Astell & Kern AK240
    Monitor(s) Displays
    Sony Bravia XR-55X90J
    Screen Resolution
    3840×2160
    Hard Drives
    512GB SSD internal
    37TB external
    PSU
    Li-ion
    Cooling
    2× Arc Flow Fans, 4× exhaust vents, 5× heatpipes
    Keyboard
    Logitech K800
    Mouse
    Logitech G402
    Internet Speed
    20Mbit/s up, 250Mbit/s down
    Browser
    FF
  • Operating System
    11 Home
    Computer type
    Laptop
    Manufacturer/Model
    Medion S15450
    CPU
    i5 1135G7
    Memory
    16GB DDR4
    Graphics card(s)
    Intel Iris Xe
    Sound Card
    Eastern Electric MiniMax DAC Supreme; Emotiva UMC-200; Astell & Kern AK240
    Monitor(s) Displays
    Sony Bravia XR-55X90J
    Screen Resolution
    3840×2160
    Hard Drives
    2TB SSD internal
    37TB external
    PSU
    Li-ion
    Mouse
    Logitech G402
    Keyboard
    Logitech K800
    Internet Speed
    20Mbit/s up, 250Mbit/s down
    Browser
    FF
I wrote a PowerShell script which makes monitoring USB devices (not volumes) an easy task.

A named volume or a "cookie" file can disappear for various reasons, like if you reformat the drive or delete a volume. But a full DeviceID is unique because it includes the device's Serial Number.

You can register a WMIEvent trigger which only lasts while a PS script is running. When the script ends, the event trigger disappears. To make it persistent, my script creates a background process which does the real work, and creates a scheduled task to start the watcher on reboot. Scanning drives will take a few seconds (depending on how many devices). The background script runs as Administrator, but is hidden by an invisible VBS wrapper.

You check which USB drives (fixed or removable) to watch and hit Update. To stop all monitoring, click on the Clear button. Clear stops all background processes, and deletes the scheduled task.

1709145956150.png

If you don't run the script as Administrator, it will ask for elevation rights.
The tool uses mshta to create the pop-up dialogs, instead of msg (which doesn't exist on Windows Home). The pop-up's time out after 24 hours, in case something happens while you're away from the screen.

1709146144600.png

PS: Drive manufacturers use a decimal number for drive sizes, and not a power of 2. But sometimes the weird math gets you a "518 MB" flash drive.
 

Attachments

  • USB_Drive_Monitor.bat
    9.5 KB · Views: 2
Last edited:

My Computer

System One

  • OS
    Windows 7
The full Device ID is not unique. That's why the PnP manager also relies on Instance ID and Hardware Instance ID to uniquely identify instances of each device that are installed in a computer, albeit in some special situations (and that tend to be a lot more complex) the resulting Device Instance ID still isn't unique either.
 

My Computers

System One System Two

  • OS
    11 Home
    Computer type
    Laptop
    Manufacturer/Model
    Asus TUF Gaming (2024)
    CPU
    i7 13650HX
    Memory
    16GB DDR5
    Graphics Card(s)
    GeForce RTX 4060 Mobile
    Sound Card
    Eastern Electric MiniMax DAC Supreme; Emotiva UMC-200; Astell & Kern AK240
    Monitor(s) Displays
    Sony Bravia XR-55X90J
    Screen Resolution
    3840×2160
    Hard Drives
    512GB SSD internal
    37TB external
    PSU
    Li-ion
    Cooling
    2× Arc Flow Fans, 4× exhaust vents, 5× heatpipes
    Keyboard
    Logitech K800
    Mouse
    Logitech G402
    Internet Speed
    20Mbit/s up, 250Mbit/s down
    Browser
    FF
  • Operating System
    11 Home
    Computer type
    Laptop
    Manufacturer/Model
    Medion S15450
    CPU
    i5 1135G7
    Memory
    16GB DDR4
    Graphics card(s)
    Intel Iris Xe
    Sound Card
    Eastern Electric MiniMax DAC Supreme; Emotiva UMC-200; Astell & Kern AK240
    Monitor(s) Displays
    Sony Bravia XR-55X90J
    Screen Resolution
    3840×2160
    Hard Drives
    2TB SSD internal
    37TB external
    PSU
    Li-ion
    Mouse
    Logitech G402
    Keyboard
    Logitech K800
    Internet Speed
    20Mbit/s up, 250Mbit/s down
    Browser
    FF
External HDD's will report their S/N under PNPDeviceId. But I agree USB flash drives probably aren't unique enough in this method.

For the purposes of what was originally asked, the script is enough. Unless they have a stack of identical model flash drives mounted at the same time.
 

My Computer

System One

  • OS
    Windows 7
For the purposes of what was originally asked, the Parent ID should also be enough. In the example I gave, you can see that it includes the VendorID, ProductID, and Serial Number for the USB flash drive:
USB\VID_13FE&PID_6023\07077314B7A1F221

As lightweight as the script may be, having it running in the background constantly is a bit overkill when you already have scheduled a task in Task Scheduler that only runs each time when the event occurs in the event log that will be monitored directly by Task Scheduler itself, due to the custom XPath event query that I earlier posted.
 

My Computers

System One System Two

  • OS
    11 Home
    Computer type
    Laptop
    Manufacturer/Model
    Asus TUF Gaming (2024)
    CPU
    i7 13650HX
    Memory
    16GB DDR5
    Graphics Card(s)
    GeForce RTX 4060 Mobile
    Sound Card
    Eastern Electric MiniMax DAC Supreme; Emotiva UMC-200; Astell & Kern AK240
    Monitor(s) Displays
    Sony Bravia XR-55X90J
    Screen Resolution
    3840×2160
    Hard Drives
    512GB SSD internal
    37TB external
    PSU
    Li-ion
    Cooling
    2× Arc Flow Fans, 4× exhaust vents, 5× heatpipes
    Keyboard
    Logitech K800
    Mouse
    Logitech G402
    Internet Speed
    20Mbit/s up, 250Mbit/s down
    Browser
    FF
  • Operating System
    11 Home
    Computer type
    Laptop
    Manufacturer/Model
    Medion S15450
    CPU
    i5 1135G7
    Memory
    16GB DDR4
    Graphics card(s)
    Intel Iris Xe
    Sound Card
    Eastern Electric MiniMax DAC Supreme; Emotiva UMC-200; Astell & Kern AK240
    Monitor(s) Displays
    Sony Bravia XR-55X90J
    Screen Resolution
    3840×2160
    Hard Drives
    2TB SSD internal
    37TB external
    PSU
    Li-ion
    Mouse
    Logitech G402
    Keyboard
    Logitech K800
    Internet Speed
    20Mbit/s up, 250Mbit/s down
    Browser
    FF
The batch script's normal window appears and gets minimised to the Taskbar in a fraction of a second where it stays for as long as the script is running.

- I don't think PS can do it.
If you are using PS, PS can do it

HideConsole.ps1
Powershell:
Add-Type -MemberDefinition '[DllImport("User32.dll")]public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);' -Namespace Win32 -Name Functions
$HideConsoleWindow=[Win32.Functions]::ShowWindow((Get-Process -Id $PID).MainWindowHandle,0)
start-sleep 3
. notepad.exe
Exit
 

My Computer

System One

  • OS
    PE
As lightweight as the script may be, having it running in the background constantly is a bit overkill when you already have scheduled a task in Task Scheduler that only runs each time when the event occurs in the event log that will be monitored directly by Task Scheduler itself, due to the custom XPath event query that I earlier posted.
The scheduled task only exists to resume the watcher script on reboot. If I didn't make it clear, you would only run the UI script to update or clear the device list. Otherwise, you can close it and the actual watcher runs in the background. I do kill off any PS instances of the watcher script before firing up a replacement process.
 

My Computer

System One

  • OS
    Windows 7
Bob,

You are quoting two lines that are about two different topics.
So I don't know what "it" refers to and therefore what your PS1 achieves.
PS can do it

Your PS1 brings up a normal PS window that is eventually closed when the PS completes and a normal Notepad window appears.
Neither are minimised.


All the best,
Denis
 

My Computer

System One

  • OS
    Windows 11 Home x64 Version 23H2 Build 22631.3447
If you are using PS, PS can do it

HideConsole.ps1
Powershell:
Add-Type -MemberDefinition '[DllImport("User32.dll")]public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);' -Namespace Win32 -Name Functions
$HideConsoleWindow=[Win32.Functions]::ShowWindow((Get-Process -Id $PID).MainWindowHandle,0)
start-sleep 3
. notepad.exe
Exit
The problem with this solution (which I've tried), is you need to run a PS script. While this script can use MainWindowHandle to hide itself, there's still a brief window flash.

Shorter than normal, but there. The gold standard remains the "invisible" VBS wrapper.
 

My Computer

System One

  • OS
    Windows 7

My Computer

System One

  • OS
    Windows 11 Home x64 Version 23H2 Build 22631.3447
The scheduled task only exists to resume the watcher script on reboot. If I didn't make it clear, you would only run the UI script to update or clear the device list. Otherwise, you can close it and the actual watcher runs in the background. I do kill off any PS instances of the watcher script before firing up a replacement process.
I am afraid you missed my point entirely. A 'watcher' script that keeps running in the background waiting for the event to occur is not required for any of this, as Task Scheduler itself already has that capability like I previously explained. It's baked into Windows by Microsoft.
 

My Computers

System One System Two

  • OS
    11 Home
    Computer type
    Laptop
    Manufacturer/Model
    Asus TUF Gaming (2024)
    CPU
    i7 13650HX
    Memory
    16GB DDR5
    Graphics Card(s)
    GeForce RTX 4060 Mobile
    Sound Card
    Eastern Electric MiniMax DAC Supreme; Emotiva UMC-200; Astell & Kern AK240
    Monitor(s) Displays
    Sony Bravia XR-55X90J
    Screen Resolution
    3840×2160
    Hard Drives
    512GB SSD internal
    37TB external
    PSU
    Li-ion
    Cooling
    2× Arc Flow Fans, 4× exhaust vents, 5× heatpipes
    Keyboard
    Logitech K800
    Mouse
    Logitech G402
    Internet Speed
    20Mbit/s up, 250Mbit/s down
    Browser
    FF
  • Operating System
    11 Home
    Computer type
    Laptop
    Manufacturer/Model
    Medion S15450
    CPU
    i5 1135G7
    Memory
    16GB DDR4
    Graphics card(s)
    Intel Iris Xe
    Sound Card
    Eastern Electric MiniMax DAC Supreme; Emotiva UMC-200; Astell & Kern AK240
    Monitor(s) Displays
    Sony Bravia XR-55X90J
    Screen Resolution
    3840×2160
    Hard Drives
    2TB SSD internal
    37TB external
    PSU
    Li-ion
    Mouse
    Logitech G402
    Keyboard
    Logitech K800
    Internet Speed
    20Mbit/s up, 250Mbit/s down
    Browser
    FF
Forgot about that. I had a different script that did something similar, but frankly I hated trying to decipher which Event ID's and doing the "pretzel logic" on multi-part XML queries.
That makes more sense, and is simpler. I'll have think about it over the weekend.

Thanks!
 

My Computer

System One

  • OS
    Windows 7
@Try3 the powershell method completely closes the window, removes the taskbar icon, and proceeds with commands

the console window basically becomes a background process

although not necessarily relevant, its a neat trick to mention while the topic is being discussed

the example waits until 3 seconds after the window is gone to open notepad
 

My Computer

System One

  • OS
    PE
Forgot about that. I had a different script that did something similar, but frankly I hated trying to decipher which Event ID's and doing the "pretzel logic" on multi-part XML queries.
That makes more sense, and is simpler. I'll have think about it over the weekend.

Thanks!
No problemo. For reference: USB storage forensics in Win10 #1 - Events
The event for insertion and removal is the same. It has the same ID and the same fields. (There is 1 unique event for removal with the same id. Its only content is a LifetimeId.) But you can differentiate them based on the values in the specific fields. For example, the “Capacity” field in the insertion events is going to have the size of the drive as value. On the other hand, the same field in the removal events is going to have the value of “0”. Therefore, this field can be used to find out which event was generated during the insertion and which was during the removal. (Timing information could be used as well obviously.)
(Emphasis in bold is mine.)
 

My Computers

System One System Two

  • OS
    11 Home
    Computer type
    Laptop
    Manufacturer/Model
    Asus TUF Gaming (2024)
    CPU
    i7 13650HX
    Memory
    16GB DDR5
    Graphics Card(s)
    GeForce RTX 4060 Mobile
    Sound Card
    Eastern Electric MiniMax DAC Supreme; Emotiva UMC-200; Astell & Kern AK240
    Monitor(s) Displays
    Sony Bravia XR-55X90J
    Screen Resolution
    3840×2160
    Hard Drives
    512GB SSD internal
    37TB external
    PSU
    Li-ion
    Cooling
    2× Arc Flow Fans, 4× exhaust vents, 5× heatpipes
    Keyboard
    Logitech K800
    Mouse
    Logitech G402
    Internet Speed
    20Mbit/s up, 250Mbit/s down
    Browser
    FF
  • Operating System
    11 Home
    Computer type
    Laptop
    Manufacturer/Model
    Medion S15450
    CPU
    i5 1135G7
    Memory
    16GB DDR4
    Graphics card(s)
    Intel Iris Xe
    Sound Card
    Eastern Electric MiniMax DAC Supreme; Emotiva UMC-200; Astell & Kern AK240
    Monitor(s) Displays
    Sony Bravia XR-55X90J
    Screen Resolution
    3840×2160
    Hard Drives
    2TB SSD internal
    37TB external
    PSU
    Li-ion
    Mouse
    Logitech G402
    Keyboard
    Logitech K800
    Internet Speed
    20Mbit/s up, 250Mbit/s down
    Browser
    FF
I found this one, which is more succinct:
The Windows 7 Event Log and USB Device Tracking

I'd still have the UI script enumerate a static table for the Device -> Drive / Volume Name mappings, so the executed task doesn't delay on startup querying this data. Especially if you had a USB hub failure, and like 6 devices got disconnected at once.

There's a trick which I've forgotten, where you write an extended Task XML so your event's data gets directly passed as a variable, and the script collapses to a simple table lookup. My current tradeoff is a background watcher saves on startup time, and is near instantaneous but I have to worry about duplicated processes. With a task trigger, I'd have to worry about keeping startup time low as possible.
 

My Computer

System One

  • OS
    Windows 7
@Try3 @garlin after taking a closer look I would rather run the vb through a PS command anyway, rather than create a script.. both are VbScript, but you dont need a file you can just have taskscheduler run the cmd. It doesnt need any special rights and has the benefits of the scriptfile.

Batch:
POWERSHELL -c "$Silent=New-Object -ComObject Wscript.Shell;$Silent.Run('cmd /c C:\path\to\binary.exe', 0)">nul
 

My Computer

System One

  • OS
    PE
But you're missing the point... The PS session which invokes binary.exe, by itself triggers a screen flash. I don't care about hiding binary.exe.

Even if you do the MainWindowHandle to hide the parent PS, it's still flashing. If you don't believe me, head over to the PowerShell GitHub issues tracker, where people have filed bugs to MS complaining that it can't be hidden w/o a trace. And the PS dev team responds "Not my problem, it's an issue with Terminal (or Console Host)".
 

My Computer

System One

  • OS
    Windows 7
Back
Top Bottom