Creating vCalendars programmatically in C#

Have you ever wanted to send out meeting requests from your code? Well I wanted to do just that today. And I didnt want to reference the Outlook dll's. I simply wanted to send an email to an attendee's email address.

After much searching and alot of useless code I found a gem at http://chuckdotnet.blogspot.com. I took the code there and modified it slightly so that you can send the meeting request to multiple recipients (attendees) at once.

below is the method that creates the system.net.mailmessage object with all the necessary alternateviews, so that when opened in outlook it behaves exactly like a meeting request that was sent from within outlook. remember to add using System.Net.Mail;using System.Net.Mime; to the top of your code.


public static MailMessage CreateMeetingRequest(DateTime start, DateTime end, string subject, string summary,
string location, string organizerName, string organizerEmail, string attendeeName, string attendeeEmail)
{
MailAddressCollection col = new MailAddressCollection();
col.Add(new MailAddress(attendeeEmail, attendeeName));
return CreateMeetingRequest(start, end, subject, summary, location, organizerName, organizerEmail, col);
}

public static MailMessage CreateMeetingRequest(DateTime start, DateTime end, string subject, string summary,
string location, string organizerName, string organizerEmail, MailAddressCollection attendeeList)
{
MailMessage msg = new MailMessage();

// Set up the different mime types contained in the message
System.Net.Mime.ContentType textType = new System.Net.Mime.ContentType("text/plain");
System.Net.Mime.ContentType HTMLType = new System.Net.Mime.ContentType("text/html");
System.Net.Mime.ContentType calendarType = new System.Net.Mime.ContentType("text/calendar");

// Add parameters to the calendar header
calendarType.Parameters.Add("method", "REQUEST");
calendarType.Parameters.Add("name", "meeting.ics");

// Create message body parts
// create the Body in text format
string bodyText = "Type:Single Meeting\r\nOrganizer: {0}\r\nStart Time:{1}\r\nEnd Time:{2}\r\nTime Zone:{3}\r\nLocation: {4}\r\n\r\n*~*~*~*~*~*~*~*~*~*\r\n\r\n{5}";
bodyText = string.Format(bodyText,
organizerName,
start.ToLongDateString() + " " + start.ToLongTimeString(),
end.ToLongDateString() + " " + end.ToLongTimeString(),
System.TimeZone.CurrentTimeZone.StandardName,
location,
summary);

AlternateView textView = AlternateView.CreateAlternateViewFromString(bodyText, textType);
msg.AlternateViews.Add(textView);

//create the Body in HTML format
string bodyHTML = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\">\r\n<HTML>\r\n<HEAD>\r\n<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=utf-8\">\r\n<META NAME=\"Generator\" CONTENT=\"MS Exchange Server version 6.5.7652.24\">\r\n<TITLE>{0}</TITLE>\r\n</HEAD>\r\n<BODY>\r\n<!-- Converted from text/plain format -->\r\n<P><FONT SIZE=2>Type:Single Meeting<BR>\r\nOrganizer:{1}<BR>\r\nStart Time:{2}<BR>\r\nEnd Time:{3}<BR>\r\nTime Zone:{4}<BR>\r\nLocation:{5}<BR>\r\n<BR>\r\n*~*~*~*~*~*~*~*~*~*<BR>\r\n<BR>\r\n{6}<BR>\r\n</FONT>\r\n</P>\r\n\r\n</BODY>\r\n</HTML>";
bodyHTML = string.Format(bodyHTML,
summary,
organizerName,
start.ToLongDateString() + " " + start.ToLongTimeString(),
end.ToLongDateString() + " " + end.ToLongTimeString(),
System.TimeZone.CurrentTimeZone.StandardName,
location,
summary);

AlternateView HTMLView = AlternateView.CreateAlternateViewFromString(bodyHTML, HTMLType);
msg.AlternateViews.Add(HTMLView);

//create the Body in VCALENDAR format
string calDateFormat = "yyyyMMddTHHmmssZ";
string bodyCalendar = "BEGIN:VCALENDAR\r\nMETHOD:REQUEST\r\nPRODID:Microsoft CDO for Microsoft Exchange\r\nVERSION:2.0\r\nBEGIN:VTIMEZONE\r\nTZID:(GMT-06.00) Central Time (US & Canada)\r\nX-MICROSOFT-CDO-TZID:11\r\nBEGIN:STANDARD\r\nDTSTART:16010101T020000\r\nTZOFFSETFROM:-0500\r\nTZOFFSETTO:-0600\r\nRRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=11;BYDAY=1SU\r\nEND:STANDARD\r\nBEGIN:DAYLIGHT\r\nDTSTART:16010101T020000\r\nTZOFFSETFROM:-0600\r\nTZOFFSETTO:-0500\r\nRRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=3;BYDAY=2SU\r\nEND:DAYLIGHT\r\nEND:VTIMEZONE\r\nBEGIN:VEVENT\r\nDTSTAMP:{8}\r\nDTSTART:{0}\r\nSUMMARY:{7}\r\nUID:{5}\r\nATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;CN=\"{9}\":MAILTO:{9}\r\nACTION;RSVP=TRUE;CN=\"{4}\":MAILTO:{4}\r\nORGANIZER;CN=\"{3}\":mailto:{4}\r\nLOCATION:{2}\r\nDTEND:{1}\r\nDESCRIPTION:{7}\\N\r\nSEQUENCE:1\r\nPRIORITY:5\r\nCLASS:\r\nCREATED:{8}\r\nLAST-MODIFIED:{8}\r\nSTATUS:CONFIRMED\r\nTRANSP:OPAQUE\r\nX-MICROSOFT-CDO-BUSYSTATUS:BUSY\r\nX-MICROSOFT-CDO-INSTTYPE:0\r\nX-MICROSOFT-CDO-INTENDEDSTATUS:BUSY\r\nX-MICROSOFT-CDO-ALLDAYEVENT:FALSE\r\nX-MICROSOFT-CDO-IMPORTANCE:1\r\nX-MICROSOFT-CDO-OWNERAPPTID:-1\r\nX-MICROSOFT-CDO-ATTENDEE-CRITICAL-CHANGE:{8}\r\nX-MICROSOFT-CDO-OWNER-CRITICAL-CHANGE:{8}\r\nBEGIN:VALARM\r\nACTION:DISPLAY\r\nDESCRIPTION:REMINDER\r\nTRIGGER;RELATED=START:-PT00H15M00S\r\nEND:VALARM\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n";
bodyCalendar = string.Format(bodyCalendar,
start.ToUniversalTime().ToString(calDateFormat),
end.ToUniversalTime().ToString(calDateFormat),
location,
organizerName,
organizerEmail,
Guid.NewGuid().ToString("B"),
summary,
subject,
DateTime.Now.ToUniversalTime().ToString(calDateFormat),
attendeeList.ToString());

AlternateView calendarView = AlternateView.CreateAlternateViewFromString(bodyCalendar, calendarType);
calendarView.TransferEncoding = TransferEncoding.SevenBit;
msg.AlternateViews.Add(calendarView);

// Adress the message
msg.From = new MailAddress(organizerEmail);
foreach (MailAddress attendee in attendeeList)
{
msg.To.Add(attendee);
}
msg.Subject = subject;

return msg;
}

