June 2005 - Posts

All delegates of TechEd 2005 Europe (for the full conference, excluding pre-conf, that is from 5 till 8 July) will receive a free copy of Virtual Server 2005 Enterprise Edition and SQL Server 2005 Standard Edition (you'll receive a voucher to register on a website; the product will be sent to you as soon it's released later this year). More information can be found on http://www.mseventseurope.com/teched/05/pre/content/mvs2005.aspx. And if you need more information about Virtual Server 2005, come to the Ask-The-Experts area where I'll be one of the guys responsible for the Virtual Server 2005 desk. Cu there?

So, this is yet another reason to come to TechEd ... time is passing by, it's time to act NOW.

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Hi folks,

Only 20 days left to TechEd 2005 Europe (Amsterdam), so if you haven't registered yet, it's about time to do so. Allow me to make some more promotion for this exciting event in this blog post :-). TechEd 2005 is built around three key pillars:

  • Technical Education: What everyone knows as "the sessions" now in 12 different tracks with over 400 sessions in total. I'm sure you won't have any timeslot left without a topic of your interest! Beside the technical sessions, there are also Hands-on Labs (HOLs) to discover the technology in a Do-It-Yourself fashion, Chalk-&-Talks where you are in the center, Panel Discussions with product team members, etc.
  • Community & Networking: TechEd 2005 is the place to be to meet European peers to network with, both in formal and informal ways. Find the people you always wanted to meet and share thoughts on various technologies, solutions for problems, best practices, etc. This key pillar includes chatting with Microsoft product team members and the Ask-The-Experts (ATE) booth where you'll find people to answer your questions about products and technologies. Don't miss out this golden opportunity to ask the questions you always wanted to ask (and remember: there are not stupid questions, only stupid answers).
  • Technology Evaluation: Watching the products in action during demos in the various sessions is one thing. Real gurus need to try it themselves to get convinced about the value of the various new features and technologies. TechEd 2005 offers you this ability with over 250 Hands-on Labs equipped with various products, including the betas, and step-by-step Do-It-Yourself scripts. And of course, there will be a bunch of experts at your service to answer all of your questions.

Wow, that's much, isn't it? If this is a little too overwhelming, watch the Maximise your time video to get a complete overview of all this stuff, loud-n-clear.

What else is up? Let's give a short overview of other exciting stuff you should be sure not to miss during these 4 or 5 days:

  • How Microsoft Does IT will show you how Microsoft manages its own infrastructure, of course with Microsoft products :-) (eat your own ... right?). Cost-effectiveness is key and these sessions will show you how to accomplish this goal.
  • The TechEd 2005 Party on July 7th is the golden opportunity for you folks to show you're not just a technology geek ;-). This year the party is sponsored by the System Center product (you know, the merge of SMS and MOM), but I'm not sure this strong product will help to improve your manageability during the party :d.
  • Bloggers: you'll find a list of TechEd bloggers on the TechEd website to get to know the latest news.
  • The well-known Andrew Cheeseman network infrastructure equipped with over 300 networked computers, the Attendee Website, Exchange-based e-mail with OWA, wireless networking and Voice-over-IP communication. Try it and be astonished about the power of all these Microsoft-technologies live in action under heavy load.
  • Women in Technology is a lunch-session followed by (for the first time) a dedicated track for people (not only women!) interested in advancing the careers of Women in Technology.
  • Post Conference DVD. I strongly agree with the argument TechEd offers sooo much stuff and it's very hard to choose the right track at the right time. I do know the feeling of wanting to attend 5 sessions in the same timeslot :-). Luckily all attendees will receive a post-conference DVD with the slides and Windows Media videos of all the sessions.
  • Much much more: the Microsoft Learning & Conference Bookshop, the Microsoft Community Lounge, Microsoft Partner Zones, Community Hosts, etc. For a full list of "features", check http://www.mseventseurope.com/TechEd/05/Pre/Content/Content.aspx.

And finally, the Session Search Tool is on-line. Planning is not finalized yet (room allocation will depend on your input when attendees fill out their planned schedule on the Attendee Website) but the content is already there. Check it out now, over here: http://www.mseventseurope.com/TechEd/05/Pre/Content/sessionsearch.aspx.

