Build a C# NotifyIcon Scheduled Outlook Mail Checker
by Peter A. Bromberg, Ph.D.

Peter Bromberg

"There's a fine line between fishing and just standing on the shore like an idiot." -- Steven Wright

I don't know about you, but where I work we all keep Outlook running in the Notification Area so we don't miss any emails. Now this is about as dumb as the Braille they put on the drive-through teller machines. The problem is that the bloated thing takes up about 30MB of memory just to sit there unused for most of the day, in case an email comes in that you don't want to miss.



I like to run my machine with as much available memory as possible, so I set out to create a better solution. The answer is a little app that gets run every 10 minutes by Task Scheduler, checks the Outlook Inbox, and if there is no unread mail, it quits. If there is unread mail, it pops up a Notification Icon Balloon Tip from the Notification Area telling you how many unread emails you have. If you click the balloon within 20 seconds, it opens up Outlook for you. If you don't click within 20 seconds, it assumes you aren't there and it goes away till the next run time.

Simple, efficient, and it saves me 30MB of overhead. If you like this idea, it can be used for other things as well - you could certainly revise it to check your POP email accounts as well. This little app has two parts - there's a Form which basically serves as the container for the NotifyIcon and the code, and there's the NotifyIcon BalloonTip component. I used one by Ivo Closs, mostly because I don't believe in reinventing the wheel and his seems to work just fine for what I needed. There are probably a dozen different implementations of these by different developers.

Let's take a look at the Form code, since you can play with the Notification Icon BalloonTip component on your own after you download the solution below:

using System;

using System.Drawing;

using System.Collections;

using System.ComponentModel;

using System.Windows.Forms;

using System.Data;

using NotifyIcon;

 

namespace TrayNotifyIcon

{

 public class Form1 : System.Windows.Forms.Form

 {

private System.ComponentModel.Container components = null;

private System.Timers.Timer timer1;

private Microsoft.Office.Interop.Outlook._Application OutlookApp=null;

public Form1()

{

Application.EnableVisualStyles();

Application.DoEvents();

InitializeComponent();

}

protected override void Dispose( bool disposing )

{

if( disposing )

{

if (components != null)

{

components.Dispose();

}

}

base.Dispose( disposing );

}

 

#region Windows Form Designer generated code

private void InitializeComponent()

{

System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(Form1));

this.timer1 = new System.Timers.Timer();

((System.ComponentModel.ISupportInitialize)(this.timer1)).BeginInit();

this.timer1.Enabled = true;

this.timer1.Interval = 20000;

this.timer1.SynchronizingObject = this;

this.timer1.Elapsed += new System.Timers.ElapsedEventHandler(this.timer1_Elapsed);

this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);

this.ClientSize = new System.Drawing.Size(115, 54);

this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));

this.Name = "Form1";

this.Text = "Outlook Check";

this.WindowState = System.Windows.Forms.FormWindowState.Minimized;

this.Resize += new System.EventHandler(this.FormResize);

this.Closing += new System.ComponentModel.CancelEventHandler(this.FormClosing);

this.Load += new System.EventHandler(this.Form1_Load);

((System.ComponentModel.ISupportInitialize)(this.timer1)).EndInit();

 

}

#endregion

 

[STAThread]

static void Main()

{

Application.Run(new Form1());

}

 

private BalloonTip TrayNotifyIcon = new BalloonTip();

private ContextMenu TrayContextMenu = new ContextMenu();

 

private void Form1_Load(object sender, System.EventArgs e)

{

string name= System.Diagnostics.Process.GetCurrentProcess().ProcessName;

System.Diagnostics.Process[] p = System.Diagnostics.Process.GetProcessesByName(name);

if(p.Length>1)

{

TrayNotifyIcon.Visible =false;

System.Environment.Exit(0);

}

 

this.Hide();

TrayContextMenu.MenuItems.Add("&Exit", new System.EventHandler(this.mnuExit_Click));

TrayNotifyIcon.Text = this.Text; // Help text for MouseLeave on Icon

TrayNotifyIcon.Icon = this.Icon; // Icon for NotifyIcon

TrayNotifyIcon.Form = this; // Form to restore when DoubleClick on Icon

TrayNotifyIcon.ContextMenu = TrayContextMenu; // ContextMenu for RightClick on Icon

TrayNotifyIcon.Visible = true; // Show icon in TaskBar

TrayNotifyIcon.BalloonClick+=new EventHandler(TrayNotifyIcon_BalloonClick);

int x= GetUnreadMessages();

OutlookApp.Application.Quit();

if(x >0)

{

string message="There are " +x.ToString() + "New Emails.";

TrayNotifyIcon.ShowBalloon("Outlook Email", message,NotifyIcon.BalloonTip.NotifyInfoFlags.Info, 100);

this.timer1.Start();

}

else

{

TrayNotifyIcon.Visible =false;

TrayNotifyIcon.Remove();

System.Environment.Exit(0);

}

}

 

