Add Custom Methods and Properties to Types in PowerShell
To give a glimpse into the writing process behind my upcoming “Windows PowerShell - The Definitive Guide” (O’Reilly,) I’ll occasionally post entries “as the author sees it.” This entry discusses PowerShell’s type extension files.
Add Custom Methods and Properties to Types
Problem
You want to add your own custom properties or methods to all objects of a certain type.¶
Solution
Use custom type extension files to add custom members to all objects of a type.¶
Discussion
Although the Add-Member cmdlet is extremely useful in helping you add custom members to individual objects, it requires that you add the members to each object that you want to interact with. It does not allow you to automatically add them to all objects of that type. For that purpose, PowerShell supports another mechanism — custom type extension files.¶
Type extensions are simple XML files that PowerShell interprets. They allow you (as the administrator of the system) to easily add your own features to any type exposed by the system. If you write code (i.e.: a script or function) that primarily interacts with a single type of object, then that code might be better suited as an extension to the type, instead.¶
Since type extension files are XML files, ensure that your customizations properly encode the characters that have special meaning in XML files — such as <, >, and &.¶
For example, imagine a script that returns the free disk space on a given drive. That might be helpful as a script, but you might find it easier to instead make PowerShell’s PsDrive objects themselves tell you how much free space they have left.¶
Getting Started
If you haven’t already, the first step in creating a types extension file is to create an empty one. The best location for this is probably in the same directory as your custom profile, with the name Types.Custom.ps1xml.¶
Example 3-2. Sample Types.Custom.ps1xml file
<?xml version="1.0" encoding="utf-8" ?>¶
<Types>¶
</Types>¶
Next, add a few lines to your PowerShell profile so that PowerShell loads your type extensions during startup:¶
$typeFile = (join-path (split-path $profile) "Types.Custom.ps1xml")¶
Update-TypeData -PrependPath $typeFile¶
By default, PowerShell loads several type extensions from the Types.ps1xml file in PowerShell’s installation directory. The Update-TypeData cmdlet tells PowerShell to also look in your Types.Custom.ps1xml file for extensions. The –PrependPath parameter makes PowerShell favour your extensions over the built-in ones should there be a conflict.¶
Once you have a custom types file to work with, adding functionality becomes relatively straight forward. As a theme, we will do exactly what we alluded to earlier: add functionality to PowerShell’s PsDrive type.¶
To support this, we need to extend your custom types file so that it defines additions to the System.Management.Automation.PSDriveInfo type. This is the type that the Get-PsDrive cmdlet generates.¶
<?xml version="1.0" encoding="utf-8" ?>¶
<Types>¶
<Type>¶
<Name>System.Management.Automation.PSDriveInfo</Name>¶
<Members>¶
_add members such as <ScriptProperty> here_¶
</Members>¶
</Type>¶
</Types>¶
Add a ScriptProperty
A ScriptProperty allows you to add properties (that get and set information) to types, using PowerShell script as the extension language. It consists of three child elements: the Name of the property, the Getter of the property (via the GetScriptBlock child,) and the Setter of the property (via the SetScriptBlock child.)¶
In both the GetScriptBlock and SetScriptBlock sections, the $this variable refers to the current object being extended. In the SetScriptBlock section, the $args[0] variable represents the value that the user supplied as the right-hand side of the assignment.¶
The following example adds an AvailableFreeSpace ScriptProperty to PSDriveInfo. When you access the property, it returns the amount of free space remaining on the drive. When you set the property, it outputs what changes you must make in order to obtain that amount of free space. ¶
Example 3-3. A ScriptProperty for the PSDriveInfo type
<ScriptProperty>¶
<Name>AvailableFreeSpace</Name>¶
<GetScriptBlock>¶
## Ensure that this is a FileSystem drive¶
if($this.Provider.ImplementingType -eq ¶
[Microsoft.PowerShell.Commands.FileSystemProvider])¶
{¶
## Also ensure that it is a local drive¶
$driveRoot = $this.Root¶
$fileZone = [System.Security.Policy.Zone]::CreateFromUrl($driveRoot).SecurityZone¶
if($fileZone -eq "MyComputer")¶
{¶
$drive = New-Object System.IO.DriveInfo $driveRoot¶
$drive.AvailableFreeSpace¶
}¶
}¶
</GetScriptBlock>¶
<SetScriptBlock>¶
## Get the available free space¶
$availableFreeSpace = $this.AvailableFreeSpace¶
## Find out the difference between what is available, and what they¶
## asked for.¶
$spaceDifference = (([long] $args[0]) - $availableFreeSpace) / 1MB¶
## If they want more free space than they have, give that message¶
if($spaceDifference -gt 0)¶
{¶
Write-Host ("To obtain $args bytes of free space, free $spaceDifference " +¶
"megabytes.")¶
}¶
## If they want less free space than they have, give that message¶
else¶
{¶
$spaceDifference = $spaceDifference \* -1¶
Write-Host ("To obtain $args bytes of free space, use up $spaceDifference " +¶
"more megabytes.")¶
}¶
</SetScriptBlock>¶
</ScriptProperty>¶
Add an AliasProperty
An AliasProperty provides an alternative name (alias) for a property. The referenced property does not need to exist when PowerShell processes your type extension file, as the property might be added later by mechanisms such as the Add-Member cmdlet.¶
The following example adds a Free AliasProperty to PSDriveInfo. When you access the property, it returns the value of the AvailableFreeSpace property. When you set the property, it sets the value of the AvailableFreeSpace property. ¶
Example 3-4. An AliasProperty for the PSDriveInfo type
<AliasProperty>¶
<Name>Free</Name>¶
<ReferencedMemberName>AvailableFreeSpace</ReferencedMemberName>¶
</AliasProperty>¶
Add a ScriptMethod
A ScriptMethod allows you to define an action on an object, using PowerShell script as the extension language. It consists of two child elements: the Name of the property, and the Script.¶
In the script element, the $this variable refers to the current object being extended. Like a stand-alone script, the $args variable represents the arguments to the method. Unlike stand-alone scripts, ScriptMethods do not support parameters (via the param statement.)¶
The following example adds a Remove ScriptMethod to PSDriveInfo. When you call this method with no arguments, the method simulates removing the drive (through the –WhatIf option to Remove-PsDrive.) If you call this method with $true as the first argument, it actually removes the drive from the PowerShell session.¶
Example 3-3. A ScriptMethod for the PSDriveInfo type
<ScriptMethod>¶
<Name>Remove</Name>¶
<Script>¶
$force = [bool] $args[0]¶
## Remove the drive if they use $true as the first parameter¶
if($force)¶
{¶
$this | Remove-PSDrive¶
}¶
## Otherwise, simulate the drive removal¶
else¶
{¶
$this | Remove-PSDrive -WhatIf¶
}¶
</Script>¶
</ScriptMethod>¶
Add other extension points
PowerShell supports several additional features in the types extension file, including CodeProperty, NoteProperty, CodeMethad, and MemberSet. Although not generally useful to end-users, developers of PowerShell providers and cmdlets will find these features helpful. For more information about these additional features, see the Windows PowerShell SDK, or MSDN documentation.¶