So, there's no excuse left not to attend TechEd :-). Register now, it's still possible!

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

A beta of this brand new tool can be found on http://www.microsoft.com/products/expression/. Although I'm not a huge graphics specialist (not to say I don't know a thing about these things) I like the tool. Maybe because a dummy like me can understand how it works. In fact this tool is a new version of Microsoft Creature House Expression which I found on the internet a couple of months ago, by accident.

As the site describes:

"Acrylic" is the codename for an innovative illustration, painting and graphics tool that provides exciting creative capabilities for designers working in print, web, video, and interactive media.

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Now that I'm in the middle of a add-in development wave, let's explain how to create a presentation clock for Microsoft PowerPoint by using C#.

First, perform the same steps as two posts ago but now with PowerPoint as the add-in host application:

  1. Open Visual Studio .NET 2003 and create a new project. In the "New Project" dialog choose Other Projects, Extensibility Projects, Shared Add-in and give the project a meaningful name.
  2. In the Add-in Wizard click Next. Choose to "Create an Add-in using Visual C#" and click Next. In the next step, uncheck every checkbox in the list except for "Microsoft PowerPoint" and click Next again. Now, give a name to the plug-in and (optionally) a description. Click Next. Check both checkboxes in the next step, i.e. to load the Add-in when the host app (in this case PowerPoint) starts and to install the plug-in for everyone on the target computer. Click Next and Finish.
  3. Now you'll end up with a Connect.cs file containing the plug-in class with all the add-in related plumbing in place.
  4. Import the code from the previous blog post in this class. This includes the Win32 function imports, the struct for IPC messages and the methods VarPtr, SendMSNMessage and RemoveMSNMessage.
  5. Now it's time for the real (PowerPoint-related) work.

First, add some references:

  1. Using Add Reference, tab COM, add the Microsoft PowerPoint 11.0 Object Library.
  2. Using Add Reference, tab .NET, add a reference to the System.Windows.Forms assembly.

Open up the Connect.cs file that was created by the wizard:

  1. Start by importing some namespaces:

    using PowerPoint = Microsoft.Office.Interop.PowerPoint;
    using System.Windows.Forms;

  2. Add the following attribute

    private PowerPoint.Application powerpnt;

  3. Change the OnConnection method as follows:

    public void OnConnection(object application, Extensibility.ext_ConnectMode connectMode, object addInInst, ref System.Array custom)
    {
         applicationObject = application;
         addInInstance = addInInst;

         powerpnt = (PowerPoint.Application) application;
         powerpnt.SlideShowBegin +=
    new Microsoft.Office.Interop.PowerPoint.EApplication_SlideShowBeginEventHandler(powerpnt_SlideShowBegin);
         powerpnt.SlideShowEnd +=
    new Microsoft.Office.Interop.PowerPoint.EApplication_SlideShowEndEventHandler(powerpnt_SlideShowEnd);
         powerpnt.SlideShowNextSlide +=
    new Microsoft.Office.Interop.PowerPoint.EApplication_SlideShowNextSlideEventHandler(powerpnt_SlideShowNextSlide);
         
        
    if(connectMode != Extensibility.ext_ConnectMode.ext_cm_Startup)
              OnStartupComplete(
    ref custom);
    }

  4. Add three other private attributes to the class:

    private Clock clock;
    private bool reappear;
    private bool silentClose;

    We will create the Clock class in a minute.

  5. Now, implement the three events for the SlideShowXYZ events:

    private void powerpnt_SlideShowBegin(Microsoft.Office.Interop.PowerPoint.SlideShowWindow Wn)
    {
         reappear =
    true;
         silentClose =
    false;
         ShowClock(Wn);
    }

    private void powerpnt_SlideShowEnd(Microsoft.Office.Interop.PowerPoint.Presentation Pres)
    {
         silentClose =
    true;
         clock.Close();
    }

    private void powerpnt_SlideShowNextSlide(Microsoft.Office.Interop.PowerPoint.SlideShowWindow Wn)
    {
         if (clock == null && reappear)
              ShowClock(Wn);
    }

  6. These methods rely on the method ShowClock to display the Clock form. The steps to create the Clock form are displayed a little further. You can do these first (if you want to rely on IntelliSense) or you can continue over here as you like.

  7. The ShowClock method looks like this:

    private void ShowClock(Microsoft.Office.Interop.PowerPoint.SlideShowWindow Wn)
    {
         clock =
    new Clock(Wn);
         clock.Show();
         clock.Closing +=
    new System.ComponentModel.CancelEventHandler(clock_Closing);
         clock.Closed +=
    new EventHandler(clock_Closed);
         Wn.Activate();
    }

    The Wn.Activate() call is used to give away the focus of the form so that the keyboard input is sent to the active slide show instead of the form.

  8. In order to ask the user whether he/she wants to permanently close the presentation clock or to have it reappear (cf. the attribute to keep track of this) on the next slide, we implement an event handler for the Closing and Closed events of the form:

    private void clock_Closing(object sender, System.ComponentModel.CancelEventArgs e)
    {
         if (!silentClose)
         {
              DialogResult res = MessageBox.Show("Do you want to close the presentation clock permantently for this slide show?", "Presentation clock", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2);
              if (res == DialogResult.Cancel)
              {
                   e.Cancel =
    true;
                   return;
              }

              reappear = res == DialogResult.No;
         }
    }

    private void clock_Closed(object sender, EventArgs e)
    {
         clock =
    null;
    }


    silentClose is used to hide the message when the slide show ends and the clock needs to be closed always.

We're done with the Connect class. Now we need to create the Clock form which will display the time:

  1. Create a new form and call it Clock.cs.
  2. Set the Text of the form to "Presentation clock".
  3. Change the FormBorderStyle property to "FixedToolWindow".
  4. Modify the size and add a label (label1) to the form. Adjust the size of the label, change the font to size 14 and choose a ForeColor (e.g. Navy). Center the label on the form (horizontally and vertically) and set TextAlign to MiddleCenter.
  5. Add a timer (timer1) to the form and set Enabled to true. Keep the interval of 100 ms.
  6. Double-click the timer to generate the code for the Tick event and add this code to the handler:

    private void timer1_Tick(object sender, System.EventArgs e)
    {
         label1.Text = DateTime.Now.ToString("HH:mm tt");
    }

    This will display something like "12:34 AM" on the label.

  7. Now that you're in the code for the form, add the following attributes:

    private static double opacity = 0.8;
    private Microsoft.Office.Interop.PowerPoint.SlideShowWindow Wn;

  8. Change the constructor to accept a SlideShowWindow instance and copy it to the attribute:

    public Clock(Microsoft.Office.Interop.PowerPoint.SlideShowWindow Wn)
    {
         InitializeComponent();
         this.Wn = Wn;
    }

  9. Go back to the designer and doubleclick the form (be sure not to doubleclick on the the label!). This should generate an empty handler for the Load event, which you have to change as follows:

    private void Clock_Load(object sender, System.EventArgs e)
    {
         this.Opacity = opacity;
         this.Left = Screen.GetWorkingArea(this).Width - this.Width - 10;
         this.Top = 10;
    }

    This code will put the label on the right position on the screen, in the upper right-hand corner, 10 pixels away from the border. This sample code was not tested for computers with multiple monitors; note however that the class SlideShowWindow of the PowerPoint interop can be used to determine the screen of the presentation and to do better positioning of the clock on the screen.

  10. Implement some other events for the form in order to give the user the ability to adjust the opacity of the clock and to give away focus in favor for the slide show window when the mouse leaves the form. The following code shows the handlers, but you'll need to add the events through the Designer first in order to establish the connection between event and handler (use the "lightning" icon in the Properties window of the IDE to access the list of events for the selected form or control):

    private void Clock_MouseLeave(object sender, System.EventArgs e)
    {
         Wn.Activate();
    }

    private void label1_MouseLeave(object sender, System.EventArgs e)
    {
         Wn.Activate();
    }

    private
    void label1_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
    {
         if (e.Button == MouseButtons.Left)
         {
              if (this.Opacity < 0.9)
                   opacity =
    this.Opacity += 0.1;
         }
        
    else
         {
             
    if (this.Opacity > 0.2)
                   opacity =
    this.Opacity -= 0.1;
         }
    }

    By using the left button of the mouse, the user can make the clock brighter. By using the right button, it can be made darker (and more transparent). Note that by using a static attribute to hold the opacity value, this value will be remembered for the next Clock instance that is created and loaded (cf. Load method code to adjust the opacity property).

