Archive for the “C#” Category

There may be, on some occassion, a need to create a class with a set of properties in PowerShell. This can’t be done natively, but it can be done with some very simple C# syntax, thanks to C# version 3.0, which is really a new compiler as opposed to a new version of .NET. The base class library is the same. Here is a very basic class with four properties.

 

   1: PS C:\Users\andy.schneider> $csharp = @"
   2: >> public class Andy
   3: >> {
   4: >>  public int age {get;set;}
   5: >>  public string firstName {get;set;}
   6: >>  public string lastName {get;set;}
   7: >>  public string blog {get;set;}
   8: >> }
   9: >> "@
  10: >>
  11: PS C:\Users\andy.schneider>

But we still have to get it into PowerShell. We can use the Add-Type Cmdlet to do this, but it will fail unless we use the C# 3.0 compiler under the covers. We can specify the language we are using with the –Language parameter. I know CSharp30 is wrong, but I like to give it garbage so it will tell me what the valid languages are. Because it takes an ENUM we can find out that CsharpVersion3 is a valid language.

 

   1: PS C:\Users\andy.schneider>
   2: PS C:\Users\andy.schneider> add-type $csharp -Language Csharp30
   3: Add-Type : Cannot bind parameter 'Language'. Cannot convert value "Csharp30" to type "Microsoft.PowerShell.Commands.Lan
   4: guage" due to invalid enumeration values. Specify one of the following enumeration values and try again. The possible e
   5: numeration values are "CSharp, CSharpVersion3, VisualBasic, JScript".
   6: At line:1 char:27
   7: + add-type $csharp -Language <<<<  Csharp30
   8: PS C:\Users\andy.schneider> add-type $csharp -Language CsharpVersion3

All right, now we are ready to to after running the command on line 8.

We can create a new object of type Andy and set it to $andy

   1: PS C:\Users\andy.schneider> $andy = new-object andy
   2: PS C:\Users\andy.schneider> $andy | gm
   3:  
   4:  
   5:    TypeName: Andy
   6:  
   7: Name        MemberType Definition
   8: ----        ---------- ----------
   9: Equals      Method     System.Boolean Equals(Object obj)
  10: GetHashCode Method     System.Int32 GetHashCode()
  11: GetType     Method     System.Type GetType()
  12: ToString    Method     System.String ToString()
  13: age         Property   System.Int32 age {get;set;}
  14: blog        Property   System.String blog {get;set;}
  15: firstName   Property   System.String firstName {get;set;}
  16: lastName    Property   System.String lastName {get;set;}

Now we have a full blown object with properties and we can create new instances of them all day long, and the C# was pretty darn straight forward.

Comments 1 Comment »

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.";
 
            }
 
        }
 
    }
 
}
 

Comments 5 Comments »

One feature of PowerShell V2 that is being touted by many developers and hard core scripters is Script Cmdlets. This is essentially the ability to build full blown cmdlets natively in PowerShell. You will no longer have to venture into the world of C# or VB .NET to build a  real Cmdlet. I’ve been working on a Cmdlet in C# over the last week or so and up until then I don’t think I was able to fully appreciate the power that is coming with Script Cmdlets. Now that I have seen what you can do in C# and really how ridiculously easy it is to do, I can’t wait to start developing Cmdlets in PowerShell.

One of the most frustrating problems in Version 1 was having to deal with parameters. Specifically, figuring out how to get input from the pipeline if its available, and from specifying the parameter name directly. Script Cmdlets solve this problem beautifully!

When developing a cmdlet in C#, you use something called attributes that get tacked on to the parameter that is being declared. Just as an example, lets say we were writing a cmdlet to create a new Hyper-V Virtual Machine. When you create a new VM, you want to be able to specify the number of CPU’s.

You can do this with the following code:

[Parameter(
            Position = 2,
            Mandatory = false,
            ValueFromPipeline = true,
            ValueFromPipelineByPropertyName = true,
            HelpMessage = "Please enter the number of Processors to add to the VM (1-4)"
            )]
        [Alias("CPU")]
        [ValidateRange(1, 4)] //can only have up to 4 processors
        public int NumberOfCpus { get; set; }

In this code, there is a bunch of things going on, but they are fairly self-explanatory if you dig in to them. First of all, the actual parameter is just an integer named NumberOfCpus that is being declared at the very bottom. All the attributes are declared above the object that they are tied to.

In this case I have added the Parameter attribute with a few options. The first is that this is this parameter is the second parameter if no name is specified. The second is saying that this parameter is not required, by declaring "Mandatory" to be false.

The next two options are the ones that solve the pipeline problem. I can’t tell you how great this will be in V2. Just by declaring these options, you can grab objects from the pipeline and inject them into the argument of your parameter.

There is a reason there are two options here. The first one is used for basic pipeline input. Take, for example, get-content servers.txt | new-vm. If there was a parameter called Name that accepted values from the pipeline, that would work.

The ValueFromPipelineByPropertyName is a little more tricky. This is the reason this command works

get-process notepad | get-childitem

