Deze post is geïmporteerd van de oude blog en is nog niet geconverteerd naar de nieuwe syntax.
Long ago, I found something strange in Windows Explorer, which I wanted to retry on Vista today.

In Explorer, first create a very big directory tree, consisting out of small directory names. Now, from the bottom, rename these directories to about 100 characters.

Directory Tree

When you reached the top, try browsing the tree, after some directories it won't allow you to open the directory anymore. You can't select it, it will jump back, you can't rename that one, you can't delete the entire tree at once.

Can not delete tree

You can however to get rid of it, start renaming from the top, to all short names again, until you can delete them.

I realize this has something to do with character limits on paths, but this shouldn't be possible, should it? You can't even delete them properly. Is there a way to prevent this from happening? Or at least be able to clean it up without too much hassle?

I created a small proof of concept, using three .bat files.

  • Extract the zip and run Destroy.bat

  • It will first call Mtree.bat to make a small directory tree, where all directories are named "1".

  • Afterwards it calls calls Rtree.bat to rename the directories to a long name.


Feel free to look at the .bat files with a text editor, to check what it does.

Does anyone know why this is happening? I'd love to know the low-level logic behind this.
 
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.
Do you find Outlook Today too boring? I do! Let's have a look at how we can transform it into something more visually appealing, something like this: (click the image for a full view)

Outlook Today - David Cumps

But just how do you achieve this? Start by creating a fresh HTML page and add the following just below your title tag:

[html]










[/html]

Afterwards, unleash your graphical talents in Photoshop or any other editor and design something beautiful, designate some zones you would like to have, and convert it to HTML.

At the location you'd like to have your Calendar events, add the following snippet:

[html]






 
 

[/html]

The code to see your Tasks is as follows:

[html]







[/html]

Displaying the message count consists out of two actions, first we have to add it to our layout as follows:

[html]




::

[/html]

Afterwards, have a look at the following registry entries:

[code]
[HKEY_CURRENT_USER\Software\Microsoft\Office\12.0\Outlook\Today\Folders]
"0"="\\\\Cumps David\\Inbox"
"1"="\\\\CumpsD\\Inbox"
[/code]

As you can see, I have two entries in my Messages section, displaying my Exchange and POP3 Inbox. Entries are simply numbered with their value being the location of the item you want to count. You can also leave this as it is by default if you wish to.

Displaying the Date is a small piece of Javascript:

[html]

[/html]

I also added some New links next to Messages, Calendar and Tasks. The code for this is as follows:

[html]
New Mail
New Appointment
New Task
[/html]

Additionally you can add external links as well. In my example I am calling two PHP scripts, running on my local webserver, which output some Javascript, my system's uptime and an RSS feed. The following code is an example of an external link and an included PHP script:

[html]
David Cumps

[/html]

You might wonder what my picture is doing there. Well, it's a direct link to my own Contact item, since I can't remember my own phone number, I usually copy paste it from my contact details. This way I simply get to those details faster :)

Once you have completed your layout, place it somewhere on your hard disk or personal webspace and add the following registry key:

[code]
[HKEY_CURRENT_USER\Software\Microsoft\Office\12.0\Outlook\Today]
"Url"="file://path_to_your_htm"
[/code]

In my case this results in:

[code]
[HKEY_CURRENT_USER\Software\Microsoft\Office\12.0\Outlook\Today]
"Url"="file://C:\\OutlookToday\\cumpsd.htm"
[/code]

The next time you open Outlook, it will load your webpage, ready to be used.

I uploaded a zip file containing the files used in my Outlook Today. Included are two PHP scripts, one to parse an rss feed, and one for the Windows uptime. In the cumpsd.htm file you can also view all available css styles which Outlook supports. Have a look at it and feel free to modify it to get a quick start. If you create something, leave a comment showing off your creation please, I'm always interested in seeing what others accomplish.

Enjoy modifying your Outlook Today! Personalize your computer!
 
Deze post is geïmporteerd van de oude blog en is nog niet geconverteerd naar de nieuwe syntax.
GUIDE+ - Scheduled CSI MiamiRecently, I bought a new DVD player/recorder with an internal hard disk. One of the nice features I discovered when playing with it is a free tv guide! GUIDE+ shows me all programs on the major television channels in my region and allows me to easily select them for recording with one press of a button.

A few times a day, my DVD player goes out to a specific host channel, which contains all tv guide data needed to feed the GUIDE+ system, and syncs its internal guide to the latest version, making it far more trustworthy then your usual paper-based tv guide and also without the cost of buying one. Having a guide stored on the hard disk of your recorder is has some nice advantages. No more entering times manually, just select the program you want and hit the record button and it's done.

Panasonic DMR-EH67EC-KAnother nice feature is the ability to filter for movies of a specific genre. Want to see which Crime movies are on this week? Apply a filter and you get a nice list, ready to be scheduled for recording.

This system is available in most European countries, supported by quite a few big brands. If you got a GUIDE+ supported player and live in a supported country, it's as simple as configuring your postal code, waiting a few hours until it has retrieved the guide for the first time, and you're ready to go. Program information is usually available for the coming week for most big stations, while the smaller stations are updated with information for the next two days. Have a look at the detailed list per country for more info on the stations and the amount of data supplied.

When this becomes available in the cheapest recorders on the market, I'm pretty sure everyone's grandmother will be able to record something, point and record! :)