19 Comments

  • Nice snippet brad, but have you thought about using the @ symbol to escape your string literals?

    Then, instead of having your entire vCalendar template in one line using \r\n, you could have literal new lines and would be able to see the format indicies e.g. "{0}" much better, as well as the various names of all the 'properties'.

    Like so:
    string bodyCalendar = @"BEGIN:VCALENDAR
    METHOD:REQUEST
    PRODID:Microsoft CDO for Microsoft Exchange
    VERSION:2.0
    BEGIN:VTIMEZONE
    TZID:(GMT-06.00) Central Time (US & Canada)
    X-MICROSOFT-CDO-TZID:11
    BEGIN:STANDARD
    DTSTART:16010101T020000
    TZOFFSETFROM:-0500
    TZOFFSETTO:-0600
    RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=11;BYDAY=1SU
    END:STANDARD
    BEGIN:DAYLIGHT
    DTSTART:16010101T020000
    TZOFFSETFROM:-0600
    TZOFFSETTO:-0500
    RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=3;BYDAY=2SU
    END:DAYLIGHT
    END:VTIMEZONE
    BEGIN:VEVENT
    DTSTAMP:{8}
    DTSTART:{0}
    SUMMARY:{7}
    UID:{5}
    ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;CN=""{9}"":MAILTO:{9}
    ACTION;RSVP=TRUE;CN=""{4}"":MAILTO:{4}
    ORGANIZER;CN=""{3}"":mailto:{4}
    LOCATION:{2}
    DTEND:{1}
    DESCRIPTION:{7}\\N
    SEQUENCE:1
    PRIORITY:5
    CLASS:
    CREATED:{8}
    LAST-MODIFIED:{8}
    STATUS:CONFIRMED
    TRANSP:OPAQUE
    X-MICROSOFT-CDO-BUSYSTATUS:BUSY
    X-MICROSOFT-CDO-INSTTYPE:0
    X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY
    X-MICROSOFT-CDO-ALLDAYEVENT:FALSE
    X-MICROSOFT-CDO-IMPORTANCE:1
    X-MICROSOFT-CDO-OWNERAPPTID:-1
    X-MICROSOFT-CDO-ATTENDEE-CRITICAL-CHANGE:{8}
    X-MICROSOFT-CDO-OWNER-CRITICAL-CHANGE:{8}
    BEGIN:VALARM
    ACTION:DISPLAY
    DESCRIPTION:REMINDER
    TRIGGER;RELATED=START:-PT00H15M00S
    END:VALARM
    END:VEVENT
    END:VCALENDAR";

  • good suggestion Dave - i will update the code later today

  • Excellent article. Working fine...
    But how to add the same in Sender's calender?

  • What version of the framework does this work in? I've tried it using 2.0 but it doesn't work for me.

  • Mike,

    I got the original code to work in C# 2.0. However, when I used Dave T's code for the bodyCalendar Outlook did not recognize the Calendar appointment.

    So use the code as it is originally given.

    Thanks to the author, this will help our business run smoother.

  • I'm having issues with Vista/Outlook 2007 users. They are not receiving the invitations at all (not even text/html format). We are on an Exchange server if that makes any difference.

    XP&Outlook 2003 work perfectly though.

  • Excellent. Thanks a lot for sharing.

  • Hi excellent stuff. I have a question. How do i make this a reoccurring appointment.

  • I'm having the opposite problem from Bob above. I'm receiving the calendar items in my Outlook 2007, but our Outlook 2003 user is not receiving them.

  • Hi,
    Nice work it works for me too....but i am having a problem that Subject and body in mail are same.
    Any clue where i am wrong?

  • Excelent work, I facing a problem when I schedule a meeting for the attendees its working fine, but the meeting it not getting saved in the organizer calendar,like it does when you work directly with outlook, I tried several things but its not working...anyone knows a solution for this issue...

  • Although this code works good when you have only one attendee. However it does not work as expected when you have multiple attendees. I have ";" seperated email address of attendees in the attendeeList

    Please help.

  • Although this code works good when you have only one attendee. However it does not work as expected when you have multiple attendees. I have ";" seperated email address of attendees in the attendeeList

    Please help.

  • Great code just apply the method without changes on it and works fine, thanx for posting this.

  • Would you know the format of a reschedule email?

  • Hi bradvin
    i am working on it from the last few hours but this article is really awesome.... it works fine...
    thanks bradvin.keep it up.

    Happy coding
    yogesh.

  • Great job Brad...

    This is what I exactly needed. However, I after sending the meeting request, I tried to cancel it. For the same code, I tried retaining the UID and incremented the sequence number and changed the method parameter from "REQUEST" to "CANCEL". But this is not working for me. Anything which I'm missing here for which the meeting is not getting cancelled??

    Any help would be appreciated.

  • Hi bradvin,

    I am unable to open calendar appointment in Hotmail any idea?

    thanks,
    chandra

  • Brad,
    You have helped me a lot. Thanks. Could you please explain how to add this same meeting to a Public Folder in Outlook, instead of just
    the attendee's calendar?

    Thanks!

Comments have been disabled for this content.