Deze post is geïmporteerd van de oude blog en is nog niet geconverteerd naar de nieuwe syntax.
The prompt is dead, long live the prompt! That's the feeling PowerShell should give you, the new command line shell from Microsoft. Today, I'll try to determine exactly what the Power part in PowerShell stands for.

What exactly makes it so powerful?

I belief it will gain its fair share of supporters from the fact that it's so extensible. To demonstrate it's flexibility, I'll be writing a small extension today, a so called Cmdlet. By the end of the post, this should be the result:

PowerShell - Layout - Cmdlet

As you can see, it looks just like a normal command prompt, on steroids however.

First of all, PowerShell has tab completion, not only for file and directory names, but also for commands and parameters. Typing 'Get-H' and hitting tab will cycle through 'Get-History', 'Get-Host' and 'Get-Help'. Typing a dash behind a command, indicating you want to add a property and hitting tab will cycle through all available properties for the command, for example typing 'Format-List -' and hitting tab will give you 'Format-List -Property' followed by all other properties.

Another thing which gives PowerShell its power is the fact that it runs on .NET, which enables you to write things like:

[code]
PS C:\> $arr = New-Object System.Collections.ArrayList
PS C:\> $arr.Add("David")
PS C:\> $arr.Add("Cumps")
PS C:\> $arr.Count
2
PS C:\> $arr
David
Cumps
[/code]

Besides typing these commands in the shell itself, you can also write them in a text file, with the extension .ps1, and execute these scripts in the shell. To execute script however, the security has to be changed first, since by default PowerShell will only run signed scripts, no matter where they come from.

To change the security to run your own unsigned scripts, but still require downloaded scripts to be signed, type the following into PowerShell:

[code]
Set-ExecutionPolicy RemoteSigned
[/code]

Just like known Linux shells, PowerShell always runs a script when you start it, the so called profile script. Typing $profile in PowerShell will display its location on your system. Initially no profile will exist yet, so let's set one up...

Type 'notepad $profile' in PowerShell to open a new Notepad instance. When you run Vista as a non-admin, this might give a 'file does not exist' message. You can safely ignore this, as long as you save your file to the correct location afterwards (the path that $profile displayed).

The first change will be a very simple one, changing the title and window colors. Paste the following into Notepad and save it:

[code]
# Set up PowerShell looks
(Get-Host).UI.RawUI.BackgroundColor = "Black"
(Get-Host).UI.RawUI.ForegroundColor = "White"
(Get-Host).UI.RawUI.WindowTitle = "David Cumps » PowerShell"

# And go!
cls
[/code]

If you now restart PowerShell, or type '. $profile', the profile will be applied and you'll notice we have a standard black and white prompt again, with our custom title. This is the first custom script PowerShell has run on your computer. To get more scripts, have a look at The Script Center Script Repository, which is filled with scripts for various system administration purposes.

Scripts are scripts however, they simple use commands available to them. To see a list of all available commands enter 'Get-Command' and have a look, typing 'Get-Alias' will give you a list of all familiar DOS commands, mapped to their respective commands. The nice part is that we can create our own custom commands, called Cmdlets, written in any .NET language and simply plug them in to PowerShell, available to help us automate tasks even easier.

By default, Microsoft ships a collection of Cmdlets, which you saw earlier, to provide all the known DOS commands for us. With these basic Cmdlets, we can already do some nice things, for example if you wanted to get all running processes ordered by CPU time, you could simply type the following:

[code]
PS C:\> Get-Process | Where-Object {$_.CPU -gt 0} | Sort-Object CPU -descending

Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
783 34 60768 62960 308 226,75 2040 explorer
404 15 112932 121448 243 163,38 5480 firefox
650 16 29720 25444 136 125,03 5052 winamp
53 2 1868 14292 48 34,22 2624 notepad
571 48 72824 67572 340 10,44 1964 devenv
[/code]

As you noticed, Cmdlets can easily be piped to each other to create powerful constructs. But, enough of the default things, let's build something!

The Cmdlet should support the following features:

  • Called stand-alone without parameters.

  • Called stand-alone with one parameter.

  • Called stand-alone with multiple parameters.

  • Called in a pipeline receiving input parameters.

  • Output its result to the pipeline.


A Cmdlet is nothing more then a Class Library which uses System.Management.Automation, inherits from Cmdlet and applies the Cmdlet attribute. Don't forget to add a reference to C:\Program Files\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0\System.Management.Automation.dll when you create the new project, to get access to the Cmdlet base class and CmdletAttribute.

