Sunday, September 17, 2006 5:03 PM bart

Your first Windows Live Messenger add-in

Introduction

Did you know that the new Windows Live Messenger comes with a add-in support? It's not enabled by default and only intended to gather first feedback from developers but hey, you know what, aren't we all developers? Time to try out! In this post, you'll learn the basics of Windows Live Messenger add-in development.

Getting started

As I said in the introduction, "it's not enabled by default". So what'bout enabling it in the first place? To do this, exit Windows Live Messenger (i.e. msnmsgr.exe shouldn't appear in the process list anymore - [PS] gps -p msnmsgr | stop-process) and open up the Registry Editor. Go to HKCU\SOFTWARE\Microsoft\MSNMessenger and add a DWORD value named AddInFeatureEnabled set to 1. Or in PS-talk (in a "verbosity is your enimy" [J. Snover] mood):

PS C:\Users\Bart> cd HKCU:\Software\Microsoft\MSNMessenger
PS HKCU:\Software\Microsoft\MSNMessenger> new-itemproperty -n AddInFeatureEnabled -path . -va 1 -t Dword


PSPath              : Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\Sof
                      tware\Microsoft\MSNMessenger
PSParentPath        : Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\Sof
                      tware\Microsoft
PSChildName         : MSNMessenger
PSDrive             : HKCU
PSProvider          : Microsoft.PowerShell.Core\Registry
AddInFeatureEnabled : 1

Lovely isn't it?

Now launch Windows Live Messenger, sign in and go to Tools, Options, Add-ins. You should see this:

We'll revisit this dialog later on to add our add-in.

The basics of Live Messenger add-ins

Windows Live Messenger add-ins are written using a managed library called MessengerClient.dll (can be found in %programfiles%\MSN Messenger) which holds the namespace Microsoft.Messenger. In order to create an add-in, open up Visual Studio 2005 (.NET 2.0 is required), create a new Class Library project (e.g. called "MyAddin") and add a reference to the MessengerClient.dll file:

Next, change the project properties so that the assembly name equals the (fully qualified) class name of the add-in. This is important to get the add-in to work. In my case the class is defined as follows:

namespace MessengerAddin
{
   public class MyAddin

