Мы пишем плагин для MS Outlook. Чтобы удовлетворить нашу бизнес-логику, он должен проверять все встречи между некоторыми датами. У нас возникло несколько проблем с получением всех элементов из календарей. Мы попробовали два варианта:
Outlook API. Мы используем стандартную логику, которая описана в MSDN - элементы сортировки по [Start], набор
IncludeRecurrences
дляTrue
и запустить Find \ Ограничить запрос через элементы календаря , как здесь . Он отлично работает в нашей тестовой среде. Однако в среде наших клиентов: для повторяющихся встреч даты начала и окончания устанавливаются на соответствующие даты «основной встречи». Например, в календаре некоторых комнат у нас есть еженедельная встреча, созданная в январе, и если мы попытаемся найти все элементы в августе, мы получим среди прочих четыре элемента этой повторяющейся встречи, но их даты начала и окончания установлены на январь. . Но Outlook отображает правильные даты в том же календаре ...Очень плохо, но у нас все еще есть WebDAV! Мы пишем простое тестовое приложение и пытаемся запросить все элементы из календаря с помощью WebDAV. Конечно, мы не изобретали велосипед, а просто вставили код из документации . Предыдущая проблема решена, но возникает следующая: он не возвращает повторяющиеся элементы, которые были созданы более шести месяцев назад. Понятия не имею - нет параметров, ограничивающих «старые» предметы!
Что случилось? Мы упускаем что-то важное?
Технические детали: Exchange 2003, Outlook 2003-2010. Честно говоря, первая ошибка исчезает, если мы включаем Cached Exchange Mode, но мы не можем этого сделать.
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, когда режим кэширования Exchange отключен. Но разработчики Outlook знали об этом и каким-то образом отображали правильные даты в календарях! Кто-нибудь знает как?
ОБНОВЛЕНИЕ 2:
Ответ от инженера по расширению поддержки Outlook:
На основании этого я могу подтвердить, что это проблема нашего продукта.
источник
Ответы:
Возможная причина:
Вот мой код модуля PowerShell, который извлекает элементы Outlook между двумя датами.
И небольшой апплет для проверки изменений и отправки электронного письма с обновлениями повестки дня, который пригодится, когда у вас нет мобильного доступа к Exchange.
Путь: 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"
источник