PowerShell Basics – .NET
One of the reasons why PowerShell is so good and so easy to sell to people is because of .NET. PowerShell is a .NET 2.0 application and it uses .NET to achieve virtually everything it does – but how can users leverage to power of .NET from within PowerShell?
How do I create an object?
There are basically two way to get an object to work with in PowerShell, either call new-object or get one returned by calling something.
Things return objects?
[D:\PsScripts]
1> get-process
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
——- —— —– —– —– —— — ———–
256 12 77716 70736 558 0.91 5680 powershell
What’s actually going on here? It turns out that the get-process cmdlet returns .NET objects, in the above case it’s an array of System.Diagnosticts.Process objects and that allows us to work with them the same as if we had created them in code.
You can work with objects by either keeping a reference to them or by passing them down the pipeline to another cmdlet, object or piece of script. If you want to know what you can do with an object, call get-member:
[D:\PsScripts]
2> $processes = get-process
[D:\PsScripts]
3> $processes.Length
90
[D:\PsScripts]
4> get-member -InputObject $processes
TypeName: System.Object[]
Name MemberType Definition
—- ———- ———-
Count AliasProperty Count = Length
… … …
An object array? Where are the process objects?
[D:\PsScripts]
5> get-member -InputObject $processes[0]
TypeName: System.Diagnostics.Process
Name MemberType Definition
—- ———- ———-
Handles AliasProperty Handles = Handlecount
Name AliasProperty Name = ProcessName
… … …
An you can achieve the same by passing the object down the pipeline:
[D:\PsScripts]
6> Get-Process | Get-Member
TypeName: System.Diagnostics.Process
Name MemberType Definition
—- ———- ———-
Handles AliasProperty Handles = Handlecount
Name AliasProperty Name = ProcessName
… … …
What if I don’t want an object of type System.Diagnostics.Process?
The other way to get an object is to call the new-object cmdlet.
[D:\PsScripts]
7> $date = New-Object DateTime
[D:\PsScripts]
8> $date
01 January 0001 00:00:00
And if you need to specify arguments to the constructer, just pass them in after the type:
[D:\PsScripts]
9> $date2 = New-Object DateTime 2007, 2, 18
[D:\PsScripts]
10> $date2
18 February 2007 00:00:00
How to I call a method on my object?
Calling methods is the same in PowerShell as it is in C# (object.method()):
[D:\PsScripts]
11> $date2.ToShortDateString()
18/02/2007
Properties are also the same (object.Property):
[D:\PsScripts]
12> $date2.Year
2007
Tab complete works with object methods and properties by the way, making it easier to get at what you want.
What type is my object?
The easiest way to determine an objects type is to just call the GetType() method of the object;
[D:\PsScripts]
13> $date2.GetType()
IsPublic IsSerial Name BaseType
——– ——– —- ——–
True True DateTime System.ValueType
[D:\PsScripts]
14> $processes.GetType()
IsPublic IsSerial Name BaseType
——– ——– —- ——–
True True Object[] System.Array
[D:\PsScripts]
15> $processes[0].GetType()
IsPublic IsSerial Name BaseType
——– ——– —- ——–
True False Process System.ComponentModel.Component
What about Statics?
Statics can be invoked by using the following syntax: [Type]::Property or [Type]::Method(). For example:
[D:\PsScripts]
16> [DateTime]::DaysInMonth(2007, 2)
28
[D:\PsScripts]
17> [DateTime]::Now
18 February 2007 20:16:55
OK, but I have this assembly with types in it I’d like to call…
Before we talk about loading a custom assembly into the PowerShell Application Domain, you’re going to want to know if you need to or not. The following shows how to get all the loaded assemblies:
[D:\PsScripts]
18> $psAppDomain = [AppDomain]::CurrentDomain
[D:\PsScripts]
19> $psAppDomain.GetAssemblies()
GAC Version Location
— ——- ——–
True v2.0.50727 C:\WINDOWS\Microsoft.NET\Framework64\v2.0.50727\mscorlib.dll
True v2.0.50727 C:\WINDOWS\assembly\GAC_MSIL\Microsoft.PowerShell.ConsoleHos…
True v2.0.50727 C:\WINDOWS\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e08…
True v2.0.50727 C:\WINDOWS\assembly\GAC_MSIL\System.Management.Automation\1….
True v2.0.50727 C:\WINDOWS\assembly\GAC_MSIL\System.Configuration.Install\2….
True v2.0.50727 C:\WINDOWS\assembly\GAC_MSIL\Microsoft.PowerShell.Commands.M…
True v2.0.50727 C:\WINDOWS\assembly\GAC_MSIL\Microsoft.PowerShell.Security\1…
True v2.0.50727 C:\WINDOWS\assembly\GAC_MSIL\Microsoft.PowerShell.Commands.U…
True v2.0.50727 C:\WINDOWS\assembly\GAC_MSIL\System.Xml\2.0.0.0__b77a5c56193…
True v2.0.50727 C:\WINDOWS\assembly\GAC_MSIL\System.Management\2.0.0.0__b03f…
True v2.0.50727 C:\WINDOWS\assembly\GAC_MSIL\System.DirectoryServices\2.0.0….
True v2.0.50727 C:\WINDOWS\assembly\GAC_64\System.Data\2.0.0.0__b77a5c561934…
False v2.0.50727 C:\Program Files (x86)\PowerShell Community Extensions\pscx.dll
The best way to load an additional assembly into the PowerShell App Domain is to use the System.Reflection.Assembly class. It exposes several statics methods for loading assemblies. You can find details of these methods on MSDN, they all begin Load – I suggest that they be studied so you know which methods to call for what type of assembly:
[D:\PsScripts]
20> [System.Reflection.Assembly]::Load(”System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a”)
GAC Version Location
— ——- ——–
True v2.0.50727 C:\WINDOWS\assembly\GAC_64\System.Web\2.0.0.0__b03f5f7f11d50a3a\System.Web.dll
[D:\PsScripts]
21> [System.Reflection.Assembly]::LoadFrom(”SolveSudoku.dll”)
GAC Version Location
— ——- ——–
False v2.0.50727 d:\PsScripts\SolveSudoku.dll
[D:\PsScripts]
22> $psAppDomain.GetAssemblies()
GAC Version Location
— ——- ——–
True v2.0.50727 C:\WINDOWS\Microsoft.NET\Framework64\v2.0.50727\mscorlib.dll
True v2.0.50727 C:\WINDOWS\assembly\GAC_MSIL\Microsoft.PowerShell.ConsoleHos…
True v2.0.50727 C:\WINDOWS\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e08…
True v2.0.50727 C:\WINDOWS\assembly\GAC_MSIL\System.Management.Automation\1….
True v2.0.50727 C:\WINDOWS\assembly\GAC_MSIL\System.Configuration.Install\2….
True v2.0.50727 C:\WINDOWS\assembly\GAC_MSIL\Microsoft.PowerShell.Commands.M…
True v2.0.50727 C:\WINDOWS\assembly\GAC_MSIL\Microsoft.PowerShell.Security\1…
True v2.0.50727 C:\WINDOWS\assembly\GAC_MSIL\Microsoft.PowerShell.Commands.U…
True v2.0.50727 C:\WINDOWS\assembly\GAC_MSIL\System.Xml\2.0.0.0__b77a5c56193…
True v2.0.50727 C:\WINDOWS\assembly\GAC_MSIL\System.Management\2.0.0.0__b03f…
True v2.0.50727 C:\WINDOWS\assembly\GAC_MSIL\System.DirectoryServices\2.0.0….
True v2.0.50727 C:\WINDOWS\assembly\GAC_64\System.Data\2.0.0.0__b77a5c561934…
False v2.0.50727 C:\Program Files (x86)\PowerShell Community Extensions\pscx.dll
True v2.0.50727 C:\WINDOWS\assembly\GAC_64\System.Web\2.0.0.0__b03f5f7f11d50…
False v2.0.50727 d:\PsScripts\SolveSudoku.dll
What’s next?
Well, that’s all you need to know to really get going with calling .NET from within Windows PowerShell. Using the information provided here and armed with what-ever SDK you need, you can use PowerShell to script anything that exposes a .NET API. This includes SharePoint and your own custom .NET application.
IT Pro’s – you better start asking developers for better class documentation so you can maintain and support bespoke applications better.
Whilst invoking .Net from the command line is powerful, it’s no substitute for cmdlets and providers. That said, application developers need to start creating cmdlets before they can be used. In the meantime use .NET API’s to script what couldn’t be scripted before J
Filed under: PowerShell
Leave a Reply