Recupere elementos del calendario (API de Outlook, WebDAV) que muestren un comportamiento extraño

79

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:

  1. API de Outlook. Nosotros utilizamos la lógica estándar que se describe en MSDN - Artículos Tipo de [Inicio], conjunto IncludeRecurrencesa Truey 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 ...

  2. 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.

Bolick
fuente
3
1. ¿Cuál es tu código? 2. No utilice WebDAV; está en desuso.
Dmitry Streblechenko
Se ve perfectamente bien ... ¿Cuál es tu código para acceder a la cita? ¿Alguna vez accede a AppointmentItem.Parent (que le dará la cita principal para una instancia de una actividad recurrente)?
Dmitry Streblechenko
Actualicé el código anterior. No, no usamos AppointmentItem.Parent. De todos modos, antes de acceder a las fechas de inicio y finalización, accedemos solo a las propiedades Subject, ResponseStatus y MeetingStatus de AppointmentItem.
Bolick
Por un lado, Outlook no usa OOM para mostrar el contenido de la carpeta Calendario; en segundo lugar, ¿por qué cree que las fechas de inicio / finalización son incorrectas? ¿Qué es exactamente lo que está mal?
Dmitry Streblechenko
Exactamente: OutlookSpy nos muestra varias citas con el mismo StartTime, en nuestro caso = 11/01/2012, y esta es definitivamente la cita maestra de la actividad semanal recurrente (el mismo organizador, el mismo tema, etc). Pero en el calendario podemos ver la imagen correcta: un elemento por semana. Le agradecería mucho si pudiera explicar cómo funciona Outlook, qué tecnología utiliza para mostrar el calendario, alguna idea de por qué obtenemos un resultado incorrecto en OOM y cómo solucionar estos errores.
Bolick

Respuestas:

1

Causa posible:

  • Ordenar después de configurar IncluirRecurrencias.

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"
José Manuel Gómez Álvarez
fuente