I'll let the code speak for itself, with a small recap afterwards.

[csharp]
using System;
using System.Management.Automation;

namespace CumpsD.HelloWorld
{
[Cmdlet(VerbsCommon.Get, "Hello")]
public class HelloCmdlet : Cmdlet
{
// An array is used to support the following scenario:
// Get-Process | Get-Hello
[Parameter(Position = 0, ValueFromPipelineByPropertyName = true, Mandatory = false, HelpMessage = "Name to greet.")]
public string[] Name { get; set; }

// Called when cmdlet is invoked.
protected override void BeginProcessing()
{
// You could open a database connection here for example.
base.BeginProcessing();
}

// Called when we have pipeline input.
protected override void ProcessRecord()
{
// this.Name could be null in BeginProcessing (no parameters passed)
// but could be filled when ProcessRecord gets called (parameters from pipeline)

if (this.Name == null)
{
// No arguments have been supplied, neither manual nor pipeline.
this.WriteObject("Hello World!");
}
else
{
foreach (string name in this.Name)
{
this.WriteObject(String.Format("Hello {0}!", name));
}
}
}

// Called after every record has been processed.
protected override void EndProcessing()
{
base.EndProcessing();
}

// Called when the user hits CTRL+C
protected override void StopProcessing()
{
base.StopProcessing();
}
}
}
[/csharp]

First of all, we define the name of our Cmdlet in the attribute, Get-Hello in this case. Then we create our only property as an array, to support multiple parameters being passed in, and specify that its allowed to retrieve its value from the pipeline. When it is called through the pipeline, it will take each object returned from the previous Cmdlet, look if there is a property called Name on there, and use that value for our property.

After this initial setup, it's time to implement the processing of the Cmdlet. The PowerShell infrastructure will always call BeginProcessing and ProcessRecord, even if you don't supply any parameters, as documented in the code above.

Simply coding up this class won't cut it however. Somehow it needs to get inserted into the PowerShell environment. For this, we create a very simple installer class, which will install all Cmdlets it can find in our assembly into PowerShell. Don't forget to add System.Configuration.Install as a reference to get access to the RunInstaller attribute.

[csharp]
using System;
using System.ComponentModel;
using System.Management.Automation;

namespace HelloWorld
{
[RunInstaller(true)]
public class HelloSnapIn : PSSnapIn
{
public override string Name
{
get { return "HelloSnapIn"; }
}

public override string Vendor
{
get { return "David Cumps"; }
}

public override string Description
{
get { return "This Windows PowerShell snap-in contains the Get-Hello cmdlet."; }
}
}
}
[/csharp]

That's all the code needed to create a Cmdlet! We could install it now, but let's make our lives easier first and configure Visual Studio to support debugging of this Cmdlet. Open the properties of the project and configure the Debug tab as follows:

Visual Studio PowerShell Debug Configuration

Start external program: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
Command line arguments: -noexit -command icmd HelloWorld.dll HelloSnapIn

This will start PowerShell and install the latest version of our Cmdlet whenever we go in Debug mode. When you run it at this stage, it'll throw an error however, because it doesn't know the icmd alias yet. Simply add the following to the PowerShell profile:

[code]
# Needed for some commands where administrator permissions are needed
function elevate
{
$file, [string]$arguments = $args;
$psi = new-object System.Diagnostics.ProcessStartInfo $file;
$psi.UseShellExecute = $true;
$psi.Arguments = $arguments;
$psi.Verb = "runas";

$p = new-object System.Diagnostics.Process;
$p.StartInfo = $psi;
$p.Start();
$p.WaitForExit();
$p.Close();
}

# Easily install new cmdlets
function InstallSnappin([string]$dll, [string]$snappin)
{
$path = Get-Location;
$assembly = $path.Path + "\" + $dll;
elevate C:\Windows\Microsoft.NET\Framework\v2.0.50727\installutil.exe $assembly | out-null;
Add-PSSnapin $snappin | out-null;
Get-PSSnapin $snappin;
}

set-alias icmd InstallSnappin
[/code]

At this stage, going in Debug mode will build the assembly, open PowerShell, execute installutil as an admin, with a UAC prompt under Vista, and install the Cmdlet, after which you are ready to test it and more importantly, use breakpoints! Don't forget to modify the command line arguments in the Debug properties to reflect your assembly name and installer class name when you create a new project.

Let's have a look if we managed to implement our requirements:

[code]
PS C:\> # Called stand-alone without parameters.
PS C:\> Get-Hello
Hello World!

