WPF & PowerShell – Part 1 ( Hello World & Welcome to the Week of WPF )

WPF & PowerShell – Part 1 ( Hello World & Welcome to the Week of WPF )

  • Comments 26

Welcome to the Week of WPF.  During the next 7 days, I’ll help show you how you can use WPF and PowerShell together.

PowerShell could always script almost everything in .NET, but, prior to the recent CTP2 you could not script Windows Presentation Foundation (WPF) in PowerShell.

Now you can script everything that WPF can do within PowerShell.

This means you can write pretty sleek looking UIs in nice little PowerShell scripts.

There are a lot of things you can do with this capability, but let’s start things off simple.

 

Windows Presentation Foundation (WPF) is a set of .NET libraries for making next-generation user interfaces.  In order to script WPF classes, you have to start them in a Single Threaded Apartment (STA).  Luckily, starting in CTP2, you can run code in an STA a few ways in PowerShell.

 

In order to script WPF, you have to do one of three things:

·         Run the Script in Graphical PowerShell, where WPF scripts will run without any changes because Graphical PowerShell runspaces are STA.

·         Run the script in PowerShell.exe and add the –STA script

·         Create a background runspace that is STA, and run the script in the background runspace

 

In this post, I’ll show you a “Hello World” script, and how to run it in each of the three modes.

 

First, let’s write our “Hello World” script.   Open up Graphical PowerShell (gPowerShell.exe) and create a new script file (CTRL + N).

 

Here’s the Hello World file:

 

$window = New-Object Windows.Window

$window.Title = $window.Content = “Hello World.  Check out PowerShell and WPF Together.”

$window.SizeToContent = “WidthAndHeight”

$null = $window.ShowDialog()

 

Now in Graphical PowerShell, you should be able to just run it and be done.

 

You can run the current script in Graphical PowerShell with F5 or the Green Run button in the upper left of the editor.

 

Now let’s run the script in STA Mode PowerShell (Run PowerShell –sta).  Save it as “HelloWorld.ps1”

 

Since PowerShell.exe –sta doesn’t load up WPF’s assemblies, lets run these three lines to add the references:

 

Add-Type –assemblyName PresentationFramework

Add-Type –assemblyName PresentationCore

Add-Type –assemblyName WindowsBase

.\HelloWorld.ps1

 

Now you have a Hello World in WPF from the console version of PowerShell.

 

Finally, you can embed scripts to generate WPF inside of an application or a background runspace.  This has some advantages over the first two approaches… namely, without a background

 

The code below will create a background runspace and set the two properties required to make WPF work in a PowerShell runspace, ApartmentState and ThreadOptions.  ApartmentState determines if the runspace PowerShell is single or multithreaded (WPF requires single threaded), and if the same thread is used every time a command is run, or if a new thread is created each time.  For WPF scripting to work in a runspace, you have to set ApartmentState to “STA” and ThreadOptions to “ReuseThread”.  In the code below, the first 3 lines set up the runspace, the next 10 lines ensure that runspace is able to load WPF classes, and the final 4 lines run our original HelloWorld.ps1

 

# Create a runspace to run Hello World

$rs = [Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace()

$rs.ApartmentState, $rs.ThreadOptions = “STA”, “ReuseThread”

$rs.Open()

# Reference the WPF assemblies

$psCmd = {Add-Type}.GetPowerShell()

$psCmd.SetRunspace($rs)

$psCmd.AddParameter("AssemblyName", "PresentationCore").Invoke()

$psCmd.Command.Clear()

$psCmd = $psCmd.AddCommand("Add-Type")

$psCmd.AddParameter("AssemblyName", "PresentationFramework").Invoke()

$psCmd.Command.Clear()

$psCmd = $psCmd.AddCommand("Add-Type")

$psCmd.AddParameter("AssemblyName", "WindowsBase").Invoke()

$sb = $executionContext.InvokeCommand.NewScriptBlock(

(Join-Path $pwd "HelloWorld.ps1")

)

$psCmd = $sb.GetPowerShell()

$psCmd.SetRunspace($rs)

$null = $psCmd.BeginInvoke()

 

This is just a taste of some of the fun that PowerShell and WPF can have together, and how to get it working.  You can now use everything in WPF in a PowerShell script.

 

During the Week of WPF, I will post one script each day that will take us a little further down the rabbit hole of what PowerShell and WPF can do together.  After the week of WPF is done, keep your eyes on this space, because every Wednesday, I’ll try to post a “WPF Wednesday Widget” that uses showcase using PowerShell and WPF together.

 

Hope this Helps,

James Brundage [MSFT]

Leave a Comment
  • Please add 8 and 8 and type the answer here:
  • Post
  • The questions is that I can't execute a powershell script without the black console window. For example when I run a vbs script, we only need to run the command: wscript.exe example.vbs and this command do not open a cmd console :-)

    Another example. Look my example.ps1 script:

    [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")

    [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")

    $objForm = New-Object System.Windows.Forms.Form

    $objForm.Text = "Example"

    $objForm.Size = New-Object System.Drawing.Size(100,100)

    $objForm.StartPosition = "CenterScreen"

    $objForm.Add_Shown({$objForm.Activate()})

    [void] $objForm.ShowDialog()

    when I run this script: powershell.exe .\example.ps1 the windows form appears correctly but I can´t hide the powershell console. How can I do?

  • Pablo,

    Ah yes, the annoying black widow... ;-)

    I've tried posting URLs in the comments before and they seem to always be rejected.  I'd go to the microsoft.public.windows.powershell newsgroup and ask...

  • I'll include this in a later post in the series, but I'll post it here for Pablo.  You can Minimize /Maximize Powershell by calling Win32 Functions through C interop:

    $showWindowAsync = Add-Type –memberDefintion @”

    [DllImport("user32.dll")]

    public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);

    “@ -name “Win32ShowWindowAsync” -namespace Win32Functions –passThru

    # Minimize PowerShell

    $showWindowAsync::ShowWindowAsync((Get-Process –id $pid).MainWindowHandle, 2)

    sleep 2

    # Then Restore it

    $showWindowAsync::ShowWindowAsync((Get-Process –id $pid).MainWindowHandle, 4)

    Hope this Helps,

    James Brundage[MSFT]

  • Great! It is what I surely hoped.

    But I hope -Minimize and -NoWindow start options for powershell.exe, too.

    Are they shipping on next milestone? Or will ship?

  • @PowerShellTeam

    Thanks. Interesting workaround :-) I'll try it. It will be very interesting for IT Administrators that we can hide the console completely (if the black window is minimized the final user can close it). Thanks again for the help.

  • My latest in a series of the weekly, or more often, summary of interesting links I come across related to Visual Studio. JetBrains has announced the release of the ReSharper 4.0 Beta . Hilton Giesenow has created a video titled How Do I: Add A Dialog

  • I`ve just tried your code on PS V2 CTP Graphical and I`ve received an exception:

    Exception calling ".ctor" with "0" argument(s): "The calling thread must be STA, because many UI components require this"

    So probably there is something to tune in runspace.

    The only change I`ve made to standard PS Runspace is

    Set-ExecutionPolicy RemoteSigned

    Regards,

    Gianluca

  • Gianluca,

    You must be running the v2 CTP2 gPowerShell.  A v2 CTP was release also, but it doesn't support STA.  I'm assuming the problem is that you're running an old CTP version.

  • You are right!

    I`ve missed the CTP2 release announce, sorry.

    Just downloaded, thanks.

  • Lee and Suzanne here makes a good point. Shame on all of us for not at least acknowledging that this

  • Hi all,

    I installed the latest PS-version for XP, SP3. When I run the example code, I also get a...

    "

    Exception calling ".ctor" with "0" argument(s): "The calling thread must be STA, because many UI components require this"

    "

    What's wrong? -A version dump shows this:

    > $PSVersionTable

    Name                           Value

    ----                           -----

    CLRVersion                     2.0.50727.3603

    BuildVersion                   6.0.6002.18111

    PSVersion                      2.0

    WSManStackVersion              2.0

    PSCompatibleVersions           {1.0, 2.0}

    SerializationVersion           1.1.0.1

    PSRemotingProtocolVersion      2.1

    TNX!

Page 2 of 2 (26 items) 12