Turning C# programs into PowerShell scripts

I mentioned PowerShell – the new Windows command shell and scripting language – last year when I first tried it out. But other than playing with it a little, I kind of put it to one side and forgot all about it.

Yesterday I picked it up again and started playing with it again – using it to develop functions for WebSphere MQ (accessing it through the .NET DLLs that come with WMQ). The idea was to start and produce something which expands on the command line administration tools that come with WMQ – adding features that perhaps we don’t already provide.

You can see a walkthrough of what I’ve come up with so far on a post I wrote for the Hursley WMQ blog, but I thought I’d quickly draw out some of the more interesting bits that I learnt in doing it.

(It’s worth pointing out that if you’ve come to this page from Google looking for PowerShell tips and best practice, that I’m very much a beginner. As I highlight in the WMQ post, this is the product of an evening’s playing around – pretty much just trial and error and seeing what tab-complete suggests. It seems to work, but whether it’s the best way to do it… dunno 🙂 )

Security

I was surprised to find that execution of PowerShell scripts is disabled by default. This is configurable using Set-ExecutionPolicy:

Set-ExecutionPolicy Unrestricted
run anything – take your chances!

Set-ExecutionPolicy RemoteSigned
what I’ve gone for: scripts and configuration files downloaded from the Internet have to be signed by a trusted publisher before they can be run, but scripts that I write locally can be run as-is

Set-ExecutionPolicy AllSigned
everything needs to be signed – so if you want to write a script you need to generate a certificate and sign it. Sounds more secure, but a pain in the neck, so not for me.

Set-ExecutionPolicy Restricted
run nothing – the default

Profiles

Profile files are places you can write functions to make them available to future PowerShell windows. Not unlike .bash_profile or similar things that I’ve used in UNIX-land before, so this was a familiar idea.

This MSDN page explains…

you can have four different profiles in Windows PowerShell. The profiles are listed in load order. The most specific profiles have precedence over less specific profiles where they apply.

  • %windir%\ system32\ WindowsPowerShell\ v1.0\ profile.ps1
    This profile applies to all users and all shells.
  • %windir%\ system32\ WindowsPowerShell\ v1.0\ Microsoft.PowerShell_profile.ps1
    This profile applies to all users, but only to the Microsoft.PowerShell shell.
  • %UserProfile%\ My Documents\ WindowsPowerShell\ profile.ps1
    This profile applies only to the current user, but affects all shells.
  • %UserProfile%\ My Documents\ WindowsPowerShell\ Microsoft.PowerShell_profile.ps1
    This profile applies only to the current user and the Microsoft.PowerShell shell.

I was a little surprised to find that these files aren’t pre-created on install, but other than that, it seems to work as you would expect.

Unfortunately, functions you write in profile files aren’t automatically added to the tab-completion list. For example, I added a function Connect-WMQQmgr – it would be nice if I could type Co then hit Tab to finish the rest off.

That said, you could always…

Override in-built functions

This is quite nice – you can find the source for a bunch of in-built functions using Get-Content. You can see how they work, and override them to work the way you want. Tweak what is already there, or start from scratch – it seems very powerful. For example:

Get-Content Function:\TabExpansion
This is how PowerShell does it’s tab-completion. By overriding it, you could teach tab-complete to do more, such as adding support for your own functions.

Get-Content Function:\prompt
The command prompt is created by calling the function prompt for every line. Override this, and you can have whatever you want as a prompt. For example, put Write-Host "BLAH $" in the prompt function, and you will get “BLAH $” at the start of every line. Nice.

Error-handling

As you would expect from a .NET-based environment, there are exceptions. The syntax is a little different to the try…catch that I’m used to in C#, but the idea is similar.

function DoSomething-WMQObject
{

    # display details of any WMQ errors encountered in this function
    Trap [IBM.WMQ.MQException]
    {
        Write-Error ("WMQ Exception: CC=" + $_.Exception.CompletionCode + " RC=" + $_.Exception.ReasonCode)
        continue
    }

    # do a bunch of WMQ stuff

}

Rather than explicitly provide the scope that a catch applies to, as you would in C#, you register an exception handler for a function – and execution jumps to that trap block if an exception of that type is thrown. You can have traps for as many different exception types as you want, and/or have a trap block without an explicit exception type to catch all the others.

Global scope for variables

Prefix a variable with $Global: to give it global scope. I used this to let me create something in one function and use it in another. Mainly becaue I was too lazy to pass the handle around properly. 🙂

Accessing constants from .NET libraries

The syntax is slightly different from writing an application in C# and took me a while to get.

If in C# you would write IBM.WMQ.MQC.MQOO_INPUT_AS_Q_DEF, in PowerShell you can write [IBM.WMQ.MQC]::MQOO_INPUT_AS_Q_DEF.

Otherwise, it seems to work okay, although typing these values was useful…

Specifying types

Prefixing an object definition with the type name surrounded by square brackets lets you tell PowerShell it’s type. For example,

[string]$dalesstring

This is a useful trick while developing scripts – by telling PowerShell what type an object is, it can give you more useful tab-completion with it, because it knows what methods are appropriate for an object.

Comparisons

This problem rears it’s head if you try and copy working C# code into a new PowerShell script – the syntax for comparisons is different. For example:

C# :

((bill == null) && (bob > 7)) || (ben < = 18)

becomes
PowerShell :

(($bill -eq $null) -and ($bob -gt 7)) -or ($ben -le 18)

I guess they want to keep | for pipes, and > for redirects!

Once you know it needs changing, it’s fairly straight-forward.

Notice also that you compare with null using $null in PowerShell.

Finding out about objects and methods

Type an object name (on it’s own) to get a list of all of it’s attributes and their values.

Type a method name (on it’s own, without parentheses) to get a description of it. E.g.

PS C:\>  $mystring = "hello world"
PS C:\>  $mystring.CompareTo


MemberType          : Method
OverloadDefinitions : {System.Int32 CompareTo(Object value), System.Int32 CompareTo(String strB)}
TypeNameOfValue     : System.Management.Automation.PSMethod
Value               : System.Int32 CompareTo(Object value), System.Int32 CompareTo(String strB)
Name                : CompareTo
IsInstance          : True

This feature was quite useful while I wrote some code using an undocumented PCF API

Lots more

These are some of the more interesting quirks I came across yesterday, but there were more. I quite like PowerShell – kinda wish I’d stuck with it when I played with it last!

4 Responses to “Turning C# programs into PowerShell scripts”

  1. dale says:

    Also…

    Return more than one object from a function

    You can use Write-Output to put an object into the output stream for a function. This way, you can return multiple objects.

    I really like this – an example of how I am using it can be found on my post on the WMQ blog.

  2. Eric says:

    This is exactly what I expected to find out after reading the title Turning C# programs into PowerShell scripts. Thanks for informative article

  3. […] you pass objects (rather than strings) between commands in a pipe. I’ve done some work on writing PowerShell scripts before, but this time I approached it more as a developer – looking at how to extend the shell with new […]

  4. Lucas says:

    Very good blog post/article. I got this site of http://www.stackoverflow.com. The information that I found here was very helpful. I can’t wait to start writing my own cmdlets.