PS C:\> # Called stand-alone with one parameter.
PS C:\> Get-Hello "David Cumps"
Hello David Cumps!

PS C:\> # Called stand-alone with multiple parameters.
PS C:\> Get-Hello "David", "Readers"
Hello David!
Hello Readers!

PS C:\> # Called in a pipeline receiving input parameters.
PS C:\> Get-Process | Where-Object {$_.CPU -gt 10} | Sort-Object CPU -desc | Get-Hello
Hello explorer!
Hello dwm!
Hello winamp!
Hello OUTLOOK!
Hello firefox!
Hello notepad!

PS C:\> # Output its result to the pipeline.
PS C:\> Get-Hello "A", "C", "B"
Hello A!
Hello C!
Hello B!
PS C:\> Get-Hello "A", "C", "B" | Sort-Object
Hello A!
Hello B!
Hello C!
[/code]

Hooray! We managed to set our development environment up to be productive, being able to easily install the latest version of our Cmdlet and having the ability to debug, and we wrote a Cmdlet offering all the nice features needed to incorporate it into our future scripts. Mission successful I'd say.

As usual, I've uploaded a .zip file which includes the solution to the above Cmdlet, including the Debug properties and the full PowerShell profile I'm using.

What do you think about PowerShell? Will it make your life easier as a system administrator?
 
Deze post is geïmporteerd van de oude blog en is nog niet geconverteerd naar de nieuwe syntax.
Yesterday I talked about personalizing Outlook Today to make it look more sexy. One of the things I had on my Outlook Today, was my picture with a direct link to my contact details. This link looked like this:

[html]
Contact Details
[/html]

As you can see, I'm pointing to 0000000031490E5EAED5DA4AAB47FD1B9F1E6BA7E43D2D00. This is the name Outlook internally assigned to my contact details, but how did I find it?

To figure this out, I wrote an Outlook Add-in which simply gives me the internal name of any selected item, be it a mail, appointment, contact or task, as shown here:

GuidFinder Result

Creating this Add-in was quite easy with Visual Studio 2008. I started by creating a new project of the type Office - 2007 - Outlook Add-in, which results in one class presenting you two important methods:

[csharp]
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
}

private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
{
}
[/csharp]

Using the following MSDN resources, I quickly hacked a small Outlook Add-in together, which would give me an additional menu item.
GuidFinder Menu Item



To add a new button, I changed the code from the article a bit, to look as follows:

[csharp]
#region Add menu item
private void AddMenuBar()
{
try
{
// Get the current menu bar.
this.menuBar = this.Application.ActiveExplorer().CommandBars.ActiveMenuBar;

// Add a new item to the menu bar.
this.newMenuBar = (CommandBarPopup)menuBar.Controls.Add(
MsoControlType.msoControlPopup, missing,
missing, missing, false);

// Add the menu bar if it doesn't exist.
if (this.newMenuBar != null)
{
this.newMenuBar.Caption = "GuidFinder";
this.newMenuBar.Tag = this.menuTag;

// Add a new menu item to the menu.
this.getGuidButton = (CommandBarButton)newMenuBar.Controls.Add(
MsoControlType.msoControlButton, missing,
missing, 1, true);

// Layout the menu item.
this.getGuidButton.Style = MsoButtonStyle.msoButtonIconAndCaption;
this.getGuidButton.Caption = "Get GUID";
this.getGuidButton.FaceId = 25; // Looking Glass icon
this.getGuidButton.Click += new _CommandBarButtonEvents_ClickEventHandler(this.getGuid_Click);

// Make our result visible.
this.newMenuBar.Visible = true;
}
}
catch (System.Exception ex)
{
MessageBox.Show("Error: " + ex.Message.ToString(), "Error Message Box", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
#endregion
[/csharp]

The FaceId property defines the icon which will be displayed next to your menu item. To get a list of possible icons, have a look at these images.

When you run the Add-in at this stage, buttons will keep on adding themselves to Outlook every time you run it. Therefore we need to write some code which removes the button in case it already exists:

[csharp]
#region Remove Menu item
private void RemoveMenubar()
{
// If the menu already exists, remove it.
try
{
CommandBarPopup foundMenu = (CommandBarPopup)
this.Application.ActiveExplorer().CommandBars.ActiveMenuBar.
FindControl(MsoControlType.msoControlPopup,
missing, menuTag, true, true);

if (foundMenu != null)
{
foundMenu.Delete(true);
}
}
catch (System.Exception ex)
{
MessageBox.Show("Error: " + ex.Message.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
#endregion
[/csharp]

Clicking on the Get GUID button should give us the internal name of the item, for which we use the following code to display it:

[csharp]
#region Display the Guid
private void getGuid_Click(CommandBarButton ctrl, ref bool cancel)
{
try
{
// Get the selected item in Outlook and determine its type.
Selection outlookSelection = this.Application.ActiveExplorer().Selection;

if (outlookSelection.Count > 0)
{
string itemGuid = string.Empty;
object selectedItem = outlookSelection[1];

if (selectedItem is MailItem)
{
MailItem mailItem = (selectedItem as MailItem);
itemGuid = mailItem.EntryID;
}
else if (selectedItem is ContactItem)
{
ContactItem contactItem = (selectedItem as ContactItem);
itemGuid = contactItem.EntryID;
}
else if (selectedItem is AppointmentItem)
{
AppointmentItem apptItem = (selectedItem as AppointmentItem);
itemGuid = apptItem.EntryID;
}
else if (selectedItem is TaskItem)
{
TaskItem taskItem = (selectedItem as TaskItem);
itemGuid = taskItem.EntryID;
}
else if (selectedItem is MeetingItem)
{
MeetingItem meetingItem = (selectedItem as MeetingItem);
itemGuid = meetingItem.EntryID;
}

if (itemGuid != String.Empty)
{
MessageBox.Show(String.Format("The GUID of this item is {0}", itemGuid), "Guid", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else
{
MessageBox.Show("The item type could not be determined.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}

}
catch (System.Exception ex)
{
MessageBox.Show("Error: " + ex.Message.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
#endregion
[/csharp]

Wiring up everything in the end, is as simple as first checking if our menu already exists and removing it, to afterwards add it to Outlook again.

[csharp]
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
this.RemoveMenubar();
this.AddMenuBar();
}
[/csharp]

Once again, I uploaded a zip file, containing the source code, together with the files from my previous Outlook Today article. The solution is created with Beta 2 of Visual Studio 2008 and works with Outlook 2007. This small GuidFinder Add-in is a nice start to discover the full potential of Outlook Add-ins.

If you aren't interested in the code, but would like to have this Add-in installed in Outlook 2007, run the ClickOnce installer to deploy it on your computer. This installer will get all prerequisites needed to run the Add-in (.NET 3.5) and the Add-in itself. If you get an error about an untrusted certificate, add wiki.cumps.be to your Trusted Sites zone.

I belief this is a very powerful technology to use inside a company and provide added value to your employees, making them more productive through useful add-ins. Thanks to the ClickOnce technology it's easy to deploy add-ins to clients and keep them updated.

What do you think about .NET Outlook Add-ins and ClickOnce?
 
Deze post is geïmporteerd van de oude blog en is nog niet geconverteerd naar de nieuwe syntax.
Visual Studio is a great editor! But when editing other file formats I miss the coloring.

When working with PHP files, it's great to be able to make a solution to organize php sources, and having code open in tabs, but when they all look black and white, I find it requires more focus on my part when coding.

Long ago, I wrote a piece on getting coloring to work in Visual Studio 2002 and 2003. Somehow I managed to skip doing any PHP development during the entire lifetime of Visual Studio 2005, but recently I had to create something small, and I missed my nice colors. I tried to apply my old method in Visual Studio 2008 (Orcas), and this is the result:

Visual Studio 2008 - PHP

Looks pretty appetizing, doesn't it?

To get this in Visual Studio 2008, do the following:

  • Download vs-php.zip and extract it somewhere.

  • Execute the preferred registry file (php_edit2008.reg for Visual Studio 2008 ofcourse).

  • This will create a File Extension association for PHP files, treating them as C++ files.

  • Copy the usertype.dat to your VS.NET directory. (default C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE)

  • Restart Visual Studio if it was opened.

  • Open a .php file and admire the colors!


For this to work, you need to have C++ support installed! Otherwise the usertype.dat file will not work, since this is specific to .cpp files. You don't need to install everything for C++, only the following will already work:

Visual Studio 2008 - Setup C++

When you look in the registry, you will see that the .php extension looks exactly like the .cpp one, you can also try experimenting with applying the .cs or .html filter to a .php file, but I found them both lacking. Using the .cs value, you will get coloring from C#, but it will also give you lots of syntax warnings. When using the .html one, it doesn't always color the entire file.

If you want to color more keywords, open up the usertype.dat file in a text editor, and simply add more words to it.

Enjoy the extra productivity gain!