Help for Functions Supported in V2 CTP 3 !

If you haven’t started reading James Brundage’s Blog entitled “Media and Microcode” I suggest you take a look. He has a lot of great insight into PowerShell in his articles.

There was a slightly subtle note in his latest article about a new feature for functions. They now have built in support for help!

To quote James,

In case you're curious, the comments inside of the function aren't just there for fun. They're an example of script-embedded help (which will not be out until CTP3). Once script-embedded Help is there, you can write help content for a function with just what's in the function below, and you'll be able to Get-Help on your function just like any other command.

This will be a great feature. Self evidence and discoverability are a huge part of the success of PowerShell, and to be able to support Help for functions directly in the console will be a big win for discoverability.

With the abilities to make PowerShell Scripts  more like full blown Cmdlets, it will become necessary for us to rigorously polish our scripts and treat them like fully developed code.. This includes useful comments and help that makes sense and is easy to consume for the user.

We definitely will not lose the pithiness of PowerShell. However, as functions are distributed throughout the community and used by a much wider audience, I think a standard (or even a template) for functions will become more and more common. I am looking forward to seeing what comes about after the release of CTP3.

By When ?

This is a question that goes un-asked and un-answered all too often. So often I am in a meeting where Project Manager X asks Engineer Y, “Can you do so and so?” and the engineer says “Absolutely!” and we move on to the next item on the agenda. This is a recipe for disaster. There is no set time that this task is due. There is no promise or commitment. More than likely, the PM has an idea in his or her head as to when this task should be complete and so does the engineer, but it has not been discussed and agreed upon by the two parties. This can lead to dissatisfaction for the PM and frustration for the engineer.

An example of this is the other day I got an Instant Message from someone asking me “Do you have the room numbers for our new MDF  IDF’s in our new building?” I had  a feeling the information was buried in my email but I was working on something and didn’t want to go digging through my inbox right at that moment. But something triggered in me to ask him, “When do you need this information?” It was a good thing I did because he was on a call and was going to need the information in the next 5 minutes. I was originally planning on getting around to it in the next hour or so. Obviously, my priorities got shifted around a bit and I got him the info he needed, but I wouldn’t have done any of this unless I had asked, “By when?”

As an engineer, I will be the first to admit we are pretty bad at estimating and making promises about when we can have something complete. Quite honestly, if an engineer I have not worked with before tells me he or she can complete task XYZ in 2 days, I assume 4 and promise completion to my manager accordingly. I pretty much automatically double any estimate an engineer makes.

Being able to make and deliver on commitments consistently is absolutely necessary to succeed. As Systems Administrators/Engineers, it can be difficult at times to make commitments,because we never know what kind of fire we are going to have to put out next.

Because of this, as I plan and commit to my project work, I tell my PM that I can work 4 hours a day on a project, because I know there will be 4 or 5 hours of email, people asking me other questions, and just dealing with all the other kind of general distractions that come my way.

Do you find yourself in this kind of situation or see it elsewhere in your company? How do you deal with the craziness of constant fire fighting and also make and fulfill on promises consistently?

PowerShell and GeoLocation

I just saw an article on Scott Hanselman’s blog about Geolocation and I thought that it might be useful to do in PowerShell. There is a website that hosts a database of IP’s and locations that Scott talks about. However, in order to use it, you need pass in your external routable IP Address, not the actual IP of your computer behind your firewall or whatever other device is doing NAT for you.

In order to find out your external IP, you can do a number of things. There is a website called WhatIsMyIPAddress.com that can find this inforation quite easily. With a simple WebRequest we can get this info into PowerShell.

   1: $whatIsMyIPUrl = "http://www.whatismyip.com/automation/n09230945.asp"
   2: $ip = (new-object System.Net.WebClient).DownloadString($whatIsMyIPUrl)

This sets $ip to a string containing the IP Address. You can type the URL into a browser and see what it returns.

Once we have that IP we can use a web service on another site to find the location for the IP address we just got.

   1: $ipInfoUrl = "http://api.hostip.info/?$ip"
   2: $ipInfo = [xml](new-object System.Net.WebClient).DownloadString($ipInfoUrl)
   3: $ipInfo.InnerText

$ipinfo is an XML document and you can probably slice it and dice it however you wish, but you can see the guts of the info using .InnerText. We can formalize this and make the output look pretty but I just wanted to post this quick and dirty version before I head out for Thanksgiving dinner.

Happy Thanksgiving!

Andy

Get-SharePointList

Osin has a great set of posts about working with Sharepoint with PowerShell. The key to all of it is his wonderful Get-WebService function that allows you to easily connect to any web service you like. I actually took his script and added it as a function to my profile.

Along with updating a list, I found myself needing to pull data from a list as well. Osin had 3/4’s of the CRUD (Create,Read,Update,Delete) operations, but I really needed the Read operation. WIth the work that he had done, it was extremely easily to figure out what I needed to do.

Once you have the webservice object, you simply need to call the GetListItems as shown in the example below. In this case, the lists variable is the webservice object.

   1: $lists = get-webservice -url $listurl
   2: $lists.Credentials = [System.Net.CredentialCache]::DefaultCredentials
   3: $lists.url = $listurl
   4: $result = $lists.GetListItems($listname,$null, $null, $null, $null, $null, $null)

You will notice that GetListItems takes a bunch of arguments. In this basic example, I am simply passing in the name of the list. In the next couple of posts I will talk about what some of the other parameters can do for us and a bit of an intro into the world of CAML or Collaborative Application Markup Language.

Supporting Wildcards in PowerShell functions

In my last post I had a function called Get-TfsWorkItem and really needed to be able to easily search based on one property of the [WorkItem] object, in this case it was title. What I needed was some kind of wildcard support in my function. Sure I could pipe it to Where-Object but that gets old, especially if I am going to be using this thing every day. So I was thinking, why not put the where directly in the function and make the back half of the where clause a parameter.

Here’s the function again:

   1: function Get-TfsWorkItem {
   2: param($title = "*",
   3:       $user="Andy Schneider",
   4:       $Project='InfrastructureAutomation'
   5:       )  
   6:         
   7: $WIQL = @"
   8: SELECT STUFF
   9: FROM THING
  10: "@
  11:  
  12: $tfs = Get-TfsServer 
  13: $workItems = $tfs.wit.query($WIQL)
  14: return $workItems | where {$_.Title -like $title}
  15: return $tfs
  16: }

Notice on line 14, rather than returning $workItems, I return $workitems piped to a where-object cmdlet that puts some condition on $workitem.Title.

That condition is specified as a parameter and defaults to * making the where-object cmdlet let everything pass. But if you wan to use it for something liket Get-TfsItem –title *PowerShell* you can, rather than doing get-tfsworkitem | where {$_.Title –like *powershell*}

You can obviously still filter on stuff later on in the pipeline, but it just gives a nice user experience, in my humble opinion.

PowerShell and TFS Work Items

James Manning has a blog post about PowerShell script that builds a Team Foundation Server Client object. Once you have this basic script, you can do all kinds of cool things with it.

As a side note, I’d be really interested to know if any other Systems Engineers or Administrators are using Team Foundation Server on their projects, and if so, are you using Agile/Scrum?

Anyway, the bottom line is that we use TFS to track a bunch of tasks that we are working on, and I got tired of having to log in and update my tasks every day. So once I saw James’ script, I started poking around a bit more to see what else I could do. I cam up with two functions in addition to the one James had written. The first is Get-TfsWorkItem and the second is Update-TfsWorkItem. (I Suppose that should be set)

Anyhow, here’s what they can do.

clip_image002

 

function Get-TfsServer {
param([string] $serverName = "itstfs")
 
# load the required dll
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.Client")
 
$propertiesToAdd = (
        ('VCS', 'Microsoft.TeamFoundation.VersionControl.Client', 'Microsoft.TeamFoundation.VersionControl.Client.VersionControlServer'),
        ('WIT', 'Microsoft.TeamFoundation.WorkItemTracking.Client', 'Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore'),
        ('CSS', 'Microsoft.TeamFoundation', 'Microsoft.TeamFoundation.Server.ICommonStructureService'),
        ('GSS', 'Microsoft.TeamFoundation', 'Microsoft.TeamFoundation.Server.IGroupSecurityService')
    )
 
    # fetch the TFS instance, but add some useful properties to make life easier
    # Make sure to "promote" it to a psobject now to make later modification easier
    [psobject] $tfs = [Microsoft.TeamFoundation.Client.TeamFoundationServerFactory]::GetServer($serverName)
    foreach ($entry in $propertiesToAdd) {
        $scriptBlock = '
            [System.Reflection.Assembly]::LoadWithPartialName("{0}") > $null
            $this.GetService([{1}])
        ' -f $entry[1],$entry[2]
        $tfs | add-member scriptproperty $entry[0] $ExecutionContext.InvokeCommand.NewScriptBlock($scriptBlock)
    }
    return $tfs
}
 
