Thanks
Jack
For example if I was to pull contact data for an existing contact and I
wanted their birthday, if the birthday field hasn't been populated in
Outlook/Exchange then the MAPI call returns an error code that has to
be dealt with. It doesn't return nil/null. It errors out. I had to
write a bunch of begin-rescue-end routines into my Ruby files in order
to account for this. Microsoft justifies this behavior by stating they
were looking to streamline their database storage by not storing
anything for nil/null values. It's more like the database columns
aren't even defined at all in these cases!
In any event, if you are interested I can either post some sample code
here or e-mail you the files. Probably better the doing the latter
since they might be kind of long ...
Please post to the group.
I would like to see them as well
Thanks,
Walt
--
No virus found in this outgoing message.
Checked by AVG Anti-Virus.
Version: 7.0.344 / Virus Database: 267.11.4/109 - Release Date: 9/21/2005
Thanks again
-----Original Message-----
From: gregarican [mailto:greg....@gmail.com]
Sent: Monday, September 26, 2005 2:27 PM
To: ruby...@ruby-lang.org
Subject: Re: Outlook calendar
If you are interested I can e-mail you some .rb files I have that
push/pull data from Outlook/Exchange sources. One word of warning.
You'll have to come up with some mechanisms for trying to pull null
data.
For example if I was to pull contact data for an existing contact and I
wanted their birthday, if the birthday field hasn't been populated in
Outlook/Exchange then the MAPI call returns an error code that has to
be dealt with. It doesn't return nil/null. It errors out. I had to
write a bunch of begin-rescue-end routines into my Ruby files in order
to account for this. Microsoft justifies this behavior by stating they
were looking to streamline their database storage by not storing
anything for nil/null values. It's more like the database columns
aren't even defined at all in these cases!
In any event, if you are interested I can either post some sample code
This may be of help:
http://www.jamesbritt.com/index.rb/Development@Outlook_Appointments_with_Ruby.txt
James
--
http://www.ruby-doc.org - The Ruby Documentation Site
http://www.rubyxml.com - News, Articles, and Listings for Ruby & XML
http://www.rubystuff.com - The Ruby Store for Ruby Stuff
http://www.jamesbritt.com - Playing with Better Toys
> Please post to the group.
>
> I would like to see them as well
>
>
> Thanks,
>
>
> Walt
You got it. The line/space formatting might not be exact since I'm
pasting from an e-mail I sent happy-jack, but here goes:
------------------------------------------------
First of all, check out the CDO Live website for details on how to
interface with all of the MAPI properties that correspond to calendar,
contact, message, etc. data. Here's a page that I relied upon -->
http://www.cdolive.com/cdo10.htm. If you wrote Perl scripts that
interfaced with MAPI objects then you probably are familiar with what
is involved.
For the most part I just looked at the VB samples on the CDO Live
website and turned the syntax around so that they worked in Ruby. Below
is a sample method of a MAPI query that I created to pull contact
information from a Sales Contacts folder that's in the Exchange Public
Folders at my company. I use the last name and the last 4 digits of the
home telephone number as selection criteria. I create the Sales
Contacts folder object in Ruby using win32ole and my own mapi profile
housed in Outlook. Since this folder object creation is used in other
areas of my program I saved it as a method that can be reused.
def getSalesContacts
# create the Exchange server session
session = WIN32OLE.new('Mapi.Session')
session.logon('Greg Kujawa')
# navigate to the Sales Contacts entry underneath All Public Folders
publicFolders = String.new
recordCount = session.InfoStores.count
recordCount.times { |i|
publicFolders = session.InfoStores.Item(i+1).RootFolder.Folders if
session.InfoStores.Item(i+1).name == "Public Folders"
}
allPublicFolders = String.new
recordCount = publicFolders.count
recordCount.times { |i|
allPublicFolders = publicFolders.Item(i+1).Folders if
publicFolders.Item(i+1).name == "All Public Folders"
}
salesContacts = String.new
recordCount = allPublicFolders.count
recordCount.times { |i|
salesContacts = allPublicFolders.Item(i+1).messages if
allPublicFolders.Item(i+1).name == "Sales Contacts"
}
return salesContacts
end
def getMAPIRecordset(lastName, homePhone)
@allContacts = getSalesContacts
@recordCount = @allContacts.count
@resultSet = Array.new
lastName.gsub!(/\s+/, "")
@lastName = lastName.downcase
homePhone.gsub!(/\s+|\D+/, "")
homePhone =~ /(\d\d\d\d)$/
@homePhone = $1
@recordCount.times do |i| # iterate through the record count to find
the record based on given criteria
begin
@contactLastName = String.new
@contactLastName <<
@allContacts.Item(i+1).Fields.Item(@cdoPR_SURNAME).Value.to_s.downcase
rescue
@contactLastName = "Invalid"
end
begin
@contactHomePhone = String.new
@contactHomePhone <<
@allContacts.Item(i+1).Fields.Item(@cdoPR_HOME_TELEPHONE_NUMBER).Value
rescue
@contactHomePhone = "Invalid"
end
if @contactLastName == @lastName and @contactHomePhone[-4..-1] ==
@homePhone
begin
@resultSet[0] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_DISPLAY_NAME).value.to_s
rescue
@resultSet[0] = " "
end
begin
@resultSet[1] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_COMPANY_NAME).value.to_s
rescue
@resultSet[1] = " "
end
begin
@resultSet[2] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_SURNAME).value.to_s
rescue
@resultSet[2] = " "
end
begin
@resultSet[3] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_MIDDLE_NAME).value.to_s
rescue
@resultSet[3] = " "
end
begin
@resultSet[4] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_GIVEN_NAME).value.to_s
rescue
@resultSet[4] = " "
end
begin
@resultSet[5] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_STREET_ADDRESS).value.to_s
rescue
@resultSet[5] = " "
end
begin
@resultSet[6] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_LOCALITY).value.to_s
rescue
@resultSet[6] = " "
end
begin
@resultSet[7] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_STATE_OR_PROVINCE).value.to_s
rescue
@resultSet[7] = " "
end
begin
@resultSet[8] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_POSTAL_CODE).value.to_s
rescue
@resultSet[8] = " "
end
begin
@resultSet[9] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_BODY).value.to_s
rescue
@resultSet[9] = " "
end
begin
@resultSet[10] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_CUSTOMER_NUMBER).value.to_s
rescue
@resultSet[10] = " "
end
begin
@resultSet[11] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_BIRTHDAY).value.to_s
rescue
@resultSet[11] = " "
end
begin
@resultSet[12] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_WEDDING_ANNIVERSARY).value.to_s
rescue
@resultSet[12] = " "
end
begin
@resultSet[13] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_SPOUSE_NAME).value.to_s
rescue
@resultSet[13] = " "
end
begin
@resultSet[14] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_HOME_ADDRESS_CITY).value.to_s
rescue
@resultSet[14] = " "
end
begin
@resultSet[15] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_HOME_ADDRESS_POSTAL_CODE).value.to_s
rescue
@resultSet[15] = " "
end
begin
@resultSet[16] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_HOME_ADDRESS_STATE_OR_PROVINCE).value.to_s
rescue
@resultSet[16] = " "
end
begin
@resultSet[17] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_HOME_ADDRESS_STREET).value.to_s
rescue
@resultSet[17] = " "
end
begin
@resultSet[18] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_HOME_ADDRESS_FAX_NUMBER).value.to_s
rescue
@resultSet[18] = " "
end
begin
@resultSet[19] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_HOME_TELEPHONE_NUMBER).value.to_s
rescue
@resultSet[19] = " "
end
begin
@resultSet[20] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_BUSINESS_FAX_NUMBER).value.to_s
rescue
@resultSet[20] = " "
end
begin
@resultSet[21] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_BUSINESS_TELEPHONE_NUMBER).value.to_s
rescue
@resultSet[21] = " "
end
begin
@resultSet[22] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_CELLULAR_TELEPHONE_NUMBER).value.to_s
rescue
@resultSet[22] = " "
end
begin
@resultSet[23] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_ENTRYID).value.to_s
rescue
@resultSet[23] = " "
end
end
end
if @resultSet == [] or homePhone !~ /\d\d\d\d\d\d\d/
return 1 # failure
else
return @resultSet # success
end
end
The begin-rescue-end statements should be better organized I'm sure.
The problem seems to be cases where the contact field hasn't been
defined. MAPI throws an error message rather than simply returning a
nill/null value. The @cdo... variables are the MAPI properties that I
pulled from the CDO Live website. A more in-depth reference can be
found at
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cdo/html/05406029-3df3-42f2-b186-b41a1097d1a2.asp.
I know my sample code doesn't directly deal with the Calendar MAPI
object but if you use my stuff as a guide and look at the CDO Live
website I'm sure you can get where you need to go. Hope this helps!
Thanks
Timgerr
I really don't want to be harsh here, believe me, but perhaps if you can't do
it in Perl the problem you're facing isn't a language related one. Why were
you unable to accomplish it in Perl? This would help me know where to start
helping you.
require 'win32ole'
CDOPR_ENTRYID = 0x0FFF0102 # the MAPI property ID for Entry ID's
# create the Exchange server session
@session = WIN32OLE.new('Mapi.Session')
@session.logon('Greg Kujawa')
# navigate to the Sales Contacts entry underneath All Public Folders
publicFolders = String.new
recordCount = @session.InfoStores.count
recordCount.times { |i|
publicFolders = @session.InfoStores.Item(i+1).RootFolder.Folders if
@session.InfoStores.Item(i+1).name == "Public Folders"
}
allPublicFolders = String.new
recordCount = publicFolders.count
recordCount.times { |i|
allPublicFolders = publicFolders.Item(i+1).Folders if
publicFolders.Item(i+1).name == "All Public Folders"
}
vacationCalendar = String.new
recordCount = allPublicFolders.count
recordCount.times { |i|
vacationCalendar = allPublicFolders.Item(i+1).messages if
allPublicFolders.Item(i+1).name == "Vacation Calendar"
}
recordCount = vacationCalendar.count
recordCount.times { |i|
p vacationCalendar.Item(i+1).Fields.Item(CDOPR_ENTRYID).value.to_s
}
For a listing off all of the MAPI property ID's check out
http://www.cdolive.com/cdo10.htm. You will have to translate the VB
some, as the &H0FFF0102 means 0x0FFF0102 or '0FFF0102'.hex. The trick
(as I have mentioned) when pulling data is that any nil/null field
values cause MAPI to return an error code. So you have to write a Ruby
routine that will catch this error and simply return a nil/null field
value instead of raising a runtime error. Does this help?
Such a reference book will clue you into what you need to do in order
to work with the MAPI data store. It's not pretty, but at least it's
better than working with the win32api stuff.
A handy source of docs for this sort of thing is msdn.microsoft.com, and
general Googling for Outlook + VBScript.
When trying to manipulate Microsoft applications or ActiveX objects, I
typically go looking for examples in VBscript or JScript, and port the
code to Ruby.
----------------------------
require 'win32ole'
# these are some CDO property tag constants
CDOPROPSETID1= "0220060000000000C000000000000046"
CDOAPPT_STARTDATE = "0x820D"
CDOAPPT_ENDDATE = "0x820E"
CDOPR_BODY = 0x1000001F
# this starts a MAPI session with Outlook/Exchange
@session = WIN32OLE.new('Mapi.Session')
@session.logon('Greg Kujawa') # replace with your Outlook profile name
# this pages through the top level Outlook folders to find the Public
Folders object
publicFolders = String.new
recordCount = @session.InfoStores.count
recordCount.times { |i|
publicFolders = @session.InfoStores.Item(i+1).RootFolder.Folders if
@session.InfoStores.Item(i+1).name == "Public Folders"
}
# this pages through the next level of Outlook folders to find the All
Public Folders object
allPublicFolders = String.new
recordCount = publicFolders.count
recordCount.times { |i|
allPublicFolders = publicFolders.Item(i+1).Folders if
publicFolders.Item(i+1).name == "All Public Folders"
}
# this pages through the final level of Outlook folders to find the
Vacation Calendar object
vacationCalendar = String.new
recordCount = allPublicFolders.count
recordCount.times { |i|
vacationCalendar = allPublicFolders.Item(i+1).messages if
allPublicFolders.Item(i+1).name == "Vacation Calendar"
}
# now that we have a Vacation Calendar object we iterate through all
items in it,
# reporting back some property values
recordCount = vacationCalendar.count
recordCount.times { |i|
begin # get into the habit of rescuing MAPI/CDO reads, as items with
unassigned values raise runtime errors
puts "The calendar event is "+vacationCalendar.Item(i+1).Subject
puts "The start date is
"+vacationCalendar.Item(i+1).Fields.Item("{#{CDOPROPSETID1}}#{CDOAPPT_STARTDATE}").Value.to_s
puts "The end date is
"+vacationCalendar.Item(i+1).Fields.Item("{#{CDOPROPSETID1}}#{CDOAPPT_ENDDATE}").Value.to_s
puts "The calendar detail is
"+vacationCalendar.Item(i+1).Fields.Item(CDOPR_BODY).Value.to_s
rescue # here we just essentially report back nothing
puts "I'm blank!\n"
end
}