Asynchronously Save Images in the Clipboard to File

PowerShell v5.0 introduces us with two cmdlets, Get-Clipboard and Set-Clipboard which, just as their names suggest, allow us to obtain and set the contents of the clipboard. What’s not immediately apparent though is that these cmdlets can process more than just text in the clipboard.

In this blog, we’ll focus on using Get-Clipboard, and create a script which is used in combination with Register-EvengineEvent to save an image to file any time that one is detected in the clipboard during the current session. A repo containing the script is available at GitHub

Get-Clipboard supports the processing of several types of data that is in the clipboard, one of which is that of an image type. Examples of these would be print screens or copying an image created in Paint into the clipboard.

When an image is in the clipboard, and we use Get-Clipboard -Format Image, an object of Bitmap type (inherited from System.Drawing.Image) is returned. One of the methods this class provides is Save. Save has several overloads, one of which writes to a file and a variety of formats of your choice.

The script below requires use of the ISE, and uses Register-EngineEvent to register a scriptblock, which runs when a PowerShell.OnIdle event is raised. In the scriptblock, we check to see if there is any content in the clipboard of image type. If there is, we take the details of the current file open in ISE the editor, and use it to generate a unique filename. Then, the content of the clipboard is saved to this filename using the method mentioned above. Finally, we clear the clipboard to ensure that there we don’t end up in a continuous looping operation.

Thanks for reading!



Sharing Events Handlers in PowerShell GUIs

When you setup an event handler and its code, it is helpful to know it can be shared amongst multiple controls whilst still allowing access to the specific control which raised the event.

The first apparent benefit of this is that it instantly reduces the amount of code and code replication in your scripts, but it also gives an insight into what information is received by an event handler.

Although there are some exceptions, a typical event will provide two sets of information that the hander can process

  • The calling object (referenced in your code by use of the variable $this), also known as the Sender
  • The event arguments, passed in as a pipelined object $_

To illustrate how we can use this, we’re going to create a form with two buttons, which share the same event handler, which change the background color to white when the mouse hovers over it, and then back to normal when the mouse leaves the button’s area. You can find a copy of the PowerShell Studio form, and exported .ps1 file (for looking at the pure PowerShell code) at my GitHub repository

  • Create a form with two buttons
  • Select the first button
  • For the MouseHover event, call its handler buttonHover
  • For the MouseLeave event, call its handler buttonLeave
  • Repeat the button setting for the second button
Sharing Events - Force and Event Design

The first buttons configuration

Sharing Events - Force and Event Design Button 2

The second buttons configuration

  For our the event code, use the following:

Shared events in action :


Introducing SMAPLEX, an SMA PowerShell Drive Powered by Simplex

How I learned to stop worrying about writing an SMA PowerShell provider and love the drive

In addition to handling storage, distribution, queuing, and execution of runbooks, Service Management Automation (aka SMA) also performs the storage of resources (referred to in the Windows Azure Pack as ‘assets’). SMA resources consist of connections, credentials, variables (though purists will rightly tell you that they’re constants), schedules, runbooks, and jobs. Access to these elements can be carried out via the cmdlets provided with SMA, from its web services, or, if you’re using it, the Windows Azure Pack admin portal itself.

I thought I’d set myself the challenge of seeing if I could write a PowerShell Provider for SMA so that I could review all my resources from a single drive. This prove to be too much of a challenge for me. Fortunately I found out that MVP Jim Christopher (@beefarino on Twitter), has written a PowerShell module called Simplex.


Written for implementation with PowerShell in a module, Simplex deals with the parts that you would normally need to write a provider for. All that’s really required is to define the structure and PowerShell code in a domain specific language (DSL) style template for the provider you wish to create.

NOTE : Simplex creates a navigation provider. This means that whilst you are able to list and access content, writing new and updating content is not possible.

As mentioned, the contents of this file use a DSL type syntax, of which there are only three keywords.

