Entries:
Comments:
Posts:

Loading User Information from Channel 9

Something went wrong getting user information from Channel 9

Latest Achievement:

Loading User Information from MSDN

Something went wrong getting user information from MSDN

Visual Studio Achievements

Latest Achievement:

Loading Visual Studio Achievements

Something went wrong getting the Visual Studio Achievements

Redirecting Outlook Reminders

  Today's article will guide you through creating an add-in to Outlook 2003 to send alerts to another computer when a reminder is raised.

Difficulty: Easy
Time Required: 1-3 hours
Cost: Free
Software: Visual Studio Express Editions
Hardware:
Download:

In my last article in this series, I worked with the new Visual Studio 2005 Tools for Microsoft Office (VSTO 2005) newly released support for Microsoft Outlook (currently beta). At that point, it was enough to just get it installed, set up the snippets, and take a look at the samples. I created a starter project to demonstrate creating a new mail item and task. Of course it was just a stepping stone to the great things you can do with the beta, but I wanted to do something useful this time.

I don't know about you, but in my house I have my laptop that floats from room to room, a main desktop in the office, a media center computer, my wife's computer, and other assorted boxes awaiting upgrades or repair. I have Outlook installed on my laptop to keep it portable. Obviously, with all of the aforementioned systems, I'm not always sitting in front of my laptop. This means that meeting and task reminders will often go off without me noticing them. Not a good situation!

As they say, necessity is the mother of invention. "Wouldn't it be nice if I could be alerted on a different computer since Outlook is bound to only one?" I thought. Then, it came to me. The new VSTO 2005 for Outlook support was the answer! Why not register for event notifications when a reminder goes off, and raise an alert somewhere else? And from that, Outlook Alerts was born.

Today's article will guide you through creating an add-in to Outlook 2003 to send alerts to another computer when a reminder is raised. In order to follow this article and run the code, you will need any of the Visual Studio Express editions. The code samples in this article are shown in Visual Basic 2005; however, the downloadable source code is available in both VB 2005 and C# 2005. Beta 2 of the Express editions can be downloaded from http://msdn.microsoft.com/express. As with last time, Microsoft Office 2003 (Service Pack 1) is also a requirement, or at least Outlook 2003 SP1. Be aware that Outlook Express will not function as a replacement for Outlook.

The first step in sending alerts remotely is to detect when reminders and alarms are triggered in Outlook. As it turns out, this is easy to hook into, as the main Application object exposes a Reminder event. By registering an event handler for this, you are notified every time the Outlook Reminder dialog appears. The item parameter contains a reference to the item causing the event. This could be a mail item, an appointment, or any other Outlook item supporting reminders. The following method declaration creates a method which will be triggered whenever the Reminder event is raised:

Visual C#

void ThisApplication_Reminder(object item)

Visual Basic

Private Sub ThisApplication_Reminder(ByVal item As Object) Handles Me.Reminder

The alerts application has two settings to keep track of. Settings can seem like a challenge. You may not be sure where to store them or what format to use, and writing the code to serialize them and deserialize them can be tedious. Fortunately, the .NET 2.0 framework makes this trivial. Simply right-click the project in the Solution Explorer, then click Properties. Choose the Settingstab and you can create and manage any settings you need. You have the ability to create user or application scope settings with arbitrary names, in a variety of data types. For this project, create a Boolean setting called RemoteRemindersEnabled, and a String setting called RemoteReminderAddress. The first flag controls whether or not alerts will be sent remotely, while the second flag specifies where to send any alerts. The address should be an IP address or net name. Note that unless the other computer is in the same workgroup or domain, it is generally easier to use IP address to guarantee delivery. The Settingsconfiguration should look like this:


Click here for larger image

(click image to zoom)

Once the settings are created, you can access them in a strongly-typed fashion using the My namespace in Visual Basic as follows:

Visual C#

Properties.Settings.Default.RemoteRemindersEnabled

Visual Basic

My.Settings.RemoteRemindersEnabled

If you use settings, you must provide an easy way to allow users to manage the setting values. Outlook allows you to add menus alongside the built-in menus. This is easy to do and provides a professional look to your add-in. The first step is to obtain a reference to the main menu bar and add a top-level menu; in this case, "Add-in Tasks." The code looks a little messy due to type casting (the CType call) and the two Type.Missing parameters. These are necessary due to the underlying COM nature of the code:

Visual C#

_menuBar = this.ActiveExplorer().CommandBars.ActiveMenuBar;
_helpMenuIndex = _menuBar.Controls[MENU_BEFORE].Index;

_topMenu = (Office.CommandBarPopup)(_menuBar.Controls.Add(Office.MsoControlType.msoControlPopup,
Type.Missing, Type.Missing, _helpMenuIndex, true));
_topMenu.Caption = "Add-in Tasks";
_topMenu.Visible = true;

Visual Basic

_menuBar = Me.ActiveExplorer().CommandBars.ActiveMenuBar
_helpMenuIndex = _menuBar.Controls(MENU_BEFORE).Index

_topMenu = CType(_menuBar.Controls.Add(Office.MsoControlType.msoControlPopup, Type.Missing,
Type.Missing, _helpMenuIndex, True), Office.CommandBarPopup)
_topMenu.Caption = "Add-in Tasks"
_topMenu.Visible = True

After creating the menu, you add menu items by calling the Controls.Add() method. Once again, the code looks a little cluttered, but is overall very understandable. The Caption properties in both cases determine what is seen when the menu is opened:

Visual C#

_settingsMenu = (Office.CommandBarButton)(_topMenu.Controls.Add(Office.MsoControlType.msoControlButton, 
Type.Missing, Type.Missing, Type.Missing, true));
_settingsMenu.Caption = "Remote Alert Notification Settings...";
_settingsMenu.Visible = true;
_settingsMenu.Enabled = true;

Visual Basic

_settingsMenu = CType(_topMenu.Controls.Add(Office.MsoControlType.msoControlButton, Type.Missing, 
Type.Missing, Type.Missing, True), Office.CommandBarButton)
_settingsMenu.Caption = "Remote Alert Notification Settings..."
_settingsMenu.Visible = True
_settingsMenu.Enabled = True

You will still need to create a settings form, and create an event handler to respond to user clicks on the item. At runtime, selecting the Add-in Tasks | Remote Alert Notification Settings menu option brings up this custom dialog:

Alerts

The final step is to determine how to actually send the remote alerts. I toyed with the idea of creating a remoting client to run on other PCs, or publish/subscribe clients, but the easiest approach is to work with what is already there. The Messenger service is a built-in Windows service for sending simple administrative messages from servers. Note that this is not related to the MSN Messenger product.

Unfortunately, in the past few years, the service has also become a target of spammers, so it is often disabled by Anti-Spyware products like Microsoft AntiSpyware. In order to enable it, go to Services configuration, then enable and start the Messenger service. Once it is enabled, you can use a built-in command, net.exe, to send messages. Given a computer's name or IP address, you can send a message as follows:

net send mycomputername This is my message. 

From code, this isn't a good option, as it causes the command prompt to appear, and it requires you to find the path to the net command. Instead, in this article, I've decided to platform-invoke the underlying Win32 method to send the alert. This is easy to do once you've located the appropriate method; in this case, NetMessageBufferSend in the Netapi32 library. Start by declaring the method signature:

Visual C#

[DllImport("Netapi32", CharSet=CharSet.Unicode)]
private static extern int NetMessageBufferSend(string servername ,
string msgname ,
string fromname ,
string buf ,
int buflen );

Visual Basic

<DllImport("Netapi32", CharSet:=CharSet.Unicode)> _
Private Shared Function NetMessageBufferSend(ByVal servername As String, _
ByVal msgname As String, _
ByVal fromname As String, _
ByVal buf As String, _
ByVal buflen As Integer) As Integer
End Function

Notice there is no body to the method. When you specify the DllImport attribute, this flags to the compiler that the actual method exists in a platform library, not in the managed class. Calling the method then, is as easy as calling any other method:

Visual C#

NetMessageBufferSend(null, address, null, message, message.Length * 2 + 2);

Visual Basic

NetMessageBufferSend(Nothing, address, Nothing, message, message.Length * 2 + 2)

Now that we have a way to actually send the alert (with no client install even!), we need to respond to the Reminder event. In the event handler, we will first check to see if the RemoteRemindersEnabled setting is true, then determine what type of item triggered the event. Mail, appointment, and task items are supported below in this project. Depending on the type, you can extract various fields to build up a simple string, then send the alert:

Visual C#

if( Properties.Settings.Default.RemoteRemindersEnabled )
{
string reminderMsg;

if(item is Outlook.MailItem)
{
Outlook.MailItem reminderMail;
reminderMail = (Outlook.MailItem)item;
reminderMsg = String.Format("Remote Outlook Reminder\nMail "+
"subject: {0}\nReminder time: {1:t}", reminderMail.Subject,
reminderMail.ReminderTime);
}
else if(item is Outlook.AppointmentItem)
{
Outlook.AppointmentItem reminderAppt;
reminderAppt = (Outlook.AppointmentItem)item;
reminderMsg = String.Format("Remote Outlook Reminder\n "+
"Appointment subject: {0}\nLocation: {1}\nReminder time: {2:t}",
reminderAppt.Subject, reminderAppt.Location, reminderAppt.Start);
}
else if(item is Outlook.TaskItem)
{
Outlook.TaskItem reminderTask;
reminderTask = (Outlook.TaskItem)item;
reminderMsg = String.Format("Remote Outlook Reminder\nTask "+
"subject: {0}\nReminder time: {1:t}", reminderTask.Subject,
reminderTask.ReminderTime);
}
else
{
// Unsupported item
return;
}

NetSend.Send(Properties.Settings.Default.RemoteReminderAddress,
reminderMsg);
}
Visual Basic
If My.Settings.RemoteRemindersEnabled Then 
Dim reminderMsg As String

