A good while back I wrote an article (one of my first) that showed a way to convert the raw bytes of a file or anything for that matter into the upper most value. For instance, if you had 100240 bytes, it would tell you that you had 97.890625 KB instead of having to work down the line using the <bytes> / 1KB/MB/GB approach to find the highest possible type.
I figured that there had to a better way to do this and the code I used back then was fairly bad . Looking around I found that the Win32 API for doing this conversion:
[DllImport("Shlwapi.dll", CharSet = CharSet.Auto)] public static extern long StrFormatByteSize( long fileSize, System.Text.StringBuilder buffer, int bufferSize );
If you’ve never worked with Win32 APIs before, then I strongly suggest you check out the p/invoke site here: http://pinvoke.net as it will give you lot of great information on different things you can do.
Back to the current situation. With the code above, I can plug that into a Here-String and then use Add-Type to load the code up into current PowerShell session.
$Signature = @" [DllImport("Shlwapi.dll", CharSet = CharSet.Auto)] public static extern long StrFormatByteSize( long fileSize, System.Text.StringBuilder buffer, int bufferSize ); "@ $Global:ConvertSize = Add-Type -Name SizeConverter -MemberDefinition $Signature -PassThru
Now that we have this, we can look at the static method that will be used for the conversion.
$ConvertSize::StrFormatByteSize
Here we can see that this method requires the filesize in bytes, a stringbuilder object as the buffer and the capacity property of the stringbuilder object in order to properly go through the conversion.
$stringBuilder = New-Object Text.StringBuilder 1024 $stringBuilder
With the StringBuilder object built, we can now try out the method with a byte size.
$ConvertSize::StrFormatByteSize( 1256896523, $stringBuilder, $stringBuilder.Capacity )
Well, that isn’t too useful, now is it? We need to actually go back to our buffer, the StringBuilder object and get the value as a string.
$stringBuilder.ToString()
Now that is a lot better! Now we can use this for as long as the PowerShell session is active. Of course, a better approach is to wrap this up in a function, which I luckily did!
There were some issues while trying to write this as a function, namely loading the code up via Add-Type. This is because once you load up the code, you cannot do it again in the same session nor can you remove the type, so the function will fail if being used. To work around this, I create a global variable for the $ConvertSize object on the initial run of the function (which will cause to it be slow the first time) and then it will check for that variable and avoid trying to re-create the type (speeding things up).
The function is called Convert-Size and it does accept value from the pipeline. Here are a couple of examples.
Convert-Size -Size 56895623
Get-ChildItem -File | Select -First 5 | Select Name, @{L='Size';E={$_ | Convert-Size}}
Pingback: Powershell Download Function with Progress – Jonathan's Tech Site