Sending Outlook meeting requests without Outlook?

61,087

Solution 1

The way to send a meeting request to Outlook (and have it recognized) goes like this:

  • prepare an iCalendar file, be sure to set these additional properties, as Outlook needs them:
  • prepare a multipart/alternative mail:
    • Part 1: text/html (or whatever you like) - this is displayed to "ordinary" mail readers or as a fall-back and contains a summary of the event in human readable form
    • Part 2: text/calendar; method=REQUEST, holds the contents of the ics file (the header method parameter must match the method in the ics). Watch out for the correct text encoding, declaring a charset header parameter won't hurt.
    • Part 3: Optionally, attach the .ics file itself, so ordinary mail readers can offer the user something to click on. Outlook does not really require the attachment because it just reads the text/calendar part.
  • Send the mail to an outlook user. If you got everything right the mail shows up as a meeting request, complete with attendance buttons and automatic entry in the users calendar upon accept.
  • Set up something that processes the responses (they go to the meeting organizer). I have not yet been able to get automatic attendee tracking to work with an Exchange mailbox because the event won't exist in the organizers calendar. Outlook needs the UIDs and SEQUENCES to match it's expectations, but with a UID you made up this will hardly work.

For help on the details and peculiarities of the ics file format, be sure to visit the iCalendar Specification Excerpts by Masahide Kanzaki. They are a light in the dark, much better than gnawing your way through RFC 2445. But then again, maybe a handy library exists for .NET.

Solution 2

See the DDay.iCal C# library on sourceforge:
http://sourceforge.net/projects/dday-ical/

Then read this codeproject article:
http://www.codeproject.com/Articles/17980/Adding-iCalendar-Support-to-Your-Program-Part-1

And read this:
Export event with C# to iCalendar and vCalendar format

Solution 3

