Windows PowerShell: Use Proxy Cmdlets in Combination with Object Events
Proxy Cmdlets is a new feature introduced in Windows PowerShell v2. You can read more about this feature in this article on the Microsoft PowerShell Team blog.
This article will show how we can combine this feature with another feature called object events. Using the Register-ObjectEvent cmdlet we can subscribe to events generated by a Microsoft .Net Framework object. In this article we`re using a PowerShell job-object as an example.
To accomplish this a Cmdlet Proxy for Start-Job is created, available on the Microsoft Script Center Gallery.
The following is a description on how this Cmdlet Proxy are built:
1) A desription of the Cmdlet Proxy are defined at the top using block comments: <# #>.
2) On line 13 we define a new function with the same name as the original cmdlet Start-Job. The function code are generated using the metadata exposed by PowerShell:
[Management.Automation.ProxyCommand]::Create((New-Object Management.Automation.CommandMetaData (Get-Command Start-Job)))
The output from the above command are inserted to the Start-Job function.
3) In the parameter-section we define the OnCompletionAction parameter (line 48-49):
[System.Management.Automation.ScriptBlock]
${OnCompletionAction},
The first line defines the data type the parameter accepts. A script block type is chosen to let the user be able to run as much PowerShell code as needed. A script block is defined by using braces, in example {Get-Service}. The second line defines the name of the parameter.
4) The next section customized is between line 70-89:
#region Initialize helper variable to create command
$scriptCmdPipeline = ''
#endregion
# add new parameter handling
#region Process and remove the OnCompletionAction parameter if it is present
if ($OnCompletionAction) {
$PSBoundParameters.Remove('OnCompletionAction') | Out-Null
$scriptCmdPipeline += " | foreach-object{
`$job = Register-ObjectEvent -InputObject `$_ -EventName StateChanged -SourceIdentifier JobEndAlert -Action {
if(`$sender.State -eq 'Completed')
{
`& {
$OnCompletionAction
}
Unregister-Event -SourceIdentifier JobEndAlert -Force
}
}
}"
}
First we define a helper variable to create the command to be run.
Due to the if-statement the following section is only run if the OnCompletionAction parameter is provided. Next the OnCompletionAction are remove, this is necessary if we are replacing builtin parameters to the Start-Job cmdlet. Next, we add custom code to the helper variable. This is where we define the actual code for the nCompletionParameter. Key points:
The Foreach-Object cmdlet is used since the Register-ObjectEvent cmdlet doesn`t accept pipeline input.
A new object event is registered for the job-object created by Start-Job, using the Register-ObjectEvent cmdlet.
When the state of the job are changed, the object-event registered will run the code defined in the –Action script block parameter.
If the job has a state of "Completed”, the scriptblock defined in the OnCompletionAction parameter is invoked using the call operator, &.
We need to put the $OnCompletion variable inside braces, since the braces are lost when converting the initial script block to a string.
Since double-quotes are used, variables we don`t want to be expanded are escaped using the escape operator (`).
At the end the Unregister-ObjectEvent cmdlet are used to unregister the object event created earlier, using the SourceIdentifier JobEndAlert.
Using the same technique it`s also possible to add a OnFailedAction if you want some custom action to be run if the job fails.
Jan Egil Ring has written an article describing a practical usage scenario for the new Start-Job Cmdlet Proxy.