Rob van der Woude's Scripting Pages
Powered by GeSHi

Source code for gethddstatusgui.ps

(view source code of gethddstatusgui.ps as plain text)

  1. <#
  2. .SYNOPSIS
  3. Display the SMART status for all local harddisks in a popup window and in Windows' Notification Area (a.k.a. System Tray)
  4.  
  5. .DESCRIPTION
  6. This script uses WMI to query the status of all local physical harddisk drives, and displays the results in a popup window and in Windows' Notification Area (a.k.a. System Tray).
  7. In the status display, each physical harddisk is associated with the drive letter of one of its volumes.
  8. Since Windows 10 limits the number of lines in desktop notifications, any errors will be placed at the top of the displayed status list, otherwise the list is sorted by drive letter.
  9. If all disks report OK, the script returns True and return code ("ErrorLevel") 0, otherwise False and return code 1.
  10. Since this script uses WMI and drive letters, it will not work in Linux.
  11.  
  12. .PARAMETER Modal
  13. Make the popup window always stay on top
  14.  
  15. .PARAMETER Hide
  16. Hide the console window when the script is started, restore it (but minimized) afterwards
  17.  
  18. .PARAMETER Version
  19. Show this script's version number; if combined with -Verbose show full script path, version number and last modified or release date
  20.  
  21. .PARAMETER Debug
  22. Display intermediate results for each disk drive in console; if combined with -Verbose show intermediate results for each associated volume too
  23.  
  24. .PARAMETER Help
  25. Show this script's help screen
  26.  
  27. .OUTPUTS
  28. True and return code 0 if all disks report OK, otherwise False and return code 1
  29.  
  30. .EXAMPLE
  31. . ./GetHDDStatusGUI.ps1
  32. Will display a popup window with the drive letters in use and the SMART status of the associated physical harddisk drive.
  33.  
  34. .EXAMPLE
  35. . ./GetHDDStatusGUI.ps1 -Debug -Verbose
  36. Will list details for all physical harddisk drives and volumes in the console, and display a popup window with the drive letters in use and the SMART status of the associated physical harddisk drive.
  37.  
  38. .EXAMPLE
  39. . ./GetHDDStatusGUI.ps1 -Version -Verbose
  40.  
  41. Will display this script's full path, version number and last modified or release date.
  42.  
  43. .LINK
  44. Script written by Rob van der Woude:
  45. https://www.robvanderwoude.com/
  46.  
  47. .LINK
  48. Disk check based on code by Geoff:
  49. http://www.uvm.edu/~gcd/2013/01/which-disk-is-that-volume-on
  50.  
  51. .LINK
  52. System Tray ToolTip Balloon code by Don Jones:
  53. http://blog.sapien.com/current/2007/4/27/creating-a-balloon-tip-notification-in-powershell.html
  54.  
  55. .LINK
  56. Extract icons from Shell32.dll by Thomas Levesque:
  57. http://stackoverflow.com/questions/6873026
  58.  
  59. .LINK
  60. Hide and restore console by Anthony:
  61. http://stackoverflow.com/a/15079092
  62.  
  63. .LINK
  64. Capture common parameters by mklement0:
  65. https://stackoverflow.com/a/48643616
  66. #>
  67.  
  68. param (
  69. 	[parameter( ValueFromRemainingArguments = $true )]
  70. 	[string[]]$Args, # Leave all argument validation to the script, not to PowerShell
  71. 	[switch]$Modal,
  72. 	[switch]$Hide,
  73. 	[switch]$Version,
  74. 	[switch]$Help
  75. )
  76.  
  77. $progver = "1.04"
  78.  
  79. [bool]$Debug = ( $PSBoundParameters.ContainsKey( 'Debug' ) )
  80. [bool]$Verbose = ( $PSBoundParameters.ContainsKey( 'Verbose' ) )
  81.  
  82. # Show help if any unnamed argument is given
  83. $Help = $Help -or ( $Args.Length -gt 0 )
  84.  
  85. # Show help if running in Linux
  86. $Help = $Help -or ( $HOME[0] -eq '/' )
  87.  
  88. # Help disables Hide parameter
  89. $Hide = $Hide -and -not $Help
  90.  
  91. # Debug disables Hide parameter
  92. $Hide = $Hide -and -not $Debug
  93.  
  94. if ( $Help ) {
  95. 	Get-Help "$PSCommandPath" -Full
  96. 	exit 1
  97. }
  98.  
  99. if ( $Version ) {
  100. 	if ( $Verbose ) {
  101. 		$lastmod = ( [System.IO.File]::GetLastWriteTime( $PSCommandPath ) )
  102. 		if ( $lastmod.ToString( "h.mm" ) -eq $progver ) {
  103. 			"`"{0}`", Version {1}, release date {2}" -f $PSCommandPath, $progver, $lastmod.ToString( "yyyy-MM-dd" )
  104. 		} else {
  105. 			# if last modified time is not equal to program version, the script has been tampered with
  106. 			"`"{0}`", Version {1}, last modified date {2}" -f $PSCommandPath, $progver, $lastmod.ToString( "yyyy-MM-dd" )
  107. 		}
  108. 	} else {
  109. 		$progver
  110. 	}
  111. 	exit 0
  112. }
  113.  
  114. #######################################
  115. # Hide console window                 #
  116. # by Anthony on StackOverflow.com     #
  117. # http://stackoverflow.com/a/15079092 #
  118. #######################################
  119.  
  120. $signature1 = @'
  121. public static void ShowConsoleWindow( int state )
  122. {
  123. 	var handle = GetConsoleWindow( );
  124. 	ShowWindow( handle, state );
  125. }
  126.  
  127. [System.Runtime.InteropServices.DllImport( "kernel32.dll" )]
  128. static extern IntPtr GetConsoleWindow( );
  129.  
  130. [System.Runtime.InteropServices.DllImport( "user32.dll" )]
  131. static extern bool ShowWindow( IntPtr hWnd, int nCmdShow );
  132. '@
  133.  
  134. $hideconsole = Add-Type -MemberDefinition $signature1 -Name Hide -Namespace HideConsole -ReferencedAssemblies System.Runtime.InteropServices -PassThru
  135.  
  136. # Hide console
  137. if ( $Hide ) {
  138. 	$hideconsole::ShowConsoleWindow( 0 )
  139. }
  140.  
  141. ##################
  142. # Disk inventory #
  143. ##################
  144.  
  145. [System.Collections.SortedList]$volumedetails = New-Object System.Collections.SortedList
  146. [System.Collections.SortedList]$volumestatus  = New-Object System.Collections.SortedList
  147.  
  148. if ( $Debug ) {
  149. 	Write-Host "Disk#`tStatus`tSize (GB)`tModel"
  150. 	Write-Host "=====`t======`t=========`t====="
  151. }
  152.  
  153. $diskdrives = Get-WmiObject -Namespace "root/CIMV2" -Class Win32_DiskDrive
  154. $warnings   = $false
  155. foreach ( $disk in $diskdrives ) {
  156. 	$diskindex  = $disk.Index
  157. 	$diskmodel  = $disk.Model -replace "Disk Device","Disk"
  158. 	$disksize   = "{0,5:F0} GB" -f ( $disk.Size / 1GB )
  159. 	$diskstatus = $disk.Status
  160. 	if ( $Debug ) {
  161. 		if ( $Verbose ) {
  162. 			Write-Host
  163. 		}
  164. 		Write-Host ( "{0}`t{1}`t{2}`t{3}" -f $diskindex, $diskstatus, $disksize, $diskmodel )
  165. 	}
  166. 	$part_query = 'ASSOCIATORS OF {Win32_DiskDrive.DeviceID="' + $disk.DeviceID.replace('\','\\') + '"} WHERE AssocClass=Win32_DiskDriveToDiskPartition'
  167. 	$partitions = @( Get-WmiObject -Query $part_query | Sort-Object StartingOffset )
  168. 	foreach ( $partition in $partitions ) {
  169. 		$vol_query = 'ASSOCIATORS OF {Win32_DiskPartition.DeviceID="' + $partition.DeviceID + '"} WHERE AssocClass=Win32_LogicalDiskToPartition'
  170. 		$volumes   = @( Get-WmiObject -Query $vol_query )
  171. 		foreach ( $volume in $volumes ) {
  172. 			# 0 = Unknown; 1 = No Root Directory; 2 = Removable Disk; 3 = Local Disk; 4 = Network Drive; 5 = Compact Disc; 6 = RAM Disk
  173. 			# DriveType 3 means harddisks only
  174. 			if ( $volume.DriveType -eq 3 ) {
  175. 				if ( $Debug -and $Verbose ) {
  176. 					Write-Host ( "{0}`t{1,2}`t{2}`t{3}" -f $diskindex, $volume.Name, $disksize, $diskmodel )
  177. 				}
  178. 				if ( -not $volumedetails.Contains( $volume.Name ) ) {
  179. 					$volumedetails.Add( $volume.Name, "[Disk {0,2}]  {1}  {2}" -f ( $diskindex, $disksize, $diskmodel ) )
  180. 					$volumestatus.Add( $volume.Name, $diskstatus )
  181. 				}
  182. 			}
  183. 		}
  184. 	}
  185. }
  186.  
  187.  
  188. #################
  189. # Dialog window #
  190. #################
  191.  
  192. Add-Type -AssemblyName System.Windows.Forms
  193.  
  194. [System.Windows.Forms.Application]::EnableVisualStyles( )
  195.  
  196. $form = New-Object System.Windows.Forms.Form
  197. $form.Width           = 640
  198. $form.Height          = 25 * $volumedetails.Count + 120
  199. $form.Font            = 'Courier New, 10'
  200. $form.BackColor       = 'White'
  201. $form.MaximizeBox     = $false;
  202. $form.FormBorderStyle = 'FixedSingle'
  203. $form.TopMost         = $Modal
  204.  
  205. $y    = 0
  206. $even = $false
  207. $volumedetails.Keys | ForEach-Object {
  208. 	$label           = New-Object System.Windows.Forms.Label
  209. 	$label.Size      = '35, 20'
  210. 	$label.Location  = New-Object System.Drawing.Point( 10, ( 15 + $y ) )
  211. 	$label.Text      = "$_"
  212. 	if ( $even ) { $label.BackColor = 'ButtonFace' }
  213. 	$form.Controls.Add( $label )
  214.  
  215. 	$status          = ( $volumestatus[$_] )
  216. 	$label           = New-Object System.Windows.Forms.Label
  217. 	$label.Size      = '40, 20 '
  218. 	$label.Location  = New-Object System.Drawing.Point( 45, ( 15 + $y ) )
  219. 	$label.Text      = $status
  220. 	if ( $status -eq "OK" ) {
  221. 		$label.ForeColor = 'Green'
  222. 	} else {
  223. 		$label.ForeColor = 'Red'
  224. 		$warnings        = $true
  225. 	}
  226. 	if ( $even ) { $label.BackColor = 'ButtonFace' }
  227. 	$form.Controls.Add( $label )
  228.  
  229. 	$label           = New-Object System.Windows.Forms.Label
  230. 	$label.Size      = '490, 20'
  231. 	$label.Location  = New-Object System.Drawing.Point( 85, ( 15 + $y ) )
  232. 	$label.Text      = $volumedetails[$_]
  233. 	if ( $even ) { $label.BackColor = 'ButtonFace' }
  234. 	$form.Controls.Add( $label )
  235.  
  236. 	$y    = $y + 25
  237. 	$even = -not $even
  238. }
  239.  
  240. if ( $warnings ) {
  241. 	$form.Text = "HDD Status Warning"
  242. } else {
  243. 	$form.Text = "HDD Status OK"
  244. }
  245.  
  246. $buttonOK           = New-Object System.Windows.Forms.Button
  247. $buttonOK.BackColor = 'ButtonFace'
  248. $buttonOK.Text      = "OK"
  249. $buttonOK.Size      = '60,24'
  250. $buttonOK.Location  = New-Object System.Drawing.Point( 85, ( 30 + $y ) )
  251. $form.Controls.Add( $buttonOK )
  252. $form.AcceptButton  = $buttonOK # Pressing Enter  closes the dialog
  253. $form.CancelButton  = $buttonOK # Pressing Escape closes the dialog
  254.  
  255.  
  256. ########################################
  257. # System tray balloon tip notification #
  258. ########################################
  259.  
  260. [System.Windows.Forms.ToolTipIcon]$icon = [System.Windows.Forms.ToolTipIcon]::Info
  261. $title = "HDD Status OK"
  262. $systraymessage = ""
  263. $volumedetails.Keys | ForEach-Object {
  264. 	$status = ( $volumestatus[$_] )
  265. 	if ( $status -eq "OK" ) {
  266. 		$systraymessage = $systraymessage + "$_`t$status`n" # list in alphabetical sort order
  267. 	} else {
  268. 		$systraymessage = "$_`t$status`n$systraymessage" # errors at top of list
  269. 		$icon = [System.Windows.Forms.ToolTipIcon]::Error
  270. 		$title = "Warning: HDD Errors"
  271. 	}
  272. }
  273.  
  274.  
  275. ################################################################
  276. # Extract system tray icon from Shell32.dll                    #
  277. # C# code to extract icons from Shell32.dll by Thomas Levesque #
  278. # http://stackoverflow.com/questions/6873026                   #
  279. ################################################################
  280.  
  281. $signature2 = @'
  282. [DllImport( "Shell32.dll", EntryPoint = "ExtractIconExW", CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall )]
  283. private static extern int ExtractIconEx( string sFile, int iIndex, out IntPtr piLargeVersion, out IntPtr piSmallVersion, int amountIcons );
  284.  
  285. public static Icon Extract( string file, int number, bool largeIcon )
  286. {
  287. 	IntPtr large;
  288. 	IntPtr small;
  289. 	ExtractIconEx( file, number, out large, out small, 1 );
  290. 	try
  291. 	{
  292. 		return Icon.FromHandle( largeIcon ? large : small );
  293. 	}
  294. 	catch
  295. 	{
  296. 		return null;
  297. 	}
  298. }
  299. '@
  300.  
  301. $iconextractor = Add-Type -MemberDefinition $signature2 -Name IconExtract -Namespace IconExtractor -ReferencedAssemblies System.Windows.Forms,System.Drawing -UsingNamespace System.Windows.Forms,System.Drawing -PassThru
  302.  
  303. # System tray icon depends on status
  304. if( $title -eq "HDD Status OK" ) {
  305. 	$systrayicon = $iconextractor::Extract( "C:\Windows\System32\shell32.dll", 223, $true )
  306. } else {
  307. 	$systrayicon = $iconextractor::Extract( "C:\Windows\System32\shell32.dll", 53, $true )
  308. }
  309.  
  310. # Show system tray icon and balloon tip
  311. $notify = New-Object System.windows.Forms.NotifyIcon
  312. $notify.BalloonTipText = $systraymessage
  313. $notify.BalloonTipTitle = $title
  314. $notify.BalloonTipIcon = [System.Windows.Forms.ToolTipIcon]$icon
  315. $notify.Icon = $systrayicon
  316. $notify.Visible = $true
  317. $notify.ShowBalloonTip( 30000 )
  318.  
  319. # Show dialog
  320. [void] $form.ShowDialog( )
  321.  
  322.  
  323. ##########################################
  324. # Restore console minimized (2)          #
  325. # Change to 1 to restore to normal state #
  326. ##########################################
  327.  
  328. if ( $Hide ) {
  329. 	$hideconsole::ShowConsoleWindow( 2 )
  330. }
  331.  
  332. #################################
  333. # Exit code 1 in case of errors #
  334. #################################
  335.  
  336. if ( $warnings ) {
  337. 	$false
  338. 	exit 1
  339. } else {
  340. 	$true
  341. 	exit 0
  342. }
  343.  

page last modified: 2024-04-16; loaded in 0.0148 seconds