iCalendar is a great general-purpose solution, and the DDay.iCal library is a great way to do this from .NET, but I believe Exchange Web Services (EWS) are a better solution in the context of the original question (Exchange, C#/.NET).

And if you're using a .NET language such as C#, you should use the EWS Managed API wrapper which greatly simplifies working with EWS.

From the docs, here's how to use the EWS Managed API to create a meeting and send the request to invitees:

// Create the appointment.
Appointment appointment = new Appointment(service);

// Set properties on the appointment. Add two required attendees and one optional attendee.
appointment.Subject = "Status Meeting";
appointment.Body = "The purpose of this meeting is to discuss status.";
appointment.Start = new DateTime(2009, 3, 1, 9, 0, 0);
appointment.End = appointment.Start.AddHours(2);
appointment.Location = "Conf Room";
appointment.RequiredAttendees.Add("[email protected]");
appointment.RequiredAttendees.Add("[email protected]");
appointment.OptionalAttendees.Add("[email protected]");

// Send the meeting request to all attendees and save a copy in the Sent Items folder.
appointment.Save(SendInvitationsMode.SendToAllAndSaveCopy);

Solution 4

The code below will send a meeting request in such a way that Outlook will render Accept/Decline buttons.

Note that UID must be unique per meeting, I've used a GUID.

Also note you need to replace CREATED, DTSTART, DTEND, DTSTAMP, LAST-MODIFIED. These are UTC date/times.

    var m = new MailMessage();

    m.Subject = "Meeting";

    m.Body = "";

    string iCal = 
@"BEGIN:VCALENDAR
PRODID:-//Microsoft Corporation//Outlook 14.0 MIMEDIR//EN
VERSION:2.0
METHOD:PUBLISH
X-MS-OLK-FORCEINSPECTOROPEN:TRUE
BEGIN:VEVENT
CLASS:PUBLIC
CREATED:20140423T045933Z
DESCRIPTION:desc
DTEND:20140430T080000Z
DTSTAMP:20140423T045933Z
DTSTART:20140430T060000Z
LAST-MODIFIED:20140423T045933Z
LOCATION:location...
PRIORITY:5
SEQUENCE:0
SUMMARY;LANGUAGE=en-us:Summary...
TRANSP:OPAQUE
UID:D8BFD357-88A7-455C-86BC-C2CECA9AC5C6
X-MICROSOFT-CDO-BUSYSTATUS:BUSY
X-MICROSOFT-CDO-IMPORTANCE:1
X-MICROSOFT-DISALLOW-COUNTER:FALSE
X-MS-OLK-AUTOFILLLOCATION:FALSE
X-MS-OLK-CONFTYPE:0
BEGIN:VALARM
TRIGGER:-PT60M
ACTION:DISPLAY
DESCRIPTION:Reminder
END:VALARM
END:VEVENT
END:VCALENDAR";

    using (var iCalView = AlternateView.CreateAlternateViewFromString(iCal, new System.Net.Mime.ContentType("text/calendar")))
    {
        m.AlternateViews.Add(iCalView);

        var c = new SmtpClient();

        // Send message
        c.Send(m);
    }

This assumes you have a local SMTP server configured in your config file:

  <system.net>
    <mailSettings>
      <smtp deliveryMethod="Network" from="[email protected]">
        <network defaultCredentials="true" host="smtp.example.local" />
      </smtp>
    </mailSettings>
  </system.net>

Solution 5

You can send meeting requests by mail to outlook using the iCal Standard (RFC 5545)

You can't send todo items this way. You may send "Appointments" but these appear in outlook as .ics attachments which have to be accepted "blindly".

Meeting requests appear in outlook with a nice preview and can be accepted or rejeted. The sending program may modify or cancel the meeting after it was sent.

It's easieset to create a valid iCal item with the DDay.iCal .Net Library

The code below is a complete working example. It builds a string with a valid iCal meeting request and sends it by mail.

The code creates a mail with:

  • plain text body for simple mail clients
  • HTML body for diplay in modern mail clients
  • iCal meeting request as AlternateView (will display in Outlook)
  • iCal meeting request as Attachment (usable in mail clients other than outlook)

The code shows how to add:

  • description text as HTML, looks nicer in outlook
  • Priority, visibility (public/private/confidential)
  • optional organizer (will show in outlook instead of the mail sender)
  • optional attendees
  • optional alarm
  • optional attachments to the meeting. will show up in outlook's calendar

Some important details:

  • mail sender (or optional organizer) and mail receiver must be different to make this work in outlook
  • METHOD in .ics and METHOD in Mime.ContentType must match
  • The meeting must lie in the future to make this work in outlook
  • the .ics part must be the last alternateView part in the MIME mail

The exact details about the way outlook interprets .ics files are detailed in [MS-OXCICAL]: iCalendar to Appointment Object Conversion Algorithm

We'll use these assemblies:

using System;
using System.IO;
using System.Net.Mail;
using DDay.iCal;
using DDay.iCal.Serialization.iCalendar;

For DDay.iCal its enough to download the DDay.iCal binary Files. If you want to add some features it's best to look at the DDay.iCal sources because the documentation is outdated and the sources contain pretty complete tests which excercise all its features.

const string filepath = @"C:\temp\ical.test.ics";
// use PUBLISH for appointments
// use REQUEST for meeting requests
const string METHOD = "REQUEST";

// Properties of the meeting request
// keep guid in sending program to modify or cancel the request later
Guid uid = Guid.Parse("2B127C67-73B3-43C5-A804-5666C2CA23C9");
string VisBetreff = "This is the subject of the meeting request";
string TerminVerantwortlicherEmail = "[email protected]";
string bodyPlainText = "This is the simple iCal plain text msg";
string bodyHtml = "This is the simple <b>iCal HTML message</b>";
string location = "Meeting room 101";
// 1: High
// 5: Normal
// 9: low
int priority = 1;
//=====================================
MailMessage message = new MailMessage();

message.From = new MailAddress("[email protected]");
message.To.Add(new MailAddress(TerminVerantwortlicherEmail));
message.Subject = "[VIS-Termin] " + VisBetreff;

// Plain Text Version
message.Body = bodyPlainText;

// HTML Version
string htmlBody = bodyHtml;
AlternateView HTMLV = AlternateView.CreateAlternateViewFromString(htmlBody,
  new System.Net.Mime.ContentType("text/html"));

// iCal
IICalendar iCal = new iCalendar();
iCal.Method = METHOD;
iCal.ProductID = "My Metting Product";            

// Create an event and attach it to the iCalendar.
Event evt = iCal.Create<Event>();
evt.UID = uid.ToString();

evt.Class = "PUBLIC";
// Needed by Outlook
evt.Created = new iCalDateTime(DateTime.Now);

evt.DTStamp = new iCalDateTime(DateTime.Now);
evt.Transparency = TransparencyType.Transparent;

// Set the event start / end times
evt.Start = new iCalDateTime(2014, 10, 3, 8, 0, 0); 
evt.End = new iCalDateTime(2014, 10, 3, 8, 15, 0); 
evt.Location = location;

//var organizer = new Organizer("[email protected]");
//evt.Organizer = organizer;

// Set the longer description of the event, plain text
evt.Description = bodyPlainText;

// Event description HTML text
// X-ALT-DESC;FMTTYPE=text/html
var prop = new CalendarProperty("X-ALT-DESC");
prop.AddParameter("FMTTYPE", "text/html");
prop.AddValue(bodyHtml);
evt.AddProperty(prop);

// Set the one-line summary of the event
evt.Summary = VisBetreff;
evt.Priority = priority;

//--- attendes are optional
IAttendee at = new Attendee("mailto:[email protected]");
at.ParticipationStatus = "NEEDS-ACTION";
at.RSVP = true;
at.Role = "REQ-PARTICIPANT";
evt.Attendees.Add(at);

// Let’s also add an alarm on this event so we can be reminded of it later.
Alarm alarm = new Alarm();

// Display the alarm somewhere on the screen.
alarm.Action = AlarmAction.Display;

// This is the text that will be displayed for the alarm.
alarm.Summary = "Upcoming meeting: " + VisBetreff;

// The alarm is set to occur 30 minutes before the event
alarm.Trigger = new Trigger(TimeSpan.FromMinutes(-30));

//--- Attachments
string filename = "Test.docx";

// Add an attachment to this event
IAttachment attachment = new DDay.iCal.Attachment();
attachment.Data = ReadBinary(@"C:\temp\Test.docx");
attachment.Parameters.Add("X-FILENAME", filename);
evt.Attachments.Add(attachment);

iCalendarSerializer serializer = new iCalendarSerializer();
serializer.Serialize(iCal, filepath);

// the .ics File as a string
string iCalStr = serializer.SerializeToString(iCal);

// .ics as AlternateView (used by Outlook)
// text/calendar part: method=REQUEST
System.Net.Mime.ContentType calendarType = 
  new System.Net.Mime.ContentType("text/calendar");
calendarType.Parameters.Add("method", METHOD);
AlternateView ICSview =
  AlternateView.CreateAlternateViewFromString(iCalStr, calendarType);

// Compose
message.AlternateViews.Add(HTMLV);
message.AlternateViews.Add(ICSview); // must be the last part

// .ics as Attachment (used by mail clients other than Outlook)
Byte[] bytes = System.Text.Encoding.ASCII.GetBytes(iCalStr);
var ms = new System.IO.MemoryStream(bytes);
var a = new System.Net.Mail.Attachment(ms,
  "VIS-Termin.ics", "text/calendar");
message.Attachments.Add(a);     

// Send Mail
SmtpClient client = new SmtpClient();
client.Send(message);
        

Here the ReadBinary() function:

private static byte[] ReadBinary(string fileName)
{
    byte[] binaryData = null;
    using (FileStream reader = new FileStream(fileName,
      FileMode.Open, FileAccess.Read))
    {
        binaryData = new byte[reader.Length];
        reader.Read(binaryData, 0, (int)reader.Length);
    }
    return binaryData;
}

Its easiest to configure the SmtpClient in the config file like this:

<configuration>
  ...
  <system.net>  
    <mailSettings>
      <smtp>
        <network host="mysmtp.server.com" port="25" userName="mySmtpUserName" password="myPassword" />
      </smtp>
    </mailSettings>
  </system.net>
  ...
Share:
61,087
Michael Stum
Author by

Michael Stum

The same thing we do every night, Pinky. Try to take over the world! Full-Stack Developer on Stack Overflow Enterprise, working to make our little corner of the Internet better for all of us.

Updated on January 17, 2020

Comments

  • Michael Stum
    Michael Stum over 4 years

    I just wonder if it is possible to send Meeting Requests to people without having Outlook installed on the Server and using COM Interop (which I want to avoid on a server at all costs).

    We have Exchange 2003 in a Windows 2003 Domain and all users are domain Users. I guess I can send 'round iCal/vCal or something, but I wonder if there is a proper standard way to send Meeting Requests through Exchange without Outlook?

    This is C#/.net if it matters.