Last active
March 18, 2025 19:08
-
-
Save joshooaj/c52325086415650e52cb8d9fe81aafa4 to your computer and use it in GitHub Desktop.
Call the GetDiskFreeSpaceExW function from PowerShell using p/invoke
This file contains hidden or 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
<# | |
.SYNOPSIS | |
Gets the free space on a disk and waits 500ms or for a specified delay before checking again. | |
.DESCRIPTION | |
This function uses p/invoke to call the GetDiskFreeSpaceEx function from the kernel32.dll library to get the free | |
space on a disk based on the path provided. It will keep checking the free space at the specified interval, or 500ms | |
by default, until an error occurs. | |
When an error occurs, the function will include the output of GetLastError and the string-formatted | |
error message. | |
.PARAMETER Path | |
Specifies a path to the drive to check the free space on. For example "C:\". | |
.EXAMPLE | |
.\watch-diskfreespace.ps1 -Path "C:\" | |
This example will check the free space on the C: drive and print the results to the console. It will check every 500 | |
milliseconds. | |
.EXAMPLE | |
.\watch-diskfreespace.ps1 -Path "C:\" -DelayMS 100 | |
This example will check the free space on the C: drive and print the results to the console. It will check every 100 | |
milliseconds, or 10 times per second. | |
.EXAMPLE | |
.\watch-diskfreespace.ps1 -Path "C:\" | Export-Csv watch.csv -NoTypeInformation | |
This example will check the free space on the C: drive and save the results to a CSV file. It will check every | |
500 milliseconds until an error occurs. | |
#> | |
param( | |
[Parameter(Mandatory, Position = 0)] | |
[string] | |
$Path, | |
[Parameter()] | |
[int] | |
$DelayMS = 500 | |
) | |
function Get-DiskFreeSpace { | |
[CmdletBinding()] | |
param ( | |
[Parameter(Mandatory, Position = 0)] | |
[string] | |
$Path | |
) | |
begin { | |
Add-Type -ErrorAction SilentlyContinue -Name 'DiskInfo' -Namespace 'Helpers' -MemberDefinition @" | |
[DllImport("kernel32.dll", SetLastError=true)] | |
public static extern bool GetDiskFreeSpaceEx(string lpDirectoryName, | |
out ulong lpFreeBytesAvailable, | |
out ulong lpTotalNumberOfBytes, | |
out ulong lpTotalNumberOfFreeBytes); | |
[DllImport("kernel32.dll")] | |
public static extern long GetLastError(); | |
[DllImport("kernel32.dll")] | |
public static extern long FormatMessage( | |
long dwFlags, | |
object lpSource, | |
long dwMessageId, | |
long dwLanguageId, | |
System.Text.StringBuilder lpBuffer, | |
long nSize, | |
object Arguments | |
); | |
public static string GetFormattedMessage(long errorCode) { | |
long FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000; | |
long FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200; | |
long flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; | |
System.Text.StringBuilder buffer = new System.Text.StringBuilder(256); | |
long result = FormatMessage(flags, null, errorCode, 0, buffer, buffer.Capacity, null); | |
return buffer.ToString(); | |
} | |
"@ | |
} | |
process { | |
try { | |
$result = [pscustomobject]@{ | |
Timestamp = Get-Date | |
Path = $Path | |
FreeBytesAvailable = 0 | |
TotalBytesAvailable = 0 | |
TotalBytesFree = 0 | |
LastError = 0 | |
ErrorMessage = $null | |
} | |
[uint64]$freeToCaller, [uint64]$totalAvailable, [uint64]$totalFree = 0 | |
if ([Helpers.DiskInfo]::GetDiskFreeSpaceEx($Path, [ref]$freeToCaller, [ref]$totalAvailable, [ref]$totalFree)) { | |
$result.FreeBytesAvailable = $freeToCaller | |
$result.TotalBytesAvailable = $totalAvailable | |
$result.TotalBytesFree = $totalFree | |
} else { | |
$lastError = [Helpers.DiskInfo]::GetLastError() | |
$errorMessage = [Helpers.DiskInfo]::GetFormattedMessage($lastError) | |
Write-Error "Call to GetDiskFreeSpaceEx failed. LastError: $lastError, $errorMessage" | |
$result.LastError = $lastError | |
$result.ErrorMessage = $errorMessage | |
} | |
$result | |
} catch { | |
throw | |
} | |
} | |
} | |
do { | |
$info = Get-DiskFreeSpace -Path $Path | |
$info | |
Start-Sleep -Milliseconds $DelayMS | |
} while (!$info.LastError) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment