Estamos escribiendo un complemento de MS Outlook. Para satisfacer nuestra lógica de negocios, debe verificar todas las citas entre algunas fechas. Tenemos varios problemas al recuperar todos los elementos de los calendarios. Probamos dos opciones:
API de Outlook. Nosotros utilizamos la lógica estándar que se describe en MSDN - Artículos Tipo de [Inicio], conjunto
IncludeRecurrences
aTrue
y ejecutar el Find \ Limitar la consulta sobre el calendario artículos como aquí . Funciona bien en nuestro entorno de prueba. Sin embargo, en el entorno de nuestro cliente: Para las citas periódicas, las fechas de inicio y finalización se establecen en las fechas correspondientes de una "cita principal". Por ejemplo, en el calendario de alguna sala tenemos una cita semanal que se creó en enero, y si tratamos de encontrar todos los elementos en agosto, obtenemos entre otros cuatro elementos de esta cita recurrente, pero sus fechas de inicio y finalización se establecen en enero. . Pero Outlook muestra las fechas correctas en el mismo calendario ...Muy mal, ¡pero todavía tenemos WebDAV! Escribimos una aplicación de prueba simple e intentamos consultar todos los elementos del calendario usando WebDAV. Por supuesto, no reinventamos la rueda y simplemente pegamos el código de la documentación . El problema anterior está resuelto, pero surge el siguiente: no devuelve elementos recurrentes que se crearon hace más de aproximadamente seis meses. No tengo ni idea, ¡no hay parámetros que restrinjan los elementos 'antiguos'!
¿Qué está mal? ¿Nos estamos perdiendo algo importante?
Detalles técnicos: Exchange 2003, Outlook 2003-2010. Hablando francamente, el primer error desaparece si activamos el modo de intercambio en caché, pero no podemos hacer eso.
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();
}
ACTUALIZACIÓN 1:
Investigaciones adicionales con OutlookSpy muestran que el problema no está en nuestro código: las fechas de inicio y finalización son incorrectas dentro de la API cuando el modo de intercambio en caché está desactivado. Pero los desarrolladores de Outlook lo sabían y de alguna manera muestran las fechas correctas en los calendarios. ¿Alguien sabe cómo?
ACTUALIZACIÓN 2:
Respuesta del ingeniero de escalamiento de soporte de Outlook:
Basado en esto, puedo confirmar que este es un problema en nuestro producto.
fuente
Respuestas:
Causa posible:
Aquí está mi código de un módulo de PowerShell que recupera elementos de Outlook entre dos fechas.
Y un pequeño subprograma para verificar los cambios y enviar un correo electrónico con las actualizaciones de la agenda, lo cual es útil cuando no tiene acceso móvil a Exchange.
Ruta: Documentos \ 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
Este es el código para invocar la función de PowerShell en el módulo:
Ruta: Documentos \ 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 }
Para ejecutar el script de PowerShell, use powershell.exe. Para ejecutar esto en el inicio, un acceso directo en "% APPDATA% \ Microsoft \ Windows \ Menú Inicio \ Programas \ Inicio \":
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass "C:\Users\%USERNAME%\Documents\WindowsPowerShell\mono.ps1"
fuente