Converting Bytes To The Largest Type Using P/Invoke

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 Smile. 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

image

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

image

With the StringBuilder object built, we can now try out the method with a byte size.

 $ConvertSize::StrFormatByteSize( 1256896523, $stringBuilder, $stringBuilder.Capacity )

 

image

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()

image

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

image

Get-ChildItem -File | 
    Select -First 5 | 
        Select Name, 
        @{L='Size';E={$_ | Convert-Size}}

Download

Technet Script Repository

About Boe Prox

Microsoft Cloud and Datacenter MVP working as a SQL DBA.
This entry was posted in powershell, scripts and tagged , , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s