Reputation: 181004
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.
Upvotes: 40
Views: 65325
Reputation: 9782
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:
The code shows how to add:
Some important details:
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>
...
Upvotes: 5
Reputation: 3297
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);
Upvotes: 6
Reputation: 9191
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>
Upvotes: 5
Reputation: 82296
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
Upvotes: 8
Reputation: 338326
The way to send a meeting request to Outlook (and have it recognized) goes like this:
multipart/alternative
mail:
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 formtext/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.text/calendar
part.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.
Upvotes: 58