Compile using CTRL-SHIFT-B and restart PowerPoint. When you start a slide show, you should see the clock popping up in the right-hand corner. You can download a compiled version over here (including an MSI installer): http://www.bartdesmet.net/download/PresentationClock.zip.

This is the result:

Happy coding!

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Personally, I'm not a huge fan of "tabbed browsing", but as a bunch of people think this is feature number one of today's browsers, Microsoft has finally provided an answer for this. So, here it is: tabbed browsing in IE. Download the brand new release of the MSN Toolbar over here http://toolbar.msn.com and off you go. Please note that the MSN Toolbar is the first step towards tabbed browsing, which will come later in IE7 as a built-in feature (beta due this summer).

Nice to know: the shortcut to create a new tab is CTRL-T. Using CTRL-TAB you can switch between the tabs. To close a tab, use CTRL-F4.

One final note: I've configured the toolbar to open up "MSN Search" whenever I create a new tab (there are four options, including the home page of IE, a blank page and a "tabbed browsing instructions" page). In the past I posted about my habit to do ALT-D, google, CTRL-ENTER, ENTER (11 keystrokes, easy to type due to the double o and g) to start searching on the net, which is far easier than the procedure CTRL-D, "search.msn.com", ENTER (17 keystrokes, nasty typing). Now I can do CTRL-T (2 keystrokes) and I'm ready to search using "MSN Search" :p, yeah cool baby.

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

In the previous post I blogged about the general techniques to communicate with MSN 7.0 to change the personal message. In this blog post I'm going to take it a little step further, by writing an Outlook 2003 add-in that updates the MSN personal message with the current item in your calendar (e.g. "Jogging", "Meeting with XYZ", "Conference call ABC").

These are the steps to create such an add-in successfully:

  1. Open Visual Studio .NET 2003 and create a new project. In the "New Project" dialog choose Other Projects, Extensibility Projects, Shared Add-in and give the project a meaningful name.
  2. In the Add-in Wizard click Next. Choose to "Create an Add-in using Visual C#" and click Next. In the next step, uncheck every checkbox in the list except for "Microsoft Outlook" and click Next again. Now, give a name to the plug-in and (optionally) a description. Click Next. Check both checkboxes in the next step, i.e. to load the Add-in when the host app (in this case Outlook) starts and to install the plug-in for everyone on the target computer. Click Next and Finish.
  3. Now you'll end up with a Connect.cs file containing the plug-in class with all the add-in related plumbing in place.
  4. Import the code from the previous blog post in this class. This includes the Win32 function imports, the struct for IPC messages and the methods VarPtr, SendMSNMessage and RemoveMSNMessage.
  5. Now it's time for the real (Outlook-related) work.

The next fragment contains the code for the OnConnection method where we will launch the add-in:

public void OnConnection(object application, Extensibility.ext_ConnectMode connectMode, object addInInst, ref System.Array custom)
{
    applicationObject = application;
    addInInstance = addInInst;

    //
    //Outlook object and calendar folder
    //
    outlook = (Outlook.Application) applicationObject;
    cal = outlook.Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderCalendar);

    //
    //Watch calendar
    //
    cal.Items.ItemAdd += new
Outlook.ItemsEvents_ItemAddEventHandler(Items_ItemAdd);
    cal.Items.ItemChange +=
new
Outlook.ItemsEvents_ItemChangeEventHandler(Items_ItemChange);
    cal.Items.ItemRemove +=
new
Outlook.ItemsEvents_ItemRemoveEventHandler(Items_ItemRemove);

    //
    //Timer to crawl through the calendar on a regular basis
    //
    timer = new
System.Timers.Timer(10000);
    timer.Elapsed +=
new
System.Timers.ElapsedEventHandler(timer_Elapsed);

    if
(connectMode != Extensibility.ext_ConnectMode.ext_cm_Startup)
        OnStartupComplete(
ref
custom);
}

In here, a couple of private attributes are used:

private object applicationObject;
private
Outlook.Application outlook;
private object
addInInstance;
private
System.Timers.Timer timer;
private Outlook.MAPIFolder cal;

The declared events for the timer and the three Calendar-events look like this:

private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
    Search();
}

private void Items_ItemAdd(object
Item)
{
    Search();
}

private
void Items_ItemChange(object Item)
{
    Search();
}

private
void Items_ItemRemove()
{
    Search();
}

Apparently the method Search has a significant role ;-). Let's show you:

private void Search()
{
    //
    //Get calendar items, including recurring appointments
    //
    Outlook.Items i = (Outlook.Items) cal.Items;
    i.IncludeRecurrences =
true
;

    //
    //Query the result based on the current date/time and the sensitivity
    //
    string
d = DateTime.Now.ToString("MM/dd/yyyy HH:mm tt");
    string
criterion = String.Format("[Start] <= \'{0}\' and [End] >= \'{0}\' and [Sensitivity] <> {1}", d, (Int32) Outlook.OlSensitivity.olPrivate);
    Outlook.Items events = i.Restrict(criterion);
    events.IncludeRecurrences =
true
;

    //
    //We'll give the first appointment in the range priority
    //
    Outlook.AppointmentItem a = (events.GetFirst() as
Outlook.AppointmentItem);

    //
    //Did we find an appointment that suits our criteria?
    //
    if (a != null
)
    {
        //
        //Determine additional status information for the personal message
        //
        string
b = "";
        switch
(a.BusyStatus)
        {
            case
Outlook.OlBusyStatus.olOutOfOffice:
                b = "OOF";
                break
;
            case
Outlook.OlBusyStatus.olBusy:
                b = "Do not disturb";
                break
;
        }

        //
        //Set the MSN personal message
        //
        SetMSNMessage("Outlook - " + a.Subject + (b != "" ? " (" + b + ")" : ""));
    }
    else
    {
        //
        //No, remove the MSN personal message
        //
        RemoveMSNMessage();
    }
}

The magic work in here is the filtering of the Calendar folder using some MAPI-related functionality. The formatting of the date is mandatory as the filter needs to have the right format (e.g. "01/01/2001 01:01 AM", thus no seconds and AM/PM spec). Also recurring appointments are included. For "OutOfOffice" and "Busy" items, a short string is added to the personal message which always contains the prefix "Outlook - " (as we will show the Office logo using SetMSNMessage, see previous post) and the Subject of the appointment. Note that the filter also avoids the display of "private items" as you probably don't want other people to know about your private appointments with ... euhm ... your mistress or so :o.

The last missing pieces are some other events for the add-in:

public void OnStartupComplete(ref System.Array custom)
{
    //
    //Start timer
    //
    timer.Start();
}

public void OnDisconnection(Extensibility.ext_DisconnectMode disconnectMode, ref System.Array custom)
{
    if(disconnectMode != Extensibility.ext_DisconnectMode.ext_dm_HostShutdown)
        OnBeginShutdown(
ref custom);
    applicationObject =
null;
}

Now, we're done. Just build the solution and launch Outlook. The COM-based add-in will be registered automatically (take a look at HKLM\Software\Microsoft\Office\Outlook\Addins) and thus it should be launched when you start Outlook. Every time you create, delete or modify an appointment, the add-in will come in action and look whether a change of the personal message is required instantaneously. Beside of the event-based updates, the timer will fire every 10 seconds to crawl through the Calendar in order to find appointments that need to be displayed. If there's overlap, only the first appointment in the list will be shown (you can change this behaviour by calling the Order method on the Outlook.Items variable that is used to do the filtering).

Finally, if you want, you can build the (automatically generated) installer project in order to have a MSI-based installer for the add-in you wrote. You can download a compiled version with installer on http://www.bartdesmet.net/download/OutlookMsnPluginSetup.zip. Please note that the author is not responsible for any damage whatsoever caused by installing and/or running this plug-in. Although I'm quite sure this build is bug-free, it was not thoroughly reviewed prior to submission to the download section of my website.

This is how the result should look:

Have (even more) fun :-)

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

