I need to save a Guid per every Appointment.
I've tried to use PolicyTag and ArchiveTag,but got an ,
"The property PolicyTag is valid only for Exchange Exchange2013 or
later versions.",
exception.
Does we have something similar for Exchange 2010?
As i understand there is appointment.ID that contains self-generated id.
I prefer not to use it.
Thank you.
A way to deal with this problem is to create an extended property, and put guid for the appointment, and it wont change unless you made a copy from another appointment (after all it is just a property)
private static readonly PropertyDefinitionBase AppointementIdPropertyDefinition = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.PublicStrings, "AppointmentID", MapiPropertyType.String);
public static PropertySet PropertySet = new PropertySet(BasePropertySet.FirstClassProperties, AppointementIdPropertyDefinition);
//Setting the property for the appointment
public static void SetGuidForAppointement(Appointment appointment)
{
try
{
appointment.SetExtendedProperty((ExtendedPropertyDefinition)AppointementIdPropertyDefinition, Guid.NewGuid().ToString());
appointment.Update(ConflictResolutionMode.AlwaysOverwrite, SendInvitationsOrCancellationsMode.SendToNone);
}
catch (Exception ex)
{
// logging the exception
}
}
//Getting the property for the appointment
public static string GetGuidForAppointement(Appointment appointment)
{
var result = "";
try
{
appointment.Load(PropertySet);
foreach (var extendedProperty in appointment.ExtendedProperties)
{
if (extendedProperty.PropertyDefinition.Name == "AppointmentID")
{
result = extendedProperty.Value.ToString();
}
}
}
catch (Exception ex)
{
// logging the exception
}
return result;
}
This solution works very well in case of single appointments and in case of using On Premises Exchange. The problem here is in case of meetings in the Online Web Access (OWA), such as Office 365, the properties of the appointments are copied from the original appointment from the organizer, among these properties are the extended properties which the AppoinmentID is among them.
Therefore to avoid this trouble, we make the appointment id of the attendee similar one to the original in the organizer, and just add the email address for the service owner (the service that produced the notification).
Without this solution , the appointment in the internal system will have similar AppointmentID to the original booking and it will be considered as one, or you might have different two appointments with the same ID.
private static void SetGuidForMeetingAppiontment(Appointment appointment)
{
var log = "";
try
{
if (!appointment.IsMeeting) return;
if (appointment.Service.ImpersonatedUserId == null) return;
/*
* The only tricky case is that if the appointment is created at the attendee with no Guid.
* In this case the application should look for the original appointment from the organizer's side, and get its guid, to paste it in the new booking
* from the attendee side, and add the attendee emailAddress.
*/
if (GetGuidForMeetingAppointement(appointment).Length <= 36)
{
// If it was an attendee, then look for the original appointment from the organizer's service
if (appointment.Service.ImpersonatedUserId.Id != appointment.Organizer.Address)
{
log += "1/5 Getting the original event of the meeting\n";
if (ExchangeLiteService.Services.ContainsKey(appointment.Organizer.Address))
{
// FindItemsResults<Appointment> originalAppointments;
var originalAppointments = ExchangeLiteService.Services[appointment.Organizer.Address].FindAppointments(WellKnownFolderName.Calendar, new CalendarView(appointment.Start, appointment.End, 1));
if (originalAppointments == null) return; //there must be an original appointment.
if (!originalAppointments.Any()) return; //there must be an original appointment.
var originalAppointment = originalAppointments.First(); // there should be only one appointment at a specifict time and date.
log += "2/5 Getting the Guid for the original event of the meeting\n";
var originalAppointmentID = GetGuidForMeetingAppointement(originalAppointment);
if (string.IsNullOrEmpty(originalAppointmentID)) return; // the original appointment must have a guid already.
var orignalAppointmentIDGuid = originalAppointmentID.Substring(0, 36);
log += "3/5 Attaching the email address to the guid extracted\n";
var newAppointmentID = orignalAppointmentIDGuid + "_" + appointment.Service.ImpersonatedUserId.Id;
log += "4/5 Setting the new Guid to the meeting appointment\n";
appointment.SetExtendedProperty((ExtendedPropertyDefinition)AppointementIdPropertyDefinition, newAppointmentID);
log += "5/5 Updateing the meeting appointment\n";
appointment.Update(ConflictResolutionMode.AlwaysOverwrite, SendInvitationsOrCancellationsMode.SendToNone);
}
else //Then the user is invited from an organizer outside the system.
{
// Delete if there are anything similar
ExchangeLiteService.OnCallDeleteBookingFromInternal(appointment.Service.ImpersonatedUserId.Id, appointment.Start, appointment.End);
//Assign a new
var appointmentIDGuid = Guid.NewGuid().ToString();
var newAppointmentID = appointmentIDGuid + "_" + appointment.Service.ImpersonatedUserId.Id;
appointment.SetExtendedProperty((ExtendedPropertyDefinition)AppointementIdPropertyDefinition, newAppointmentID);
appointment.Update(ConflictResolutionMode.AlwaysOverwrite, SendInvitationsOrCancellationsMode.SendToNone);
}
//Get only the guid part of it (without the address of the organizer)
}
else // if he was the organizer
{
log += "If it was new meeting appointment and the notification from the organizer\n";
var appointmentIDGuid = Guid.NewGuid().ToString();
var newAppointmentID = appointmentIDGuid + "_" + appointment.Service.ImpersonatedUserId.Id;
appointment.SetExtendedProperty((ExtendedPropertyDefinition)AppointementIdPropertyDefinition, newAppointmentID);
appointment.Update(ConflictResolutionMode.AlwaysOverwrite, SendInvitationsOrCancellationsMode.SendToNone);
}
}
else
{
log += "If it was an updated meeting appointment and has Guid already\n";
var appointmentID = GetGuidForMeetingAppointement(appointment);
var appointmentIDGuid = appointmentID.Substring(0, 36);
var newAppointmentID = appointmentIDGuid + "_" + appointment.Service.ImpersonatedUserId.Id;
appointment.SetExtendedProperty((ExtendedPropertyDefinition)AppointementIdPropertyDefinition, newAppointmentID);
appointment.Update(ConflictResolutionMode.AlwaysOverwrite, SendInvitationsOrCancellationsMode.SendToNone);
}
}
catch (Exception ex)
{
//Logging the exception
}
}
Related
UPDATE
Ultimate goal is to send the updated body to the existing recipients of the meeting occurrence.
I'm trying to update HTMLBody of occurrence of existing meeting with number of recipients.
After updated the HTMLBody, recipients count is become zero.
And sending failed with an error saying "message contains no recipients"
Here's the code I've tried.
public static void GetSomething()
{
var entryID = "00000000C97FEB5BB4BE2046B4C77ADEB3C423010700BA6ADFD7533DD244B122D7B9F3B5664000000000010D0000BA6ADFD7533DD244B122D7B9F3B56640000269310F930000";
RDOAppointmentItem appointment = null;
RDORecurrencePattern pattern = null;
RDOMail occurrence = null;
try
{
appointment = rSession.GetMessageFromID(entryID) as RDOAppointmentItem;
if (appointment != null)
{
pattern = appointment.GetRecurrencePattern();
if (pattern != null)
{
occurrence = pattern.GetOccurrence(1);
if (pattern != null) Marshal.ReleaseComObject(pattern);
if (appointment != null) Marshal.ReleaseComObject(appointment);
occurrence.HTMLBody = #"<!DOCTYPE html>\r\n<html>\r\n<head>\r\n<title>Page Title</title>\r\n</head>\r\n<body>\r\n<p>This is a paragraph.</p>\r\n\r\n</body>\r\n</html>";
occurrence.Send();
}
}
}
catch (Exception e)
{
Debug.DebugMessage(2, $"Error in {MethodBase.GetCurrentMethod().Name} | {e.Message}");
}
finally
{
if (occurrence != null) Marshal.ReleaseComObject(occurrence);
}
}
I then tried the approach of adding and removing a recipient which worked but added recipient is not removed from the meeting request sent, although it was removed from the appointment in my calendar.
Here's the code I've tried for that.
public static void GetSomething()
{
var entryID = "00000000C97FEB5BB4BE2046B4C77ADEB3C423010700BA6ADFD7533DD244B122D7B9F3B5664000000000010D0000BA6ADFD7533DD244B122D7B9F3B56640000269310F930000";
RDOAppointmentItem appointment = null;
RDORecurrencePattern pattern = null;
RDOMail occurrence = null;
try
{
appointment = rSession.GetMessageFromID(entryID) as RDOAppointmentItem;
if (appointment != null)
{
pattern = appointment.GetRecurrencePattern();
if (pattern != null)
{
occurrence = pattern.GetOccurrence(1);
if (pattern != null) Marshal.ReleaseComObject(pattern);
if (appointment != null) Marshal.ReleaseComObject(appointment);
RDORecipients oldRecipients = occurrence.Recipients;
var oldRecCount = oldRecipients.Count;
RDORecipient recipient = oldRecipients.AddEx("Test", "test#gmail.com", "SMTP", 1);
if (recipient != null) Marshal.ReleaseComObject(recipient);
if (oldRecipients != null) Marshal.ReleaseComObject(oldRecipients);
occurrence.HTMLBody = #"<!DOCTYPE html>\r\n<html>\r\n<head>\r\n<title>Page Title</title>\r\n</head>\r\n<body>\r\n<p>This is a paragraph.</p>\r\n\r\n</body>\r\n</html>";
RDORecipients newRecipients = occurrence.Recipients;
newRecipients.Remove(oldRecCount + 1);
if (newRecipients != null) Marshal.ReleaseComObject(newRecipients);
occurrence.Send();
}
}
}
catch (Exception e)
{
Debug.DebugMessage(2, $"Error in {MethodBase.GetCurrentMethod().Name} | {e.Message}");
}
finally
{
if (occurrence != null) Marshal.ReleaseComObject(occurrence);
}
}
}
Keep in mind that occurrences do not physically exist - when you ask for an occurrence, Redemption creates a fake appointment object that gets most of its properties (except for the start/end and recurrence) from the master appointment.
When you create an exception (by modifying one of the properties), besides modifying the exception pattern, an appointment is created and added an an embedded message attachment to the master appointment. That appointment stores mostly modified properties. Since the recipients were not modified for the exception, the recipient table is empty.
I am attempting to import a selected contact from the phone directory into my app. Using my code below, The contact name and phone number return successfully, but email always return null (the if (emailCursor.MoveToFirst()) clause always returns false). The ImportedContact is a simple class with 3 string properties, Name, Phone and Email. I've made sure my contact has an email address assigned.
static ImportedContact GetContactFromUri(Android.Net.Uri contactUri)
{
var importedContact = new ImportedContact();
try
{
string[] projection =
{
ContactsContract.Contacts.InterfaceConsts.Id,
ContactsContract.Contacts.InterfaceConsts.DisplayName,
ContactsContract.CommonDataKinds.Phone.Number
};
var cursor = CrossCurrentActivity.Current.Activity.ContentResolver.Query(contactUri, projection, null, null, null);
if (cursor.MoveToFirst())
{
importedContact.Name = cursor.GetString(cursor.GetColumnIndex(projection[1]));
importedContact.Phone = cursor.GetString(cursor.GetColumnIndex(projection[2]));
};
var id = cursor.GetString(cursor.GetColumnIndex(projection[0]));
var emailCursor = CrossCurrentActivity.Current.Activity.ContentResolver.Query(
ContactsContract.CommonDataKinds.Email.ContentUri,
null,
ContactsContract.CommonDataKinds.Email.InterfaceConsts.ContactId + " = " + id, null, null);
if (emailCursor.MoveToFirst())
{
int colId = emailCursor.GetColumnIndex(ContactsContract.CommonDataKinds.Email.InterfaceConsts.Data);
importedContact.Email = emailCursor.GetString(colId);
}
emailCursor.Close();
return importedContact;
}
catch
{
return null;
}
}
The actual issue was due to to the way I was calling the picker - I was incorrectly setting the intent type as specific to phone data:
public static void OpenContactPicker(Action<ImportedContact> callback)
{
_callback = callback;
Intent intent = new Intent(Intent.ActionPick);
intent.SetType(ContactsContract.CommonDataKinds.Phone.ContentType);
CurrentActivity.StartActivityForResult(intent, RequestCodes.ContactPicker);
}
and this was returning only the phone information and excluding the emails, website, etc. I changed this to:
public static void OpenContactPicker(Action<ImportedContact> callback)
{
_callback = callback;
Intent intent = new Intent(Intent.ActionPick, ContactsContract.Contacts.ContentUri);
CurrentActivity.StartActivityForResult(intent, RequestCodes.ContactPicker);
}
and this is returning all the contact information as required.
I have a DB ad Microsoft Identity Manager to generate user accounts from HR to MS Active Directory and so on.
I have a such code for generate unique email:
case "mailgenerate":
if (mventry["email"].IsPresent)
{
// Do nothing, the mail was already generated.
}
{
if (csentry["FIRST"].IsPresent && csentry["LAST"].IsPresent);
{
string FirstName = replaceRUEN(csentry["FIRST"].Value);
string LastName = replaceRUEN(csentry["LAST"].Value);
string email = FirstName + "." + LastName + "#test.domain.com";
string newmail = GetCheckedMail(email, mventry);
if (newmail.Equals(""))
{
throw new TerminateRunException("A unique mail could not be found");
}
mventry["email"].Value = newmail;
}
}
break;
//Generate mail Name method
string GetCheckedMail(string email, MVEntry mventry)
{
MVEntry[] findResultList = null;
string checkedmailName = email;
for (int nameSuffix = 1; nameSuffix < 100; nameSuffix++)
{
//added ; and if corrected
findResultList = Utils.FindMVEntries("email", checkedmailName,1);
if (findResultList.Length == 0)
{
// The current mailName is not in use.
return (checkedmailName);
}
MVEntry mvEntryFound = findResultList[0];
if (mvEntryFound.Equals(mventry))
{
return (checkedmailName);
}
// If the passed email is already in use, then add an integer value
// then verify if the new value exists. Repeat until a unique email is checked
checkedmailName = checkedmailName + nameSuffix.ToString();
}
// Return an empty string if no unique mailnickName could be created.
return "";
}
Problem:
When I run sync cycle for first time I get normal email like
duplicateuser1#test.domain.com
For next sync cycle this emails are updated to
duplicateuser#test.domain.com1
This code I'm also using to generate mailnickname and accountname without any problems.
Can anybody say why it is happens?
Thanks!
The problem is the line:
checkedmailName = checkedmailName + nameSuffix.ToString();
checkedmailName has a value like this: firstName.lastName#test.domain.com
So, you're doing this:
checkedmailName = firstName.lastName#test.domain.com + 1;
You need to do something like this:
checkedmailName = checkedmailName.Split('#')[0] + nameSuffix.ToString()+ "#" + checkedmailName.Split('#')[1];
Whith this, you're getting the part before #, adding a int value and then, appending the #+ domain.
Updated by author of thread I changed split -> Split and it works. Thanks!
I have been searching for an EWS pullsubscription example that allows me to get a list of calendar events that have been created or modified for a user since the pull subscription has been started. I have working code to get this info for the Inbox but I haven't found a good example for how to do this for the Calendar.
Below is an example for the Inbox; can anyone provide me with a link or a code example to accomplish the same thing for Calendar events or appointments using an Exchange Web Service pull subscription?
ExchangeService service;
PullSubscription subscriptionInbox;
private void SetService() {
service = new ExchangeService(ExchangeVersion.Exchange2007_SP1);
service.Url = new Uri("https://mail.myserver.com/EWS/Exchange.asmx");
}
private void SetSubscription() {
if(service == null) {
SetService();
}
// Subscribe to pull notifications in the Inbox folder, and get notified when
// a new mail is received, when an item or folder is created, or when an item
// or folder is deleted.
subscriptionInbox = service.SubscribeToPullNotifications(
new FolderId[] { WellKnownFolderName.Inbox },
5 /* timeOut: the subscription will end if the server is not polled within 5 minutes. */,
null /* watermark: null to start a new subscription. */,
EventType.NewMail, EventType.Modified);
}
private void btnGetLatestMessages_Click(object sender, EventArgs e) {
if(subscriptionInbox == null) {
SetSubscription();
}
GetEventsResults eventsInbox = subscriptionInbox.GetEvents();
EmailMessage message;
// Loop through all item-related events.
foreach(ItemEvent itemEvent in eventsInbox.ItemEvents) {
switch(itemEvent.EventType) {
case EventType.NewMail:
try {
Item item = Item.Bind(service, itemEvent.ItemId);
if(item.ItemClass.ToLower() == "IPM.Note".ToLower()) {
message = EmailMessage.Bind(service, itemEvent.ItemId);
MessageBox.Show("Inbox/NewMail - " + message.Subject);
}
} catch(Exception ex) {
MessageBox.Show("EventType.NewMail - " + itemEvent.ItemId);
}
break;
case EventType.Modified:
try {
Item item = Item.Bind(service, itemEvent.ItemId);
if(item.ItemClass.ToLower() == "IPM.Note".ToLower()) {
message = EmailMessage.Bind(service, itemEvent.ItemId);
MessageBox.Show("Inbox/Modified - " + message.Subject);
}
} catch(Exception ex) {
MessageBox.Show("EventType.NewMail - " + itemEvent.ItemId);
}
break;
}
}
}
Simply subscribe to the calendar instead of your inbox with the event types specified.
var subscriptionCalendar = service.SubscribeToPullNotifications(
new[] { new FolderId(WellKnownFolderName.Calendar) },
1440,
null,
EventType.Created, EventType.Modified);
Alternatively, you could create one pull notification for FolderId(WellKnownFolderName.Inbox) and FolderId(WellKnownFolderName.Calendar) with the EventType's you want.
We have a public calendar for our company set up in an Exchange 2007 Public Folder. I am able to retrieve my personal calendar appointments for the current day using the code below. I have searched high and low online and I cannot find one example of someone retrieving calendar information from a Public Folder calendar.
It seems like it should be doable, but I cannot for the life of me get it working. How can I modify the code below to access the calendar? I am not interested in creating any appointments through asp.net, just retrieving a simple list. I am open to any other suggestions as well. Thanks.
ADDED BOUNTY
- I can't be the only person that ever needed to do this. Let's get this problem solved for future generations.
UPDATED AGAIN DUE TO IGNORANCE
- I failed to mention that the project I am working on is .NET 2.0 (very important don't you think?).
* ADDED MY CODE SOLUTION BELOW *
- I have replaced my original code example with the code that ended up working. Many thanks to Oleg for providing the code to find the public folder, which was the hardest part.. I have modified the code using the example from here http://msexchangeteam.com/archive/2009/04/21/451126.aspx to use the simpler FindAppointments method.
This simple example returns an html string with the appointments, but you can use it as a base to customize as needed. You can see our back and forth under his answer below.
using System;
using Microsoft.Exchange.WebServices.Data;
using System.Net;
namespace ExchangePublicFolders
{
public class Program
{
public static FolderId FindPublicFolder(ExchangeService myService, FolderId baseFolderId,
string folderName)
{
FolderView folderView = new FolderView(10, 0);
folderView.OffsetBasePoint = OffsetBasePoint.Beginning;
folderView.PropertySet = new PropertySet(FolderSchema.DisplayName, FolderSchema.Id);
FindFoldersResults folderResults;
do
{
folderResults = myService.FindFolders(baseFolderId, folderView);
foreach (Folder folder in folderResults)
if (String.Compare(folder.DisplayName, folderName, StringComparison.OrdinalIgnoreCase) == 0)
return folder.Id;
if (folderResults.NextPageOffset.HasValue)
folderView.Offset = folderResults.NextPageOffset.Value;
}
while (folderResults.MoreAvailable);
return null;
}
public static string MyTest()
{
ExchangeService myService = new ExchangeService(ExchangeVersion.Exchange2007_SP1);
myService.Credentials = new NetworkCredential("USERNAME", "PASSWORD", "DOMAIN");
myService.Url = new Uri("https://MAILSERVER/ews/exchange.asmx");
Folder myPublicFoldersRoot = Folder.Bind(myService, WellKnownFolderName.PublicFoldersRoot);
string myPublicFolderPath = #"PUBLIC_FOLDER_CALENDAR_NAME";
string[] folderPath = myPublicFolderPath.Split('\\');
FolderId fId = myPublicFoldersRoot.Id;
foreach (string subFolderName in folderPath)
{
fId = Program.FindPublicFolder(myService, fId, subFolderName);
if (fId == null)
{
return string.Format("ERROR: Can't find public folder {0}", myPublicFolderPath);
}
}
Folder folderFound = Folder.Bind(myService, fId);
if (String.Compare(folderFound.FolderClass, "IPF.Appointment", StringComparison.Ordinal) != 0)
{
return string.Format("ERROR: Public folder {0} is not a Calendar", myPublicFolderPath);
}
CalendarFolder AK_Calendar = CalendarFolder.Bind(myService, fId, BasePropertySet.FirstClassProperties);
FindItemsResults<Appointment> AK_appointments = AK_Calendar.FindAppointments(new CalendarView(DateTime.Now,DateTime.Now.AddDays(1)));
string rString = string.Empty;
foreach (Appointment AK_appoint in AK_appointments)
{
rString += string.Format("Subject: {0}<br />Date: {1}<br /><br />", AK_appoint.Subject, AK_appoint.Start);
}
return rString;
}
}
}
Like promised here is a code example. I used the Microsoft Exchange Web Services (EWS) Managed API 1.0 and recommend you to do the same. The most comments I included in the code
using System;
using Microsoft.Exchange.WebServices.Data;
using System.Net;
namespace ExchangePublicFolders {
class Program {
static FolderId FindPublicFolder (ExchangeService myService, FolderId baseFolderId,
string folderName) {
// We will search using paging. We will use page size 10
FolderView folderView = new FolderView (10,0);
folderView.OffsetBasePoint = OffsetBasePoint.Beginning;
// we will need only DisplayName and Id of every folder
// se we'll reduce the property set to the properties
folderView.PropertySet = new PropertySet (FolderSchema.DisplayName,
FolderSchema.Id);
FindFoldersResults folderResults;
do {
folderResults = myService.FindFolders (baseFolderId, folderView);
foreach (Folder folder in folderResults)
if (String.Compare (folder.DisplayName, folderName, StringComparison.OrdinalIgnoreCase) == 0)
return folder.Id;
if (folderResults.NextPageOffset.HasValue)
// go to the next page
folderView.Offset = folderResults.NextPageOffset.Value;
}
while (folderResults.MoreAvailable);
return null;
}
static void MyTest () {
// IMPORTANT: ExchangeService is NOT thread safe, so one should create an instance of
// ExchangeService whenever one needs it.
ExchangeService myService = new ExchangeService (ExchangeVersion.Exchange2007_SP1);
myService.Credentials = new NetworkCredential ("MyUser#corp.local", "myPassword00");
myService.Url = new Uri ("http://mailwebsvc-t.services.local/ews/exchange.asmx");
// next line is very practical during development phase or for debugging
myService.TraceEnabled = true;
Folder myPublicFoldersRoot = Folder.Bind (myService, WellKnownFolderName.PublicFoldersRoot);
string myPublicFolderPath = #"OK soft GmbH (DE)\Gruppenpostfächer\_Template - Gruppenpostfach\_Template - Kalender";
string[] folderPath = myPublicFolderPath.Split('\\');
FolderId fId = myPublicFoldersRoot.Id;
foreach (string subFolderName in folderPath) {
fId = FindPublicFolder (myService, fId, subFolderName);
if (fId == null) {
Console.WriteLine ("ERROR: Can't find public folder {0}", myPublicFolderPath);
return;
}
}
// verify that we found
Folder folderFound = Folder.Bind (myService, fId);
if (String.Compare (folderFound.FolderClass, "IPF.Appointment", StringComparison.Ordinal) != 0) {
Console.WriteLine ("ERROR: Public folder {0} is not a Calendar", myPublicFolderPath);
return;
}
CalendarFolder myPublicFolder = CalendarFolder.Bind (myService,
//WellKnownFolderName.Calendar,
fId,
PropertySet.FirstClassProperties);
if (myPublicFolder.TotalCount == 0) {
Console.WriteLine ("Warning: Public folder {0} has no appointment. We try to create one.", myPublicFolderPath);
Appointment app = new Appointment (myService);
app.Subject = "Writing a code example";
app.Start = new DateTime (2010, 9, 9);
app.End = new DateTime (2010, 9, 10);
app.RequiredAttendees.Add ("oleg.kiriljuk#ok-soft-gmbh.com");
app.Culture = "de-DE";
app.Save (myPublicFolder.Id, SendInvitationsMode.SendToNone);
}
// We will search using paging. We will use page size 10
ItemView viewCalendar = new ItemView (10);
// we can include all properties which we need in the view
// If we comment the next line then ALL properties will be
// read from the server. We can see there in the debug output
viewCalendar.PropertySet = new PropertySet (ItemSchema.Subject);
viewCalendar.Offset = 0;
viewCalendar.OffsetBasePoint = OffsetBasePoint.Beginning;
viewCalendar.OrderBy.Add (ContactSchema.DateTimeCreated, SortDirection.Descending);
FindItemsResults<Item> findResultsCalendar;
do {
findResultsCalendar = myPublicFolder.FindItems (viewCalendar);
foreach (Item item in findResultsCalendar) {
if (item is Appointment) {
Appointment appoint = item as Appointment;
Console.WriteLine ("Subject: \"{0}\"", appoint.Subject);
}
}
if (findResultsCalendar.NextPageOffset.HasValue)
// go to the next page
viewCalendar.Offset = findResultsCalendar.NextPageOffset.Value;
}
while (findResultsCalendar.MoreAvailable);
}
static void Main (string[] args) {
MyTest();
}
}
}
You should update the string myPublicFolderPath to the value with your public calender folder. I set myService.TraceEnabled = true which produce long output with debug information. You should of cause remove the line for production.
UPDATED: Some additional links you could find in Create new calendar system support in Exchange OWA. If you not yet seen the videos and you want to use Exchange Web Services I would recommend you to watch there. It could save your time in the future.
I did similar thing for Exchange 2003, but using WebDAV Search method (http://msdn.microsoft.com/en-us/library/aa143053%28v=EXCHG.65%29.aspx).
http://geekswithblogs.net/cskardon/archive/2008/12/01/hunting-those-elusive-public-folders-using-exchange-web-services-part.aspx may help.