   {

So, we need the following assembly name:

Next, import the namespace Microsoft.Messenger and implement the IMessengerAddin interface:

using Microsoft.Messenger;

namespace MessengerAddin
{
   public class MyAddin :
IMessengerAddIn
   {
      private MessengerClient
messenger;

      public void Initialize(MessengerClient messenger)
      {
         this
.messenger = messenger;
      }
   }
}

So, you've just created your first non-functional add-in for Windows Live Messenger.

Something useful - Say Hello

Let's try to do something useful now. What about some simple "Hello World" alike add-in (did I say useful?)... In order to do this, we'll do some nice things inside the Initialize method:

public void Initialize(MessengerClient messenger)
{
   this
.messenger = messenger;

   messenger.AddInProperties.Creator =
"Bart De Smet"
;
   messenger.AddInProperties.Description =
"Some sample add-in"
;
   messenger.AddInProperties.FriendlyName =
"MyAddin"
;
   messenger.AddInProperties.PersonalStatusMessage =
"Greetings from MyAddin"
;
   //messenger.AddInProperties.Status = ...;
   messenger.AddInProperties.Url = new Uri("http://blogs.bartdesmet.net/bart"
);
   messenger.AddInProperties.UserTile = GetImage(i);
}

The GetImage method is a custom helper method which relies on System.Drawing, so add a reference to System.Drawing.dll and import the System.Drawing namespace:

private Image GetImage(int i)
{
   Bitmap bmp = new Bitmap
(40, 40);
   Graphics g = Graphics
.FromImage(bmp);
   g.FillPie(
Brushes
.Red, 5, 5, 30, 30, i * 90 , 90);
   g.FillPie(
Brushes
.Blue, 5, 5, 30, 30, (i + 1) * 90, 90);
   g.FillPie(
Brushes
.Yellow, 5, 5, 30, 30, (i + 2) * 90, 90);
   g.FillPie(
Brushes
.Green, 5, 5, 30, 30, (i + 3) * 90, 90);
   return
bmp;
}

Now compile the project and go to Windows Live Messenger. Load the add-in via Tools, Options, Add-ins:

Now you should something like this:

Click OK to continue. In the Windows Live Messenger main window, click your nickname and choose "Turn on "MyAddin"":

Watch what happens to the personal message and the display picture:

Something even more useful - A simple conversation content filter

Cool stuff, we're in control now. Time for something more useful: a simple conversation content filter. Every time a message is sent by the Windows Live Messenger client, we can receive an event to control what's going on. Go back to the Initialize method, comment out everything from our first sample and add the following:

public void Initialize(MessengerClient messenger)
{
   this
.messenger = messenger;

   messenger.IncomingTextMessage += new EventHandler<IncomingTextMessageEventArgs>(messenger_IncomingTextMessage);
}

Using tab-completion (after the += operator) you'll get a stub for the event handler. Fill it out as follows:

void messenger_OutgoingTextMessage(object sender, OutgoingTextMessageEventArgs e)
{
   string
msg = e.TextMessage.ToLower();
   if (msg.Contains("password"
))
      e.Cancel =
MessageBox.Show("Your outgoing message seems to contain a password. Do you really want to send it?", "MyAddin", MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) == DialogResult
.No;
}

(Note: you can get the destination user using e.UserTo)

Needless to say you need to add a reference to System.Windows.Forms.dll and import the System.Windows.Forms namespace to get this to work. Time to recompile. VS2005 will most likely complain:

Error 1 Unable to copy file "obj\Debug\MessengerAddin.MyAddin.dll" to "bin\Debug\MessengerAddin.MyAddin.dll". The process cannot access the file 'bin\Debug\MessengerAddin.MyAddin.dll' because it is being used by another process. MessengerAddin

The best way to get around this is to exit Live Messenger (and/or to kill msnmsgr.exe). Next, compile the project and then and re-launch Live Messenger. Now the following should happen in a conversation (if the add-in is enabled, see above on how to do that). Type something with password in it:

Click Send and watch the following message box coming up:

Click No and see how the message delivery is cancelled:

Other ideas:

  • intercept outgoing messages and apply more configurable filtering (warning versus instant blocking)
  • logging of outgoing messages (requires CAS stuff - strong-name your assembly)
  • a spelling-checker (won't be easy and the API doesn't allow to alter outgoing messages straight away)

Something just for fun - A string reverser responder

Wow, what the **** is this? I couldn't (or wasn't in the mood to) think about something useful in case a message arrives on the client. And I wanted to show you how to send messages to some user. An AutoReply kind of thing was too trivial (will post that leter on), so take a look at this. Again, uncomment our previous piece of Initialize-code and add the following:

public void Initialize(MessengerClient messenger)
{
   this
.messenger = messenger;

   messenger.OutgoingTextMessage += new EventHandler<OutgoingTextMessageEventArgs>(messenger_OutgoingTextMessage);
}

Again, the event handler can be stubbed automatically, but of course you need to supply some additional code:

void messenger_IncomingTextMessage(object sender, IncomingTextMessageEventArgs e)
{
   string
s = e.TextMessage;
   StringBuilder sb = new StringBuilder
();
   for (int
i = s.Length - 1; i >= 0; i--)
      sb.Append(s[i]);
   string
reverse = sb.ToString();
   messenger.SendTextMessage(reverse, e.UserFrom);
}

(alternative implementation using Array.Reverse and String.ToCharArray possible). This piece of code illustrates how to get the sender user with e.UserFrom and how to send a text message using SendTextMessage (you could send other things as well, see IntelliSense).

Kill Live Messenger, compile, restart Live Messenger, make sure the add-in is enabled and have someone say something to you. He'll see something weird:

Other ideas:

  • an AutoReply tool that queries the current user's status to determine when to auto-reply
  • logging stuff

Timers, timers, timers

Of course, actions shouldn't be event-driven (or better: not user-initiated) all the time. We could also hook in a timer and use that one to change (for instance) the personal message and the display picture on a regular basis. Here's how:

public void Initialize(MessengerClient messenger)
{
   this
.messenger = messenger;

   System.Timers.Timer tmr = new System.Timers.Timer
(5000);
   tmr.Elapsed +=
new System.Timers.ElapsedEventHandler
(tmr_Elapsed);
   tmr.Start();
}

private DateTime birthday = new DateTime
(1983, 2, 11);
private int
i = 0;

void tmr_Elapsed(object sender, System.Timers.ElapsedEventArgs
e)
{
   i = (i + 1) % 4;
   TimeSpan span = DateTime
.Now - birthday;
   messenger.AddInProperties.PersonalStatusMessage =
String.Format("{0} days {1} hours {2} minutes {3} seconds on planet Earth"
, span.Days, span.Hours, span.Minutes, span.Seconds);
   messenger.AddInProperties.UserTile = GetImage(i);
}

(using the GetImage method used in the first demo above). I think you don't need screenshots to figure out what will happen now...

End-user configuration

Our birthday example is already pretty cool but nobody wants to hardcode the birthday in code of course. Time to enter the scene of end-user configuration. Not difficult at all. Start by capturing the event ShowOptionsDialog from the MessengerClient. If Live Messenger sees that an event handler is available, the Settings button in the Add-ins pane of the Live Messenger Options dialog will become available as show a little further.

private System.Timers.Timer tmr;
private DateTime birthday;
private int i = 0;


public
void Initialize(MessengerClient
messenger)
{
   this
.messenger = messenger;

messenger.ShowOptionsDialog += new EventHandler(messenger_ShowOptionsDialog);

   tmr = new System.Timers.Timer
(5000);
   tmr.Elapsed +=
new System.Timers.ElapsedEventHandler
(tmr_Elapsed);
   StartTimer();
}

private void StartTimer()
{
   if (DateTime.TryParse(messenger.SavedState, out birthday))
      tmr.Start();
}

private void messenger_ShowOptionsDialog(object sender, EventArgs e)
{
   tmr.Stop();
   SettingsDialog settings = new SettingsDialog(birthday);
   if (settings.ShowDialog() == DialogResult.OK)
      messenger.SavedState = settings.Birthday.ToShortDateString();
   StartTimer();
}

The StartTimer method I've added over here takes the SavedState string property from the add-in and tries to parse it to a DateTime object. Next, we'll create the SettingsDialog form:

A quick overview of settings:

  • Button - btnOK - Text="OK"
  • Button - btnCancel - Text="Cancel"
  • Label - label1 - Text="Birthday:"
  • DateTimePicker - birthday
  • Form - this - AcceptButton=btnOK
  • Form - this - CancelButton=btnCancel
  • Form - this - FormBorderStyle=FixedDialog
  • Form - this - MaximizeButton=false
  • Form - this - MinimizeButton=false
  • Form - this - StartPosition=CenterParent
  • Form - this - Text="MyAddin settings"

The code is straightforward:

public partial class SettingsDialog : Form
{
   public SettingsDialog(DateTime
birthday)
   {
      InitializeComponent();

      if (birthday != null
)
         this
.birthday.Value = birthday;
      else
         this.birthday.Value = DateTime
.Now;
   }

   private void btnOK_Click(object sender, EventArgs
e)
   {
      this
.bd = birthday.Value;
      this.DialogResult = DialogResult
.OK;
   }

   private DateTime
bd;

   public DateTime
Birthday
   {
      get { return
bd; }
      set { bd = value
; }
   }
}

Time to try out. Usual steps again: exit Live Messenger, compile, start Live Messenger and enable the add-in. Go to Tools, Options, Add-ins and look at the enabled Settings button now:

When you click it, you'll see the dialog popping up (unfortunately somewhere in the back - have to check this little issue) with a security warning (caused by the CAS feature):

Ignore this for now (you can solve it by strong-naming the add-in and some good deal of CAS knowledge; add-ins in Live Messenger are sandboxed by putting them in the Internet security zone) and set your birthday:

Windows Live Messenger will now start counting the days, hours, minutes and seconds we've spent so far on our lovely planet:

Warning: This might cause a relatively big amount of internet traffic!

I've created a similar out-of-process counter for MSN 7.0 in using the technologies described in these two blog posts: one and two. Some time in the future I'll upload a counter based on Windows Live Messenger technology that allows to countdown and to count"up" to or from a given event (e.g. Windows Vista launch date ;-)).

