Отримати елементи календаря (Outlook API, WebDAV), що відображають дивну поведінку


79

Ми пишемо плагін MS Outlook. Щоб задовольнити нашу бізнес-логіку, він повинен перевірити всі зустрічі між деякими датами. Ми стикаємось із кількома проблемами з отриманням усіх елементів із календарів. Ми спробували два варіанти:

  1. API Outlook. Ми використовуємо стандартну логіку, яка описана в MSDN - елементи сортування по [Start], набір IncludeRecurrencesдля Trueі запустити Find \ Обмежити запит через елементи календаря , як тут . Це добре працює в нашому тестовому середовищі. Однак у середовищі нашого замовника: для періодичних зустрічей датою початку та закінчення встановлюються відповідні дати „головного побачення”. Наприклад, у календарі деяких кімнат у нас є щотижнева зустріч, яка була створена в січні, і якщо ми спробуємо знайти всі предмети в серпні, ми отримаємо серед інших чотири елементи цієї періодичної зустрічі, але датою їх початку та закінчення встановлено січень . Але Outlook відображає правильні дати в тому ж календарі ...

  2. Дуже погано, але у нас все ще є WebDAV! Ми пишемо простий тестовий додаток і намагаємося запитувати всі елементи з календаря за допомогою WebDAV. Звичайно, ми не винаходили колесо, а просто вставляли код із документації . Попередня проблема вирішена, але виникає наступна: вона не повертає повторювані елементи, створені більше приблизно півроку тому. Я не маю поняття - немає параметрів, що обмежують "старі" предмети!

Що не так? Ми пропускаємо щось важливе?

Технічні деталі: Exchange 2003, Outlook 2003-2010. Чесно кажучи, перша помилка зникає, якщо ми вмикаємо кешований режим обміну, але ми не можемо цього зробити.

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();
}

ОНОВЛЕННЯ 1:

Додаткове дослідження за допомогою OutlookSpy показує, що проблема не в нашому коді - дати початку / кінця неправильні в API, коли кешований режим обміну вимкнено. Але розробники Outlook це знали, і вони якось відображають правильні дати в календарях! Хтось знає як?

ОНОВЛЕННЯ 2:

Відповідь інженера з питань ескалації підтримки Outlook:

На основі цього я можу підтвердити, що це проблема у нашому продукті.


3
1. Який ваш код? 2. Не використовуйте WebDAV; воно застаріло.
Дмитро Стреблеченко

Виглядає чудово ... Який ваш код отримує доступ до зустрічі? Ви коли-небудь отримували доступ до AppointmentItem.Parent (який дасть вам головну зустріч для екземпляра періодичної діяльності)?
Дмитро Стреблеченко

Я оновив код вище. Ні, ми не використовуємо AppointingItem.Parent. У будь-якому випадку, перед тим, як отримати доступ до дат початку та закінчення, ми отримуємо доступ лише до властивостей Subject, ResponseStatus та MeetingStatus AppointingItem.
Bolick

По-перше, Outlook не використовує OOM для відображення вмісту папки Календар, по-друге, чому ви вважаєте, що дати початку / кінця неправильні? Що саме не так?
Дмитро Стреблеченко

Точно: OutlookSpy показує нам кілька зустрічей з однаковим часом початку, у нашому випадку = 11.01.2012, і це, безумовно, головне призначення періодичної щотижневої діяльності (той самий організатор, той самий предмет тощо). Але в календарі ми можемо бачити правильну картинку - по одному предмету на тиждень. Я був би дуже вдячний, якби Ви пояснили, як працює Outlook, яку технологію він використовує для відображення календаря, будь-які ідеї, чому ми отримуємо неправильний результат в OOM, і як виправити ці помилки?
Bolick

Відповіді:


1

Можлива причина:

  • Сортувати за налаштуванням IncludeRecurrences.

Ось мій код модуля PowerShell, який отримує елементи Outlook між двома датами.

І невеликий аплет для перевірки змін та надсилання електронного листа із оновленнями порядку денного, що стане в нагоді, коли у вас немає мобільного доступу до біржі.

Шлях: 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 = “your-email@your-mail-domain.com“

  $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

Це код для виклику функції PowerShell в модулі:

Шлях: 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

}

Щоб запустити сценарій PowerShell, використовуйте powershell.exe. Щоб запустити це під час запуску, ярлик на "% APPDATA% \ Microsoft \ Windows \ Start Menu \ Programs \ Startup \":

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass "C:\Users\%USERNAME%\Documents\WindowsPowerShell\mono.ps1"
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.