A co-worker asked the other day about a communication between different powershell scripts. First solution was using Mutex but it showed pretty fast that Mutex and Semaphore does only work in Powershell if you start with the –STA switch. By default Powershell uses a Multithreaded Apartment with each command running in its own thread. So when the simples method failed I looked for other means

Named Pipes to the Rescue

Named Pipes are not supported natively by Powershell (as far as I know). But as you can use any .Net Classes in Powershell thats not a Problem. You only need to load the System.Core Assembly (from .Net 3.5) via either

add-Type -assembly "System.Core" (in PS V2)

or old fashioned via


Some are telling you to load with explicit version but IMHO this only
ties you to a certain combination of Powershell and .Net. Usualy newer versions of .Net ensures backwards compability anyway.

Server Side

$pipe=new-object System.IO.Pipes.NamedPipeServerStream("\\.\pipe\Wulf");
'Created server side of "\\.\pipe\Wulf"'

$sr = new-object System.IO.StreamReader($pipe); 
while (($cmd= $sr.ReadLine()) -ne 'exit') 


In this case the server only emits the given command but feel free to parse it and even extract parameters or just ignore the cmd and always do the same.

Client Side

$pipe = new-object System.IO.Pipes.NamedPipeClientStream("\\.\pipe\Wulf");

$sw = new-object System.IO.StreamWriter($pipe);
$sw.WriteLine("start abc 123"); 


On the client side you are writing to the pipe. As long as you don’t send the exit command you can connect from different clientscripts.

Naming the Pipe

Named Pipes must have uniq names per System so you better use something like \\.\pipe\ While the server side is bound to use localhost ‘.’ the client can use a remote connection with the \\servername\pipe\… syntax. (If the policies and firewall settings permit it).


I might revisit it and bring it to a proper module form but for the moment it is enough. Stay alert as this is not tested in production qualitiy scripts there might be a lot of caveats. Don’t say I didn’t told you.

Are you jealously looking over the fence to Java Enums? Enums in Java are much more than the barely disguised integer constants in C#. In Java the Enum is a special form of a class with a set of fixed instances always inheriting the Enum baseclass They can have local variables and methods.

In C# (or VB so far) enums are much more like strongly typed and named integer subsets in the tradition of C #define. It is possible to give a different base but only different sizes of integer. No way to put any logic onto an enum. No way?


Extension methods to the rescue. I often have Enums coming from a WSDL proxy. As I don’t want the dependency to the webservice to leak into the domain model I convert from and to the internal version. Conversion could be done by a service but I prefer Extension Methods.

Assume the following definition coming from the webservice:

public enum ExternalEnum

As I don’t care for the specific reason the service is unavailable I define:

public enum InternalEnum

With this extension method defined:

public static class EnumExtensions
    public static InternalEnum ToInternalEnum(this ExternalEnum value)
        switch (value)
            case ExternalEnum.Ready:
                return InternalEnum.DatabaseReady;
            case ExternalEnum.HardwareUpdate:
            case ExternalEnum.SoftwareUpdate:
                return InternalEnum.Maintenance;
            case ExternalEnum.TimeOut:
            case ExternalEnum.Error:
                return InternalEnum.ServerError;
                throw new InvalidCastException("Unknown value for ExternalEnum value " + value);

I can convert (and get even Intellisense):

ExternalEnum wsResult = ...;
return wsResult.ToInternalEnum();

It’s again that time of the year, its MIX in Las Vegas. Lots and lots of sessions and every session is recorded on video. So even if you can’t be in Vegas get the whole thing. But I don’t like the idea of downloading all of those via the browser. So I wrote a script to download them via BitsTransfer from PowerShell loosely based on a script from poshcode I used to get videos from PDC. I can’t find the original author and his script again but thanks a lot.

Update: I polished the script a little bit like warnings if a file is already on disk, listing all missing sessions, sorting index, removing the dummy, some minor bugfixing with naming, etc. Still not perfect but better. Rename to to extract.