MSN Messenger 7.0 introduced the notion of a personal message, a small message that's not poisening your nickname if you want to share something with your contacts. Quite a lot of people are using this nowadays to put their favorite quote over there or just to share a random thought. Beside of the manual entry of a personal message, such a message can be automatically generated as well by an external application. This way, it's possible to show your friends "what you're listening to". Basically, a music player such as Windows Media Player does some IPC (Inter-Process Communication) to MSN Messager using the Win32 API every time the song which is currently playing changes. The nice thing is that you can write your own plug-in using some relatively simple C#:

  1. Create a Windows Forms application and open the source code view for the Form1.cs form.
  2. Add the namespace:

    using System.Runtime.InteropServices;

  3. In the class definition, put two DllImports as follows:

    [DllImport("user32", EntryPoint="SendMessageA")]
    private static extern int SendMessage(int Hwnd, int wMsg, int wParam, int lParam);

    [DllImport("user32", EntryPoint="FindWindowExA")]
    private static extern int FindWindowEx(int hWnd1, int hWnd2, string lpsz1, string lpsz2);

  4. Import the WM_COPYDATA constant for the Win32 API calls:

    private
    const short WM_COPYDATA = 74;

  5. Declare a struct for the IPCs we're going to perform:

    public struct COPYDATASTRUCT
    {
         public int dwData;
         public int cbData;
         public int lpData;
    }

    public COPYDATASTRUCT data;

I forgot this function in my initial post:

public int VarPtr(object e)
{
     GCHandle GC = GCHandle.Alloc(e, GCHandleType.Pinned);
     int gc = GC.AddrOfPinnedObject().ToInt32();
     GC.Free();
     return gc;
}

Now the plumbing is done, we can start implementing the IPC-method to MSN. The only thing you need to know is the identification string of the MSN Messager 7 application, which is "MsnMsgrUIManager". The code for the method is displayed below:

private void SendMSNMessage(bool enable, string category, string message)
{
    
string buffer = "\\0" + category + "\\0" + (enable ? "1" : "0") + "\\0{0}\\0" + message + "\\0\\0\\0\\0\0";
    
int handle = 0;

     data.dwData = 0x0547;
     data.lpData = VarPtr(buffer);
     data.cbData = buffer.Length * 2;

     handle = FindWindowEx(0, handle, "MsnMsgrUIManager",
null);
     if (handle > 0)
          SendMessage(handle, WM_COPYDATA, 0, VarPtr(data));
}

Basically, this method takes three parameters. The first indicated whether to display a message or not. The second one contains a category which can be "Office", "Games" or "Music". The last parameter takes the message itself. Assuming MSN Messenger 7.0 is running on your machine (in the current user session), a call to:

SendMSNMessage(true, "Office", "Hello World");

would put "(Office logo) Hello World" behind your nickname in MSN. By calling:

SendMSNMessage(false, "Office", "");

the message will be gone and the old personal message of MSN will be restored. Note that for this to work, you should put on the feature "What I'm listening to" in MSN 7.0. It's this feature which listens to incoming IPC messages in order to display them.

Finally a note on how personal messages are sent to your friends. In contrast to your nickname on MSN, which is centrally stored on the MSN Directory Servers, the Personal Message is not sent to Microsoft and is shared with your friends in a peer-to-peer fashion. So, when someone is viewing his/her MSN contact list or when someone is chatting with you, the message will be send to that person. The same holds for your display picture, which is also sent peer-to-peer. So, you don't have to worry about privacy issues ("oh no, Microsoft can see this message" simply doesn't hold) and as the matter in fact, storing the personal message in a directory service would be one of the worst things to do. As the personal message can change quite often (assume you're playing a CD of 60 minutes with 100 tracks :o, or just write a program using the displayed code to change the message every second or so) this would be the ideal feature to create a DDoS attack on the MSN servers. That's in my opinion one of the reasons (beside privacy, you don't want Microsoft to know what music you're playing) why personal messages are computer-bound and not MSN-member-bound.

Using the displayed code, I created a simple countdown for product releases that puts something like "Countdown to XYZ - D days, H hours, M minutes, S seconds" behind my name on MSN. Note however, that the refresh speed (peer-to-peer) is somewhere between 5 and 10 seconds on the average, so your contacts won't see a smooth countdown in seconds.

Have fun!

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

More Posts « Previous page