Root is at the top of your provider code and defines what you will see if you are at the root of the providers drive. e.g. SMA:\. Nested under there, using braces to indicate the blocks can be the two other keyboards.

Folder indicates a container within our tree structure. The text immediately after this is the name given to the folder itself. The main thing to keep  in mind with folder keywords in a configuration script is that they represent a static item whose contents will not change.

Script is similar to a folder, but with the exception that when a PS Drive compatible command, such as Get-ChildItem, is used to list contents, the code within braces is dynamically run.

Configuring the SMA PowerShell Provider

Ensuring that you’re running on a system with the SMA cmdlets already installed, there’s four things we need to do in order to setup our SMA drive (or indeed any provider) :

  • Download the latest Simplex module
  • Install the module
  • Create our provider template
  • Register the PS Drive

Download and Install the Simplex Module

         smaplex - jims repo

  • Click Download Module Zip

         smaplex - jims appveyor build

  • Download Simplex
  • Create a folder called Simplex in one of your module paths
  • Extract the contents of the ZIP file to this folder

         smaplex - installing the module

Provider Configuration Document

Copy the content below into the clipboard, and save the content to a location on your hard disk.

(Note that there is no code for Connections, simply because it is not a feature I use at all within my Runbooks)

Configuration Document Structure

The document content for our SMA PS Drive above will generate a structure of the following type.

  • ROOTDRIVE: (We’ll be calling the drive SMA in this blog, so it will be SMA:)
  • Connections
    • Result of Connections scriptblock
  • Credentials
    • Result of Credentials scriptblock
  • Schedules
    • Result of Schedules scriptblock
  • Variables
    • Result of Variables scriptblock
  • Jobs
    • Result of Jobs scriptblock
  • Runbooks
    • Result of Runbooks scriptblock

A Quick Look at a Script section

Let’s take a look at the code within the Credentials scriptblock above.

The first thing to notice is that is now pure PowerShell code. The tasks it performs is to define the default parameters that will be returned, execute the command Get-SMACredential, an SMA cmdlet, and then output this object. You’ve probably noticed that we’re using a splat for it’s parameters. This is defined in our intialisation script, which is covered later in this article.

The rest of the script sections use a similar methodology to obtain the associated information from SMA. However, in the Runbook section, you’ll see that the runbook definition section is commented out. The reason for this is becaue it can result in substantially long waits before the information is returned. Because i am using about 50 runbooks, this is an option i’m currently not . It may be possible to speed up this operation by a direct SQL query or using multiple jobs. I’ve yet to test this though.

Initialising the PS Drive

Now that we’ve installed Simplex and have written our initial configuration document, all this is left to do is initialise the drive. After importing the module in a PowerShell session we use the New-PSDrive command with custom PSProvider and Root parameter settings. The value for Root is set to the path of the configuration document we earlier created.

In this same script we create a credential object, which is included along with the WebServiceEndpoint in our hashtable, used for paramter splatting in the configuration document.

SMA PSDrive in Action

Run the code above. All being well, you will shortly see confirmation that the drive has been initialised.


From this point, you can navigate and use navigational and filtering cmdlets, such as Get-ChildItem, and Where to customise your output.


  • Set-Location SMA:
  • Get-ChildItem
sma - get-childitem

Change to SMA drive and list contents


  • Set-Location -Path .\Runbook
  • Get-ChildItem | Sort RunbookName
sma - list runbooks

List Runbooks,  sorting on the RunBookName property


  • [datetime] $before = ‘1/5/2016’
  • Get-ChildItem | Where StartTime -LT $before
sma - get and filter jobs

Only list jobs which started before a certain date


Combining the use of SMA cmdlet’s with Jim’s module to make a PS Drive has made the retrieval of information from its database a relatively simple process, and makes it easy for extension at a later date. The data abstraction achieved by implementing a PS-Provider is also something for me personally that is desirable for writing other scripts that can use this for data retrieval.

Thanks for reading, and you can find the files for SmaPlex at my GitHub repo.