But where are our settings? The answer: first look in the registry under HKCU\SOFTWARE\Microsoft\MSNMessenger\PerPassportSettings\<passport identifier>\InstalledAddInsList\MessengerAddin.MyAddin.dll:

The StateObjectStoreName value contains a piece of XML with a reference to (and additional hashes to ensure integrity) the file holding the settings:

<msnobj Creator="bartdesmet@hotmail.com" Size="20" Type="12" Location="TFR22A9.dat" Friendly="QwA6AFwAVQBzAGUAcgBzAFwAQgBhAHIAdABcAEQAbwBjAHUAbQBlAG4AdABzAFwAVgBpAHMAdQBhAGwAIABTAHQAdQBkAGkAbwAgADIAMAAAAA==" SHA1D="TxXeos2NbAv1uHBCxWYkbUmN2Y8=" SHA1C="Q3L8ZsFzRfP2Xu7aYuZaGEXty6I="/>

The specified file (TFR22A9.dat) in my case can be found in the Application Data folder of the currently logged in user, under Microsoft\MSN Messenger\<passport identifier>\PlugInState, as shown below (notice the slightly different file location in Vista due to the renewed profile system):

Conclusion

Although the add-in functionality of Windows Live Messenger is still hidden and should be considered as kind of a sneak preview towards developers, you can already use it to do some cool things. Nevertheless, the API still lacks some much-desired functionality to control even more aspects of Windows Live Messenger (that is, you'll hit the boundaries quite rapidly).

Useful resources

Special thanks to Robin Vermeirsch who was so kind to be my test victim on Windows Live Messenger.

Happy Live Messenger messing-around!

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

Filed under: ,

Comments

# Your first Windows Live Messenger add-in

Sunday, September 17, 2006 7:19 PM by It's Way Too Early For This

Once again, Bart delivers a very verbose &quot;how-to&quot;. This one is about MSN Live Messenger Add-ins and contains

# re: Your first Windows Live Messenger add-in

Tuesday, September 19, 2006 10:39 AM by Monkeyget

Looks like there are quite a bit of fun things to do.
This add-in system looks way better than the SDK i've tested here : https://www.robotinvaders.com/main/About.aspx
(Hint:  you know your sdk is bad when it requires to install a webserver that's always running just to configure a few settings)

# re: Your first Windows Live Messenger add-in

Tuesday, September 19, 2006 2:43 PM by CodeKilla@Gmail.com

This addin programming is very interesting, the only issue is, to get a good idea.

Display Uptime as personal message.

This is an idea popped into my mind the other day. To have the messenger display System Uptime in the personal message. Nice for bragging infront of your buddys :-)

based on:
http://community.bartdesmet.net/blogs/bart/archive/2005/06/08/967.aspx
thanx to the author



_______________________________________________
//Form1.Designer.cs
namespace MSNMessengerPrivateMessageChanger
{
   partial class Form1
   {
       /// <summary>
       /// Required designer variable.
       /// </summary>
       private System.ComponentModel.IContainer components = null;

       /// <summary>
       /// Clean up any resources being used.
       /// </summary>
       /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
       protected override void Dispose(bool disposing)
       {
           if (disposing && (components != null))
           {
               components.Dispose();
           }
           base.Dispose(disposing);
       }

       #region Windows Form Designer generated code

       /// <summary>
       /// Required method for Designer support - do not modify
       /// the contents of this method with the code editor.
       /// </summary>
       private void InitializeComponent()
       {
           this.components = new System.ComponentModel.Container();
           this.groupBox1 = new System.Windows.Forms.GroupBox();
           this.label2 = new System.Windows.Forms.Label();
           this.txtIntervalChange = new System.Windows.Forms.TextBox();
           this.chkShowSystemUptime = new System.Windows.Forms.CheckBox();
           this.btnChangeMSNPrivateMessage = new System.Windows.Forms.Button();
           this.label1 = new System.Windows.Forms.Label();
           this.txtMSNPrivateMessage = new System.Windows.Forms.TextBox();
           this.timerChange = new System.Windows.Forms.Timer(this.components);
           this.groupBox2 = new System.Windows.Forms.GroupBox();
           this.radioButtonOffice = new System.Windows.Forms.RadioButton();
           this.radioButtonMusic = new System.Windows.Forms.RadioButton();
           this.radioButtonGames = new System.Windows.Forms.RadioButton();
           this.groupBox1.SuspendLayout();
           this.groupBox2.SuspendLayout();
           this.SuspendLayout();
           //
           // groupBox1
           //
           this.groupBox1.Controls.Add(this.groupBox2);
           this.groupBox1.Controls.Add(this.label2);
           this.groupBox1.Controls.Add(this.txtIntervalChange);
           this.groupBox1.Controls.Add(this.chkShowSystemUptime);
           this.groupBox1.Controls.Add(this.btnChangeMSNPrivateMessage);
           this.groupBox1.Controls.Add(this.label1);
           this.groupBox1.Controls.Add(this.txtMSNPrivateMessage);
           this.groupBox1.Location = new System.Drawing.Point(7, 1);
           this.groupBox1.Name = "groupBox1";
           this.groupBox1.Size = new System.Drawing.Size(300, 162);
           this.groupBox1.TabIndex = 1;
           this.groupBox1.TabStop = false;
           //
           // label2
           //
           this.label2.AutoSize = true;
           this.label2.Location = new System.Drawing.Point(281, 52);
           this.label2.Name = "label2";
           this.label2.Size = new System.Drawing.Size(12, 13);
           this.label2.TabIndex = 8;
           this.label2.Text = "s";
           //
           // txtIntervalChange
           //
           this.txtIntervalChange.Location = new System.Drawing.Point(172, 49);
           this.txtIntervalChange.Name = "txtIntervalChange";
           this.txtIntervalChange.RightToLeft = System.Windows.Forms.RightToLeft.Yes;
           this.txtIntervalChange.Size = new System.Drawing.Size(108, 20);
           this.txtIntervalChange.TabIndex = 7;
           this.txtIntervalChange.Text = "30";
           //
           // chkShowSystemUptime
           //
           this.chkShowSystemUptime.AutoSize = true;
           this.chkShowSystemUptime.Checked = true;
           this.chkShowSystemUptime.CheckState = System.Windows.Forms.CheckState.Checked;
           this.chkShowSystemUptime.Location = new System.Drawing.Point(12, 51);
           this.chkShowSystemUptime.Name = "chkShowSystemUptime";
           this.chkShowSystemUptime.Size = new System.Drawing.Size(151, 17);
           this.chkShowSystemUptime.TabIndex = 6;
           this.chkShowSystemUptime.Text = "Show system uptime every";
           this.chkShowSystemUptime.UseVisualStyleBackColor = true;
           //
           // btnChangeMSNPrivateMessage
           //
           this.btnChangeMSNPrivateMessage.Location = new System.Drawing.Point(187, 106);
           this.btnChangeMSNPrivateMessage.Name = "btnChangeMSNPrivateMessage";
           this.btnChangeMSNPrivateMessage.Size = new System.Drawing.Size(106, 42);
           this.btnChangeMSNPrivateMessage.TabIndex = 5;
           this.btnChangeMSNPrivateMessage.Text = "Change it!";
           this.btnChangeMSNPrivateMessage.UseVisualStyleBackColor = true;
           this.btnChangeMSNPrivateMessage.Click += new System.EventHandler(this.btnChangeMSNPrivateMessage_Click);
           //
           // label1
           //
           this.label1.AutoSize = true;
           this.label1.Location = new System.Drawing.Point(6, 10);
           this.label1.Name = "label1";
           this.label1.Size = new System.Drawing.Size(50, 13);
           this.label1.TabIndex = 4;
           this.label1.Text = "Message";
           //
           // txtMSNPrivateMessage
           //
           this.txtMSNPrivateMessage.Location = new System.Drawing.Point(9, 26);
           this.txtMSNPrivateMessage.Name = "txtMSNPrivateMessage";
           this.txtMSNPrivateMessage.Size = new System.Drawing.Size(271, 20);
           this.txtMSNPrivateMessage.TabIndex = 3;
           this.txtMSNPrivateMessage.Text = "This is my private message";
           //
           // timerChange
           //
           this.timerChange.Tick += new System.EventHandler(this.btnChangeMSNPrivateMessage_Click);
           //
           // groupBox2
           //
           this.groupBox2.Controls.Add(this.radioButtonGames);
           this.groupBox2.Controls.Add(this.radioButtonMusic);
           this.groupBox2.Controls.Add(this.radioButtonOffice);
           this.groupBox2.Location = new System.Drawing.Point(11, 75);
           this.groupBox2.Name = "groupBox2";
           this.groupBox2.Size = new System.Drawing.Size(152, 73);
           this.groupBox2.TabIndex = 9;
           this.groupBox2.TabStop = false;
           this.groupBox2.Text = "Icon";
           //
           // radioButtonOffice
           //
           this.radioButtonOffice.AutoSize = true;
           this.radioButtonOffice.Location = new System.Drawing.Point(39, 9);
           this.radioButtonOffice.Name = "radioButtonOffice";
           this.radioButtonOffice.Size = new System.Drawing.Size(53, 17);
           this.radioButtonOffice.TabIndex = 0;
           this.radioButtonOffice.TabStop = true;
           this.radioButtonOffice.Text = "Office";
           this.radioButtonOffice.UseVisualStyleBackColor = true;
           //
           // radioButtonMusic
           //
           this.radioButtonMusic.AutoSize = true;
           this.radioButtonMusic.Checked = true;
           this.radioButtonMusic.Location = new System.Drawing.Point(39, 31);
           this.radioButtonMusic.Name = "radioButtonMusic";
           this.radioButtonMusic.Size = new System.Drawing.Size(53, 17);
           this.radioButtonMusic.TabIndex = 1;
           this.radioButtonMusic.TabStop = true;
           this.radioButtonMusic.Text = "Music";
           this.radioButtonMusic.UseVisualStyleBackColor = true;
           //
           // radioButtonGames
           //
           this.radioButtonGames.AutoSize = true;
           this.radioButtonGames.Location = new System.Drawing.Point(39, 54);
           this.radioButtonGames.Name = "radioButtonGames";
           this.radioButtonGames.Size = new System.Drawing.Size(58, 17);
           this.radioButtonGames.TabIndex = 2;
           this.radioButtonGames.TabStop = true;
           this.radioButtonGames.Text = "Games";
           this.radioButtonGames.UseVisualStyleBackColor = true;
           //
           // Form1
           //
           this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
           this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
           this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
           this.ClientSize = new System.Drawing.Size(311, 169);
           this.Controls.Add(this.groupBox1);
           this.MaximizeBox = false;
           this.Name = "Form1";
           this.ShowIcon = false;
           this.ShowInTaskbar = false;
           this.Text = "MSN Messenger System Uptime Plugin";
           this.groupBox1.ResumeLayout(false);
           this.groupBox1.PerformLayout();
           this.groupBox2.ResumeLayout(false);
           this.groupBox2.PerformLayout();
           this.ResumeLayout(false);

       }

       #endregion

       private System.Windows.Forms.GroupBox groupBox1;
       private System.Windows.Forms.Button btnChangeMSNPrivateMessage;
       private System.Windows.Forms.Label label1;
       private System.Windows.Forms.TextBox txtMSNPrivateMessage;
       private System.Windows.Forms.CheckBox chkShowSystemUptime;
       private System.Windows.Forms.Timer timerChange;
       private System.Windows.Forms.TextBox txtIntervalChange;
       private System.Windows.Forms.Label label2;
       private System.Windows.Forms.GroupBox groupBox2;
       private System.Windows.Forms.RadioButton radioButtonGames;
       private System.Windows.Forms.RadioButton radioButtonMusic;
       private System.Windows.Forms.RadioButton radioButtonOffice;

   }
}

_____________________________________________________________________________________________________________________________________________
///////////////////////////////////////
// Form1.cs

using System;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace MSNMessengerPrivateMessageChanger
{
   public partial class Form1 : Form
   {
       [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);
       
       //Import the WM_COPYDATA constant for the Win32 API calls    
       private const short WM_COPYDATA = 74;

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

       public Int64 uptime;   // Here will the UpTime value be stored
       
       // In such structure will the GetUptime() store the proper time
       public struct TimeStruct
       {
           public int Seconds;
           public int Minutes;
           public int Hours;
           public Int64 Days;
       }
       public TimeStruct uptimeAll = new TimeStruct();
       
       public int VarPtr(object e)
       {
            GCHandle GC = GCHandle.Alloc(e, GCHandleType.Pinned);
            int gc = GC.AddrOfPinnedObject().ToInt32();
            GC.Free();
            return gc;
       }

       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));
       }

       public Form1()
       {
           InitializeComponent();
       }
       
       // This function will calculate how much days, hours,
       // minutes, seconds have passed.
       // Requires a ref to a TimeStruct as parameter.
       //
       public void GetUptime(ref TimeStruct time)
       {
           time.Seconds = (int)uptime / 1000; //1000 miliseconds = 1 second
           time.Minutes = time.Seconds / 60;
           time.Hours = time.Minutes / 60;
           
           time.Days = time.Hours / 24; //we got days

           //now let's calculate the others
           time.Hours = time.Hours - (int)time.Days * 24;
           time.Minutes = time.Minutes - time.Hours * 60;
           time.Seconds = time.Seconds -((int)time.Days * 24 * 60 * 60) - (time.Hours * 60 * 60) - (time.Minutes * 60);

           //easy does it :-)
       }
       
       private void GetSystemUptime()
       {
           uptime = System.Environment.TickCount; //Gets the number of milliseconds elapsed since the system started.
           GetUptime(ref uptimeAll);          
       }

       private void btnChangeMSNPrivateMessage_Click(object sender, EventArgs e)
       {

           string MSNIcon = "Music"; //Music = Default;

           GetSystemUptime();

           if (radioButtonGames.Checked == true) MSNIcon = "Games";
           if (radioButtonMusic.Checked == true) MSNIcon = "Music";
           if (radioButtonOffice.Checked == true) MSNIcon = "Office";


           //"Office", "Games" or "Music"
           if (chkShowSystemUptime.Checked == true)
           {
               //show system uptime
               timerChange.Enabled = true;
               timerChange.Interval = int.Parse(txtIntervalChange.Text) * 1000;

               //show system uptime in msn like: //System Up Time: 0 Days, 11 Hours, 52 Minutes, 30 Seconds
               SendMSNMessage(true, MSNIcon, "System Up Time: " + uptimeAll.Days + " Days, " + uptimeAll.Hours + " Hours, " + uptimeAll.Minutes + " Minutes, " + uptimeAll.Seconds + " Seconds");
               
           }
           else
           {   //or else
               timerChange.Enabled = false; //turn off the timer
               
               SendMSNMessage(true, MSNIcon, txtMSNPrivateMessage.Text);  //set the text
           }

       }
   }
}

