Making Windows PowerShell ISE “Good Enough.”

by oising 14. February 2011 19:23

I don’t generally write a lot of big scripts, but when I do, I usually fire up the PowerShell 2.0 graphical IDE. However, every time I use it to develop a module I always get irritated by the lack of any session management. When I hack away on a module, I usually have a couple of files open. I rarely finish a module in one sitting either, so I’ll come back to it in a day or two. I don’t always leave ISE open though so when I fire it up again I have to navigate to the location (or locations) where my files are and re-open them. Another annoying situation is playing with assemblies either by loading some external ones, or by using Add-Type to create your own in-memory types/classes. Debugging these things can get annoying as once loaded or created, you have to quit ISE to “clean” memory of all your dabblings. Well, I lie when I say you have to quit ISE. Strictly speaking, you can create a new tab with CTRL+T, close the old one and load all of your scripts into the new tab. Tabs are isolated from one another and assemblies loaded in one are not available elsewhere. So, taking advantage of this trick, let me introduce:

ISE Session Tools Module v1.0

I have more features planned for v1.1 to give ISE a proper “project” based feel to it, but in the interest of shipping something, v1.0 has the following features:

  • AutoSaving of current session (files open in current tab.)
    • This can be disabled and manually controlled if desired.
  • Prompt to reload last session on ISE open
    • A hint is shown to you reminding you of some of the files you had open.
    • Press <enter> to accept the default of “Yes, reload my last session.”
  • Restarting of the current tab
    • Essentially cleaning memory and keeping your files open in the editor.
    • You get prompted for this action. Press <enter> to accept default of “Yes, restart this tab.”
  • All commands available under “Add-ons” menu for the mouse-fixated.

image

The commands exported by the module are:

  • Restart-PowerShellTab  (CTRL+ALT+R)
  • Export-PowerShellTab (CTRL+ALT+S)
  • Import-PowerShellTab (CTRL+ALT+L)
  • Enable-AutoSaveSession (CTRL+ALT+A)
  • Disable-AutoSaveSession (CTRL+ALT+X)

As you can see, they have hotkeys. This is possible because these commands are also bound to the “Add-ons” menu. I was careful to choose keys that do not interfere with IsePack if you like to use that module too.

image

Installing IseSessionTools

Extract the files  into a folder named “IseSessionTools” under <my documents>\WindowsPowerShell\Modules\. In my ISE $profile, I have the following lines at the end of the file:

Import-Module ISESessionTools
Enable-AutoSaveSession

If you want to clear your session, disable autosaving and delete the session file at ~\psise.session.clixml.

Download ISE Session Tools Module v1.0

Have fun!

Tags:

Eventing | Modules | Monad | PowerShell | PowerShell 2.0 | PowerShell ISE

PowerShell 2.0–Implementing a Matrix-Style Console Screen Saver

by oising 18. December 2010 03:20

I’ve always been jealous of XTerm’s uber-geeky console mode screensaver, CMatrix. It was written shortly after the Matrix came out about ten years ago, and when I first saw it I thought it was really cool. I accidentally ran into it again recently and thought: “Hey, I could write that for PowerShell.” So I did.

PowerShell Screen Saver

The Matrix animation code uses the RawUI interface and is probably not all that interesting. It uses a simple “game loop” routine, repeatedly calling a “Step” function on multiple instances of a custom object, each representing an animating vertical column of letters. Each column is a dynamic module instantiated using the “-ascustomobject” parameter to allow encapsulation of state. I had originally intended to just prototype it using immediate writes to the console buffer, then moving onto a “frame buffer” implementation whereby the entire screen would be rendered into a BufferCell[,] array with a single SetBufferContents each cycle. It turns out though that the simpler implementation runs fast enough so there’s no need to get fancy pants. The idle detection was a little trickier. It uses PowerShell 2.0’s Eventing feature to start an instance of System.Timers.Timer running in the background. Instead of starting and stopping the timer during idle detection, which is actually quite a sluggish operation, the timer’s Elapsed handler increments a global variable every second. Each time the prompt function executes, it resets the counter back to 0. If this variable reaches the timeout value (default 3 minutes) it will invoke the screen saver from within the event handler. This allows the screen saver to activate without any user interaction. When running in an Action handler like this, you have to use CTRL+C to exit it; for some reason the $host KeyAvailable property doesn’t work correctly in this context. If you manually invoke the screen saver with Start-ScreenSaver, you can hit any key to exit. There is also some extra state being kept to temporarily disable the timer when the screen saver is active, or if the user has issued Disable-ScreenSaver. The implementation is a PowerShell 2.0 Module named “ScreenSaver.” To set it up on your console (sorry, it doesn’t work in graphical shells like ISE, PowerGUI or PoshConsole) just put in the following three lines in your $profile after you have installed the module.

import-module screensaver
set-screensavertimeout (new-timespan -minutes 5)
enable-screensaver

There are a couple of self explanatory functions exported from the module: Enable-ScreenSaver, Disable-ScreenSaver, Set-ScreenSaverTimeout <timespan | int>, Get-ScreenSaverTimeout and Start-ScreenSaver. Remove-Module ScreenSaver will do the right thing and clean up, disposing the timer. By the way, it is possible to have implemented this without the user of global variables but it would have added more complexity for little gain.

Or view the source on PoshCode. Normally I would paste the code inline, but there’s a bit more than my normal posts would have. Feel free to clone the PoshCode script and add your own screen saver patterns. Btw, you can tweak the number of columns and the animation speed by examining the Start-ScreenSaver function.

Have fun!

Tags:

.NET | Eventing | Functions | Modules | Monad | PowerShell | PowerShell 2.0

PowerShell ISE Hacking: Change default save encoding to ASCII

by oising 21. May 2010 21:58

If you want to do this, you won’t really need much of an explanation as to why I’m posting this. As to the rest of you, never mind; stick with your BigEndian Unicode (hint: powershell console and other console applications prefer ASCII) :)

First up, create yourself a Windows ISE Profile script that will be loaded by default when ISE starts (and/or when you open a new “Tab”)

# run this one-liner from within ISE through the interactive window (command pane):
if (-not (test-path $profile)) { md -force (split-path $profile); "" > $profile; psedit $profile }

Now, put this one-liner (well, it could fit on one line) in your $profile:

# watch for changes to the Files collection of the current Tab
register-objectevent $psise.CurrentPowerShellTab.Files collectionchanged -action {
    # iterate ISEFile objects
    $event.sender | % {
         # set private field which holds default encoding to ASCII
         $_.gettype().getfield("encoding","nonpublic,instance").setvalue($_, [text.encoding]::ascii)
    }
}
Every time the tabs "files" collection changes, it will set the default save encoding to ASCII for all files in that tab. As the profile is loaded in each tab, all files in all tabs will default to ASCII when saving. No more "save as" annoyances; just hit save and ASCII will be used for encoding. "Save as" will still let you save as unicode if you wish.

Have fun!

Tags:

.NET | Eventing | Monad | PowerShell | PowerShell 2.0 | PowerShell ISE | Reflection

PowerShell 2.0 – Introducing the PModem File Transfer Protocol

by oising 2. November 2009 01:57

One of the things that never quite fit well with me with the remoting feature in PowerShell 2.0 is that while you can “telnet” to remote systems with Enter-PSSession and import commands and do all sorts of cool tricks, there is no way to send or retrieve files from the console. It seems like such a waste that you configure WinRM up with SSL and Kerberos and get this nice encrypted channel up, but if you want to transfer files you have to revert to file shares, remote desktop or classic ftp.

Back in the “good ole’ days” of BBSs and FidoNet, people used to use simple protocols like XModem (advancing to YModem and then ZModem) or Kermit that worked by streaming character data directly to your terminal. It wasn’t fast or particularly efficient, but it got the job done. I thought I’d take a crack at building something similar for PowerShell, and this first 0.5 release is the fruits of this weekend’s tinkering. At the moment it only can “pull” a file to the local system from a remote session, but the next release will allow “pushing” a file from a local system to a remote session.

image

The reason I named it after XModem is because it works in a similar way: files are not “pulled” from the remote server, but instead are “pushed.” X[YZ]Modem file transfer was initiated by the remote end. I’ll not spoil the fun by explaining how it works, but I think you’ll enjoy pulling it apart. It’s in a module format and is implemented in pure script.

Requirements

  • PowerShell 2.0 installed on both client and server with remoting enabled to the location of the file being transferred.

E.g. if you want to grab a file using Get-RemoteFile from a remote server, you must be able to create a valid PSSession to it with the New-PSSession cmdlet. When Send-LocalFile is implemented, you’ll need remoting enabled in the other direction too.

  • The PMODEM module must be findable on both the client and server via import-module and must be the same version.

Here’s the Get-RemoteFile function help (via –?):

NAME
    Get-RemoteFile

SYNOPSIS
    Retrieves a file from a remote computer via a supplied PSession.


SYNTAX
    Get-RemoteFile [-Session] <pssession> [-RemoteFile] <string> [[-LocalPath] <string>] [[-PacketSize] <int32>]
	[-PassThru] [-AsJob] [<commonparameters>]


DESCRIPTION
    Retrieves a remote file from a server via a supplied PSSession. All communication
    is performed out-of-band, yet inside the secure WinRM channel.

    No other ports, file shares or any other special configuration is needed. However,
    the PMODEM module must be on the remote computer and findable in its $ENV:PSModulePath;
    the protocol versions must also match on both ends. You will be warned of any
    misconfiguration(s).

    When not running asynchronously, progress records are generated.


RELATED LINKS

REMARKS
    To see the examples, type: "get-help Get-RemoteFile -examples".
    For more information, type: "get-help Get-RemoteFile -detailed".
    For technical information, type: "get-help Get-RemoteFile -full".

Things coming in later releases: wildcards/multiple file support, compression and integration via proxy functions (copy-item/move-item/remove-item/rename-item etc).

Download PModem

Grab pmodem-0.5.zip and unzip it into a folder in your $ENV:PSModulePath on the client and server computers you want to use PMODEM on.

Have fun!

Tags:

Eventing | Modules | Monad | PModem | PowerShell | PowerShell 2.0 | Remoting | Windows 7

PowerShell 2.0 – Asynchronous Callbacks from .NET

by oising 9. October 2009 18:09

Asynchronous callback delegates are not a friend to PowerShell. They are serviced by the .NET threadpool which means that if they point to script blocks, there will be no Runspace available to execute them. Runspaces are thread-local resources in the PowerShell threadpool. The .NET threadpool, operating independently, is not too interested in coordinating callbacks with PowerShell. So what do we do?

There is one feature of PowerShell 2.0 that is capable of running scriptblocks in a pseudo-asynchronous manner: Eventing. Any events bound to with Register-ObjectEvent, EngineEvent or WmIEvent can have associated scriptblocks that will get executed when the associated event is raised. So, if we can somehow convert an asynchronous callback to a .NET event then we can run scriptblocks in response to Async .NET Callbacks. I’ve written a simple function called New-ScriptBlockCallback that helps us do exactly that:

#requires -version 2.0

function New-ScriptBlockCallback {
    param(
        [parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [scriptblock]$Callback
    )
<#
    .SYNOPSIS
        Allows running ScriptBlocks via .NET async callbacks.

    .DESCRIPTION
        Allows running ScriptBlocks via .NET async callbacks. Internally this is
        managed by converting .NET async callbacks into .NET events. This enables
        PowerShell 2.0 to run ScriptBlocks indirectly through Register-ObjectEvent.         

    .PARAMETER Callback
        Specify a ScriptBlock to be executed in response to the callback.
        Because the ScriptBlock is executed by the eventing subsystem, it only has
        access to global scope. Any additional arguments to this function will be
        passed as event MessageData.
        
    .EXAMPLE
        You wish to run a scriptblock in reponse to a callback. Here is the .NET
        method signature:
        
        void Bar(AsyncCallback handler, int blah)
        
        ps> [foo]::bar((New-ScriptBlockCallback { ... }), 42)                        

    .OUTPUTS
        A System.AsyncCallback delegate.
#>
    # is this type already defined?    
    if (-not ("CallbackEventBridge" -as [type])) {
        Add-Type @"
            using System;
            
            public sealed class CallbackEventBridge
            {
                public event AsyncCallback CallbackComplete = delegate { };

                private CallbackEventBridge() {}

                private void CallbackInternal(IAsyncResult result)
                {
                    CallbackComplete(result);
                }

                public AsyncCallback Callback
                {
                    get { return new AsyncCallback(CallbackInternal); }
                }

                public static CallbackEventBridge Create()
                {
                    return new CallbackEventBridge();
                }
            }
"@
    }
    $bridge = [callbackeventbridge]::create()
    Register-ObjectEvent -input $bridge -EventName callbackcomplete -action $callback -messagedata $args > $null
    $bridge.callback
}

Tags:

.NET | Eventing | Monad | PowerShell | PowerShell 2.0

About the author

Oisin Grehan lives in Montreal, Canada and builds all sorts of crap for all sorts of people.

Month List

Page List