The script takes a session code as parameter. It then grabs the RSS-Feed to extract title, speaker, tags etc. These values will inserted into an index.xml file (maybe I write a corresponding XSLT for viewing but you can already view it in the browser). After that a BitsTransfer Job is started to download the video.

If the script is called without parameter it will list all missing sessions.

I use some small functions in my profile to control downloads:

# Downloads with BitsTransfer
function Get-Downloads {
  Import-Module BitsTransfer
  Get-BitsTransfer | % { "{0}: {2} {3:0.00} MB/{4:0.00} MB ( {1:0.00}% )" -f $_.DisplayName, $(100*$_.BytesTransferred/$_.BytesTotal), $_.JobState, ($_.BytesTransferred/1MB), ($_.BytesTotal/1MB) }
Set-Alias "dl" "Get-Downloads"

# Get a list of all downloads
function Complete-Downloads 
  Import-Module BitsTransfer
  Get-BitsTransfer | ? { $_.JobState -eq "Transferred" } | Complete-BitsTransfer 

# suspend all Downloads (optional all below a certain threshhold
function Suspend-Downloads( [int] $maxPercent = 100 )
  Get-BitsTransfer | ? { $_.JobState -eq "Transferring"  -and ($_.BytesTransferred/$_.BytesTotal) -lt ($maxPercent/100) } | Suspend-BitsTransfer 

#resume all Downloads (optional all above a certain threshhold
function Resume-Downloads( [int] $minPercent = 0 )
  Get-BitsTransfer | ? { $_.JobState -eq "Suspended" -and ($_.BytesTransferred/$_.BytesTotal) -gt ($minPercent/100) } | Resume-BitsTransfer -Async

# download Mix Session Videos. Call it like mix "EX21" "CL01" "FT05"
function Mix()
  complete-downloads; $args | % { & $scripthome\get-Mix10Video.ps1 $_ } ; sleep -Seconds 5; get-downloads

Use Get-Downloads to view the progress of the download and Complete-Downloads to finish it (The file will not show up until you complete it)

Use the mix function to call the script with multiple sessions e.g. mix “EX14” “EX06” will download the excellent talks of Laurent Bugnion about MVVM Pattern and Robby Ingebretsen about Design Principles and other things.

Extract the zip. Pay attention to not overwrite your index.xml if you already have it. Put the script at any place appropriate to you and the index.xml into the destination directory. I suggest putting your destination path into the script instead of my default value. Call it either with ? to get the list of available sessions or with a session code to download these video.

An Example:

.\Get-Mix10Video.ps1 CL01

will put the file CL01-Introduction to Windows Phone 7 Series.wmv into your destination directory.

#requires -version 2.0
   [Parameter(Position=1, Mandatory=$false)]

   [Parameter(Position=2, Mandatory=$false)]
   [ValidateSet("wmv","wmv-hq","pptx", "mp4")]
   [String]$MediaType ="wmv-hq",
   [string]$Destination = "F:\Videos\Mix10",
   [string]$rss = ""

Import-Module BitsTransfer

#$illegalChars = "[{0}]" -f ([Regex]::Escape([String] [System.IO.Path]::GetInvalidFileNameChars()))
$illegalChars = '[:*?\\\/\t\n<>|"]'

# Get Session RSS, parse Titel, Speaker, Tags, Description, ...
$wc = new-object System.Net.WebClient
$rssdata = [xml]$wc.DownloadString($rss)
$item = $ | ? { $$Video) }

Push-Location $Destination
$Extension = $(switch -wildcard($MediaType){"wmv*"{"wmv"} "mp4"{"mp4"} "pptx"{"pptx"}})
$SrcUrl = "{0}/{2}.{1}" -f  $MediaType, $Extension, $Video
$Destfile = ( "{0}-{1}.{2}" -f $Video, $Title, $Extension ) -replace $illegalChars, ""
$Destpath = $("{0}\{1}" -f $Destination, $Destfile)
$indexFile =  $("{0}\{1}" -f $Destination, "index.xml")