# Walkthroughs on writing Windows Live Messenger Addins with .NET

Saturday, September 30, 2006 3:12 PM by David Boschmans Weblog

Since version 8 of MSN Messenger it's possible to write add-ins with .NET for MSN/Windows Live Messenger....

# re: Your first Windows Live Messenger add-in

Sunday, October 01, 2006 1:25 AM by Andreas

Do you know if it's possible to start an addin automatically? I've created and addin with some of the functions above (generic count-down and forbidden words (passwords)).

I'm also thinking about saving stats (how many messages from each user and stuff) but the addin will be almost useless if I have to load it manually since I will most likely forget most times.

# re: Your first Windows Live Messenger add-in

Monday, October 23, 2006 8:10 PM by kho

hey, i'm new to programming, but can i write those add-in using C++?

# re: Your first Windows Live Messenger add-in

Monday, October 23, 2006 10:03 PM by bart

Hi Kho, Yes you can, as long as you write "managed code", for example in Managed C++. -Bart

# re: Your first Windows Live Messenger add-in

Saturday, October 28, 2006 9:52 AM by Rob Sitter

I might add that they should sign their assemblies with a strong name key file and then gacutil the sucker on build or they'll come back to you asking "Why can't I do this, why can't I do that" blah blah blah :)

# Dog Training &raquo; Blog Archive &raquo; Messenger By the Millions