image

 

This is because get-process notepad produces a "Process Object" which has a property called path. When that object gets sent to get-childitem, get-childitem has a parameter called path and PowerShell links the two up and moves on.

The last couple options are pretty self explanatory, but worth mentioning. You get interactive help for free, just by declaring the HelpMessage string, and you can do all kinds of validations on the input. Here I have specified a range of valid numbers for the Number of CPU’s, so someone doesn’t try to create a VM with 197 processors.

It is truly amazing how much plumbing that the PowerShell engine does that we can use for free. Its gonna be awesome. Oh yeah, and then distributing and sharing these things within the community is going to be orders of magnitude easier with the advent of modules.

Comments 1 Comment »

I mentioned earlier that I had told a colleague about using Hashtables in PowerShell and C#.  Later on in the afternoon, he came across a situation where he needed to generate a random string of characters.

He came up with this little snippet of code.  (I think he may have a been a little high on Hash . . . . . tables)

   1: function new-array {$args}                                                                                        
   2: $CharacterArray = new-array A B C D E F G H I J K L M N O P Q R S T U V X Y Z;                                    
   3: $CharacterHashTable = new-object System.Collections.Hashtable                                                                                                                                                                     
   4: for ($i=0; $i -ilt $CharacterArray.Count; $i++) {$CharacterHashTable.Add($i, $CharacterArray[$i])}                
   5: $randomNumber = new-object System.Random                                                                          
   6: $randomCharacter = $CharacterHashTable[$randomNumber.next(0,25)]                         

“Wow!  That is great!  You basically reinvented ASCII !”

Then I showed him this neat little trick :)

   1: PS >  [char]65
   2: A

He came back with these two lines to generate a random character.

   1: $randomObject = New-Object System.Random
   2: $randomChar = [char]$randomObject.next(65,90)

It turns out that if you know ASCII exists, it can indeed be used to generate random characters :)

However, I have to give some credit for coming up with a neat little subset of ASCII in a couple lines of PowerShell. Not too shabby!

Comments 2 Comments »

I have an intern working with us and he is writing some PowerShell and C# WinForms to automate some of our administrative tasks. He had a problem with a C# app and when he started asking me about it, it sounded like a hash table was just what the doctor ordered.

He had seen hash tables in PowerShell but wasn’t quite sure how they would work in C# (he had only cracked open C# maybe 5 weeks ago). Rather than firing up Visual Studio and typing in a bunch of “blah-blah/yadda-yadda” I was able to demo a “C# version” of hash tables in PowerShell.

Once he saw this, he was able to understand how to use hash tables in C#. 

   1: 268 >  $hash = New-Object System.Collections.Hashtable
   2: 269 >  $hash.Add("one",1)
   3: 270 >  $hash
   4:  
   5: Name                           Value
   6: ----                           -----
   7: one                            1
   8:  
   9:  
  10: 271 >  $hash.one
  11: 1
  12: 272 >

I just love how PowerShell is continuing to bridge a gap between Developers and Admins. These are obviously different disciplines and both require different skills, but the more we can learn about each one, the more effective we can be in our respective roles.

Comments 4 Comments »

The gentlemen over at the Power Scripting Podcast recently posted a tip on how to start processes in Powershell.

I had run into the same problem they had. How do you pass in both an executable and its arguments to the Start method of a System.Diagnostics.Process object.

Something like this works fine in Powershell:

[System.Diagnostics.Process]::Start(”calc”)

But as soon as you try to pass in something like “ipconfig /all” the thing blows up.

Turns out you can pass in arguments a couple of ways. The Powerscripting guys noted that if you pass in the argument of the executable as a second argument to Start, it will work great.

[System.Diagnostics.Process]::Start(”ipconfig”,”all”)

However, there is another way if you want to get a little bit more fancy and do things like hide windows or redirect output from StdOut or StdErr.

You can use a System.Diagnostic.ProcessStartInfo object. Using get-member we can take a look at all the options we have for such a thing.

   1: PS 124 >  $si = New-Object System.Diagnostics.ProcessStartInfo
   2: PS 125 >  $si | gm  -type property | select Name
   3:  
   4: Name
   5: ----
   6: Arguments
   7: CreateNoWindow
   8: Domain
   9: EnvironmentVariables
  10: ErrorDialog
  11: ErrorDialogParentHandle
  12: FileName
  13: LoadUserProfile
  14: Password
  15: RedirectStandardError
  16: RedirectStandardInput
  17: RedirectStandardOutput
  18: StandardErrorEncoding
  19: StandardOutputEncoding
  20: UserName
  21: UseShellExecute
  22: Verb
  23: Verbs
  24: WindowStyle
  25: WorkingDirectory

Lots of goodness here that we can play with. Once you build up the ProcessStartInfo object, you pass that whole object in as the arg to the Start method of system.diagnostics.process.