function Get-TfsWorkItem {
param($title = "*",
      $user="Andy Schneider",
      $Project='InfrastructureAutomation'
      )  
        
$WIQL = @"
SELECT [System.Id], [System.WorkItemType], [System.State], [System.AssignedTo], [System.Title] 
FROM WorkItems 
where [System.TeamProject] = '$Project'  AND [System.AssignedTo] = '$user' 
ORDER BY [System.WorkItemType], [System.Id] 
"@
 
$tfs = Get-TfsServer 
$workItems = $tfs.wit.query($WIQL)
return $workItems | where {$_.Title -like $title}
return $tfs
}
 
function Update-WorkItem {
param($item,$field,$value)
if (!$item.IsOpen) {$item.open()}
$item.Fields[$field].Value = $value
$item.Save()
return $item
}
 
 
function Update-TfsWorkItemTime {
param ($item,$hours)
 
begin {}
 
process {
 
  if ($_ -is [Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem]) {
    $item = $_
    if (!$item.IsOpen) {$item.open()}
    if ($item.Fields["State"].Value = "Not Started") {$item.Fields["State"].Value = "In Progress"}
    $remainingWork = $item.Fields["Remaining Work"].Value
    $completedWork = $item.Fields["Completed Work"].Value
    $item.Fields["Completed Work"].Value = $completedwork + $hours
    $item.Fields["Remaining Work"].Value = $remainingWork - $hours
    $item.Save() | out-nulll
}
}
 
end {}
}

One point in this code that may need some clarification is the $wiql HERE string. WIQL apparently is “Work Item Query Language.’ Just like WMI uses WQL for WMI Query Language. These are just special version of SQL that are geared for a specific domain. You can find out more about WIQL up on MSDN and search TFS to your heart’s content.

The Update-TfsWorkItem takes an item and add hours to Work Completed and also subtracts from Remaining work.

This should give you enough to start messing with TFS if your PM is hounding you to update your tasks!

Encrypting and Decrypting Strings with a Key in PowerShell

