Tcom examples for Microsoft Outlook

This page provides examples of how to read and write a Microsoft Outlook document using the Tcom package. See How one discovers the API for a COM-exporting application for more help on finding Outlook commands.


Creating a Calendar Entry

package require tcom

proc ConvertTime {t} {
  regexp {([+-])(\d\d)(\d\d)} [clock format [clock seconds] -format %z] m sign hours minutes
  set tlocal [expr "$t $sign ($hours *60 +$minutes)*60"]
  return [expr {(25569+$tlocal/24/3600)+(($tlocal%(24*3600))/(24.0*3600))}]
}

set outlook   [::tcom::ref getactiveobject Outlook.Application]
set app       [$outlook Application]
set entry     [$app CreateItem [expr 1]]

$entry Subject "This is the subject"
$entry Start [ConvertTime [clock scan {2009-02-13 21:00}]]
$entry End [ConvertTime [clock scan {2009-02-13 21:30}]]
$entry Save

Jaf:A Small Outlook Calendar Clien/Server Pair I used these to query my appointments from my UNIX machine, I tend to miss meetings while working on a Unix station.

This could help finish the work on A Calendar Widget, see Future Feature discussion.

First the Server:

proc msdate_to_iso {val} {
    set base_ticks [clock scan 20000101]
    set base_offset 36526;# days since 31. Dec 1899, ARRRGGHHHHH
    set offset  [expr {int($val)-$base_offset}]
    set clkdate [clock scan "$offset days" -base $base_ticks]
    set isodate [clock format $clkdate -format %Y%m%d]
    set fhours  [expr {24.0*($val-int($val))}]
    set hours   [expr {int($fhours)}]
    set mins    [expr {int(($fhours-$hours)*60)}]
    return "$isodate $hours:$mins"
  }

proc get_appointments  {startdate enddate} {
    # set up search scope        
    set w_startdate \
      [clock format [clock scan "${startdate}T000000"] -format "'%d/%m/%Y'"]
    set w_enddate   \
      [clock format [clock scan "${enddate}T000000"]   -format "'%d/%m/%Y'"]
    set restriction "\[Start\] <= $w_enddate AND \[End\] >= $w_startdate"
    # tcom stuff
    if {[catch {::tcom::ref getactiveobject Outlook.Application} outlook]} {
        return {}
      }
    set app [$outlook Application]
    set session [$app Session]
    $session Logon
    set calitems [$session GetDefaultFolder 9]
    set calendar [$calitems Items]
    $calendar IncludeRecurrences 1
    $calendar Sort "\[Start\]"
    set wanted [$calendar Restrict $restriction]
    $wanted Sort "\[Start\]"
    # make list out of the returned data
    set retlist {}
    ::tcom::foreach ent $wanted {
        lappend retlist [list "Start"      [msdate_to_iso [$ent Start]] \
                              "End"        [msdate_to_iso [$ent End]]   \
                              "Subject"    [$ent Subject]               \
                              "Location"   [$ent Location]              \
                              "Categories" [$ent Categories]]
      }
    puts "returning [llength $retlist] items"  
    return $retlist  
  }
proc process {chan addr port} {  
  # if nothing there
  if {[eof $chan]} { close $chan; return }
  # buffering to lines to accomodate puts/gets on the other side
  fconfigure  $chan -buffering line  
  # retrieve message        
  set mess [gets $chan]
  # report message
  puts "$addr:$port \@ $chan tells me \"$mess\""
  # remove extra blanks
  set mess_clean [string trim [regsub -all -- { +} $mess { }]]
  # retrieve command and argument
  set mesg_list [split $mess_clean { }]
  set command   [lindex $mesg_list 0]
  set startdate [lindex $mesg_list 1]
  set enddate   [lindex $mesg_list 2]
  # react
  switch -exact -- $command {
      "get_appointments"  { puts $chan [$command $startdate $enddate] }
      default {
          puts $chan "Only \"get_appointments\" allowed"
          puts "erroneous command: $command"
        }              
    }
  close $chan  
  }
package require tcom  
set listen [socket -server process 3456]
vwait forever  

The client is trivially simple:

set ipserver "mypcname_or_inetaddress_here"; # put PC name or address here
set ipport     "3456"    ; # big (> 1024) port here
proc get_appointments {startdate enddate} {
    set channel [socket $::ipserver $::ipport]
    fconfigure $channel -buffering line
    puts $channel "get_appointments $startdate $enddate"
    gets $channel retval
    close $channel
    return $retval
  }

By the way: Does anybody know why Microsoft chose to format dates as floats (apart from the fact that they might have wanted to do something different than Unix)?

An integer part counting the days since 31 Dec 1899 (Day 0) while the fractional part represents the fraction of the day that elapsed is kind of odd. Or is there a hiden benefit I'm not aware of?

Cheers, Jaf.