I put together a quick function to show how this could be used more generically.

   1: function Start-Proc  {
   2:     param (
   3:             [string]$exe = $(Throw "An executable must be specified"),
   4:             [string]$arguments,
   5:             [switch]$hidden,
   6:             [switch]$waitforexit
   7:             )    
   8:     
   9:     # Build Startinfo and set options according to parameters
  10:     $startinfo = new-object System.Diagnostics.ProcessStartInfo 
  11:     $startinfo.FileName = $exe
  12:     $startinfo.Arguments = $arguments
  13:     if ($hidden){
  14:                 $startinfo.WindowStyle = "Hidden"
  15:                 $startinfo.CreateNoWindow = $TRUE
  16:                 }
  17:     $process = [System.Diagnostics.Process]::Start($startinfo)
  18:     if ($waitforexit) {$process.WaitForExit()}
  19:     
  20: }
  21:  
  22:  Start-Proc calc
  23:  Start-Proc calc -waitforexit
  24:  Start-Proc -exe ipconfig -arguments /all
  25:  Start-Proc ipconfig /all
  26:  Start-Proc ipconfig /all -hidden

I used the alias Start-Proc so that it would not collide with the PSCX Cmdlet Start-Process.

They have done the same writing their CmdLet  in C#, but with all kinds of options.

   1: PS 130 >  gcm Start-Process | fl *
   2:  
   3:  
   4: DLL              : C:\Program Files (x86)\PowerShell Community Extensions\Pscx.dll
   5: Verb             : Start
   6: Noun             : Process
   7: HelpFile         : Pscx.dll-Help.xml
   8: PSSnapIn         : Pscx
   9: ImplementingType : Pscx.Commands.StartProcessCommand
  10: ParameterSets    : {[[-Path] <String>] [[-Arguments] <String>] [-Verb <String>] [-WorkingDirectory <String>] [-Credential <PSCredential>] [-NoShellExecute] [-NoWindow]
  11:                    [-WindowStyle <ProcessWindowStyle>] [-LoadUserProfile] [-WaitTimeout <Int32>] [-Boost] [-Priority <ProcessPriorityClass>] [-Verbose] [-Debug] [-Error
  12:                    Action <ActionPreference>] [-ErrorVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>] [-WhatIf] [-Confirm], [[-ScriptBlock] <ScriptBlock>
  13:                    ] [-NoProfile] [-WorkingDirectory <String>] [-Credential <PSCredential>] [-NoShellExecute] [-NoWindow] [-WindowStyle <ProcessWindowStyle>] [-LoadUser
  14:                    Profile] [-WaitTimeout <Int32>] [-Boost] [-Priority <ProcessPriorityClass>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-ErrorVariable <St
  15:                    ring>] [-OutVariable <String>] [-OutBuffer <Int32>] [-WhatIf] [-Confirm]}
  16: Definition       : Start-Process [[-Path] <String>] [[-Arguments] <String>] [-Verb <String>] [-WorkingDirectory <String>] [-Credential <PSCredential>] [-NoShellExecute]
  17:                     [-NoWindow] [-WindowStyle <ProcessWindowStyle>] [-LoadUserProfile] [-WaitTimeout <Int32>] [-Boost] [-Priority <ProcessPriorityClass>] [-Verbose] [-D
  18:                    ebug] [-ErrorAction <ActionPreference>] [-ErrorVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>] [-WhatIf] [-Confirm]
  19:                    Start-Process [[-ScriptBlock] <ScriptBlock>] [-NoProfile] [-WorkingDirectory <String>] [-Credential <PSCredential>] [-NoShellExecute] [-NoWindow] [-W
  20:                    indowStyle <ProcessWindowStyle>] [-LoadUserProfile] [-WaitTimeout <Int32>] [-Boost] [-Priority <ProcessPriorityClass>] [-Verbose] [-Debug] [-ErrorAct
  21:                    ion <ActionPreference>] [-ErrorVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>] [-WhatIf] [-Confirm]
  22:  
  23: Name             : Start-Process
  24: CommandType      : Cmdlet
  25: Visibility       : Public

Comments 5 Comments »

The concept of IT Pros VS Developers is an idea that simply needs to die. IT Guys/Gals need Developers and Developers need IT folks.

I was listening to .NET Rocks the other day. As they were reading one of their fan’s emails on the air I thought it sounded really familiar. Pretty quickly I realized that they were reading an email I had sent them about a month ago and had forgotten about.

Basically, my email was talking about how using Powershell has enabled me to jump into the world of .NET and start playing around with C#. This has enabled me to have much more meaningful conversations with Developers in my department. I have been learning their language, if you will.

The way I see it, the IT Pro is going to be doing more and more automation and the more we know about writing code, or at least how it is written, can only help us in our careers.

As IT Pros, we can have discussions about instrumentation requirements for an app that we will need to support way more effectively if we understand the basic capabilities of building instrumentation and management into an app in the first place. Maybe its getting familiar with System.Diagnostics.EventLog or learning a little bit about tracing. Maybe its understanding how MOM management packs are created. With SCOM 2007, developers can very easily create management packs now.

In fact, Carl and Richard were talking to David Aiken about this very topic on this episode of DNR

Comments No Comments »