Note:  After writing up this post,  I found a great script by Oisin up on Poshcode that does all of this using System.Security.Cryptography.RijndaelManaged in a much more elegant way, but I had fun hammering through some of this stuff so I thought I would share. I was recently working on a script that required encryption for some of the data. This has been dealt with quite a bit in the PowerShell Community. There are some great posts here (Lee Holmes Precision Computing) and here (/\/\o\/\/'s old blog). I had the unique constraint that the user who encrypts the data may not necessarily be the same user who decrypts it. The way I chose to solve this was with a Shared key, that both users would know. Here's where it gets a little tricky. All the cmdlets for managing Secure Strings seem to encrypt using some sort of hash from the given logged on user. This obviously would not work for use with a shared key. It turns out that there is a -key parameter on the ConvertTo-SecureString. There is also a -asPlainText parameter. But you can't use them both at the same time. They are in different parameter sets. This poses the question, How can you encrypt a plainText string with a key in PowerShell? Well here is what I came up with. First of all, we need to generate a key. Wouldn't it be nice if you could just use an alphanumeric string? But that wouldn't have been nearly as much fun. It turns out the key needs to be a 16,24, or 32 byte array. That made me come up with this handy little function:
function Set-Key {
param([string]$string)
$length = $string.length
$pad = 32-$length
if (($length -lt 16) -or ($length -gt 32)) {Throw "String must be between 16 and 32 characters"}
$encoding = New-Object System.Text.ASCIIEncoding
$bytes = $encoding.GetBytes($string + "0" * $pad)
return $bytes
}
It takes a string, forces the length, and then returns an appropriate byte array that we can use generate a key to encrypt stuff with. So the next hurdle is to actually encrypt a string of plain text with they key we generated. Even though you cannot encrypt a whole string in a secureString, there is a method on System.SecureString called AppendChar. So, if we break our string out in chars, we can build our SecureString.  
function Set-EncryptedData {
param($key,[string]$plainText)
$securestring = new-object System.Security.SecureString
$chars = $plainText.toCharArray()
foreach ($char in $chars) {$secureString.AppendChar($char)}
$encryptedData = ConvertFrom-SecureString -SecureString $secureString -Key $key
return $encryptedData
}
Finally, to decrypt everything, we can use a technique that /\/\o\/\/\ came up with a long time ago using [Runtime.InteropServices.Marshal]
function Get-EncryptedData {
param($key,$data)
$data | ConvertTo-SecureString -key $key |
ForEach-Object {[Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($_))}
}
Here is how you could use these functions together to encrypt and decrypt strings to your heart's content: image  
187 >  $plainText = "Some Super Secret Password"
188 >  $key = Set-Key "AGoodKeyThatNoOneElseWillKnow"
189 >  $encryptedTextThatIcouldSaveToFile = Set-EncryptedData -key $key -plainText $plaintext
190 >  $encryptedTextThatIcouldSaveToFile
507964ed3a197b26969adead0212743c378a478c64007c477efbb21be5748670a7543cb21135ec324e37f80f66d17c76c4a75f6783de126658bce09ef19d50da
191 >  $DecryptedText = Get-EncryptedData -data $encryptedTextThatIcouldSaveToFile -key $key
192 >  $DecryptedText
Some Super Secret Password

Editing PowerShell Scripts with VIM

Many *nix folks love the VI editor. There is a very cool improved version of VI called VI Improved, or VIM. VIM is text editor with no mouse. All you get is a keyboard in the console window. They actually do have a GUI’er version that is pretty slick as well. But to really fly on this, the keyboard navigation is the way to go. I am definitely not there but I have found that it can be a nice tool to do a quick update to a script when you are in the console and don’t want to fire up an external editor. Here’s what it looks like

image

There are some plugins that you can get to get PS highlighting that were written by Peter Provost.

If you are an VI type of guy, or gal, this could be a great tool for you.

Single Threaded Apartment in PowerShell V1

I’ve been working on a Winform that I use in a PowerShell script and I needed to display HTML in the form. As I was playing with the form designer in Visual Studio, I found a Web Browser Control that I thought would do the trick. It worked great in C# but when I ported it over to PowerShell, I found I had problems with STA and MTA. The WebBrowser Control will only work in STA mode. Not so good for PS V1 which only runs in MTA.

Marco Shaw reminded me of a blog post on the PowerShell Team Blog about a cmdlet that could run a command in STA mode. This worked like a champ as soon as I downloaded the code and built the Snapin. I was able to run invoke-apartment STA ShowWinform and everything was great.

But then I tried to parameterize my function and it got a little more hairy. The Invoke-Apartment cmdlet takes two parameters, the first is the apartment you want to use (MTA or STA) and the second is called expression that is a STRING.

I wanted to pass in something like this

invoke-apartment –apartment STA –expression “ShowWinform –html <h1>Hello World</h1>”

This blew up in my face, and I was getting parsing errors and all kinds of weird stuff.

Now mind you, I am not a developer, but PowerShell has given me enough background to be willing to crack open some C# code now and then, really just enough to make me dangerous.

So I started looking at the invoke-apartment cmdlet’s parameters. I saw that both parameters were of type string. I thought, hmm, I wonder what would happen if I set them to ScriptBlock instead of String.

I poked around the code a bit more to see where these parameters were being used and gave it a shot. To my utter amazement, the thing actually compiled. Furthermore, to my utter, utter amazement, it actually did what I was hoping. It now took a ScriptBlock instead of a string and I wasn’t getting my parsing errors. The only trick was that I had to wrap my expression in curly braces so PowerShell would know that it was a ScriptBlock.

So now the following code totally worked in PowerShell.

invoke-apartment –apartment STA –expression {“ShowWinform –html <h1>Hello World</h1>”}

The point of all of this is that I love how PowerShell has enabled me to get comfortable enough with C# so that I can look at something and tweak it to make it work for what I needed to do.

I am not saying that all admin should crack open C#, but if you haven’t used a cli yet, fire up PowerShell. If you have played with the cli, but haven’t written any functions, write one. If you haven’t written any scripts, take some of your functions and put them together in a script. It’s all about the journey. PoweShell has really helped take a bunch of next steps.

Oh yeah, and if you are interested, here is the source code with my modifications. Given the code below and reading this blog post, you can build your own Invoke-Apartment cmdlet and Snapin.

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.ComponentModel;
using System.Configuration;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
 
 
namespace Powershell.Blogs {
 
    /// <summary>
 
    /// Class implemeting Invoke-Apartment
 
    /// </summary>
 
    [Cmdlet("Invoke", "Apartment")]
 
    public sealed
 
    class
 
    InvokeApartmentCommand : PSCmdlet {
 
        internal class ExecutionResult {
 
            private object output;
 
            private Exception error;
 
 
 
            public Object Output {
 
                get { return output; }
 
                set { output = value; }
 
            }
 
 
 
            public Exception Error {
 
                get { return error; }
 
                set { error = value; }
 
            }
 
        }
 
 
 
        #region Private Data
 
 
 
        private ManualResetEvent waitHandle;
 
 
 
        private Runspace runspace;
 
        private Runspace Runspace {
 
            get {
 
                return runspace;
 
            }
 
 
 
            set {
 
                runspace = value;
 
            }
 
        }
 
 
 
        #endregion
 
 
 
        #region parameters
 
 
 
        private ScriptBlock command;
 
        private ApartmentState apartment = ApartmentState.MTA;
 
 
 
        /// <summary>
 
        /// Apartment to run the cmdlet int
 
        /// </summary>
 
        [Parameter(Position = 0, Mandatory = true)]
 
        public ApartmentState Apartment {
 
            get { return apartment; }
 
            set { apartment = value; }
 
        }
 
 
 
        /// <summary>
 
        /// Command to execute.
 
        /// </summary>
 
        [Parameter(Position = 1, Mandatory = true, ValueFromPipeline = true)]
 
        public ScriptBlock Expression {
 
            get { return command; }
 
            set { command = value; }
 
        }
 
 
 
        #endregion parameters
 
 
 
        protected override void BeginProcessing() {
 
            // Set the runspace
 
            Runspace = Runspace.DefaultRunspace;
 
        }
 
 
 
        /// <summary>
 
        /// For each record, execute it, and push the results into the 
 
        /// success stream.
 
        /// </summary>
 
        protected override void ProcessRecord() {
 
            ExecutionResult result = new ExecutionResult();
 
 
 
            if (Thread.CurrentThread.GetApartmentState() == apartment) {
 
                // Since the current apartment state is same as the one requested
 
                // do the work in same thread.
 
                DoWork(result);
 
            } else {
 
                // the apartment state is different..perform the task in 
 
                // a differnt thread.
 
                Thread executionThread = new Thread(new ParameterizedThreadStart(PerformExecution));
 
                executionThread.SetApartmentState(apartment);
 
 
 
                // Create a handle to wait for completion
 
                waitHandle = new ManualResetEvent(false);
 
                executionThread.Start(result);
 
 
 
                waitHandle.WaitOne();
 
            }
 
 
 
            if (null != result.Error) {
 
                throw result.Error;
 
            }
 
 
 
            if (null != result.Output) {
 
                WriteObject(result.Output);
 
            }
 
        }
 
 
 
        private void PerformExecution(object outputToWriteTo) {
 
            ExecutionResult result = (ExecutionResult)outputToWriteTo;
 
 
 
            // Use the runspace to execute the script
 
            Runspace.DefaultRunspace = Runspace;
 
 
 
            DoWork(result);
 
 
 
            if (null != waitHandle) {
 
                waitHandle.Set();
 
            }
 
        }
 
 
 
        private void DoWork(ExecutionResult result) {
 
            try {
 
                //ScriptBlock myScriptBlock = InvokeCommand.NewScriptBlock(Expression);
                ScriptBlock myScriptBlock = Expression;
                result.Output = myScriptBlock.InvokeReturnAsIs(null);
 
            } catch (Exception e) {
 
                result.Error = e;
 
            }
 
        }
 
 
 
    }
 
 
 
    /// <summary>
 
    /// Create this sample as a PowerShell snap-in
 
    /// </summary>
 
    [RunInstaller(true)]
 
    public class InvokeApartmentPSSnapIn : PSSnapIn {
 
        /// <summary>
 
        /// Create an instance of the InvokeApartmentPSSnapIn
 
        /// </summary>
 
        public InvokeApartmentPSSnapIn()
 
            : base() {
 
        }
 
 
 
        /// <summary>
 
        /// Get a name for this PowerShell snap-in. This name will be used in registering
 
        /// this PowerShell snap-in.
 
        /// </summary>
 
        public override string Name {
 
            get {
 
                return "InvokeApartment";
 
            }
 
        }
 
 
 
        /// <summary>
 
        /// Vendor information for this PowerShell snap-in.
 
        /// </summary>
 
        public override string Vendor {
 
            get {
 
                return "PowershellBlog";
 
            }
 
        }
 
 
 
        /// <summary>
 
        /// Description of this PowerShell snap-in.
 
        /// </summary>
 
        public override string Description {
 
            get {
 
                return "This is a PowerShell snap-in that includes the invoke-apartment cmdlet.";
 
            }
 
        }
 
    }
 
}
 

Single Threaded Apartment in PowerShell V1

I’ve been working on a Winform that I use in a PowerShell script and I needed to display HTML in the form. As I was playing with the form designer in Visual Studio, I found a Web Browser Control that I thought would do the trick. It worked great in C# but when I ported it over to PowerShell, I found I had problems with STA and MTA. The WebBrowser Control will only work in STA mode. Not so good for PS V1 which only runs in MTA.

Marco Shaw reminded me of a blog post on the PowerShell Team Blog about a cmdlet that could run a command in STA mode. This worked like a champ as soon as I downloaded the code and built the Snapin. I was able to run invoke-apartment STA ShowWinform and everything was great.

But then I tried to parameterize my function and it got a little more hairy. The Invoke-Apartment cmdlet takes two parameters, the first is the apartment you want to use (MTA or STA) and the second is called expression that is a STRING.

I wanted to pass in something like this

invoke-apartment –apartment STA –expression “ShowWinform –html <h1>Hello World</h1>”

This blew up in my face, and I was getting parsing errors and all kinds of weird stuff.

Now mind you, I am not a developer, but PowerShell has given me enough background to be willing to crack open some C# code now and then, really just enough to make me dangerous.

So I started looking at the invoke-apartment cmdlet’s parameters. I saw that both parameters were of type string. I thought, hmm, I wonder what would happen if I set them to ScriptBlock instead of String.

I poked around the code a bit more to see where these parameters were being used and gave it a shot. To my utter amazement, the thing actually compiled. Furthermore, to my utter, utter amazement, it actually did what I was hoping. It now took a ScriptBlock instead of a string and I wasn’t getting my parsing errors. The only trick was that I had to wrap my expression in curly braces so PowerShell would know that it was a ScriptBlock.

So now the following code totally worked in PowerShell.

invoke-apartment –apartment STA –expression {“ShowWinform –html <h1>Hello World</h1>”}

The point of all of this is that I love how PowerShell has enabled me to get comfortable enough with C# so that I can look at something and tweak it to make it work for what I needed to do.

I am not saying that all admin should crack open C#, but if you haven’t used a cli yet, fire up PowerShell. If you have played with the cli, but haven’t written any functions, write one. If you haven’t written any scripts, take some of your functions and put them together in a script. It’s all about the journey. PoweShell has really helped take a bunch of next steps.

Oh yeah, and if you are interested, here is the source code with my modifications. Given the code below and reading this blog post, you can build your own Invoke-Apartment cmdlet and Snapin.

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.ComponentModel;
using System.Configuration;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
 
 
namespace Powershell.Blogs {
 
    /// <summary>
 
    /// Class implemeting Invoke-Apartment
 
    /// </summary>
 
    [Cmdlet("Invoke", "Apartment")]
 
    public sealed
 
    class
 
    InvokeApartmentCommand : PSCmdlet {
 
        internal class ExecutionResult {
 
            private object output;
 
            private Exception error;
 
 
 
            public Object Output {
 
                get { return output; }
 
                set { output = value; }
 
            }
 
 
 
            public Exception Error {
 
                get { return error; }
 
                set { error = value; }
 
            }
 
        }
 
 
 
        #region Private Data
 
 
 
        private ManualResetEvent waitHandle;
 
 
 
        private Runspace runspace;
 
        private Runspace Runspace {
 
            get {
 
                return runspace;
 
            }
 
 
 
            set {
 
                runspace = value;
 
            }
 
        }
 
 
 
        #endregion
 
 
 
        #region parameters
 
 
 
        private ScriptBlock command;
 
        private ApartmentState apartment = ApartmentState.MTA;
 
 
 
        /// <summary>
 
        /// Apartment to run the cmdlet int
 
        /// </summary>
 
        [Parameter(Position = 0, Mandatory = true)]
 
        public ApartmentState Apartment {
 
            get { return apartment; }
 
            set { apartment = value; }
 
        }
 
 
 
        /// <summary>
 
        /// Command to execute.
 
        /// </summary>
 
        [Parameter(Position = 1, Mandatory = true, ValueFromPipeline = true)]
 
        public ScriptBlock Expression {
 
            get { return command; }
 
            set { command = value; }
 
        }
 
 
 
        #endregion parameters
 
 
 
        protected override void BeginProcessing() {
 
            // Set the runspace
 
            Runspace = Runspace.DefaultRunspace;
 
        }
 
 
 
        /// <summary>
 
        /// For each record, execute it, and push the results into the 
 
        /// success stream.
 
        /// </summary>
 
        protected override void ProcessRecord() {
 
            ExecutionResult result = new ExecutionResult();
 
 
 
            if (Thread.CurrentThread.GetApartmentState() == apartment) {
 
                // Since the current apartment state is same as the one requested
 
                // do the work in same thread.
 
                DoWork(result);
 
            } else {
 
                // the apartment state is different..perform the task in 
 
                // a differnt thread.
 
                Thread executionThread = new Thread(new ParameterizedThreadStart(PerformExecution));
 
                executionThread.SetApartmentState(apartment);
 
 
 
                // Create a handle to wait for completion
 
                waitHandle = new ManualResetEvent(false);
 
                executionThread.Start(result);
 
 
 
                waitHandle.WaitOne();
 
            }
 
 
 
            if (null != result.Error) {
 
                throw result.Error;
 
            }
 
 
 
            if (null != result.Output) {
 
                WriteObject(result.Output);
 
            }
 
        }
 
 
 
        private void PerformExecution(object outputToWriteTo) {
 
            ExecutionResult result = (ExecutionResult)outputToWriteTo;
 
 
 
            // Use the runspace to execute the script
 
            Runspace.DefaultRunspace = Runspace;
 
 
 
            DoWork(result);
 
 
 
            if (null != waitHandle) {
 
                waitHandle.Set();
 
            }
 
        }
 
 
 
        private void DoWork(ExecutionResult result) {
 
            try {
 
                //ScriptBlock myScriptBlock = InvokeCommand.NewScriptBlock(Expression);
                ScriptBlock myScriptBlock = Expression;
                result.Output = myScriptBlock.InvokeReturnAsIs(null);
 
            } catch (Exception e) {
 
                result.Error = e;
 
            }
 
        }
 
 
 
    }
 
 
 
    /// <summary>
 
    /// Create this sample as a PowerShell snap-in
 
    /// </summary>
 
    [RunInstaller(true)]
 
    public class InvokeApartmentPSSnapIn : PSSnapIn {
 
        /// <summary>
 
        /// Create an instance of the InvokeApartmentPSSnapIn
 
        /// </summary>
 
        public InvokeApartmentPSSnapIn()
 
            : base() {
 
        }
 
 
 
        /// <summary>
 
        /// Get a name for this PowerShell snap-in. This name will be used in registering
 
        /// this PowerShell snap-in.
 
        /// </summary>
 
        public override string Name {
 
            get {
 
                return "InvokeApartment";
 
            }
 
        }
 
 
 
        /// <summary>
 
        /// Vendor information for this PowerShell snap-in.
 
        /// </summary>
 
        public override string Vendor {
 
            get {
 
                return "PowershellBlog";
 
            }
 
        }
 
 
 
        /// <summary>
 
        /// Description of this PowerShell snap-in.
 
        /// </summary>
 
        public override string Description {
 
            get {
 
                return "This is a PowerShell snap-in that includes the invoke-apartment cmdlet.";
 
            }
 
        }
 
    }
 
}