Friday, January 26, 2007 9:51 PM by Dog Training » Blog Archive » Messenger By the Millions

# Your first Windows Live Messenger Add-In

Monday, January 29, 2007 4:21 AM by Stray Thoughts

Here is an excellent tutorial called "Your first Windows Live Messenger Add-In" written by MVP Visual

# FVN Blog &raquo; Blog Archive &raquo; ??????MSN/WLM Add-in?????????

# My Fist Live Messenger Add-in : BlogPublisher &laquo; Syed Faraz Mahmood

# Mi primer WLM add-in &laquo; felipEx&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp; trucos, howto, scripts y más

# Como hacer add-in en WLM by felipEx &laquo; AbarK ??? Programas, Myl, Lifo y m??s

Pingback from  Como hacer add-in en WLM by felipEx &laquo; AbarK   ???   Programas, Myl, Lifo y m??s

# TweetieBot - A BizTalk Services Experiment

Wednesday, May 16, 2007 12:43 PM by Clemens Vasters and the WS Stars

Having an Internet Service Bus up in the cloud is not very entertaining unless there are services in

# koolung blog &raquo; MSN add-on

Wednesday, October 10, 2007 2:29 AM by koolung blog » MSN add-on

Pingback from  koolung blog &raquo; MSN add-on

# Three Headed Monkeys &raquo; Simple Youtube Video Watcher Addin for MSN Messenger

