How can a PSObject Show Size as GB when displayed as object but show as bytes when called as a property?

Viewed 168

When I use Get-Volume

$Volume = Get-Volume
$Volume

I get the results

DriveLetter FriendlyName FileSystemType DriveType HealthStatus OperationalStatus SizeRemaining      Size
----------- ------------ -------------- --------- ------------ ----------------- -------------      ----
 R          Recovery     NTFS           Fixed     Healthy      OK                     482.8 MB    499 MB

When I call the property Size I get

$Volume.Size

I get the results

523235328

How can the property Size display the 499MB when I call the object but when I call the property directly It shows the int of bytes.

I have looked for a script property or alias, I do not see those in the $Volume.PSObject.Properties or $Volume.PSObject.Methods

3 Answers

One is just screen formatting for human readability, the other is real data.

(Get-Disk)[0] | Get-Member
# Results
<#
...
Size                      Property       uint64 Size {get;}
...
#>

(Get-Volume)[0] | Get-Member
# Results
<#
...
Size                      Property       uint64 Size {get;}
...
#>

((Get-Disk)[0]).PSObject.Properties
# Results
<#
...
BaseObject      : MSFT_Disk (ObjectId = "{1}\\lab01\root/Microsoft/Windows/Storag...)
Tag             : AllocatedSize = 512109142016
MemberType      : Property
Value           : 512109142016
IsSettable      : False
IsGettable      : True
TypeNameOfValue : uint64
Name            : AllocatedSize
IsInstance      : True

...

BaseObject      : MSFT_Disk (ObjectId = "{1}\\lab01\root/Microsoft/Windows/Storag...)
Tag             : Size = 512110190592
MemberType      : Property
Value           : 512110190592
IsSettable      : False
IsGettable      : True
TypeNameOfValue : uint64
Name            : Size
IsInstance      : True

...
#>

((Get-Volume)[0]).PSObject.Properties
# Results
<#
...
BaseObject      : MSFT_Volume (ObjectId = "{1}\\lab01\root/Microsoft/Windows/Storag...)
Tag             : Size = 510815686656
MemberType      : Property
Value           : 510815686656
IsSettable      : False
IsGettable      : True
TypeNameOfValue : uint64
Name            : Size
IsInstance      : True

BaseObject      : MSFT_Volume (ObjectId = "{1}\\lab01\root/Microsoft/Windows/Storag...)
Tag             : SizeRemaining = 312891092992
MemberType      : Property
Value           : 312891092992
IsSettable      : False
IsGettable      : True
TypeNameOfValue : uint64
Name            : SizeRemaining
IsInstance      : True
...
#>

If you want to use the human-readable, regardless of how it is called, then you have to format that in your code or mess with the formatter files.

Trace-Command -Name * -Expression {Get-Volume C | Format-Table} -PSHost returns a lot of information, including:

DEBUG: 2021-03-02 10:18:20.5439 FormatViewBinding Information: 0 :         MATCH FOUND Table NAME: VolumeTableView  TYPE: Microsoft.Management.Infrastructure.CimInstance#MSFT_Volume

Now, some will probably take issue with the clumsy way I get to the formatter for this, but here goes:

Get-FormatData -TypeName Microsoft.Management.Infrastructure.CimInstance#MSFT_Volume |
  Select -Expand FormatViewDefinition |
  Select -Expand Control |
  Select -Expand Rows |
  Select -Expand Columns |
  Select -Expand DisplayEntry |
  Select -Expand Value

(Guess you could also do (Get-FormatData -TypeName Microsoft.Management.Infrastructure.CimInstance#MSFT_Volume).FormatViewDefinition.Control.Rows.Columns.DisplayEntry.Value, but I'm not convinced that's a lot better.)

reveals a script value for a column entry of:

 $size = $_.Size;
 $postfixes = @( "B", "KB", "MB", "GB", "TB", "PB" )
 for ($i=0; $size -ge 1024 -and $i -lt $postfixes.Length; $i++) { $size = $size / 1024; }
 return "" + [System.Math]::Round($size,2) + " " + $postfixes[$i];

So, Format-Table is invoking a pre-defined formatter on your Size column that you don't get when selecting the value directly.

To expand on the other answers regarding format types, if you just want the value displayed nicely in MB:

'{0:f0} MB' -f ($Volume.Size / 1MB)

This divides the value by 1048576 (10242) and displays it with zero decimal places and the MB suffix.

Related