If TypeOf item Is Outlook.MailItem Then
Dim reminderMail As Outlook.MailItem
reminderMail = CType(item, Outlook.MailItem)
reminderMsg = String.Format("Remote Outlook Reminder\nMail subject: {0}\nReminder time: {1:t}",
reminderMail.Subject, reminderMail.ReminderTime)
ElseIf TypeOf item Is Outlook.AppointmentItem Then
Dim reminderAppt As Outlook.AppointmentItem
reminderAppt = CType(item, Outlook.AppointmentItem)
reminderMsg = String.Format("Remote Outlook Reminder\nAppointment subject: {0}\nLocation: {1}\nReminder time: {2:t}",
reminderAppt.Subject, reminderAppt.Location, reminderAppt.Start)
ElseIf TypeOf item Is Outlook.TaskItem Then
Dim reminderTask As Outlook.TaskItem
reminderTask = CType(item, Outlook.TaskItem)
reminderMsg = String.Format("Remote Outlook Reminder\nTask subject: {0}\nReminder time: {1:t}",
reminderTask.Subject, reminderTask.ReminderTime)
Else
' Unsupported item
Return
End If

NetSend.Send(My.Settings.RemoteReminderAddress, reminderMsg)
End If

The NetSend.Send method actually performs a quick replacement on any carriage returns, as they are not directly supported, then sends the alert using the Win32 call. The message will pop up on the specified computer over any other application:

Next Steps

Future expansion might be to allow messages to be broadcasted to multiple machines. This is easy on a domain, but would require additional work on many home networks. Net send has some limitations, including sparse formatting options and a bad perception due to spammers. A more robust client, or tying it into an existing instant messenger network could be useful.

Conclusion

Building this was fun. It served a definite purpose and allowed me to use a number of techniques to get it working. Download Visual C# 2005 Express Edition or Visual Basic 2005 Express Edition, the beta VSTO Outlook installer, then give this application a try. Get started at http://msdn.microsoft.com/express.

Additional Resources

Follow the Discussion

  • SteelCadmanSteelCadman

    I downloaded VC# Express and your project but for some reason, when I try to open it, it says "blah blah csproj cant be opened. The project type is not supported by this installation"

    What can I do?

  • shilpyshilpy

    Thanks.Very useful article.

    I feel -It would be helpful to add

    using System.Runtime.InteropServices;

    Thanks once again.

    Regards.

  • Gareth RidoutGareth Ridout

    I also get "The project type is not supported by this installation" if I try to load into VB 2005. I figured something that would work in Express oughta work in 2005 but no.

  • Reality ManReality Man

    Makes sense. Don't allow the people who paid top dollar for the flagship development tool to use examples or tutorials. Just people who downloaded the free version.

    Get used to the "NEW" Microsoft. All the baggage that used to slow down Sun and IBM have a new place to coagulate!

  • Clint RutkasClint I'm a "developer"

    Sorry Guys, looks like when this article was posted, it worked with Express but it no longer does!  It looks like the new versions of VSTO require a non-express SKU.

    From the time stamp of the article, it was from the first version of coding4fun so this could very well be over 2 years old.

  • Ricky RightRicky Right

    So, "Project cant load... not supported by this installation", "Me.ActiveExplorer is not a member of <applicationName>"... Does this mean that VSTO is only for the express edition. More MS garbage?

  • Ricky RightRicky Right

    If anyone interested, i have got this to work in VB2005 professional edition. Where it referenced:  _menuBar = Me.ActiveExplorer().CommandBars.ActiveMenuBar

    I changed this to     Dim myOutlookApp As New Outlook.Application  

    _menuBar = myOutlookApp.ActiveExplorer().CommandBars.ActiveMenuBar

    Also declare  _menuBar As Office.CommandBar

    ONE more thing of Importance: It will only work if you "Sing the Assembly" in Project Properties, Signing: Checkbox "Sign the assembly", create new, and give it the same name as the project, I think the password is optional.

  • enrique.prados@a-e.esenrique.​prados@a-e.​es

    Please more sample code source using Outlook and C#.

    For example, get all address of Body Emails, or all contacts, or all contacts in Distribution List.

    Please, any application sample with code source, like MSDN Video..

    Please Microsoft, save us with code source !!!

Remove this comment

Remove this thread

close

Comments Closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums,
or Contact Us and let us know.