$Title = $item.title

# get index.xml
$index = new-object XML

# returns the list of the missing sessions if code is ? or item not found
# use filter like | ? { $_.tags -contains "WindowsPhone" } | % { .\ $_.code }
if ( $Video -eq "?" -or $item -eq $null )
    $ |  % {
         New-Object PsObject -Property @{ 
            code    = $$"/") +1 )
            title   = $_.title
            speaker = $
            tags    = @( $_.category ) 
    } | ? {
        $xpath = "//session"
       $index.SelectSingleNode($xpath) -eq $null
    } | sort -Property "code"

# does index already contains session?
$session = $index.SelectSingleNode("//session")
if ($session -eq $null) {
  $firstSession = @( $index.sessions.session )[0]
  $session = $firstSession.Clone()
  $session.code = $Video

$session.title = $Title
$session.speaker = $
$session.tags = $item.category  -join ","
$session.sessionref = $
$session.local = $Destpath
$session.href = $SrcUrl
$session.description = $item.description

# remove dummy as it is no longer needed
$dummy = $session = $index.SelectSingleNode("//session")
if ($dummy -ne $null) { $index.sessions.RemoveChild( $dummy ) }

# sort sessions by code
[void] ( $index.sessions.session | sort -Property "code" | % { $index.sessions.RemoveChild($_); $index.sessions.AppendChild($_) } )


# file downloaded in former session?
if (Test-Path "$Video*.$Extension") {
  $title = "File $Video*.$Extension already exists!"
  $message = "Do you want to delete the existing file and download it new?"

  $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", `
      "Delete $Destfile and start download"

  $continue = New-Object System.Management.Automation.Host.ChoiceDescription "&Continue", `
      "Continue without deleting file"
  $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", `
      "Hold file and skip download"

  $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $continue, $no)

  $result = $host.ui.PromptForChoice($title, $message, $options, 2) 

  switch ($result)
        0 { delete $Destpath }
        1 { "Continuing with download" }
        2 {             


#start download
[void] ( Start-BitsTransfer -Source $SrcUrl -Destination $Destpath -DisplayName ($Video +": "+$Title)  -Description $item.description -Async )


Write-Host "You may now use Get-BitsTransfer to check on the status of the downloads. By default, failed transfers will be retried every 10 minutes for two weeks."

The index.xml uses the following format:

<?xml version="1.0" encoding="utf-8"?>
<!-- Sessionlist unter brauchbar? -->
    <title>Changing our Game – an Introduction to Windows Phone 7 Series</title>
    <speaker>Joe Belfiore</speaker>
    <tags>Mobile, Windows Phone</tags>
    <local>CL01-Introduction to Windows Phone 7 Series.wmv</local>
      Major changes are coming to Windows Phone! This session goes in-depth on the design and 
      features of Windows Phone and gives a comprehensive picture of what’s 
      coming in this exciting new release.

I wanted a good visible Prompt with a shortend Path for common working directories. It consists now on the parts Historynumber of the current command, End of last command Time, the shortend path and a > Sign. Every Part is colored differently. The Full Path is shown in Title. Global foreground color is reset to white.

Feel free to use and modifiy it:

# prompt
function prompt {
 $host.ui.rawui.windowtitle = "PowerShell - "+ (Get-Location).ToString()
 $host.ui.rawui.foregroundcolor = "White"
 $private:history = @(get-history)
 if ($private:history.count -eq 0) {
  $private:intCommandCount = 1
 } else {
  $private:intCommandCount = $private:history[$private:history.count - 1].ID +1
 $private:location = @((Get-Location).ToString().replace($home, "~").replace("C:\entwicklung\","~"))

Write-Host -NoNewline -ForeGroundColor Yellow ("[$private:intCommandCount] ")
 Write-Host -NoNewline -ForeGroundColor Gray @(Get-Date -format "HH:mm:ss " )
 Write-Host -NoNewline -ForeGroundColor Yellow $private:location
" > "