Automatic Parameter Splatting ISE Add-On

Here’s some code I put together which converts parameters in a command to a splatted variant of them. There’s definately room for improvement with the code (it can struggles with some sequences of parameters), so any contributions or ideas would be great for this. It’s posted in my GitHub area.

The function below parses the selected text and attempts to split them into separate chunks of parameters, which are processed and then concatenated into a single string. Once complete, the existing parameters are removed from the command and replaced with the splat, and the hash table placed on the line above.

Run the function to store it memory. Now, in order for us to be able to call the function either from a menu or keyboard option in the ISE, run the following code. :

With this done, all that’s required is to :

  • Select the parameters you wish splatted in the command
  •  Either
    • Click Add-ons in the menu bar
    • Click Get Splat
  •  Or
    • Press ALT + T
Select the parameters

Selection of the parameters and splatting option

  • View the results
The result of the autosplat

The results of the autosplat

Here’s a video of it in action:


Creating a Shortcut for a Button in a PowerShell GUI

While most of us are all still friends with the mouse in GUIs, keyboard shortcuts once learned are very effective ways of manipulating form controls quicker.

Serveral controls, such as buttons can use keyboard accelerators. This is simply done by prefixing the Text property of the control by an ampersand. By doing this, you can perform the same as the Click event for that control simply by holding down the ALT key on the keyboard, and then the appropriate letter.

For example, a button control with its text property set to &OK will respond to the keyboard combination ALT + O as if the mouse had clicked on it. In keeping with normal Windows functionality, you can identify the keyboard shortcut by holding down the ALT key, and the letter for the accelerator will be listed with an underline.

Here’s how you’d do this in PowerShell Studio :

Keyboard Shortcut

And in PowerShell code itself, this is represented as :


Font Anti-Aliasing in PowerShell GUIs


The text on winform projects may seem blocky sometimes, particularly at larger font sizes. It never seems to look as polished as those we see with Microsoft products.

Here’s a standard form using Segio 24pt in white. It’s pretty blocky, especially around letters such as ‘S’ and ‘P’. Click on the image below to see it in normal size to get an idea how this looks.


Text without Anti-Aliasing

In order to make our text smoother and more, anti-aliasing will need to be used.

What’s Anti-Aliasing?

Anti-aliasing is the name given to actions which aim to minimise this type of blockiness. It works by shading the pixels along the borders of an image. In order to achieve this for ourselves, we need to dig into some .NET methods and events.

This post shows how we do just that for our fonts in PowerShell Studio, but can be applied in whatever way you develop your GUI apps.

Create a Project

Create a new forms project, making the form a good size. Now, drag a PictureBox control onto the form, and set the size of it close to the size of the form itself.

Set the properties for the PictureBox as follows :

Dock = ‘Top’
BackColour = 1;3;86

Create Positioning Functions

Because we’re going to be working with graphics, we need to define placement based on a horizontal and vertical basis. Whilst we could use absolute positioning, where we manually specify a fixed location, this style is rendered invalid if we make changes to size of the control or the graphic itself. Its relative positioning is changed.

Instead, if we use calculations for positioning, it gives us the flexibility to change control and graphic sizes after the code has been written without having to modify it later.

Thinking about this, there are several things we need to do :

  • Get the midpoint of the control (our picture box)
  • Get the midpoint of the graphic (our drawn string)
  • Get the position for placement of the graphic on the control

Picture Box Control

In this function, we simply pass in the PictureBox control, and work out its center point by adding the left most point with its width, and then divide by two. This gives us the horizontal (aka x) location. Similarly, we add the control’s top most point with it’s height and divide by two to get its vertical center point. These values are then returned as an object.

Drawn String

We’re working in a similar manner to the above function, but require to do a bit more calculation. We pass in a graphics object, previously instantiated, the string to be drawn, and the font itself. The MeasureString method of the grahpics control is then invoked, using the other two parameters we’ve just mentioned. Once that is completed, we obtain the horizontal and vertical midpoints.

Position from Drawn String

Now that we have our two sets of midpoints, we can calculate where the drawn string should be placed. This is done by subtracting the result of the latter function from the former for both x and y positions, and returning the results.

Create Event Code

Now that we’ve defined out functions, we can setup the event action and code.

The Paint event occurs when a control requires repainting. This can happen as a result of other actions on a form or control, such as scrolling to the bottom of a form at to the top again.
Go to the Paint event of the PictureBox in the properties panel, and double click on it. Insert the following :

Code explanation

What happens in this code is the following:

  • We obtain the Graphics properties from the event data and assigning it to a variable $g.
  • We define some of the properties of the picturebox control.
  • A Font type variable is created using Sergoe UI typeface in size 84 as the supplied parameters
  • A SoldBrush object is created. This is the drawing style we use, just like in the Paint application.
  • The functions mentioned previously are then used
  • With the above completed, we define a floating point coordinate object
  • We also set the type of anti-alias rendering to be carried out. Several options are available. See the links at the bottom of this page to get more information on these
  • We now run the DrawString method to create and position the anti aliased rendering of the font.

View the New Results

Now run the project to see a much smoother version.


Text with Anti-Aliasing

I’d recommend taking a look at the System.Drawing namespaces documentation on MSDN to get more information on the methods, properties, and enumerations used in this code, which will explain in more detail about each.

You can find the Project code and exported code (for those not using PSStudio) at my GitHub repository, and also a video of this in action on my YouTube channel.




Logging Functions

Here’s a collection of logging functions I came across that I’ve found quite useful.

Write-Log takes the string to be written, along with $logfile, $consoleoutput, and an $iserror switch. The time is identified. The message is then added to it, prepended with ERROR : if the iserror switch is passed.

simply configures a script level variable. We use this variable to decide whether information logged to file is also output to the screen at the same time.

Start-Log begins the process of creating a file. If a file already exists, the overwrite parameter will determine what is done with this current file. When this is identified, the script level variable, $logfile is set.

Stop-Log sets the value of the above script variable to null, which will result in an error the if we still attempt to use Write-Log

Get-Log returns the current value of the script level logfile variable

A very simple example of its use would be :

Output wise, we then can get something like this :

[2015-12-31 14:22:53] Begun executing myfile.ps1
[2015-12-31 14:22:53] Configuring provisioning account
[2015-12-31 14:22:53] Provisioning account credentials configured
[2015-12-31 14:22:53] Creating secure string password
[2015-12-31 14:22:53] Credential object created
[2015-12-31 14:22:57] Azure Active Directory parameter is not set
[2015-12-31 14:22:57] RDWEB URL is set to https://rdweb.contoso.local
[2015-12-31 14:22:57] Multitenancy is not enabled
[2015-12-31 14:22:57] Email subject is Provisioning setup
[2015-12-31 14:22:58] Text body configured as HTML
[2015-12-31 14:22:53] Execution of myfile.ps1 is complete


Using .NET Event Handlers in a PowerShell GUI

GUI development tools, such as PowerShellStudio, make it very easy to manage events for controls on our winforms.

Once the control is on the form, and we select it, click on the Events button (the lightning symbol), the Properties panel gives us a list of the events available for us to manage. However, events are not just restricted to controls. There’s a world of other events out there that we can use to interact with our winforms projects.

In this article, we’ll create a forms project that downloads the latest 64 bit antimalware definitions from Microsoft and updates a progress control to show how far the download is to completion, using methods and events from a .NET class.

Updates to the latest antimalware definitions can be obtained through and a look through MSDN shows us that we can use the .NET WebClient class to carry out downloads programmatically.

To start this process, create a new forms project, and drag a progress bar, label, and button onto the form. Then set the properties of the controls as below. Note that properties with text controls will automatically be named for you if you set the text property first.

Text : Progress
Name : labelProgress

Text : Download
Name : buttonDownload

Progress Bar
Name : progressbarDownload

Here’s how my form looks.