private void FormClosing(object sender, System.ComponentModel.CancelEventArgs e)

{

e.Cancel = true;

this.Hide();

this.WindowState = System.Windows.Forms.FormWindowState.Minimized;

}

 

private void FormResize(object sender, System.EventArgs e)

{

if (this.WindowState == FormWindowState.Minimized)

{

this.Hide();

}

}

 

private int GetUnreadMessages()

{

OutlookApp=new Microsoft.Office.Interop.Outlook.ApplicationClass();

if (OutlookApp != null)

{

Microsoft.Office.Interop.Outlook.MAPIFolder inbox =

OutlookApp.Session.GetDefaultFolder(

Microsoft.Office.Interop.Outlook.OlDefaultFolders.olFolderInbox);

return inbox.UnReadItemCount;

}

else

{

return -1;

}

}

 

 

private void mnuExit_Click(object sender, EventArgs e)

{

TrayNotifyIcon.Visible =false;

TrayNotifyIcon.Dispose();

System.Environment.Exit(0);

 

}

private void TrayNotifyIcon_BalloonClick(object sender, EventArgs e)

{

System.Diagnostics.Process.Start("Outlook.exe");

TrayNotifyIcon.Visible =false;

TrayNotifyIcon.Dispose();

System.Environment.Exit(0);

}

 

private void timer1_Elapsed(object sender, System.Timers.ElapsedEventArgs e)

{

TrayNotifyIcon.Visible =false;

TrayNotifyIcon.Dispose();

System.Environment.Exit(0);

}

 }

}

All the "action" is in the Form_Load handler. First, I check to see if there is another instance of this process running, and if so, I exit gracefully. You don't really need this code, but I've left it in for completeness sake. Then, I set up my TrayNotifyIcon and it's events. Then, I make a call to the GetUnreadMesages method, which simply starts an instance of the OutLook.ApplicationClass, sets it to the Inbox folder, and gets the count of unread Inbox Items, and returns it. Back in the calling method, I call .Quit() to get rid of Outlook, since I am done with it for now. If my count is greater than zero, I pop up a NotifyIcon BalloonTip with the message, and start a 20 second timer.

Note that in the downloadable code, I do even more- I get a hold of the CommandBar and call Send/Receive All to ensure that anything on the server is downloaded first. I also call ReleaseComObject on all the little boogers and finally kill the Outlook process after a 1 second sleep. After all, the whole point of this experiment is to keep Outlook unloaded!

If the user clicks on the balloontip before the 20 seconds are up, the TrayNotifyIcon_BalloonClick event is fired, and Outlook is opened for real using the Process class so that I can read the mail. At this point, we can get rid of our app since it has completed it's mission. The rest is just window dressing. If nobody clicks, the timer kicks in and closes the app for us.

One last thought: Because of the way Microsoft has clobbered the PIA's with Office, you have to build this by setting a COM reference to the exact version of Outlook (9,10,11, XX?) which doesn't exactly make it as portable as could be. However, you can use a workaround with Late Binding. Here's an example:

 private int GetUnreadMessagesLateBound()

{

Type outlook;

object oApp;

outlook = Type.GetTypeFromProgID("Outlook.Application");

oApp = Activator.CreateInstance(outlook);

Object oNameSpace = oApp.GetType().InvokeMember("GetNamespace",

BindingFlags.GetProperty, null, oApp, new object[1]{"MAPI"});

Object oFolder = oNameSpace.GetType().InvokeMember("GetDefaultFolder",

BindingFlags.GetProperty, null, oNameSpace, new object[] {6}); // ("6" is inbox)

object oItems = oFolder.GetType().

InvokeMember("UnreadItemCount",BindingFlags.GetProperty,null,oFolder,null);

return (int)oItems;

}

The only thing left to do with this is build a Release, go into the Control Panel and set up Scheduled Tasks to run this every X minutes during the work day. That's it. Enjoy.

Download the VS.NET Solution that accompanies this article


Peter Bromberg is a C# MVP, MCP, and .NET consultant who has worked in the banking and financial industry for 20 years. He has architected and developed web - based corporate distributed application solutions since 1995, and focuses exclusively on the .NET Platform.
Article Discussion: