Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Retrieve calendar items (Outlook API, WebDAV) displaying strange behaviour

We are writing an MS Outlook plugin. To satisfy our business-logic, it should check all appointments between some dates. We are experiencing several problems with retrieving all items from calendars. We tried two options:

  1. Outlook API. We use the standard logic that is described in MSDN - sort items by [Start], set IncludeRecurrences to True and run the Find\Restrict query over calendar items like here. It works fine in our test environment. However, in our customer's environment: For recurring appointments, start and end dates are set to the corresponding dates of a 'master appointment.' For example, in some room's calendar we have a weekly appointment that was created in January, and if we try to find all items in August, we get among others four items of this recurring appointment, but their start and end dates are set to January. But Outlook displays correct dates in the same calendar...

  2. Very bad, but we still have WebDAV! We write a simple test application and try to query all items from the calendar using WebDAV. Of course, we didn't reinvent the wheel and just pasted the code from documentation. The previous problem is solved, but the next one arises: It doesn't return recurring items that were created more than approximately six months ago. I Haven't a clue - there are no parameters restricting 'old' items!

What is wrong? Are we missing something important?

Technical details: Exchange 2003, Outlook 2003-2010. Frankly speaking, the first error disappears if we turn on Cached Exchange Mode, but we can't do that.

var nameSpace = application.GetNamespace("MAPI"); var recepient = nameSpace.CreateRecipient(roomEMail); recepient.Resolve(); var calendar = nameSpace.GetSharedDefaultFolder(recepient, OlDefaultFolders.olFolderCalendar); var filter = string.Format("[Start]<'{1}' AND [End]>'{0}'",   dateFrom.ToString("dd/MM/yyyy HH:mm", CultureInfo.InvariantCulture), dateTo.ToString("dd/MM/yyyy HH:mm", CultureInfo.InvariantCulture) ); var allItems = calendar.Items; allItems.Sort("[Start]"); allItems.IncludeRecurrences = true; var _item = allItems.Find(filter); while (_item != null) {   AppointmentItem item = _item as AppointmentItem;   if (item != null) {     if (item.Subject != "some const")       && (item.ResponseStatus != OlResponseStatus.olResponseDeclined)       && (item.MeetingStatus != OlMeetingStatus.olMeetingReceivedAndCanceled        && item.MeetingStatus != OlMeetingStatus.olMeetingCanceled))     {       /* Here we copy item to our internal class.        * We need: Subject, Start, End, Organizer, Recipients, MeetingStatus,        * AllDayEvent, IsRecurring, RecurrentState, ResponseStatus,        * GlobalAppointmentID */     }   }   _item = allItems.FindNext(); } 

UPDATE 1:

Additional research using OutlookSpy shows that the problem is not in our code - the Start\End dates are incorrect inside the API when Cached Exchange Mode is off. But Outlook developers were aware of it, and they somehow display correct dates in calendars! Does anyone know how?

UPDATE 2:

Answer from Outlook Support Escalation Engineer:

Based on this, I can confirm that this is a problem in our product.

like image 518
Bolick Avatar asked Aug 28 '12 16:08

Bolick


People also ask

What is a WebDAV Server for calendar?

Calendaring Extensions to WebDAV, or CalDAV, is an Internet standard allowing a client to access and manage calendar data along with the ability to schedule meetings with users on the same or on remote servers. It lets multiple users in different locations to share, search and synchronize calendar data.


1 Answers

Possible cause:

  • Sort after setting IncludeRecurrences.

Here is my code of a PowerShell module that retrieves Outlook items between two dates.

And a little applet to check for changes and send an email including the agenda updates, which comes handy when you don't have mobile access to the Exchange.

Path: Documents\WindowsPowerShell\Modules\Outlook\expcal.ps1

Function Get-OutlookCalendar {   <#    .Synopsis     This function returns appointment items from default Outlook profile    .Description     This function returns appointment items from the default Outlook profile. It uses the Outlook interop assembly to use the olFolderCalendar enumeration.     It creates a custom object consisting of Subject, Start, Duration, Location     for each appointment item.    .Example     Get-OutlookCalendar |     where-object { $_.start -gt [datetime]"5/10/2011" -AND $_.start -lt `     [datetime]"5/17/2011" } | sort-object Duration     Displays subject, start, duration and location for all appointments that     occur between 5/10/11 and 5/17/11 and sorts by duration of the appointment.     The sort is the shortest appointment on top.    .Notes     NAME:  Get-OutlookCalendar     AUTHOR: ed wilson, msft     LASTEDIT: 05/10/2011 08:36:42     KEYWORDS: Microsoft Outlook, Office     HSG: HSG-05-24-2011    .Link      Http://www.ScriptingGuys.com/blog  #Requires -Version 2.0  #>   echo Starting... Initialize variables   Add-type -assembly "Microsoft.Office.Interop.Outlook" | out-null  $olFolders = "Microsoft.Office.Interop.Outlook.OlDefaultFolders" -as [type]  $olCalendarDetail = "Microsoft.Office.Interop.Outlook.OlCalendarDetail" -as [type]   echo ... Getting ref to Outlook and Calendar ...   $outlook = new-object -comobject outlook.application  $namespace = $outlook.GetNameSpace("MAPI")  $folder = $namespace.getDefaultFolder($olFolders::olFolderCalendar)   echo ... Calculating dates ...   $now = Get-Date -Hour 0 -Minute 00 -Second 00   echo From $a To $b   echo ... Getting appointments ...   $Appointments = $folder.Items  $Appointments.IncludeRecurrences = $true  $Appointments.Sort("[Start]")   echo ... Setting file names ...   $oldfile = "$env:USERPROFILE\outlook-calendar.bak"  echo oldfile: $oldfile  $newfile = "$env:USERPROFILE\outlook-calendar.txt"  echo newfile: $newfile  $calfile = "$env:USERPROFILE\outlook-calendar.ics"  echo calfile: $calfile   echo ... Exporting calendar to $calfile ...   $calendarSharing = $folder.GetCalendarExporter()  $calendarSharing.CalendarDetail = $olCalendarDetail::olFullDetails  $calendarSharing.IncludeWholeCalendar = $false  $calendarSharing.IncludeAttachments = $false  $calendarSharing.IncludePrivateDetails = $true  $calendarSharing.RestrictToWorkingHours = $false  $calendarSharing.StartDate = $now.AddDays(-30)  $calendarSharing.EndDate = $now.AddDays(30)  echo $calendarSharing  $calendarSharing.SaveAsICal($calfile)   echo ... Backing up $newfile into $oldfile ...   if (!(Test-Path $newfile)) {   echo "" |Out-File $newfile  }   # Backup old export into $oldfile  if (Test-Path $oldfile) {   echo "Deleting old backup file $oldfile"   del $oldfile   }  echo " ... moving $newfile into $oldfile ... "  move $newfile $oldfile   echo "... Generating text report to file $newfile ..."   $Appointments | Where-object { $_.start -gt $now -AND $_.start -lt $now.AddDays(+7) } |    Select-Object -Property Subject, Start, Duration, Location, IsRecurring, RecurrenceState  |   Sort-object Start |   Out-File $newfile -Width 100   echo "... Comparing with previous export for changes ..."   $oldsize = (Get-Item $oldfile).length  $newsize = (Get-Item $newfile).length   if ($oldsize -ne $newsize ) {   echo "!!! Detected calendar change. Sending email..."   $mail = $outlook.CreateItem(0)    #2 = high importance email header   $mail.importance = 2    $mail.subject = $env:computername + “ Outlook Calendar“    $mail.Attachments.Add($newfile)   $mail.Attachments.Add($calfile)   $text = Get-Content $newfile | Out-String   $mail.body = “See attached file...“ + $text    #for multiple email, use semi-colon ; to separate   $mail.To = “[email protected]“    $mail.Send()   }  else {   echo "No changes detected in Calendar!"  }   } #end function Get-OutlookCalendar  Function Get-OutlookCalendarTest {  echo starting...  Add-type -assembly "Microsoft.Office.Interop.Outlook" | out-null  $olFolders = "Microsoft.Office.Interop.Outlook.OlDefaultFolders" -as [type]  $outlook = new-object -comobject outlook.application  $namespace = $outlook.GetNameSpace("MAPI")  $folder = $namespace.getDefaultFolder($olFolders::olFolderCalendar)   $a = Get-Date -Hour 0 -Minute 00 -Second 00  $b = (Get-Date -Hour 0 -Minute 00 -Second 00).AddDays(7)  echo From $a To $b   $Appointments = $folder.Items  $Appointments.IncludeRecurrences = $true  $Appointments.Sort("[Start]")   $Appointments | Where-object { $_.start -gt $a -AND $_.start -lt $b } | Select-Object -Property IsRecurring, RecurrenceState, Subject, Start, Location  } #end function Get-OutlookCalendarTest 

This is the code to invoke the PowerShell function in the module:

Path: Documents\WindowsPowerShell\mono.ps1

Import-Module -Name Outlook\expcal.psm1 -Force  $i=0  #infinite loop for calling connect function    while(1) {    $i = $i +1    Write-Output "Running task Get-OutlookCalendar ($i)"    Get-OutlookCalendar     start-sleep -seconds 300  } 

To run the PowerShell script, use powershell.exe. To run this on startup, a shortcut on "%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\":

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass "C:\Users\%USERNAME%\Documents\WindowsPowerShell\mono.ps1" 
like image 177
Jose Manuel Gomez Alvarez Avatar answered Sep 21 '22 16:09

Jose Manuel Gomez Alvarez