Blog - Adding Events - Form Design

Once this is complete, we can begin writing the event code.

In our forms Load event, we create an instance of the System.Net.Webclient class. This is assigned to the script level variable, $webclient. This scope is required in order for the other parts of the solution to be able to process the object and its events.

The next two lines add event handlers for the DownloadProgressChanged and DownloadFileCompleted events. DownloadProgressChanged indicates a change in the state of the transfer with regards to the amount of content downloaded, whilst DownloadFileCompleted is fired on the completion of a download. The scriptblocks for these are $webclient_DownloadProgressChanged and $webclient_DownloadFileCompleted respectively.

The event handler for updating the progress of the download is written next:

To make it easier to read, $progressInfo is used for the rest of the code instead of $_. The variable contains the values given to us by the System.Net.DownloadProgressChangedEventArgs class instance that is passed into the handler.

The DownloadProgressChangedEventArgs class contains ProgressPercentage, BytesReceived, and TotalBytesToReceive properties. We use these for changing the progress meter value property, and also updating the text in the label below to show bytes received and the total size of the download.

The event handler for DownLoadFileCompleted is next:

When DownloadFileCompleted is fired, the label text is changed to indicate the download’s completion.

Lastly, the download button’s Click event is set to begin an asynchronous download of the antimalware definition.

Blog - Adding Events - Code

Our project code

And when we run the project and click on Download! We see this in action, with the progress bar being updated and the progress text below it also, using the code we wrote earlier.

Blog - Adding Events - Downloader Running

The downloader in action

This same methodology can be employed for using .NET events, creating an instance of the object, adding the event handler definition, and then the scriptblock code to be used.

You can find exported project code and the project files at my repository on GitHub, and a short video of the project in action on the YouTube channel.



Copying files to and from remote sessions

Those of you running Windows 10 or Server vNext (I’ve yet check WMF 5.0 RTM on other operating systems after it was pulled by Microsoft from DownloadCenter), may have missed the inclusion of new parameters for Copy-Item, which allow to copying of files to a remote session and from a remote session. These are names ToSession and FromSession respectively.

Oddly, Microsoft do not appear to have updated their help to provide information on this. You’ll only notice it if you use Get-Command or alternatively tab through Copy-Item.

What’s nice is that PowerShell 5 is not required to be installed on the system for which we have created a remote session.

Usage is simply a case of providing a valid value of PSSession type to the ToSession or FromSession parameters, in addition to your standard Path and Destination parameters.

Examples are below :

Copy Remote Sessions

Unfortunately at present, FromSession and ToSession are mutually exclusive, preventing the possibility of being able to directly copy from one remote session to another.


Removing Items from Lists Using the Keyboard

In an earlier post, we created a form with two listgridview controls, and used both add and remove buttons, and double click functionality to allow an item to move an item from one control to another in an intuitive manner.

Todays post shows how we can use a remove items from a list by pressing a key. Specifically, we’ll be using the DELETE key for removing items. This will be carried out in Sapien’s PowerShell Studio 2015, but the same methodology applies to however you generate winforms in PowerShell.

For initial setup, carry out the following :

  • create a new form with a header and several items.
  • create a listview control called $lstControl
  • populate the control with a header
  • add some items to the listview control

Sample form and control layout


  • select the control
  • select Events in the properties pane
  • double click on KeyDown
  • Insert the following code :

The KeyDown event’s code


Go ahead and test it, adding an item to the control on the right hand side, selecting it, and then pressing the DELETE key.

Also, because the code loops through each selected item in the control, we can also use standard multiple selection functionality offered by the various use of the SHIFT and CTRL combinations.

Two items are selected using a click SHIFT combination

The DELETe key is pressed, and the previous selected items are removed from the list

You can find the files, if you are using PowerShell Studio 2015, or exported .ps1 file at my gitlab repository.

Additionally, there’s a (basic) video of this code in action at my channel on You Tube.

Thanks for reading, and feel free to provide feedback.