Wrap .Net method in PowerShell the lazy way

This is a work through of some exploration I implemented in PowerObject and MethodHelpers, the gist shows an example of the latter.

netMethodToCmdlet

[edit: Code updated and new gist to illustrate bundling two methods in one Cmdlet should you need to. This will work only if there is no method overload conflict (two method with same signature) or if variables have same name but different types within or across method overloads.]

Some time ago I started implementing log4net for PowerShell for my Log4ps module, and I started to wrap some methods or object creation in commandlet to make them more PowerShell-y-ish. There are many classes and methods, so that quickly became annoying and repetitive, even though I did not need all of them…
And I’m a bit of a DRY adept, when I can, so I started exploring…

The first helpers I did was to make a similar function to New-Object, but that would be a bit more clever based on the type you where giving as a parameter.

The hypothesis was that if you know which type (class) you wanted to instantiate, DotNet is clever enough to already have the metadata about the constructors and the writable properties of the object:

 PS C:\> ([System.Drawing.Rectangle].GetConstructors() | select Name,@{N='params';E={($_.getParameters() | % { "[$($_.ParameterType)] $($_.name)"}) -join ',' }})
Name params 
---- ------ 
.ctor [int] x,[int] y,[int] width,[int] height 
.ctor [System.Drawing.Point] location,[System.Drawing.Size] size

With that information, you could define your different Commandlet signature, or ParameterSets, you’d have one parameter Set with parameters x, y, width and height, and one with location and size.
Keeping the order is important for the next step, and all parameters are mandatory for each constructor/parameterset they’re defined in.

So only with the type, I could find all the metadata I needed for creating New-Object parameters specific to that type on the fly using DynamicParam { } block.

So while I was down there, I thought let’s explore further ‘down the rabbit hole’…

Second assumption was that each type defines properties that can be set once the object is instantiated. Wouldn’t it be cool, if you could add those writable properties as parameters, and in one command instantiate the object and set properties straight after that.

[System.drawing.Rectangle].GetProperties().Where{$_.CanWrite} | select name,propertytype

Name PropertyType 
---- ------------ 
Location System.Drawing.Point
Size System.Drawing.Size 
X System.Int32 
Y System.Int32 
Width System.Int32 
Height System.Int32

So that’s cool, but those properties are already set by the constructor, so I don’t need to set them again.

Looking at other examples:

[System.Drawing.Bitmap].GetProperties().Where{$_.canwrite} | select name,PropertyType
Name PropertyType 
---- ------------ 
Tag System.Object 
Palette System.Drawing.Imaging.ColorPalette

I found objects that are writable, and not in the constructors, so doing the same thing than with Constructors parameters, you can dynamically add those parameters via the DynamicParam{} block, and set those properties after the object is instantiated in the process block.

As we kept the parameters for each parametersets in order (via the Position attribute of the Parameter), we can simply invoke the original New-Object command with the parameters in ArgumentList.

$instanceOfObject = New-Object TypeName $type.ToString() ArgumentList $parameters

The whole thing wrapped up in a function called New-PowerObject, the parameters populates and show in intellisense as soon as you have typed the class wanted.

New_powerobject.png

Finally, the second helper used similar techniques to find the overload definitions of a public static method; use regex to extract type and name; and return the dynamic parameters to be inserted in the DynamicParam{} of a wrapper commandlet. In the process block of the wrapper, you would call some code that would invoke the right method overload, by finding it based on the ParameterSetName in use, and sending the $PSBoundParameters.

The end result allows you to quickly create a wrapper function with very few lines, and the ability to extend on the functionality of that method:

function Resolve-DNSHost {
 [cmdletBinding()]
 Param ()
 DynamicParam {
 Get-DynamicParamForMethod -method ([System.Net.Dns]::Resolve)
 }
 process {
 Invoke-MethodOverloadFromBoundParam -method ([System.Net.Dns]::Resolve) -parameterSet $PSCmdlet.ParameterSetName -Parameters $PSBoundParameters
 }
}

CmdletFromMethod.png

I had that in my repo for a while and used it a bit, and today thought it could be useful to others!

Let me know if you think it’s useful and if you use it!

Gael

 

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