Pingback from  Three Headed Monkeys  &raquo; Simple Youtube Video Watcher Addin for MSN Messenger

# CodeSnippet: WCF WPF Making Snarl.NET

Friday, January 11, 2008 1:48 AM by Student Union

# CodeSnippet: Snarl.NET using WCF (Part I) - Paul&#8217;s Blog

Sunday, January 20, 2008 7:49 PM by CodeSnippet: Snarl.NET using WCF (Part I) - Paul’s Blog

Pingback from  CodeSnippet: Snarl.NET using WCF (Part I) - Paul&#8217;s Blog

# http://bartdesmet.net/blogs/bart/archive/2006/09/17/4431.aspx

# Windows Live Messenger da Eklentilere ??zin Verin {Add-in} &raquo; MSN ciyiz

Pingback from  Windows Live Messenger da Eklentilere ??zin Verin {Add-in} &raquo; MSN ciyiz

# Windows Live Messenger Add-ins

Monday, June 09, 2008 10:22 PM by alFador en punto NET

Windows Live Messenger Add-ins

# Leuk voor J &raquo; Blog Archive &raquo; Writing Add-ins for Windows Live Messenger

Pingback from  Leuk voor J  &raquo; Blog Archive   &raquo; Writing Add-ins for Windows Live Messenger

# SDK for WLM | keyongtech

Wednesday, January 21, 2009 6:46 PM by SDK for WLM | keyongtech

Pingback from  SDK for WLM | keyongtech

# WLM 14.0.8064.206 + addin

Sunday, May 03, 2009 6:42 PM by WLM 14.0.8064.206 + addin

Pingback from  WLM 14.0.8064.206 + addin

# alFador en punto net &raquo; Windows Live Messenger Add-ins

Saturday, August 14, 2010 1:53 PM by alFador en punto net » Windows Live Messenger Add-ins

Pingback from  alFador en punto net  &raquo; Windows Live Messenger Add-ins

# How to enable addins on Windows Live Messenger 2011? | DeveloperQuestion.com

Pingback from  How to enable addins on Windows Live Messenger 2011? | DeveloperQuestion.com

# How To Enable Addins On Windows Live Messenger 2011? | Click &amp; Find Answer !

Pingback from  How To Enable Addins On Windows Live Messenger 2011? | Click &amp; Find Answer !