Programming Microsoft Outlook and Microsoft Exchange, Second Edition (DV-MPS Programming)

[Previous] [Next]

The Session object is the top-level object in the CDO hierarchy, which is shown in Figure A-1. It contains session-wide settings, options, and properties that return top-level objects. When using the CreateObject method in your applications, you use the ProgID of the Session object, MAPI.Session, to create a CDO object. With the exception of the SetLocaleIDs method, CDO does not allow you to access any method or property in the library until you have successfully logged on using the Logon method of the Session object. The SetLocaleIDs method sets the Locale and CodePage IDs for the user. The following figure shows the CDO hierarchy.

Figure A-1 The CDO Library hierarchy.

Session Object Methods

The following sections describe some of the Session object methods.

Logon Method

Since you must successfully log on using the Logon method before you can access the other objects and properties in the CDO library, Logon is the first method we will look at. The Logon method takes a number of different parameters depending on the needs of your application, as shown here:

objSession.Logon( [profileName] [, profilePassword] [, showDialog] [, newSession] [, parentWindow] [, NoMail] [, ProfileInfo] )

The most common ways to log on to a CDO session are to pass in a MAPI profile name, or pass in the specific information that CDO needs to dynamically generate a profile for you. Dynamically generated profiles are the preferred method when building ASP applications with CDO. Since ASP filess cannot access client profiles, CDO has no way to pull information from a profile located on the machine of the user.

Authenticated Logon Using a Profile

To log on using a profile, you pass the profile name as the first parameter to the Logon method. If you do not know the name of the profile you want to use, you can set the ShowDialog parameter to True, and CDO will prompt the user to pick a profile. The second parameter is the password for the profile, if a password exists. You can leave this parameter blank and set the ShowDialog parameter to True, and CDO will prompt the user for a password. The fourth parameter is NewSession. By setting the NewSession parameter to False, you can make CDO take advantage of an existing MAPI session, which eliminates the overhead of creating a new MAPI session on the machine of the user. The following code snippet shows you how to use the Logon method with a profile named MS Outlook Settings.

oSession.Logon ProfileName:="MS Outlook Settings", _ showDialog:=True, NewSession:=True

Authenticated Logon Using a Dynamically Generated Profile

When your application is running in an environment where profiles or the ability to prompt a user for a profile is not be available, CDO allows you to dynamically generate a profile for the user by passing in the user's server name and mailbox name to the Logon method. To retrieve this information, you can make your application prompt the user for a server name and mailbox name. CDO can pull the default Exchange Server name from the Registry by using the ConfigParameter properties in the CDO Rendering library. For right now, the code sample assumes that you know at least one Exchange Server name in your organization. The following code sample shows you how to log on to CDO using a dynamically generated profile:

strServer = "Exchange Server Name" strMailbox = "User Alias Name (Not Display Name)" strProfileInfo = strServer + vbLf + strMailbox oSession.Logon "", "", False, True, 0, True, strProfileInfo 'Check for a valid logon set oInbox = oSession.GetInbox if err.number <> 0 then 'Not Successful oSession.Logoff Response.write "Unsuccessful Logon! " End if

NOTE


For the mailbox name of the user, don't use the display name, such as Thomas Rizzo (Exchange). Instead use the user's alias name, such as thomriz. Also, when using the ProfileInfo parameter, you should attempt to access an item in a CDO message store, such as the first message in the Inbox, since the Logon method will return success even when the parameters in the ProfileInfo string are incorrect. If attempting to access items returns an error, the user was not successfully logged on.

Anonymous Access

CDO also allows users to anonymously access the Exchange Server public folder store as well as the Exchange Server directory. Anonymous access must be enabled by the administrator of the Exchange Server system. Also, the administrator or developer can control which folders and which directory entries the anonymous user can see by setting some options in the Exchange Administrator program.

To use anonymous access, you must pass in to the ProfileInfo parameter the distingushed name of the Exchange Server and the anon account by using the following format:

<server distinguished name> & vbLf & vbLf & "anon"

The server's distingushed name takes the form of:

/o=<enterprise>/ou=<site>/cn=Configuration/cn=Servers/cn=<server>

The enterprise parameter corresponds to the Exchange Server organization, and the site parameter corresponds to the Exchange Server site you want to access. The following code shows you how to log on using anonymous access:

StrProfileInfo = "/o=" & "Your Exchange Org" & "/ou=" & _ "Your Site" & "/cn=Configuration/cn=Servers/cn=" & "Your Server" & _ vbLF & vbLF & "anon" oSession.Logon "", "", False, True, 0, True, strProfileInfo if err.number <> 0 then oSession.Logoff response.write "Unsuccessful Logon!" end if

AddressBook Method

The AddressBook method allows you to display a customized modal dialog box that prompts the user to select specific entries from the address book. You should not use this method in web applications running on a web server, because your ASP will display the address book on the monitor of the server rather than on the machine of the user.

The syntax for the AddressBook method is as follows:

Set objRecipients = objSession.AddressBook( [recipients] [, title] [, oneAddress] [, forceResolution] [, recipLists] [, toLabel] [, ccLabel] [, bccLabel] [, parentWindow ] )

As you can see, this method returns to you a Recipients collection that contains the entries selected by the user. If the user does not select any entries, the AddressBook method returns Nothing.

The parameters of this method allow you to customize the user interface of the address book. They also allow you to force the user to resolve any ambiguous names she enters into the interface. To understand the parameters and how they work, you need to look at some examples. The following code displays an address book with only the To button enabled, the caption of the address book titled To Only, and the text on the To button labeled Select To:

On Error Resume Next Set oRecips = oSession.AddressBook(, "To Only", , , 1, _ "Select To", , , 0) If Err.Number <> 0 Then 'No Users selected Err.Clear End If

NOTE


If a user clicks the Cancel button in the address book dialog box, CDO will return a run-time error. Therefore, you must place code in your application to handle this run-time error, as shown in the preceding snippet.

The following example displays an address book in which all of the address buttons are enabled:

On Error Resume Next Set oRecips = oSession.AddressBook(, "All Buttons", , , 3, _ "Select To", "Select CC", "Select BCC", 0) If Err.Number <> 0 Then 'No users selected Err.Clear End If

NOTE


The Outlook object library does not include the functionality to enable all the address buttons, so in your Outlook applications, use the AddressBook method of the CDO library. You can see an example of this functionality in the Account Tracking application in Chapter 7 of the book.

GetXXX Methods

The CDO library contains a number of GetXXX methods. The XXX suffix corresponds to CDO objects, as in the GetAddressEntry method and the GetDefaultFolder method. All the GetXXX methods allow you to quickly retrieve a particular object from the CDO library without scrolling through the object hierarchy. The following table lists the methods and the parameters they take:

Method Name

Returns

Parameters

GetAddressEntry

AddressEntry object

The EntryID of an AddressEntry object. You can get the EntryID for an address entry by using the ID property on an AddressEntry object and storing this value in a location to use for this method.

GetAddressList

AddressList object from the directory

This method takes one parameter, which is a Long with one of the following values:

0—Global Address List

1—Personal Address Book

GetArticle

Message object

There are two required parameters and one optional parameter: ArticleID, FolderID and (optional) StoreID. An ArticleID parameter is a 32-bit unique identifier for a message. An EntryID is a 70-byte identifier. The difference between the two is that for an ArticleID parameter, you must specify the folder where the parameter resides. You do not have to specify the folder for an EntryID.

GetDefaultFolder

Folder object

This method takes a Long that identifies the default folder you wish to retrieve from the following list:

0—Calendar

1—Inbox

2—Outbox

3—Sent Items

4—Deleted Items

5—Contacts

6—Journal

7—Notes

8—Tasks

Note that these constants are different from the constants for the Outlook GetDefaultFolder method.

GetFolder

Folder object

This method takes a folder ID as well as an optional store ID to quickly retrieve a folder.

GetInfoStore

InfoStore object

This method takes, as a parameter, the name as a string or a unique ID of the information store you want to retrieve. For example, to access the Public Folder store, for the parameter, you can type in Public Folders. For an individual mailbox such as the Thomas Rizzo mailbox on the Exchange Server. you can type in Mailbox—Thomas Rizzo (Exchange).

GetMessage

AppointmentItem, MeetingItem, or Message object

This method takes one required parameter, the EntryID of the message, and an optional parameter—the StoreID—for the message store. If you are using Outlook 98 and CDO 1.21 on the same machine and logging onto CDO using a shared profile between the two applications, this method will always return a Message object, even if you are retrieving an object of a different type. To avoid this, use a dynamically generated profile to log on.

The following example shows how you can use some of the methods in the table in your applications. This Microsoft Visual Basic example retrieves the Global Address List using the GetAddressList method and displays the name of the first person in the list. Then it retrieves the calendar of the current user and prints out the subject of an appointment as well as its start time using the GetDefaultFolder method. Finally, it uses the GetInfoStore method to retrieve the Public Folder infostore, and prints out all the top-level folders in the infostore.

Set oGAL = oSession.GetAddressList(0) Set oAE = oGAL.GetFirst() MsgBox "Name: " & oAE.Name Set oCal = oSession.GetDefaultFolder(0) Set oCalMsgs = oCal.Messages Set oFirstCal = oCalMsgs.GetFirst() MsgBox "Appt Name: " & oFirstCal.Subject & vbLf & "Time: " & _ oFirstCal.StartTime Set oPublicStore = oSession.InfoStores("Public Folders") Set oRootPFFolder = oPublicStore.RootFolder Set oTopLevelFolders = oRootPFFolder.Folders For i = 1 To oTopLevelFolders.Count MsgBox oTopLevelFolders.Item(i).Name Next

GetOption Method

The GetOption method returns specific calendar rendering options for the current session. These options can correspond to criteria such as the business day start and end time for the user as well as the default calendar store for the user, regardless of whether the calendar store is Microsoft Schedule+ or Outlook. You can change any of these options by using the SetOption method described later in this supplement. The following Visual Basic code sample retrieves the calendar redering options and displays them on the screen:

MsgBox oSession.GetOption("BusinessDayStartTime") MsgBox oSession.GetOption("BusinessDayEndTime") MsgBox oSession.GetOption("CalendarStore") MsgBox oSession.GetOption("TimeZone")

Logoff Method

The Logoff method terminates the currently active session created by the Logon method. Before calling the Logoff method, make sure that you destroy any ContainerRenderer or ObjectRenderer objects. The following code sample logs a user off the current session:

If Not (oSession Is Nothing) Then oSession.Logoff MsgBox "Sucessfully Logged Off" Else MsgBox "No current session" End If

SetOption Method

The SetOption method is used in conjunction with the GetOption method to get and set the calendar rendering options for the current CDO user. Using SetOption, you can set calendar options such as the business day start time and whether the calendar should be based on a 24-hour clock. The following code sample shows you how to use this method to change the business day start and end times for the current user:

oSession.SetOption "BusinessDayStartTime", "08:30:00 AM" oSession.SetOption "BusinessDayEndTime", "06:00:00 PM" MsgBox oSession.GetOption("BusinessDayStartTime") MsgBox oSession.GetOption("BusinessDayEndTime")

Session Object Properties

The following sections describe some of the Session object properties.

AddressLists Property

The AddressLists property returns either a single AddressList object or an AddressLists collection, depending on the parameters you pass to the property. For example, if you do not pass any parameters, the AddressLists collection, which contains all of the AddressList objects for the current session, is returned to you. If you pass to the AddressLists property the index number or the name of an AddressList object, that object will be returned to you as long as it exists in the current AddressLists collection. The following sample retrieves and prints out all the current AddressList objects by using the AddressLists property:

Set oALS = oSession.AddressLists For i = 1 To oALS.Count MsgBox i & ": " & oALS.Item(i).Name Next

CurrentUser Property

This CurrentUser property returns the active user as an AddressEntry object. When using this property, the most common mistake made by developers is assuming that the property returns the name of the current user rather than an object. To retrieve the name of the current user, use the Name property of the AddressEntry object that is returned by the CurrentUser property. The following example demonstrates how to do this:

Set oCurrentUser = oSession.CurrentUser strText = "The current user is:" & oCurrentUser.Name strText = strText & vbLf & "Address: " & oCurrentUser.Address MsgBox strText

Inbox and Outbox Properties

To allow you quick access to both the Inbox and Outbox of the current user, CDO provides the Inbox and Outbox properties. You might find it easier, however, to learn how to use the GetDefaultFolder method because you have to use it to get other types of folders anyway. The following example uses these properties to count and display the number of items in each folder:

MsgBox "Inbox: " & oSession.Inbox.Messages.Count MsgBox "Outbox: " & oSession.Outbox.Messages.Count

InfoStores Property

This property is similar to the AddressLists property in that its return value depends on which parameters you pass to it.. If you pass no parameters, you will receive an InfoStores collection. If you pass an index or the name of an InfoStore object, the InfoStores property will return a single InfoStore object. This property allows you to access not only mailbox stores but also Public Folder stores. For the root Public Folder store, the name is always Public Folders. The following code uses the InfoStores property to scroll through all available InfoStore objects for the current user and prints out their names:

Set oInfoStores = oSession.InfoStores For i = 1 To oInfoStores.Count MsgBox i & ": " & oInfoStores.Item(i).Name Next

If you run this example on your own machine, you will find that a number of InfoStore objects appear for you, such as Public Folders, your mailbox, and any .pst files you have in your current profile.

Name Property

The Name property returns the display name of the current profile, not to be confused with the name of the current user. The following example displays the profile name of the current user:

Msgbox "Profile Name: " & oSession.Name

OutofOffice and OutofOfficeText Property

CDO allows you to set whether a user is out of the office and also the text that should be received users who send mail to a user whose OutofOffice property is set to True. The OutofOffice and OutofOfficeText properties are equivalent to using the Out of Office Assistant in the Outlook client. Note that you can set these properties only when connected to an Exchange Server. You will receive an error if you attempt to set these properties in an offline state. The following code sets the current user as out of the office and prepares an appropriate response:

oSession.OutOfOffice = True oSession.OutOfOfficeText = "I'm returning Oct 1st."

InfoStores Collection and InfoStore Object

The InfoStores collection supports no methods and only two properties that you'll be interested in, Count and Item. The main purpose of the InfoStores collection is to house individual InfoStore objects. You primarily will use the InfoStores collection to find individual stores for items such as Public Folders, mailboxes, or .pst files. When using the Item method of the InfoStores collection, you can pass in either the index or the name of the InfoStore object you want to access. To see how to access the InfoStores collection in code, refer to the code sample in the "InfoStores Property" section in the section titled "Session Object Properties"

InfoStore Object Methods

The only method supported on the InfoStore object is the IsSameAs method, which allows you to compare two InfoStore objects to see if they are the same.

InfoStore Properties

The following sections describe some of the InfoStore object properties.

Fields Property

The Fields property returns either a single Field object or a Fields collection, depending on the parameters you pass to it. By passing MAPI property tags to the Fields property on an InfoStore object, you can access more detailed information about the infostore. For a list of MAPI property tags, you should refer to the CDO help file. The following code sample uses the CdoPR_STORE_SUPPORT_MASK property tag to figure out what types of functionality the current InfoStore object supports:

CdoPR_STORE_SUPPORT_MASK = &H340D0003 STORE_ENTRYID_UNIQUE = &H1 STORE_READONLY = &H2 STORE_SEARCH_OK = &H4 STORE_MODIFY_OK = &H8 STORE_CREATE_OK = &H10 STORE_ATTACH_OK = &H20 STORE_OLE_OK = &H40 Set oStore = oSession.InfoStores("Mailbox - Thomas Rizzo (Exchange)") bitStoreSupport = oStore.Fields(CdoPR_STORE_SUPPORT_MASK) Unique = bitStoreSupport And STORE_ENTRYID_UNIQUE If Unique <> 0 Then MsgBox "EntryIDs are unique" End If If (bitStoreSupport And STORE_READYONLY) <> 0 Then MsgBox "Readonly for the store" End If If (bitStoreSupport And STORE_MODIFY_OK) <> 0 Then MsgBox "Modify ok for the store" End If If (bitStoreSupport And STORE_CREATE_OK) <> 0 Then MsgBox "Create ok for the store" End If If (bitStoreSupport And STORE_ATTACH_OK) <> 0 Then MsgBox "Attachments supported in the store" End If If (bitStoreSupport And STORE_OLE_OK) <> 0 Then MsgBox "OLE objects supported in the store" End If

When writing your applications, you will find that the Fields collection is used on many objects throughout CDO. This collection provides a way to access functionality that CDO does not currently provide objects for, such as determining the permissions of the current user on a folder. You should browse through the MAPI property tags in the help file and become familiar with them. You can use them in your applications.

ID Property

The ID property returns the EntryID of the infostore. This ID is unique and does not change between sessions, so you can store it and pass it to the GetInfoStore method on the Session object to quickly get a specific InfoStore object.

Index Property

The Index property returns the index number for the InfoStore object so that you can use it with the parent InfoStores collection.

Name Property

This property returns the display name of the InfoStore object. You can pass this name as a parameter to the Item property of the InfoStores collection to quickly retrieve a specific InfoStore object.

RootFolder Property

The RootFolder property returns a Folder object, which represents the root of all the folders for the current InfoStore object. For your mailbox and Public Folder stores, the RootFolder property returns a Folder object named IPM_Subtree. From that Folder object, you can scroll through all of the folders in your mailbox or top-level Public Folders. There is, however, one caveat with this property: you cannot use the RootFolder property when you develop ASP or Visual Basic applications that run as a service in Windows NT and need access to Public Folders. Instead, you need to use the Fields collection with the specific property tag PR_IPM_PUBLIC_FOLDERS_ENTRYID (&H66310102). After you retrieve the EntryID for the Public Folder root folder from the Fields collection, you can use the GetFolder method to retrieve the root Public Folder. Then you can use the Folders collection of the root Public Folder to retrieve its subfolders. The following two examples show you both methods. The first sample uses the RootFolder property from a Visual Basic application not running as a service but as a client application on the machine of a user. The second example shows you how to retrieve the Public Folder root folder and print out the subfolders of the root folder by using the Fields collection from an ASP application.

'Running in a Visual Basic Client application Set oStore = oSession.InfoStores("Public Folders") Set oRoot = oStore.RootFolder For i = 1 To oRoot.Folders.Count MsgBox oRoot.Folders.Item(i).Name Next 'Running in an ASP application 'This is the EntryId for the Root public folder objInfoStore = oSession.InfoStores("Public Folders") bstrPublicRootID = objInfoStore.Fields.Item( &H66310102 ).Value Set myrootfolder = objOMSession.GetFolder(bstrPublicRootID, _ objInfoStore.ID) 'Now get the Folders collection below the root Set myfoldercollect = myrootfolder.Folders For I = 1 to myfoldercollect.Count Response.write I & ": " & myfoldercollect.item(I).name Next

Folders Collection and Folder Object

The Folders collection object is a child object of the InfoStores collection. The Folders collection object represents a group of folders in an InfoStore object and is usually accessed either by using the RootFolder property of the InfoStore object or by using the GetDefaultFolder method on the Session object. Once you retrieve a Folders collection object, you can use a number of methods and properties to manipulate the collection.

Folders Collection Methods

The following sections describe the Folders collection methods.

Add Method

To add a new Folder object to the collection, you pass the name of the folder as a parameter to the Add method. The user must have sufficient rights to create the folder in the collection, or CDO will fail on the method. The following code sample uses the Fields collection of a Folders collection to check the permissions on the folder for the current user. If the user has MAPI_ACCESS_CREATE_HIERARCHY permissions, which means the user can create subfolders under the current folder, the example uses the Add method to create a new folder.

MAPI_ACCESS_CREATE_ASSOCIATED = &H20 'owner (inheritance) MAPI_ACCESS_CREATE_CONTENTS = &H10 'post messages MAPI_ACCESS_CREATE_HIERARCHY = &H8 'create subfolders MAPI_ACCESS_DELETE = &H4 MAPI_ACCESS_READ = &H2 MAPI_ACCESS_MODIFY = &H1 MAPI_E_COLLISION = &H80040604 MAPI_E_NOT_FOUND = &H8004010F MAPI_E_PERMISSIONS = &H46 MAPI_E_NO_ACCESS = &H80070005 MAPI_E_FAIL = &H80004005 mapiW_PARTIAL_COMPLETION = &H40680 Set oStore = oSession.Inbox curpermissions = oStore.Fields(&HFF40003) 'PR_ACCESS If (curpermissions And MAPI_ACCESS_CREATE_HIERARCHY) <> 0 Then 'Can Create Subfolders oStore.Folders.Add ("New Folder") MsgBox "Folder successfully created" Else MsgBox "You do not have permissions to create folders" End If

Delete Method

The Delete method deletes all the items in the collection, so be careful before you call it, because you could potentially lose data. The following example prompts the user about whether she really wants to delete all of her folders before calling the Delete method on a Folders collection:

Set oStore = oSession.InfoStores("Mailbox - Thomas Rizzo (Exchange)") Set oFolders = oStore.RootFolder.Folders result = MsgBox("Are you sure you want to delete all folders", _ vbYesNo, "Delete Folders") If result = vbYes Then oFolders.Delete If Err.Number = 0 Then MsgBox "Folders deleted successfully" End If Else SMsgBox "User canceled operation" End If

NOTE


Instead of deleting the entire collection, you might want to scroll through the collection and use the MoveTo method of the Folder object, which moves each folder to the Deleted Items folder so that the user has the optoin can get back her information. An example of using MoveTo is in the " MoveTo Method" section.

GetFirst, GetLast, GetNext, GetPrevious Methods

By using these methods, you can scroll through the Folders collection. These methods are similar to the using a For...Each and For...Next loops in the collection. The following example shows how to use these methods to print out the names of all folders in the collection:

Set oStore = oSession.InfoStores("Mailbox - Thomas Rizzo (Exchange)") Set oFolders = oStore.RootFolder.Folders Set oFirstFolder = oFolders.GetFirst() MsgBox oFirstFolder.Name If oFirstFolder Is Nothing Then MsgBox "There are no folders!" Else Set oNextFolder = oFolders.GetNext() While Not (oNextFolder Is Nothing) MsgBox oNextFolder.Name Set oNextFolder = oFolders.GetNext() Wend End If

Sort Method

The Sort method sorts the collection on the property that you pass in as a parameter. This method can sort either CdoAscending (1) or CdoDescending (2). If you do not pass a sort order to this method, it will default to CdoAscending. Also, if you do not pass a property to sort on, it will default to CdoPR_DISPLAY_NAME. The following example does an ascending sort on a Folders collection by using the creation time (&H30070040) property as the criteria for the sort.

Set oStore = oSession.InfoStores("Mailbox - Thomas Rizzo (Exchange)") Set oFolders = oStore.RootFolder.Folders oFolders.Sort 1, &H30070040 For i = 1 To oFolders.Count MsgBox oFolders.Item(i).Name & ": " & _ oFolders.Item(i).Fields(&H30070040) Next

Folders Collection Properties

The only interesting properties on the Folders collection (which are also on all of the collections in CDO object) are the Count and Item properties. They enable you to quickly figure out how many items are in the collection. You can scroll through each item.

Folder Object Properties

The Folder object represents a single folder stored in a Folders collection. The following sections describe some of the more interesting properties on the Folder object.

Fields Property

As we have seen with the other objects, the Fields property allows you to access specific information stored on an item even if the item does not expose the information as an explicit property on the object. This access is achieved by passing in the MAPI property tag for the item or the name of the property (if it is a custom property). An interesting property named CdoPR_CONTAINER_CONTENTS (&H3613001E) contains the message class of the default type of item contained in the folder. For example, if you set the default item type for a Public Folder in Outlook to Contacts, the Fields property will contain IPF.Contact. Many developers ask how to change the default item type in a folder programmatically through CDO. By using the Fields collection on a folder and the Fields property, you can do this, as shown in the next snippet of sample code, which changes the default item type of a folder to tasks. To find other properties and their descriptions that you can use as parameters for the Fields collection, look in the CDO help file under the section titled "MAPI Property Tags."

Set oStore = oSession.InfoStores("Mailbox - Thomas Rizzo (Exchange)") Set oFolder = oStore.RootFolder.Folders("Contacts") MsgBox "Before Update: " & oFolder.Fields(&H3613001E) oFolder.Fields(&H3613001E) = "IPF.Tasks" oFolder.Update MsgBox "After Update: " & oFolder.Fields(&H3613001E)

FolderID Property

The FolderID property returns the unique ID for the parent of the current folder. Note that if you do obtain a Schedule+ calendar folder using the GetDefaultFolder method described earlier, the FolderID property will not be available, and CDO will return a CdoE_NOT_FOUND error. The following code retrieves and prints out the FolderID of a particular folder. In this case, the ID is for the root of a mailbox store—the parent of the Inbox folder is the root folder of the store.

Set oInbox = oSession.Inbox Msgbox "Parent ID: " & oInbox.FolderID

Folders Property

The Folders property returns a Folders collection, which contains the subfolders for the current folder. If the current folder has no subfolders, the Folders property returns a collection with no objects. On a return from this call, you should check the count of the returned collection to see whether any Folder objects are contained in the collection. The following code recurses through child folders of the Inbox, printing out their names until there are no more folders in the collection:

Sub ListChildren(oTempFolder) MsgBox "Folder Name: " & oTempFolder.Name Set oTempFolders = oTempFolder.Folders If oTempFolders.Count <> 0 Then For j = 1 To oTempFolders.Count ListChildren oTempFolders.Item(j) Next End If End Sub Set oInbox = oSession.Inbox Set oFolders = Inbox.Folders MsgBox "Folders under the Inbox folder" For i = 1 To oFolders.Count ListChildren oFolders.Item(i) Next

HiddenMessages Property

The HiddenMessages property returns a Messages collection, which contains all of the hidden messages in the folder. These messages are not visible to any of the Outlook clients and are usually view definitions, agents, out-of-office templates, or rules stored in a particular folder. You should not modify this collection unless you are sure that the modifications you make will not affect other users. You can add new messages to this collection to implement specific tasks in your applications. For example, in your application, you could create a hidden data source for a list box. When a user launched your form, the list box would be populated with specific messages from the collection returned by the HiddenMessages property. The following sample prints all of the hidden messages in a folder with their message classes and message subjects, if the message subjects exist:

Set oHidden = oInbox.HiddenMessages MsgBox oHidden.Count For i = 1 To oHidden.Count Set oMessage = oHidden.Item(i) MsgBox oMessage.Type & ": " & oMessage.Subject & " " & _ oMessage.Text Next

Running the program in my Inbox produced some of the following results:

IPM.Microsoft.FolderDesign.NamedView: My New View IPM.Note.Rules.OofTemplate.Microsoft: I will return on the 5th of June. Please page me if urgent.

ID Property

The ID property returns the unique identifier for the current folder. You can store this ID and then use it later with the GetFolder method of the Session object to quickly retrieve a folder rather than search the hierarchy for the folder. The following sample code prints out the folder ID, stores it, and then uses the GetFolder method to retrieve the folder:

Set oInbox = oSession.Inbox strInboxID = oInbox.ID MsgBox strInboxID Set oNewInbox = oSession.GetFolder(strInboxID, Null) MsgBox oNewInbox.Name

Messages Property

The Messages property returns a Messages collection, which represents the messages contained in the folder. If no messages are contained in the folder, this property returns Nothing. The most common mistake beginning CDO developers make is forgetting to use this property when trying to add a new message to a folder. Many times, developers use the Add method of the Folder object, not realizing they are actually adding a new Folder object rather than a Message object into the folder. The following example adds a new message to the Outbox folder by retrieving the Messages collection by using the Messages property, and then it sends the message using the methods of the Message object:

Set oOutbox = oSession.Outbox Set oMessage = oOutbox.Messages.Add Set oRecip = oMessage.Recipients.Add oRecip.Type = 1 'On the To line oRecip.Address = "SMTP:julia@juno.com" oRecip.Resolve oMessage.Subject = "Hello from Seattle" oMessage.Text = "Hope you can come visit soon!" oMessage.Send

Name Property

This property returns the name of the folder. You can see this property in action in the ID property example. The Name property is read/write, so you can use it to not only read the name of the folder but also change the name of the folder.

Folder Object Methods

The following sections describe many of the Folder object methods.

CopyTo Method

This method copies the current folder and all of its contents below another folder. You must specify as a parameter to this method the ID of the new parent folder. As optional parameters, you can specify the store that should contain the folder (if different from the current store) or, if you want the folder to have a different name from the current name, you can also specify the new name for the folder after copying it.

You can also pass an optional Boolean value that tells CDO to copy any subfolders contained below the folder being copied. The default value for this Boolean is True, so be careful in your code when using this method—you might, by accident, copy a large number of folders and items, and that could take a long time to process as well as use up a large amount of disk space. The CopyTo method does not change the message class or default item type for the folder.

You should be aware that CDO might not be able to access the items, depending on what type of items are contained in the folder. For example, you could use this method to copy your calendar to a Public Folder. CDO currently does not support Public Folder calendars, so even though CDO copies the items intact, you will not be able to access your new folder and any special calendar information using CDO. You can, however, access this information from Outlook, which does support Public Folder calendars. The following example uses the CopyTo method to copy your Inbox to a new folder. The code then prints out the number of items contained in the new folder. Notice how you set an object as the return value for the CopyTo method. The object contains the newly created folder from this method.

Set oFolder = oSession.Inbox Set oCopy = oFolder.CopyTo(oFolder.ID, Null, "Test2", False) MsgBox oCopy.Messages.Count

Delete Method

As its name implies, the Delete method deletes the current folder from the Folders collection. Once you call this method, the collection is refreshed. This means that the Count property is reduced by one, and any variables you have that store the count property must be updated accordingly. If you want to delete all the folders in a Folders collection, you don't have to scroll through the entire collection and use this method. Instead, you should call the Delete method on the Folders collection. The following example deletes a temporary folder under the Inbox folder, and displays the folder count both before and after the deletion:

Set oInbox = oSession.Inbox Set oSubFolder = oInbox.Folders("Temp") MsgBox "Before: " & oInbox.Folders.Count oSubFolder.Delete MsgBox "After: " & oInbox.Folders.Count

MoveTo Method

The MoveTo method is very similar to the CopyTo method except that all messages in the folder and all subfolders below the folder are also moved by default. You do not have the option about whether to move these items. After the folder is moved, it is unavailable. The following sample code shows an example of moving a temporary folder below the Inbox folder:

Set oInbox = oSession.Inbox Set oSubFolder = oInbox.Folders("Temp") Set oOutbox = oSession.Outbox Set newMovedFolder = oSubFolder.MoveTo(oOutbox.ID, Null) If Err.Number = 0 Then MsgBox "Temp Folder moved." End If

Update Method

The Update method saves changes to the current folder. This method takes two parameters. The first is an optional Boolean that tells the method whether to flush the cache and commit all changes to the underlying message store. A setting of True commits the changes. A setting of False flushes the cache but does not commit the changes to storage. The default value for this parameter is True.

The second parameter is also an optional Boolean that tells the method whether to reload and refresh the values for the cache for the current folder from the message store or not to reload the values. By setting this parameter to False, which is the default, the values in the cache are not reloaded from the store.

You should call this method after you make any changes to the folder, such as renaming or changing one of the values for a field on the folder. The following example renames a folder, calls the Update method to make the change permanent, and then reloads the cache from storage.

Set oInbox = oSession.Inbox Set oSubFolder = oInbox.Folders("Temp") oSubFolder.Name = "New Temp" oSubFolder.Update True, True

Messages Collection and Message Object

The Messages collection is accessed by calling the Messages property on a Folder object. The Messages collection consists of Message objects that you can manipulate to change items in folders or to create new items. The following section first describes the methods and properties of the Messages collection, and then the methods and properties of the Message object.

Messages Collection Methods

The following sections describe the methods of the Messages collection.

Add Method

The Add method adds a new item to the collection, depending on which folder you call. For example, if you call the Add method in your Inbox, CDO will return a new Message object. If you call the Add method in your Calendar folder, CDO will return a new AppointmentItem object.

This method takes four parameters, in this order: the subject of the item you are creating, the text or body of the item, the type or message class of the item, and the importance of the item. When creating AppointmentItem objects in your calendar, you cannot use these parameters. Instead, you should create the new item and then use the properties of the AppointementItem object to set your desired values.

You must call the Update method on the new item if you want to save the item into permanent storage. Also, pass the message class of IPM.Post as the third parameter if you want to post an item into a folder. The following example uses this method to create new mail, post, and custom items with high importance in your Inbox folder:

Set oInbox = oSession.Inbox Set oNewMail = oInbox.Messages.Add("New mail for you", "Hello!") oNewMail.Update Set oNewPost = oInbox.Messages.Add("New Post", "Hello!", "IPM.Post") oNewPost.Update Set oNewCust = oInbox.Messages.Add("Custom","Hello!", _ "IPM.Post.My Post", 2) oNewCust.Update

Delete Method

The Delete method removes all objects from the collection. Please note that by using this method, the items are deleted; they are not first moved to the deleted items folder. For this reason, you might want to give the user the option to move the current items in the collection to the deleted items folder rather automatically delete the items. The user can then retrieve any items that he did not mean to delete. The following example shows you how to implement this solution using the MoveTo method of the Message object, which you will learn about later:

Set oInbox = oSession.Inbox Set oTemp = oInbox.Folders("Temp") Set oTempMsgs = oTemp.Messages If oTempMsgs.Count > 0 Then result = MsgBox("Do you want to move the items to the " & _ "deleted items folder?", vbYesNoCancel, "Delete Items") If result = vbYes Then '4 is the constant for deleted items Set oDeletedItems = oSession.GetDefaultFolder(4) For Each oMessage In oTempMsgs Set oNewMsg = oMessage.MoveTo(oDeletedItems.ID) Next ElseIf result = vbNo Then 'Delete the items oTempMsgs.Delete Else MsgBox "Action canceled by user." End If End If

GetFirst, GetLast, GetNext, GetPrevious Methods

By using the GetFirst, GetLast, GetNext, and GetPrevious methods, you can scroll through a collection. These methods are similar to the using the For...Each and For...Next loops through the collection. The GetFirst method takes an optional parameter that allows you to specify a string for the message class of the first item you want to find. For example, to find the first post item in your Inbox, you could pass "IPM.Post" to the GetFirst method. The following example shows how you can print out the first two post items in your Inbox using the GetFirst and GetNext methods:

Set oInbox = oSession.Inbox Set oMessages = oInbox.Messages Set oPost = oMessages.GetFirst("IPM.Post") MsgBox oPost.Subject & ": " & oPost.Text Set oNextPost = oMessages.GetNext MsgBox oNextPost.Subject & oNextPost.Text

Sort Method

The Sort method allows you to sort the collection using a built-in or custom property as the criterion, and a sort order using either CdoNone(0) CdoAscending (1) or CdoDescending(2). If you do not supply a sort order, CDO defaults to ascending. You cannot use this method on the Messages collection in a calendar folder. The following example sorts your Inbox by the individual the message is from, in ascending order, and then prints out the name, subject, and text of the new first message in the Inbox.

Set oInbox = oSession.Inbox Set oMessages = oInbox.Messages '&H42001E is CdoPR_SENT_REPRESENTING_NAME oMessages.Sort 1, &H42001E Set oMessage = oMessages.GetFirst MsgBox "From: " & oMessage.Sender & vbLf & "Subject: " & _ oMessage.Subject & vbLf & oMessage.Text

Messages Collection Properties

The Messages collection contains the standard properties for all other collections, such as Count and Item properties, as well as one special property, the Filter property. The Filter property allows you to create filters on your messages to restrict which messages appear in the collection. We'll look at the Filter property and corresponding MessageFilter object, which the Filter property returns, after we look at the Message object properties and methods. You'll want to leverage the properties of the Message object in your filters.

Message Object Methods

The Message object contains nine primary methods that you will use in many of your applications: CopyTo, Delete, Forward, MoveTo, Options, Reply, ReplyAll, Send, and Update. All of these methods are described in detail in the following sections.

CopyTo Method

The CopyTo method copies a specific Message object to a specific folder. You must supply the ID of the folder to which you want to copy the current message. Note that AppointmentItem objects do not support the CopyTo method—you will receive an error if you attempt to call it on any AppointmentItem objects. Also, you must call the Update method on the copied message to commit the changes to the store. Before calling the Update method, you can modify the properties of the new Message object. For example, the following code copies the first Message object from the Inbox to the Outbox and changes the time the message was both received and sent to the current time. Then the code calls the Update method to commit the changes to the store.

Set oInbox = oSession.Inbox Set oMessages = oInbox.Messages Set oFirstMsg = oMessages.GetFirst("IPM.Note") Set oOutbox = oSession.Outbox Set oNewCopiedMessage = oFirstMsg.CopyTo(oOutbox.ID, Null) oNewCopiedMessage.TimeSent = Now oNewCopiedMessage.TimeReceived = Now oNewCopiedMessage.Update

Delete Method

The Delete method deletes the message. With this method, you can pass in a Boolean parameter that tells CDO whether to move the object to the deleted items folder rather than permanently delete it. If you set the parameter to True, CDO will perform the move to the deleted items folder for you. By setting the paramter to False, which is the default, CDO will permanently delete the object. Note that AppointmentItem objects do not support this optional parameter and will ignore it. The following example deletes all the items in an Inbox and moves them to the deleted items folder by using the optional parameter of the Delete method:

Set oInbox = oSession.Inbox Set oMessages = oInbox.Messages For Each oMessage In oMessages oMessage.Delete True Next

Forward Method

The Forward method returns a new Message object that you can use to forward the current message to another user. This method follows the same semantics of forwarding a message using Outlook, that is, the attachments of the current message are copied to the new message, all recipients are not copied to the new message, and the subject is copied to the new message. Note that the current implementation of CDO does not copy over the message text, but you can copy over the text yourself. We do that in the next code example.

You must use the Recipients collection on the newly created Message object to set the recipient for the forwarded item. Once you have done that, you must call either the Update or the Send method to either save or send, respectively, the new item to your specified recipients. The following example finds the first mail message in your Inbox and forwards it. Notice how the code adds the FW: to the subject as well as copies over the text from the original message to the forwarded message.

Set oInbox = oSession.Inbox Set oMessages = oInbox.Messages Set oMessage = oMessages.GetFirst("IPM.Note") Set oNewMsg = oMessage.Forward oNewMsg.Subject = "FW: " & oNewMsg.Subject oNewMsg.Text = oMessage.Text MsgBox oNewMsg.Subject & vbLf & oNewMsg.Text Set oRecip = oNewMsg.Recipients.Add oRecip.Address = "SMTP:mrizzo@msn.com" oRecip.Resolve oNewMsg.Send set oNewMsg = Nothing

MoveTo Method

The MoveTo method moves the current Message object to the folder whose ID you pass in as a parameter to the method. You can also optionally pass in the store ID if the folder is on a different store than the folder you are moving the item from. You cannot use this method currently on AppointmentItem objects; if you attempt to do this, CDO will return an error. The following example moves the first item in your Inbox to your Outbox by using the MoveTo method:

Set oInbox = oSession.Inbox Set oMessages = oInbox.Messages Set oMessage = oMessages.GetFirst("IPM.Note") Set oOutBox = oSession.Outbox Set oMovedMsg = oMessage.MoveTo(oOutBox.ID, Null)

Options Method

This method displays a modal dialog box that allows the user to change the options for the current message. The dialog box that appears is determined by the back-end system that CDO is using for its store. You cannot use the Options method on an Appointment or MeetingItem object; CDO will return Cdo_E_NO_SUPPORT if you attempt this. The following code creates a new mail message in your Inbox and calls the Options method, which allows you to set specific message options before sending the item. Note On Error Resume Next in the code. If the user clicks the Cancel button on the dialog box, CDO will return a run-time error. Your code must be able to handle this error.

On Error Resume Next Set oInbox = oSession.Inbox Set oMessages = oInbox.Messages Set oMessage = oMessages.Add("Options Test", "This is a test", _ "IPM.Note") oMessage.Options

Reply Method

The Reply method is used to reply to a message in the same way the reply button is used in Outlook. The Reply method returns a new Message object with the current message subject copied over to the new item as well as the original sender as a recipient for the reply. Attachments and message text are not copied over to the new message. You must call either the Update method or the Send method to commit the newly created reply Message object to storage. If you do not call these methods and the object goes out of scope, your changes will be lost. The following example shows you how you can change the behavior of the default Reply method with some custom code so that your subject on the reply is preceded with RE:, and the text from the original item is copied over to the new item:

Set oInbox = oSession.Inbox Set oMessages = oInbox.Messages Set oMessage = oMessages.GetFirst("IPM.Note") Set oNewReply = oMessage.Reply MsgBox "Original Subject: " & oMessage.Subject & _ vbLf & "Original Text: " & oMessage.Text oNewReply.Subject = "RE: " & oNewReply.Subject oNewReply.Text = vbLf & vbLf & oMessage.Text MsgBox "Reply Subject: " & oNewReply.Subject & _ vbLf & "Reply Text: " & oNewReply.Text oNewReply.Send Set oNewReply = Nothing

ReplyAll Method

The ReplyAll method is similar to the Reply method. The only difference between them is that the ReplyAll method copies all recipients of the original message over to the new Message object that corresponds to the reply. Again, the text and attachments of the original message are not copied over to the new message. The following example shows you how you can warn users that their attachments will be lost if they use ReplyAll:

Set oInbox = oSession.Inbox Set oMessages = oInbox.Messages Set oMessage = oMessages.GetFirst("IPM.Note") If oMessage.Attachments.Count > 0 Then 'Attachments present result = MsgBox("There are attachments that will be lost by " _ & "using this method. Are you sure you want to " _ & "continue?", vbQuestion & vbYesNo, "ReplyAll") If result = vbYes Then Set oNewReply = oMessage.ReplyAll MsgBox "Original Subject: " & oMessage.Subject & _ vbLf & "Original Text: " & oMessage.Text oNewReply.Subject = "RE: " & oNewReply.Subject oNewReply.Text = vbLf & vbLf & oMessage.Text MsgBox "Reply Subject: " & oNewReply.Subject & _ vbLf & "Reply Text: " & oNewReply.Text oNewReply.Send set oNewReply = Nothing End If End If

Send Method

The Send method commits the message to the underlying messaging system. This method takes three optional parameters. The first is a Boolean that, if you set to True, will save a copy of the message in the Sent Items folder. The default for this parameter is True. The second parameter is also a Boolean that, if you set to True, makes CDO display a dialog box that allows the user to change the message contents or recipients before sending the message. The default for this parameter is False. You cannot set this parameter to True for AppointmentItem or MeetingItem objects. The third parameter is a Long value that refers to the parent window for the dialog box if you set the first parameter to be True. Normally, programmers do not pass in a value for this parameter because it makes the dialog box modal.

For an AppointmentItem object, you must first set its MeetingStatus property (which we discuss later) to CdoMeeting (1) before attempting to call the Send method. Otherwise, CDO will return an error.

After calling the Send method, it's a good practice to set the object that contains the Message object just sent to Nothing. Send will invalidate that object, and setting it to Nothing will remove the object from memory. The following example creates a new message in your Inbox, assigns some recipients, and then sends the message. It displays the send options and saves a copy of the message in the Sent Items folder. Notice the error handling in the example. If you do display the dialog box for Send, you have to handle the run-time error CDO returns if the user cancels the dialog box.

On Error Resume Next Set oInbox = oSession.Inbox Set oMessages = oInbox.Messages Set oNewMsg = oMessages.Add Set oRecip = oNewMsg.Recipients.Add oRecip.Address = "SMTP:thomriz@microsoft.com" oRecip.Resolve With oNewMsg .Subject = "Hello!" .Text = "Reading your book right now!" End With oNewMsg.Send True, True Set oNewMsg = Nothing

Update Method

This method commits to storage the changes you have made to the Message object. You can also pass in parameters that will commit the changes to permanent storage as well as cause CDO to refresh its property cache. This method has the same effect as the Update method on the Folder object. The following example modifies the first message in your Inbox by adding some text to the message body, and then saves changes by calling the Update method on the Message object:

Set oInbox = oSession.Inbox Set oMessages = oInbox.Messages Set oMessage = oMessages.GetFirst("IPM.Note") oMessage.Text = "Changed by CDO" & vbLf & vbLf _ & oMessage.Text oMessage.Update

Message Object Properties

There are too many Message object properties to describe in this section, but I discuss a few key ones you will want to work with: the Attachments, Categories, DeliveryReceipt, ID, Importance, ReadReceipt, Recipients, Sender, Sensitivity, Sent, Size, Subject, Submitted, Text, TimeReceived, TimeSent, Type and Unread properties. You can read about all the properties in the CDO help file.

Two properties in particular that I won't cover have limitations you should be aware of: the Encrypted and Signed properties. Most developers assume that by setting these properties to True, CDO will sign and encrypt messages, but these properties only request that the message store or transport actually sign or encrypt the message. Today, the Exchange Server transport does not support this request. To create digitally signed and encrypted messages, you either need to use the standard Outlook message form with advanced security enabled or create a custom form in Outlook based on the standard message form. Then, the custom form will inherit the ability to be encrypted and digitally signed.

Attachments Property

Depending on whether you pass the index of a particular attachment as a parameter to the property, the Attachments property returns either the Attachments collection or a particular Attachment object on the Message object. (The Attachments collection is discussed in more detail later in this supplement.) The following example scrolls through your Inbox searching for the first message with an attachment. The example then displays the name and type of attachment.

Set oInbox = oSession.Inbox Set oMessages = oInbox.Messages Set oMessage = oMessages.GetFirst("IPM.Note") Do While oMessage.Attachments.Count = 0 Set oMessage = oMessages.GetNext Loop MsgBox "There are " & oMessage.Attachments.Count & " attachments." For Each oAttach In oMessage.Attachments MsgBox "Attachment Name: " & oAttach.Name & vbLf _ & "Attachment Type: " & oAttach.Type Next

Categories Property

The Categories property corresponds to the categories you set for a message or appointment in Outlook. This property returns a string array that corresponds to the different categories set for the message. The easiest way to parse through the array returned is to use the For...Each statement in both Visual Basic as well as Microsoft Visual Basic Scripting Edition (VBScript). The following example finds the first message in your Inbox with the categories set and displays each category in a message box:

Set oInbox = oSession.Inbox Set oMessages = oInbox.Messages Set oMessage = oMessages.GetFirst("IPM.Note") strCats = oMessage.Categories Do While IsEmpty(strCats) Set oMessage = oMessages.GetNext strCats = oMessage.Categories Loop For Each strCat In strCats strText = strText & vbLf & strCat Next MsgBox "Subject: " & oMessage.Subject & vbLf & vbLf & _ "Categories: " & strText

DeliveryReceipt and ReadReceipt Properties

Use DeliveryReceipt and ReadReceipt when you want to ensure that the message you are sending is delivered and read by the message recipients. Both of these properties can be set to a Boolean that tells CDO whether to request delivery and read receipts. You can also query this property on exisiting messages to see whether other people have requested read or delivery receipts on their messages. The following example counts the number of delivery and read receipt requested messages in your Inbox. When you run this code, you might be surprised at how many people set these types of receipts on their messages. For example, in my Inbox, I had 96 Read Receipt requests and 172 Delivery Receipt requests!

Dim intReadReceipts, intDeliverReceipts intReadReceipts = 0 intDeliverReceipts = 0 Set oInbox = oSession.Inbox Set oMessages = oInbox.Messages Set oMessage = oMessages.GetFirst("IPM.Note") Do While Not (oMessage Is Nothing) boolReadReceipt = oMessage.ReadReceipt boolDeliverReceipt = oMessage.DeliveryReceipt If boolReadReceipt = True Then intReadReceipts = intReadReceipts + 1 End If If boolDeliverReceipt = True Then intDeliverReceipts = intDeliverReceipts + 1 End If Set oMessage = oMessages.GetNext Loop MsgBox "There were " & intReadReceipts & " Read " _ & "Receipt requested messages in your inbox." _ & vbLf & "There were " & intDeliverReceipts & "" _ & " Delivery Receipt requested messages in your " _ & "inbox."

ID Property

The ID property returns the unique identifier for the current message. You can store this identifier and then use it later with the GetMessage method of the Session object to quicky retrieve the message in a new session.

Importance Property

By using this property, you can set the importance for your Message object. You can set this property to three constants: CdoLow(0), CdoNormal(1), or CdoHigh(2). The following code example counts the different importance levels of messages in your Inbox and returns that count to you.

Dim intLow, intNormal, intHigh intLow = 0 intNormal = 0 intHigh = 0 Set oInbox = oSession.Inbox Set oMessages = oInbox.Messages Set oMessage = oMessages.GetFirst("IPM.Note") Do While Not (oMessage Is Nothing) intImportance = oMessage.Importance Select Case intImportance Case 0 intLow = intLow + 1 Case 1 intNormal = intNormal + 1 Case 2 intHigh = intHigh + 1 End Select Set oMessage = oMessages.GetNext Loop MsgBox "There were " & intLow & " Low " _ & "Priority messages." & vbLf _ & "There were " & intNormal & "" _ & " Normal Priority messages." & vbLf _ & "There were " & intHigh & "" _ & " High Priority messages."

Recipients Property

The Recipients property returns either a Recipients collection or a single Recipient object depending on whether you pass in the index for a particular recipient to it. (The Recipients collection and individual objects are discussed later in this supplement.) The following code sample grabs the first message in your Inbox and displays all the recipients on the message using a For...Each statement:

Set oInbox = oSession.Inbox Set oMessages = oInbox.Messages Set oMessage = oMessages.GetFirst("IPM.Note") Set oRecips = oMessage.Recipients For Each oRecip In oRecips MsgBox "Name: " & oRecip.Name & vbLf & "Address: " & _ oRecip.Address Next

Sender Property

The Sender property returns or sets the sender of a message as an AddressEntry object. When composing a message, you can call this property twice to create a message that is sent on behalf of a specific user. To do this, set Sender to the originating sender you want identified on the message. Then set Sender to the user who will send the message on behalf of the original sender. When you set this property the second time, the second person you set this property to is displayed as the sender of the message on behalf of the first user.

The user who originally sent the item can be retrieved by using specific properties on the message's Fields collection. The following example shows you how to retrieve the Sender property and also the properties for the original sender. Note that if the message was not sent on behalf of another user, the sender and original sender properties will contain the same information. The property values that are pulled from the Message object.

Set oInbox = oSession.Inbox Set oMessages = oInbox.Messages Set oMessage = oMessages.GetFirst("IPM.Note") MsgBox oMessage.Subject & vbLF & oMessage.Text If oMessage.Sender.Name = oMessage.Fields(&H42001E) Then MsgBox "No On Behalf Of Yet!" End If MsgBox "Message Sender Property: " & oMessage.Sender MsgBox "PR_SENDER_NAME: " & oMessage.Fields(&HC1A001E) MsgBox "PR_SENT_REPRESENTING_NAME: " & oMessage.Fields(&H42001E) MsgBox "PR_SENT_REPRESENTING_EMAIL: " & oMessage.Fields(&H65001E)

Sensitivity Property

The Sensitivity property sets the sensitivity of the message. You can set this property to four different values: CdoNoSensitivity(0, default), CdoPersonal(1), CdoPrivate(2), CdoConfidential(3). The private value allows you to stop the user from modifying the original text in the message when the user forwards the item in Outlook. CDO applications, however, can modify the text of the item even when forwarding the item. The following example changes the sensitivity of the first message in your Inbox to private. When you try to forward the message in Outlook for which you've changed the text, you should get a dialog box telling you that you cannot change the text.

Set oInbox = oSession.Inbox Set oMessages = oInbox.Messages Set oMessage = oMessages.GetFirst("IPM.Note") MsgBox oMessage.Subject & vbLF & oMessage.Text MsgBox "Current Sensitivity: " & oMessage.Sensitivity oMessage.Sensitivity = 2 oMessage.Update True, True

Size Property

The Size property returns an approximation of the number of bytes the message and all of its attachments take up in the message store. This property is read-only and useful if you want to quickly determine the total size of a folder, either in a program or in a custom agent. The following example adds up all the message sizes in your Inbox and displays the total size:

Dim longTotal As Long Set oInbox = oSession.Inbox Set oMessages = oInbox.Messages Set oMessage = oMessages.GetFirst("IPM.Note") Do While Not (oMessage Is Nothing) longTotal = longTotal + oMessage.Size Set oMessage = oMessages.GetNext Loop MsgBox "Total bytes in Inbox: " & longTotal

Subject and Text Properties

The Subject and Text properties let you set the subject and body, respectively, of the Message object. Both are read/write. You should note however, that the Text property can contain only unformatted text, and the size of the Text property can be limited depending on which programming tool you are using and the length of string variables it supports. For an example of using these two properties, look at the example for the Sensitivity property.

Type Property

The Type property is a string that identifies the message class of the object. You can set this property at the time you create the new message, or you can set it on messages that are already in a folder to change their message class. CDO does not check to make sure that the message class you assign to the object has a corresponding custom form in the forms library. Therefore, be sure that you have published your form in a forms library before creating new messages or changing existing ones to have a custom message class. The following example creates a new item in your Inbox. This item is actually a contact item, and it is created by setting the message class of the item to IPM.Contact. The Send method in the example brings up the item and, if you have Outlook on your machine, you will see a contact item appear. You can use this property with web forms as well. All you need to do is set the message class to be the message class of your custom web form.

On Error Resume Next Set oInbox = oSession.Inbox Set oMessages = oInbox.Messages Set oNewMsg = oMessages.Add oNewMsg.Text = "This is a new contact" oNewMsg.Type = "IPM.Contact" oNewMsg.Send , True Set oNewMsg = Nothing

Sent, Submitted, TimeReceived, TimeSent, Unread Properties

These properties are lumped together because you need to set all of them to successfully submit a message to a Public Folder. To create a new message in a Public Folder, use the Add method on the Messages collection for the Public Folder. Then set the desired default properties for the message, such as Subject or Text, or any custom properties you have on the item. Before calling the Update method, you need to set the Sent, Submitted, TimeReceived, TimeSent, and Unread properties. The example shows you what you should set these values to when submitting a message into a Public Folder:

'Assuming oPF contains a valid PF Folder Set oMessages = oPF.Messages Set oNewMsg = oMessages.Add oNewMsg.Subject = "My Subject" oNewMsg.Text = "My Text" 'You can also set the sender using the Sender property oNewMsg.Sender = oSession.CurrentUser 'Set the properties for a PF message oNewMsg.TimeReceived = Now oNewMsg.TimeSent = Now oNewMsg.Sent = True oNewMsg.Unread = True oNewMsg.Submitted = False oNewMsg.Update

AppointmentItem Object

The AppointmentItem object represents a calendar appointment in a calendar folder. The AppointmentItem object is a subclass of the Message object, so the AppointmentItem object inherits most of the properties and methods of the Message object with the exception of the Forward, Reply, and ReplyAll methods. Since the Message and AppointmentItem objects are so closely related, we will look at the AppointmentItem object in this section before discussing message filters and the other objects in the CDO library. Because the MeetingItem object is so closely related to the Message object as a subclass of it, we will discuss it as well.

The AppointmentItem object is distinguished from a Message object by its Type property, which contains IPM.Appointment rather than IPM.Note. You can create new AppointmentItem objects by using the Add method of the calendar folder Messages collection, as shown in this example:

'0 is the value for the constant CdoDefaultFolderCalendar Set oCalendar = oSession.GetDefaultFolder(CdoDefaultFolderCalendar) Set oAppts = oCalendar.Messages Set oNewAppt = oAppts.Add oNewAppt.Subject = "Meet with Paul Miller" oNewAppt.Text = "This is to discuss the new budget." oNewAppt.StartTime = #6/9/98 4:30:00 PM# oNewAppt.EndTime = #6/9/98 5:30:00 PM# oNewAppt.Update

AppointmentItem Object Methods

The following section describes, in detail, the methods and properties on the AppointmentItem object.

GetRecurrencePattern and ClearRecurrencePattern Methods

As you saw in the preceding example, creating appointments is easy. Most of the work involved in creating a new appointment is setting the properties correctly on the appointment. The real power of calendaring in the CDO library comes from the ability to programmatically create recurring appointments. The GetRecurrencePattern method helps you create recurring appointments by returning a RecurrencePattern object that you can set up with the correct recurrences for your appointment. If the appointment was not recurring before you call this method, CDO makes the appointment recurring and sets the IsRecurring property to True. For this reason, you should not use this method to test for recurrence. Instead, you should use the IsRecurring property, which you learn about later. To remove the recurrence from an appointment, use the ClearRecurrencePattern method, which also resets the IsRecurring property to False.

When creating recurrences by using the RecurrencePattern object returned by the GetRecurrencePattern method, you normally first set the property on the RecurrencePattern object. The RecurrenceType property determines the basic time unit for recurrence of the appointment. This time unit can be a day, a week, a month or a year. You can also set exceptions to the pattern so that the appointment occurs only during certain times, even in the basic time unit, by using either the Instance property or the Interval property.

The DayOfMonth, DayOfWeek, and MonthOfYear properties on the RecurrencePattern object allow you to specify which days or months the appointment should recur. Similarly, the StartTime and EndTime properties determine the time for the recurrence. To specify the overall time period for the recurrence, such as "start the pattern today" and "end the pattern next year," you would set the PatternStartDate, PatternEndDate, Occurrences, and NoEndDate properties accordingly. A number of the previous properties are interdependent. For example, if you change the PatternEndDate to an earlier date, CDO automatically recalculates the Occurrence property.

The best way to learn to use the properties of the RecurrencePattern object is to take a look at a number of examples. The first example sets the properties on the RecurrencePattern object so that a new recurrence is created—in this case, the appointment recurs every week on Tuesday for the next three months from the current date.

Set oCalendar = oSession.GetDefaultFolder(CdoDefaultFolderCalendar) Set oAppts = oCalendar.Messages Set oNewAppt = oAppts.Add oNewAppt.Subject = "Tuesday Meetings" oNewAppt.StartTime = #6/9/98 4:30:00 PM# oNewAppt.EndTime = #6/9/98 5:30:00 PM# oNewAppt.Location = "My Office" Set oNewApptRecur = oNewAppt.GetRecurrencePattern 'CdoRecurTypeWeekly constant is 1 oNewApptRecur.RecurrenceType = CdoRecurTypeWeekly 'CdoTueday constant is 4 and CdoThursday constant is 16 oNewApptRecur.DayOfWeekMask = CdoTuesday + CdoThursday oNewApptRecur.NoEndDate = False dateEndDate = DateAdd("m", 3, oNewApptRecur.PatternStartDate) oNewApptRecur.PatternEndDate = dateEndDate oNewAppt.Update

The next example uses the Instance property in conjunction with the RecurrenceType property set to CdoRecurTypeMonthlyNth(3) to create a recurrence pattern of every second Thursday of the month. This example also shows you how you can set the NoEndDate property to make the appointment recur indefinitely. (Actually, the appointment will recur to the year 4001!)

'0 is the value for the constant CdoDefaultFolderCalendar Set oCalendar = oSession.GetDefaultFolder(CdoDefaultFolderCalendar) Set oAppts = oCalendar.Messages Set oNewAppt = oAppts.Add oNewAppt.Subject = "2nd Thursday Meetings" oNewAppt.StartTime = #4:30:00 PM# oNewAppt.EndTime = #5:30:00 PM# oNewAppt.Location = "My Office" Set oNewApptRecur = oNewAppt.GetRecurrencePattern 'CdoRecurTypeMonthlyNth constant is 3 oNewApptRecur.RecurrenceType = CdoRecurTypeMonthlyNth 'CdoThursday constant is 16 oNewApptRecur.DayOfWeekMask = CdoThursday oNewApptRecur.NoEndDate = True 'Set the Instance to be the 2nd Thursday oNewApptRecur.Instance = 2 oNewAppt.Update

The final example shows you how you can use the Interval property to make the appointment reoccur every other time unit or every couple of time units. The appointment in this example occurs on Mondays, every other week, for the next year:

'0 is the value for the constant CdoDefaultFolderCalendar Set oCalendar = oSession.GetDefaultFolder(CdoDefaultFolderCalendar) Set oAppts = oCalendar.Messages Set oNewAppt = oAppts.Add oNewAppt.Subject = "Every other Monday Meetings" oNewAppt.StartTime = #6/9/98 4:30:00 PM# oNewAppt.EndTime = #6/9/98 5:30:00 PM# oNewAppt.Location = "My Office" Set oNewApptRecur = oNewAppt.GetRecurrencePattern 'CdoRecurTypeWeekly constant is 99 oNewApptRecur.RecurrenceType = CdoRecurTypeWeekly 'CdoMonday is constant 2 oNewApptRecur.DayOfWeekMask = CdoMonday oNewApptRecur.NoEndDate = False dateEndDate = DateAdd("yyyy", 1, oNewApptRecur.PatternStartDate) oNewApptRecur.PatternEndDate = dateEndDate 'Set the interval to be every other week oNewApptRecur.Interval = 2 oNewAppt.Update

Respond Method

The Respond method returns a MeetingItem object to enable a response to a meeting request. This method takes one parameter, which is your response to the meeting request. This parameter can have three possible values: CdoResponseTenative(2), CdoResponseAccepted(3), and CdoResponseDeclined(4). To send your response, you must call the Send method on the MeetingItem object, which is returned by the Respond method. The following example finds the first MeetingItem object in your Inbox and responds to it by accepting the meeting request using the the Respond method:

Set oInbox = oSession.Inbox Set oMessages = oInbox.Messages Set oMtgRequest = oMessages.GetFirst("IPM.Schedule.Meeting.Request") 'Get Associated Appointment Set oAppt = oMtgRequest.GetAssociatedAppointment 'Check to see if it's recurring. If it is, 'we will not be able to use the Appt's start and 'end time but rather the recurring start and end time, 'because both are disabled on the appt object. If oAppt.IsRecurring Then Set oRecur = oAppt.GetRecurrencePattern MsgBox "This appointment starts at " & oRecur.StartTime _ & " and ends at " & oRecur.EndTime & vbLf _ & " at the following location: " & oAppt.Location _ & vbLf & vbLf & "It is a recurring meeting!" Else 'Not recurring MsgBox "This appointment starts at " & oAppt.StartTime _ & " and ends at " & oAppt.EndTime & vbLf _ & " at the following location: " & oAppt.Location End If 'Accept the meeting request Set oResponse = oMtgRequest.Respond(3) oResponse.Text = "I'll attend." OResponse.ReplyTime = Now oResponse.Send

AppointmentItem Object Properties

As a subclass of the Message object, the AppointmentItem object inherits all properties of a Message object, so you can set the Subject property or retrieve the ID property of an AppointmentItem object by using the object's inherited Message object properties. The following section describes the properties other than inherited that the AppointmentItem object contains:

AllDayEvent Property

The AllDayEvent property allows you to get or set the appointment as either an all-day event or a multiple-day event. This property returns True if the appointment takes up one or more days without any free blocks. It returns False if the duration of the appointment is not a multiple of 24 hours and the item is limited to a specified start time.

The AllDayEvent property is read/write, but if you change it from False to True, CDO will automatically reset the StartTime property for the appointment to midnight preceding the current start time, and the EndTime property to midnight following the current end time. You can change those StartTime and EndTime properties, but you must make sure their values differ by a nonzero multiple of 24 hours. For example, you could not change the StartTime of the appointment to 12:30 PM and the EndTime of the appointment to 4:45 PM the next day, because the time interval is not a nonzero multiple of 24 hours. If you do not follow the rule for changing the start and end times, CDO will return a CdoE_INVALID_OBJECT when you call the Update method.

The following example creates a new appointment in your calendar. It sets the AllDayEvent property to True and changes the EndTime property to make the appointment a two-day event.

Set oCalendar = oSession.GetDefaultFolder(CdoDefaultCalendarFolder) Set oAppts = oCalendar.Messages Set oAppt = oAppts.Add oAppt.StartTime = Date oAppt.AllDayEvent = True oAppt.EndTime = DateAdd("d", 2, Date) oAppt.Subject = "2 day all day meeting" oAppt.Location = "Your office" oAppt.Text = "This meeting discusses developing apps" oAppt.Update

BusyStatus Property

The BusyStatus property returns or sets the status of the current user for the appointment. This property can return or be set to four values: CdoFree(0), CdoTenative(1), CdoBusy(2), and CdoOutOfOffice(3). If CDO finds two or more conflicting appointments in your calendar, the highest value (meaning the most restrictive value) is returned. For example, if for a specific appointment you have marked the appointment as CdoTenative, but you also have another appointment that conflicts with that appointment and it is marked CdoOutOfOffice, when you query CDO for the timeslot for the first appointment, CDO will return CdoOutOfOffice.

The following example retrieves the first appointment in your calendar, and it displays the value in the BusyStatus property in a message box with the name of the appointment:

Set oCalendar = oSession.GetDefaultFolder(CdoDefaultCalendarFolder) Set oAppts = oCalendar.Messages Set oAppt = oAppts.GetFirst Select Case oAppt.BusyStatus Case 0: strStatus = "Free" Case 1: strStatus = "Tenative" Case 2: strStatus = "Busy" Case 3: strStatus = "Out of the Office" End Select MsgBox "For the " & oAppt.Subject & " appointment, the time " _ & "on your calendar is marked as " & strStatus & "."

Duration, StartTime, EndTime Properties

The Duration, StartTime, and EndTime properties are lumped together here because they all have an impact on one another. By setting the StartTime and EndTime properties, which take a date and a time, CDO automatically calculates the Duration property for you. You cannot modify the Duration property directly and the Duration is always returned in minutes. You can change the minutes to hours or days by using the methods that the programming language you are using provides for manipulating time.

CDO saves the StartTime and EndTime properties in UTC or GMT format. This means that the appointment will appear to be at different times according to the time zone of the user who is viewing the appointment. The following example sets the time zone for the user to be CdoTmzEastern(10) and displays the start and end times for the appointment. The example then sets the time zone for the user to be CdoTmzPacific(13) and displays the start and end times for the appointment. The difference between the two time zones is that the CdoTmzPacific time zone is 3 hours earlier than the CdoTmzEastern time zone, so an 11:00 AM appointment in the CdoTmzEastern time zone appears as an 8:00 AM appointment in the CdoTmzPacific time zone.

Set oCalendar = oSession.GetDefaultFolder(CdoDefaultCalendarFolder) Set oAppts = oCalendar.Messages Set oAppt = oAppts.GetFirst oSession.SetOption "TimeZone", 10 MsgBox "For the " & oAppt.Subject & " appointment in the Eastern" _ & " timezone the start time is: " & oAppt.StartTime & vbLf _ & "The end time is: " & oAppt.EndTime & vbLf _ & "The duration of the appointment is: " & oAppt.Duration _ & " minutes." oSession.SetOption "TimeZone", 13 MsgBox "For the " & oAppt.Subject & " appointment in the Pacific" _ & " timezone the start time is: " & oAppt.StartTime & vbLf _ & "The end time is: " & oAppt.EndTime & vbLf _ & "The duration of the appointment is: " & oAppt.Duration _ & " minutes."

IsRecurring Property

The IsRecurring property returns a value indicating whether or not an appointment is a recurring appointment. When the property returns True, the appointment is recurring; False means the appointment is not recurring. For an example of using the IsRecurring property, see the example in the " Respond Method" section.

Location Property

The Location property allows you to set or get the location for the appointment. It is a free-form string. For an example of how to use this property, see the AllDayEvent property example.

MeetingStatus and MeetingResponseStatus Properties

The MeetingStatus and MeetingResponseStatus properties work together to help you create and track meeting requests and responses. The MeetingStatus property returns or sets the meeting status for the appointment. This property can have four possible values: CdoNonMeeting(0), CdoMeeting(1), CdoMeetingCanceled(5), or CdoMeetingReceived(3). To turn a regular appointment into a meeting, you need to set the MeetingStatus property on the appointment to CdoMeeting, and then add recipients to the message. CDO will create a MeetingItem object, set you as the organizer of the meeting by placing you in the Organizer property, and send a meeting request to all the recipients. Note that when you add add recipients to your meeting requests, you should avoid adding distribution lists to your meeting requests because CDO cannot track the individual response of distribution list members. The sample code for this property shows you how to detect distribution lists and send the request to each member in the list individually.

If you decide at a later point to cancel your meeting, set the MeetingStatus property to CdoMeetingCanceled, and send the item to all the recipients. Release the underlying AppointmentItem object by using the Set statement and assigning the object to Nothing. If you are using Outlook as your calendar, you need to perform one extra step: after releasing the AppointmentItem object using the Set statement, use the Delete method to delete the item from the Messages collection. If you do not do this, you might get unexpected results when working with the folder.

The MeetingResponseStatus property returns or sets the status of this particular appoinment for the current user. The property can have five possible values: CdoResponseNone(0), CdoResponseOrganized(1) (Schedule+ only), CdoResponseTenative(2), CdoResponseAccepted(3), and CdoResponseDeclined(4). This property is also available on the Recipient object so that you can pull the same information for all the recipients of a particular meeting request.

The following example creates a new meeting request by setting the MeetingStatus property to CdoMeeting. It then sets the properties for the meeting and adds recipients to the meeting by finding the first distribution list in your Global Address List. It expands the members of the distribution list and sends a meeting request to each member, which enables you to use the MeetingResponseStatus property to check the reponse status for each member in the distribution list.

Set oCalendar = oSession.GetDefaultFolder(CdoDefaultCalendarFolder) Set oAppts = oCalendar.Messages Set oAppt = oAppts.Add oAppt.StartTime = Now oAppt.EndTime = DateAdd("h", 2, Now) oAppt.Subject = "2 hour meeting" oAppt.Location = "Your office" oAppt.Text = "This meeting discusses AppointmentItem objects" oAppt.MeetingStatus = CdoMeeting Set oGAL = oSession.GetAddressList(CdoAddressListGAL) Set oGALAEs = oGAL.AddressEntries Foundone = 0 Set oGALAE = oGALAEs.GetFirst Do While Not (oGALAE Is Nothing) If oGALAE.DisplayType = CdoDistList Then Exit Do End If Set oGALAE = oGALAEs.GetNext Loop If Not (oGALAE Is Nothing) Then For Each oDLMember In oGALAE.Members oAppt.Recipients.Add oDLMember Next End If oAppt.Send

Organizer Property

The Organizer property is set automatically when you create an AppointmentItem object. It cannot be changed after the appointment is saved. When you retrieve this property, it returns an AddressEntry object that corresponds to the person who created the AppointmentItem object. The following example displays the information about the organizer of the first appointment in your calendar by using the Organizer property:

Set oCalendar = oSession.GetDefaultFolder(CdoDefaultCalendarFolder) Set oAppts = oCalendar.Messages Set oAppt = oAppts.GetFirst MsgBox "Subject: " & oAppt.Subject & vbLf & "Location: " _ & oAppt.Location & vbLf & "Organizer Name: " _ & oAppt.Organizer.Name & vbLf & "Organizer Address:" _ & oAppt.Organizer.Address

ReminderSet and ReminderMinutesBeforeStart Properties

The ReminderSet and ReminderMinutesBeforeStart properties allow you to set the reminder for your appointments or meetings. The ReminderSet property must be True before you can set the ReminderMinutesBeforeStart property. The ReminderSet property returns whether a reminder is set for the current appointment. Please note that Outlook Web Access does not support reminders, so whatever values you set here will be retained when a user looks at an appointment in Outlook Web Access (OWA), but the reminder will not pop up as it does in Outlook.

Once the ReminderSet property is set to True, you can set the ReminderMinutesBeforeStart property to the number of minutes before the appointment that you want the reminder to appear. The following example creates a new appointment in your calendar and sets the reminder at 30 minutes before the start of the appointment:

Set oCalendar = oSession.GetDefaultFolder(CdoDefaultCalendarFolder) Set oAppts = oCalendar.Messages Set oAppt = oAppts.Add oAppt.StartTime = DateAdd("d", 1, Now) oAppt.EndTime = DateAdd("h", 2, oAppt.StartTime) oAppt.Subject = "30 minute reminder" oAppt.Location = "Your office" oAppt.Text = "Let's discuss CDO" oAppt.ReminderSet = True oAppt.ReminderMinutesBeforeStart = 30 oAppt.Update

ReplyTime Property

The ReplyTime property returns or sets the date and time that a recipient replied to a meeting request. This property is not useful if you call it on an item in the calendar of the meeting organizer. Instead call this method on the calendar, appointments, or responses from other recipients. Please note that CDO does not currently set the ReplyTime property when you call the Respond method on a meeting request. This means that you must set this property when responding to meeting requests, as shown in the code example for the Respond method.

ResponseRequested Property

The ResponseRequested property contains a Boolean that determines whether the organizer of a meeting wants responses from recipients. This property defaults to True for new meetings. The following sample creates a meeting request that does not ask the user to respond to the request:

Set oCalendar = oSession.GetDefaultFolder(CdoDefaultCalendarFolder) Set oAppts = oCalendar.Messages Set oAppt = oAppts.Add oAppt.StartTime = DateAdd("d", 1, Now) oAppt.EndTime = DateAdd("h", 2, oAppt.StartTime) oAppt.Subject = "No Response Requested" oAppt.Location = "Your office" oAppt.Text = "Let's discuss CDO Calendaring" oAppt.MeetingStatus = CdoMeeting Set oRecip = oAppt.Recipients.Add oRecip.Address = "SMTP:thomriz@microsoft.com" oRecip.Resolve oAppt.ResponseRequested = False oAppt.Send

MeetingItem Object

The MeetingItem object rounds out the calendaring portions of CDO. Similar to the AppointmentItem object, the MeetingItem object is a subclass of the Message object, so it exposes the same properties and methods as the Message object in addition to its own unique properties and methods. The message class for a MeetingItem object is IPM.Schedule.Meeting.Request, which is contained in its Type property. The way you create MeetingItem objects is by setting the MeetingStatus property of an AppointmentItem object to CdoMeeting.

MeetingItem Methods

The following section describes the methods and properties unique to the MeetingItem object, and it explains when you should use them.

GetAssociatedAppointment Method

The GetAssociatedAppointment method returns an AppointmentItem object that is associated with the MeetingItem. For the receipient of a meeting request, the AppointmentItem object returned by the GetAssociatedAppointment method is actually not the original appointment. Instead, the object returned is the user's individual copy of the appointment for the user's calendar. The AppointmentItem object returned to the user does contain all of the same properties of the original AppointmentItem object, and the user can modify them. If the organizer of the meeting sends an update, however, the changes made to the AppointmentItem object in the user's calendar will be lost. The following example finds the first meeting request in the user's Inbox and uses this method to return the corresponding AppointmentItem. The code then displays information from the AppointmentItem.

Set oInbox = oSession.Inbox Set oMessages = oInbox.Messages Set oMtgRequest = oMessages.GetFirst("IPM.Schedule.Meeting.Request") 'Get Associated Appointment Set oAppt = oMtgRequest.GetAssociatedAppointment MsgBox "The first meeting request is called: " & oAppt.Subject

Respond Method

You might be wondering why the MeetingItem and AppointmentItem objects both have a Respond method that performs the same function. Well, the two Respond methods do produce the same results. CDO just provides this shortcut so that you do not have to call the GetAssociatedAppointment method and then the Respond method. Note that even though in Outlook you can set up alternate recipients for meeting requests so that a delegate can receive the meeting requests of a different user, CDO does not currently support this functionality. Users can view the MeetingItem objects that they receive as a delegate but they cannot respond to them. To see the Respond method in action, see code sample in the Respond method section under the AppointmentItem object section.

MeetingItem Properties

The MeetingItem property has only one distinct property, the MeetingType property.

MeetingType Property

The MeetingType property returns the type of meeting for the MeetingItem object. The return value can have two possible values: CdoMeetingRequest(1) or CdoMeetingResponse(2). The following example reports how many meeting requests and meeting responses a user has in his Inbox using the MeetingType property:

Set oInbox = oSession.Inbox Set oMessages = oInbox.Messages Set oMtgRequest = oMessages.GetFirst intResponses = 0 intRequests = 0 Do While Not (oMtgRequest Is Nothing) If InStr(1, oMtgRequest.Type, "IPM.Schedule.Meeting") <> 0 Then Select Case oMtgRequest.MeetingType Case 1: intRequests = intRequests + 1 Case 2: intResponses = intResponses + 1 End Select End If Set oMtgRequest = oMessages.GetNext Loop MsgBox "There are " & intRequests & " meeting requests " _ & "and " & intResponses & " meeting responses in your inbox.".

MessageFilter Object

Now that we have covered the major properties of the Message object and its subclass objects, a discussion of the MessageFilter object will have more context. When building applications, you might want to display a subset of items based on a certain criteria, such as the type of item or the sender of the item. For example, in a helpdesk application, you might want to filter out all items displayed on a web page based on which technician is viewing the web page. CDO allows you to perform these types of filters using the MessageFilter object. Before diving into the details of the MessageFilter object, you should know that when you instantiate a Messages collection, a filter is automatically created for that collection. However, you can't specify certain criteria on that filter, so the collection will contain all the items. By using the MessageFilter object, you can set the properties for your filter, as described in the following section.

Also be aware that the filter you set on a Messages collection is persistent until you either release the Messages collection or explicitly set the Filter property of the Messages collection you are working with to Nothing—for example, Set oMessages.Filter = Nothing.

Once you set a message filter on a Messages collection, you should then use the For...Each or For...Next statements to actually go through the filtered collection. Then use the standard Message object properties and methods on the filtered messages.

NOTE


CDO does not support filtering on all the properties of calendar items. You can filter only on the start and end dates of calendar items. This will be discussed in the next section.

MessageFilter Object Properties

The MessageFilter object contains mostly properties. It does contain one method, IsSameAs, but you will probably never use this method in your applications. The following section outlines the specific properties on the MessageFilter object and shows you how to use them to create simple and complex filters on both your mail items and calendar items.

Or, Not Properties

The Or and Not properties are important to creating message filters because they allow you to change the behavior of a filter to meet the needs of your application. For example, the Or property, when set to True, will tell CDO to "OR" the properties that you set for the filter. Imagine you set the Or property to True, the Sender property to "Thomas Rizzo," and the Subject property to "CDO". CDO will display only the messages that are either from Thomas Rizzo or have a subject that contains CDO. If you set the Or property to False with the same restrictions on the filter, CDO will display messages from Thomas Rizzo that have CDO somewhere in their subjects.

The Not property tells CDO whether to negate the restriction values or leave them unchanged. When you set the Not property to True, CDO negates the value of each restriction set on the filter. Consider the example in preceding paragraph. If you set the Not property on the filter to True and the Or property to False, CDO would return messages that were not from Thomas Rizzo and did not have CDO in their subjects. The following example changes the filter on your Inbox a number of different ways by using the Or and Not properties, and it displays the various results:

Set oInbox = oSession.Inbox Set oMessages = oInbox.Messages 'Reset the message filter Set oMessages.Filter = Nothing Set oMsgFilter = oMessages.Filter oMsgFilter.Subject = "CDO" oMsgFilter.Sender = "Doug Wood (Exchange)" oMsgFilter.Or = False 'This enables "AND" on the conditions MsgBox "Number of messages: " & oMessages.Count 'The above statement returns 16 messages in my inbox oMsgFilter.Or = True 'This enables "OR" on the conditions MsgBox "Number of messages: " & oMessages.Count 'The above statement returns 235 messages in my inbox 'Reset the filter Set oMessages.Filter = Nothing Set oMsgFilter = oMessages.Filter oMsgFilter.Not = True oMsgFilter.Subject = "CDO" 'This negates the conditions such as 'only messages where CDO does not appear anywhere 'in the subject MsgBox "Number of messages: " & oMessages.Count 'The above statement returns 4070 messages in my Inbox

Properties

The Conversation, Sender, Sent, Importance, Size, Subject, Text, Type, and Unread properties, which were covered in detail on the Message object, are available as filter properties. For all the filter properties that take strings, such as the Text, Sender, Conversation, and Subject properties, CDO will search for the string you set in the filter property as a substring in the property of a message. This means that you do not have to specify the filter properties exactly as they appear on the messages you want to find. The Type property is the only exception to this rule. With the Type propety, filter using a string that identifies a specific message class.

For the Size property, CDO will only return messages whose size, in bytes, is greater than the value you set in the property. The Sent property is a Boolean that designates a filter which, when set to True, returns only messages sent through a MAPI system. Messages posted into a Public Folder or saved in a folder would not be returned by CDO. You can set the Importance property to the three different values that CDO provides: CdoHigh, CdoNormal, and CdoLow. The following example shows some of these properties set on a MessageFilter object:

Set oInbox = oSession.Inbox Set oMessages = oInbox.Messages 'Reset the message filter Set oMessages.Filter = Nothing Set oMsgFilter = oMessages.Filter oMsgFilter.Subject = "CDO" oMsgFilter.Sender = "Doug Wood (Exchange)" oMsgFilter.Conversation = "CDO" oMsgFilter.Type = "IPM.Note" oMsgFilter.Unread = False oMsgFilter.Size = 3 'Bytes oMsgFilter.Sent = True oMsgFilter.Subject = "CDO" oMsgFilter.Text = "CDO" MsgBox "Number of messages: " & oMessages.Count 'The above statement returns one message in my Inbox

TimeFirst, TimeLast Properties

The TimeFirst and TimeLast properties can be set to date and time criteria for your filter. For the TimeFirst property, a message must have been received on or since the specified date/time. For the TimeLast property, a message must have been received on or before the date/time. If you do not set either of these properties, CDO will return all messages in the folder. The following example shows you different ways to use these properties to filter messages from the collection:

Set oInbox = oSession.Inbox Set oMessages = oInbox.Messages 'Reset the message filter Set oMessages.Filter = Nothing Set oMsgFilter = oMessages.Filter oMsgFilter.TimeLast = DateAdd("d", -2, Now) 'Return only message received at least two days ago MsgBox "Number of messages: " & oMessages.Count 'In my Inbox, this returns 3795 messages 'Reset the TimeLast property to Now oMsgFilter.TimeLast = Now oMsgFilter.TimeFirst = DateAdd("d", -2, Now) 'Return only messages received in the last two days MsgBox "Number of messages: " & oMessages.Count 'In my Inbox, this returns 456 messages. 'Reset the filter to get rid of the TimeFirst property. Set oMessages.Filter = Nothing Set oMsgFilter = oMessages.Filter oMsgFilter.TimeFirst = #6/1/98 4:30:00 PM# oMsgFilter.TimeLast = #6/1/98 6:30:00 PM# 'Return the messages delivered before 4:30 and 6:30 on 6/1/98 MsgBox "Number of messages: " & oMessages.Count 'In my Inbox, this returns two messages

Fields Property

The Fields property is provided on the MessageFilter object so that you can filter on other types of properties that might not be provided as a default property. By passing either the name or the identifier of the property that you want to add to the filter, you can create a custom filter for these other properties. For example, by using the Add method of the Fields collection, which is returned by the Fields property, you can filter out all the messages with one or more attachments. To make CDO filter only messages that have attachments, you pass the identifier for PR_HASATTCH (&HE1B000B) and the value you want for this property—True—to the Add method of the Fields collection. This process is shown in the following code sample:

Set oInbox = oSession.Inbox Set oMessages = oInbox.Messages 'Reset the message filter Set oMessages.Filter = Nothing Set oMsgFilter = oMessages.Filter Set oFiltProp = oMsgFilter.Fields.Add(&HE1B000B, True) 'Return only messages with attachments MsgBox "Number of messages: " & oMessages.Count

However, you do not have to limit your filters to using just built-in properties. You can also use custom properties on your messages and have CDO filter based on your custom properties. For this, you need to pass the name, class, and value for your custom property. For example, to filter only messages that contain the word Test in a custom property named New Property, you would call the Add method on the Fields collection and pass in the name New Property, the class, the value 8 for a string, and the value you want to filter on, Test. CDO implements the filter for you, as shown in the following example:

Set oInbox = oSession.Inbox Set oMessages = oInbox.Messages 'Reset the message filter Set oMessages.Filter = Nothing Set oMsgFilter = oMessages.Filter Set oFiltProp = oMsgFilter.Fields.Add("New Property", 8, "Test") 'Return only messages that contain Test in the New Property field MsgBox "Number of messages: " & oMessages.Count

Recipients Property

The Recipients property allows you to pass a string to the filter specifying that at least one recipient name must contain the string. You do not have to pass an exact match for the string. CDO will use your string and search for substrings in recipient names. The following example searches for any messages where Tom is a substring in at least one of the recipient names:

Set oInbox = oSession.Inbox Set oMessages = oInbox.Messages 'Reset the message filter Set oMessages.Filter = Nothing Set oMsgFilter = oMessages.Filter oMsgFilter.Recipients = "Tom" 'Return only messages that contain Tom in a Recipient MsgBox "Number of messages: " & oMessages.Count

Filtering AppointmentItem Objects

CDO does not support all of the functionality described in the preceding sections, which will affect your code if the Messages collection you are attempting to filter contains AppointmentItem objects. You can filter only collections that contain AppointmentItem objects on the start time and end time of the appointments. To pass the desired restrictions for the start and end times, you must use the Fields collection of the MessageFilter object. The two values you should add to the Fields collection are CdoPR_START_DATE (&H00600040) and Cdo_PR_END_DATE (&H00610040). For any date value you set the start date to, CDO will pass you appointments that have a starting date on or earlier than that date. For the end date, CDO will pass you appointments that have an ending date on or after the date you pass.

The following example will make this all clearer. If you want to filter out all the appointments for a specific month, January, you would pass 1/31/1999 as the Cdo_PR_START_DATE value. CDO would then pass all appointments that begin on or earlier than January 1999. Because you do not want December or November 1998 appointments, you must pass 1/1/1999 as the Cdo_PR_END_DATE value. Doing so tells CDO to pass only appointments that end on or after January 1, 1999. By combining these two properties, you can see that CDO will contain in the Messages collection only the appointments that occur in January 1999. The following code illustrates this example:

Set oCalendar = oSession.GetDefaultFolder(CdoDefaultCalendarFolder) Set oAppts = oCalendar.Messages 'Reset the message filter Set oAppts.Filter = Nothing Set oApptFilter = oAppts.Filter oApptFilter.Fields(&H600040) = #1/31/99# oApptFilter.Fields(&H610040) = #1/1/99# 'Return only appointments in January, 1999 MsgBox "Number of Appointments: " & oAppts.Count

There might be times when you want to filter on more properties for AppointmentItem objects. While you cannot use the MessageFilter object to do this, you can write custom code. The Calendar of Events sample in Chapter 11 explains how to implement advanced filtering of AppointmentItem objects using code.

AddressLists Collection and AddressList Object

To access directory or address book information in CDO, you must work with the AddressLists collection and the individual objects in it. The AddressLists collection contains all the various address book containers, including the global address list (both online and offline), the personal address book, and the Outlook address book.

The AddressLists collection itself exposes only two properties, Count and Item. Count returns the number of AddressList objects in the collection. The Item property allows you access specific AddressList objects. You can pass either an index or a name to the Item property, and the property will return the corresponding AddressList object. When you work with Exchange Server, you know that the name of the address lists provided by the server will be either Global Address List or Global Address List (Offline). Using these names, you can quickly find the correct AddressList you are looking for. The following example shows how to use both of these properties to retrieve all AddressList objects in the collection and print out their names:

Set oAddressLists = oSession.AddressLists For Each oAddressList In oAddressLists MsgBox "Name: " & oAddressList.Name If oAddressList.Name = "Global Address List (Offline)" Then tempWorkingOffline = 1 End If Next If tempWorkingOffline = 1 Then MsgBox "You're working offline" End If

AddressList Object

Of all the properties and methods on the AddressList object, you probably will use only two in your applications: the AddressEntries property and the IsReadOnly property. The AddressEntries property returns the AddressEntries collection for the current address book container. (The AddressEntries collection and individual AddressEntry objects are discussed later in this supplement.)

The IsReadOnly property returns a Boolean that indicates whether the current AddressList is a read-only list. If IsReadOnly returns True, you cannot add or delete entries in the address list. If it returns False, you can add or delete entries. Note that this property represents only the adding and deleting of entries in the list and not the modification of existing entries. The following example lists all your AddressList objects, how many AddressEntries each object contains, and whether the AddressList object is read-only:

Set oAddressLists = oSession.AddressLists For Each oAddressList In oAddressLists Set oAddressEntries = oAddressList.AddressEntries MsgBox "Name: " & oAddressList.Name & vbLf _ & "Number of Address Entries: " & oAddressEntries.Count _ & vbLf & "Read-only: " & oAddressList.IsReadOnly Next

AddressEntries Collection and AddressEntry Object

From an AddressList object, you can get a collection of the individual entries in the address book by using the AddressEntries property. In turn, the AddressEntries collection contains AddressEntry objects that expose the individual information about specific users in the address book. These objects and collections are the same objects provided in the Outlook Object library with only one difference. CDO exposes more methods and properties on the AddressEntry object than Outlook. For example, through Outlook, you would not be able to query specific information about a user in the directory, such as the user's office location, because Outlook does not provide a Fields collection on the AddressEntry object. Through CDO, however, you would be able to retrieve that kind of information. CDO provides the Fields collection to retrieve this and other types of custom information from the directory. The following section describes the properties and methods of both the AddressEntries collection and the AddressEntry object.

AddressEntries Methods

The AddressEntries collection provides many of the same methods you would find on the other collections, such as the GetFirst, GetNext, GetPrevious, and GetLast navigation methods. In addition to these standard methods, the AddressEntries collection provides other methods for adding, deleting, and sorting information in the collection. However, you should be aware that CDO does not support adding or deleting entries in the Microsoft Exchange Server Global Address List. CDO does allow you to modify the exisiting entries in the list to update information such as the phone number, the office location, and the department name. To modify these entries, you must use the Fields collection of the AddressEntry object and have the correct permissions to modify the information. An explanation and sample is provided under the description of the Fields property of the AddressEntry object.

Add Method

The Add method creates a new AddressEntry object in the collection. You can pass to this the Add method parameters that correspond to the address type of the new entry, such as SMTP or EXCH. You can also pass the display name, which is the friendly name of the user, as well as the full address for the entry. Currently, Add allows you to add new entries to only your personal address book and not to your Outlook or Global Address book. The following example adds a new Exchange Server, SMTP, and cc:Mail entries in the personal address book of the user. Notice how you must call the Update method of the AddressEntry object to submit the changes to the AddressEntries collection.

Set oPAB = oSession.GetAddressList(CdoAddressListPAB) Set oAEs = oPAB.AddressEntries Set oNewSMTPAE = oAEs.Add("SMTP", "Thomas Rizzo(SMTP)", _ "thomriz@microsoft.com") Set oNewEXCHAE = oAEs.Add("EX", "Thomas Rizzo(EXCH)", _ "thomriz") Set oNewCCMAILAE = oAEs.Add("CCMAIL", "Thomas Rizzo(ccMail)", _ "Rizzo, Thomas at CCMAILPO") oNewSMTPAE.Update oNewEXCHAE.Update oNewCCMAILAE.Update

Delete Method

The Delete method removes all the objects in the collection. Be careful when using this method because its actions are irreversible. Once the all the AddressEntry objects are deleted, you cannot recover them. The following example uses the Delete method to prompt the user before deleting all the objects from the AddressEntries collection:

Set oPAB = oSession.GetAddressList(CdoAddressListPAB) Set oAEs = oPAB.AddressEntries result = MsgBox("Are you sure you want to delete all entries in " _ & "your PAB?", vbYesNo + vbExclamation, "Delete Entries") If result = vbYes Then oAEs.Delete End If

Sort Method

The Sort method allows you to sort the objects in the collection using either a built-in or custom named property as the criteria. If the sort is too complex for Exchange Server to handle, CDO will return an error, Cdo_E_TOO_COMPLEX, to your application.

The Sort method takes two parameters. The first parameter is the sort order, and it can have three possible values: CdoNone(0), CdoAscending(1), or CdoDescending(2). The second is either the property identifier for a built-in property or the name of a custom property.

The following example shows you how to sort, in ascending order, your Global Address List using the display or friendly names of the users:

Set oGAL = oSession.GetAddressList(CdoAddressListGAL) Set oAEs = oGAL.AddressEntries 'CdoAscending = 1, CdoPR_DISPLAY_NAME = &H3001001E oAEs.Sort 1, &H3001001E MsgBox oAEs.GetFirst.Name & vbLf & oAEs.GetLast.Name

AddressEntries Collection Properties

The AddressEntries collection contains the common properties, such as the Count property, for all CDO collections. It retrieves the number of items in the collection and the Item property to get a specific AddressEntry object either by value or by index. The AddressEntries collection also contains the Filter property, which allows you to create filters that specify which AddressEntry objects you wish to restrict from the collection. The following section describes how to use the Filter property.

Filter Property

The Filter property returns an AddressEntryFilter object, which is similar to the MessageFilter object in that you can set specific properties to restrict which AddressEntry objects are returned to your application. The AddressEntryFilter object has three primary properties of interest for creating filters: the Address, Fields, and Name properties. As you can guess, the Address property allows you to specify the full address information to set restrictrictions on, such as SMTP:thomriz@microsoft.com. The Fields property allows you to specify which property identifiers and values to set restrictrictions on. You will see an example of using the Fields property in the next code example. The Name property allows you to specify what substring to search for in the display name of the users. For example, if you type in eric as the value for the Name property, CDO will return any display name that has eric as a string, such as Eric Rothenberg or Gregory Erickson.

This filter, like the MessageFilter object, also supports the Not and Or properties. For information on these properties and how to use them, refer to the discussion of the MessageFilter object earlier in this supplement.

Note that some of the advanced filtering capabilities of CDO are not available to the offline version of the Global Address List. If you are building a client application with CDO, and your users might run your application against an offline address list, make sure that your filter uses only the capabilities provided by that address list. These capabilities include searching only for substrings in the display name using the Name property of the AddressEntryFilter object.

The following example uses the AddressEntryFilter object to restrict the Global Address List in Exchange Server, first by using the Name property with a string, and then by using different fields and Fields property to restrict the collection. In the example, the only users in the directory whose title is systems analyst and whose office location is in building 43 will be returned by the collection. The example includes all the unique identifiers for the other commonly used properties in the directory, such as department name or city. You can use these identifiers in your own code to make search applications using more complex filters:

Set oGAL = oSession.GetAddressList(CdoAddressListGAL) Set oAEs = oGAL.AddressEntries Set oAEFilter = oAEs.Filter oAEFilter.Name = "eric" MsgBox "There are " & oAEs.Count & " Address Entries." For Each oAE In oAEs MsgBox "Name: " & oAE.Name & vbLf _ & "Address: " & oAE.Address Next 'Reset the filter Set oAEs.Filter = Nothing Set oAEFilter = oAEs.Filter 'Unique Identifiers for the most common properties 'Department Name = &H3A18001F 'Account Name (Alias) = &H3A00001F 'Title = &H3A17001F 'Office Location = &H3A19001F 'Given Name (First Name) = &H3A06001F 'Surname (Last Name) = &H3A11001F 'Company Name = &H3A16001F 'Locality (State) = &H3A27001F 'State or Province = &H3A28001E 'Filter by Title and Office Location oAEFilter.Fields.Add &H3A17001F, "Systems Analyst" oAEFilter.Fields.Add &H3A19001F, "Bldg 43" MsgBox "There are " & oAEs.Count & " Address Entries." For Each oAE In oAEs MsgBox "Name: " & oAE.Name & vbLf _ & "Address: " & oAE.Address Next

AddressEntry Object

The AddressEntry object represents a single valid address in a messaging system. Usually, this address corresponds to a person or a process that the system can deliver messages to. The AddressEntry object is the way you can get and set user information in the Exchange Server directory.

AddressEntry Object Methods

The following sections describe the methods of the AddressEntry object.

Delete Method

The Delete method removes the AddressEntry object from the AddressEntries collection. This removal operation is irreversible, so be careful when you use it. You can delete AddressEntry objects out of address books only when you have the appropriate permissions. CDO does not support deleting members from a distribution list or the Global Address List using the Delete method. It only supports deleting items from your personal address book. The following sample finds the first person in your personal address book and deletes that person:

Set oPAB = oSession.GetAddressList(CdoAddressListPAB) Set oAEs = oPAB.AddressEntries Set oAE = oAEs.GetFirst MsgBox "The first person in the PAB is " & oAE.Name oAE.Delete

Details Method

The Details method displays a modal dialog box that contains detailed information about the current AddressEntry object. This method takes an optional parameter, which is a long value that identifies the parent window. Most times, you will not use this optional parameter. Notice the error handling in the following sample code. If the user clicks Cancel in the dialog box that appears, CDO will return a run-time error named MAPI_E_USER_CANCEL. Your code has to handle this error.

On Error Resume Next 'For the user hitting cancel Set oPAB = oSession.GetAddressList(CdoAddressListPAB) Set oAEs = oPAB.AddressEntries Set oAE = oAEs.GetFirst oAE.Details

GetFreeBusy Method

The GetFreeBusy method in CDO is exactly the same as the GetFreeBusy method in the Outlook object library. You need to pass four parameters to this method:

When calling the GetFreeBusy method, CDO will return the most committed value in the time slots. So if a user has overlapping appointments in a particular slot, and one is tenative and the other is out of the office, CDO will return the out-of-the-office value. CDO will not return free for a time slot unless the entire time slot is free. Be cautious when working with free/busy information far beyond the current date. By default, free/busy information is published only two months past the current date. If you query beyond the published information for free/busy, CDO will return free slots even though the user might have appointments during those slots. If you need longer-term information, you can have your Outlook users change the number of months beyond the current date for free/busy information to a maximum of 12 months.

The return value for GetFreeBusy returns a string of numbers that correspond to the free/busy status of the user for all the time slots you specified. Your code must then parse the string and make a graphical respresentation of this information in your application. Note that Free/Busy information will be available only when you are working online with your Exchange Server, not in offline mode.

The following example finds your manager in the directory using the Manager property and displays the free/busy information for your manager:

Set oManager = oSession.CurrentUser.Manager StrFreeBusy = oManager.GetFreeBusy(Now, DateAdd("d",1,Now), 30, True) MsgBox "GetFreeBusy returned: " & strFreeBusy

Update Method

The Update method saves your changes to an AddressEntry object to the system. For performance reasons, CDO uses a property cache and will post the changes only when you call the Update method. This means that if you make changes to an AddressEntry object and do not call the Update method, your changes will be lost if the object is destroyed. This method is the same as Message object Update method. For a sample and further information, refer to discussion of Update in the Message object section.

AddressEntry Properties

Now let's take a look at the properties of the AddressEntry object.

Address Property

The Address property is a read/write property that specifies the messaging address of the current AddressEntry object. This property can be combined with the Type property to create a full address. The following example displays the current user's address in a message box:

Set oAE = oSession.CurrentUser MsgBox "Address Property: " & oAE.Address

DisplayType Property

The DisplayType property returns the type of the AddressEntry object. This property has seven possible return values: CdoUser(0), CdoDistList(1), CdoForum(2), CdoOrganization(4), CdoPrivateDistList(5), and CdoRemoteUser(6). It corresponds to the MAPI property CdoPR_Display_Type (&H39000003). Use DisplayType to filter or find specific types of AddressEntry objects. The following code uses an AddressEntryFilter object to filter out only distribution lists using DisplayType. This sample will only work with the online Global Address List.

Set oGAL = oSession.GetAddressList(CdoAddressListGAL) Set oAEs = oGAL.AddressEntries MsgBox "Before the Filter: " & oAEs.Count Set oAEs.Filter = Nothing Set oAEFilter = oAEs.Filter oAEFilter.Fields.Add &H39000003, CdoDistList MsgBox "After the Filter: " & oAEs.Count

Fields Property

The Fields property returns the Fields collection for the current AddressEntry object. You can use the Fields collection to retrieve specific information about the AddressEntry object using MAPI properties. The following example pulls out the details of your AddressEntry object using this property:

Set oUser = oSession.CurrentUser strType = oUser.Fields(&H39000003) strTitle = oUser.Fields(&H3A17001F) strLocation = oUser.Fields(&H3A19001F) MsgBox "Your AddressEntry type is " & strType _ & vblf & "Your Title is " & strTitle _ & vblf & "Your Location is " & strLocation

ID Property

The ID property returns the EntryID for the AddressEntry object. You can store the EntryID and use it later with the GetAddressEntry method on the Session object.

Manager Property

The Manager property returns an AddressEntry object that corresponds to your manager from the directory. If you do not have a manager in the directory, this property returns nothing. To see an example that uses this property, take a look at the code for GetFreeBusy method on the AddressEntry object.

Members Property

The Members property returns an AddressEntries collection that corresponds to the members of a distribution list. If you use this property on an AddressEntry object that is not a distribution list, this property will return nothing. After retrieving the Members property, you can scroll through its returned collection to get more information about the distribution list members. The following example uses the filter we saw earlier for the DisplayType property to filter out only distribution lists. It then grabs the first distribution list and displays the number of members and the names of all the members.

Set oGAL = oSession.GetAddressList(CdoAddressListGAL) Set oAEs = oGAL.AddressEntries Set oAEs.Filter = Nothing Set oAEFilter = oAEs.Filter oAEFilter.Fields.Add &H39000003, CdoDistList Set oFirstDL = oAEs.GetFirst StrName = oFirstDL.Name Set oMembers = oFirstDL.Members lCount = oMembers.Count For each oMember in oMembers StrMembers = strMembers & vblf & oMember.Name Next MsgBox "In the " & strName & " DL, there are " & lCount & _ " members." & vblf & strMembers

Name Property

The Name property returns or sets the display name for the AddressEntry objects. This property corresponds to the friendly name you see for users, such as Thomas Rizzo, instead of the users' alias names.

Type Property

The Type property returns or sets the address type. The address type returned or set depends on the messaging system you have installed. For example, in an Exchange Server system, the type would be EXCH. You can combine this property with the Address property to create a full address for the AddressEntry objects.

Recipients Collections and Recipient Object

The Recipients collection is a child collection off the Message object. It contains a collection of Recipient objects that are the actual recipients for a message or meeting item. The Recipients collection and Recipient object are similar to the AddressList and AddressEntry objects—all of these objects deal with messaging addresses or directory information. The following section describes the methods and properties of the Recipients collection and the methods and properties of the Recipient object.

Recipients Collection Methods

The following sections describe the methods of the Recipients collection.

Add Method

When you first create a Message object, the Recipients collection is empty. To add recipients, you must call the Add method or the AddMultiple method. The Add method creates a new Recipient object in the collection. You can pass, as optional parameters, the display name, the full address, and the type of recipient to this method. The types are CdoTo(1), CdoCc(2), and CdoBcc(3). If you have the identifier for a valid AddressEntry object, you can pass the identifer to the Add method and leave out the display name and full address. Using the With statement in Visual Basic, you can also set these properties after creating the Recipient object. You should always call the Resolve method on the collection after finishing adding your recipients to make sure there are no ambiguous names or addresses. The following code sample creates two recipients. The first uses the parameters for the Add method to create a new recipient. The second uses the With statement.

Set oOutbox = oSession.Outbox Set oMessage = oOutbox.Messages.Add Set oRecips = oMessage.Recipients 'Add to the To line thomriz@microsoft.com Set oFirstRecip = oRecips.Add("Tom Rizzo", _ "SMTP:thomriz@microsoft.com", 1) Set oSecondRecip = oRecips.Add With oSecondRecip .Name = "Stacy Eckstein" .Address = "SMTP:seckstein@msn.com" .Type = 2 End With oRecips.Resolve If oRecips.Resolved <> True Then MsgBox "There are ambiguous recipients!" Else oMessage.Subject = "Hello!" oMessage.Text = "Sent to you by CDO." oMessage.Send End If

AddMultiple Method

To quickly add multiple recipients to a message, use the AddMultiple method. This method takes a semicolon-delimited string that contains the display name, the SMTP address, or the full address of each of the multiple recipients you want to add. Optionally, you can pass, after the string, the type of recipients the new recipients should be, such as Bcc recipients. Make sure to call the Resolve method on the Recipients collection because it checks for ambiguous names in the collection. The following example shows you how to use the AddMultiple method with the three different types of e-mail names you can use in the semicolon-delimited string:

Set oOutbox = oSession.Outbox Set oMessage = oOutbox.Messages.Add Set oRecips = oMessage.Recipients 'Display Name only strRecips = "Thomas Rizzo (Exchange)" 'SMTP Address strRecips = strRecips & ";thomriz@microsoft.com" 'Full Address strRecips = strRecips & ";Thomas Rizzo (Exchange)" _ & "[SMTP:thomriz@microsoft.com]" 'Add Recipients as CC recipients oRecips.AddMultiple strRecips, 2 oRecips.Resolve If oRecips.Resolved <> True Then MsgBox "There are ambiguous recipients!" Else oMessage.Subject = "Hello Mutliple Recipients!" oMessage.Text = "Sent to you by using the AddMultiple method." oMessage.Send End If

Delete Method

The Delete method deletes all the Recipient objects in the collection. You can use this property if the application or the user needs to remove all the previous recipients on a message and add new ones.

Resolve, GetFirstUnresolved, and GetNextUnresolved Methods, and Resolved Property

By using the Resolve, GetFirstUnresolved, and GetNextUnresolved methods with the Resolved property, you can quickly find the ambiguous recipient names in your collection. An ambiguous name is a name that CDO cannot identify for certain as your desired recipient because other recipients with the same name or letters might exist. For example, if you attempt to send an e-mail to a recipient named Bob and Bob is all you pass to CDO, your application will probably return an ambigious recipient error because most likely, many Bobs in your address book. You must resolve this ambiguity before you can send your message, and the Resolve, GetFirstUnresolved, and GetNextUnresolved methods and the Resolved property can help to do this.

The Resolve method recurses through the Recipients collection and attempts to resolve the address information of every recipient into a full address. This method takes one parameter, which allows you to specify a Boolean that tells the method to show the resolution dialog box if any ambiguous names are found. You should set this parameter to False if you are writing your application to run in an environment where you do not want user-interface elements to appear, such as in a Windows NT service or in an Microsoft Active Server Pages (ASP) application.

After the Resolve method is called, if there are no ambiguous recipients found, it sets the Resolved property to True. You can then check the Resolved method and use the GetFirstUnresolved and the GetNextUnresolved methods to traverse through the collection of unresolved recipients to allow your users to reenter or resolve the names of the users. If no ambiguous names are present, the GetFirstUnresolved and GetNextUnresolved methods return Nothing.

The following example picks common names for two recipients and tries to resolve those recipients. The code then checks the Resolved property and, if ambiguous recipients are present, the code displays their names using the GetFirstUnresolved and GetNextUnresolved methods. Notice that for the On Error Resume Next statement, CDO will return a MAPI_E_AMBIGUOUS_RECIP run-time error because ambiguous recipients exist in the collection. Your application must be able to handle this error gracefully.

On Error Resume Next Set oOutbox = oSession.Outbox Set oMessage = oOutbox.Messages.Add Set oRecips = oMessage.Recipients 'Display Names only to make them ambiguous strRecips = "Tom" strRecips = strRecips & ";Bob" oRecips.AddMultiple strRecips, 1 oRecips.Resolve False If oRecips.Resolved <> True Then MsgBox "There are ambiguous recipients!" Set oAmbig = oRecips.GetFirstUnresolved Do While Not (oAmbig Is Nothing) MsgBox "Name: " & oAmbig.Name Set oAmbig = oRecips.GetNextUnresolved Loop Else oMessage.Subject = "Hello Mutliple Recipients!" oMessage.Text = "There are no ambiguities here!" oMessage.Send End If

GetFreeBusy Method

The GetFreeBusy method on the Recipient object is the same as the GetFreeBusy method on the AddressEntry object. For an explanation of this method and a code sample, see the discussion of the GetFreeBusy method of the AddressEntry object earlier in this supplement.

Fields Collection and Field Object

The Fields collection contains one or more Field objects. You have seen the Fields collection used throughout this supplement to get at information that CDO does not have explicit objects for. The Fields collection occurs on many of the default CDO objects, and you can use it to add new properties to your items.

Fields Collection Methods

The following sections describe the methods of the Fields collection.

Add Method

The Add method creates and returns a new Field object in the collection. This method can take multiple types of syntax:

No matter which syntax you use, you must call the Update method or Send method on the item if you want to persist your changes. If you do not call the Update or Send method, your changes will be discarded.

The following example creates a new message in your Outbox and adds two custom fields to the message. It then saves the item into the Outbox so that you can look at it with Outlook. Outlook has the ability to see CDO custom properties you create on your messages. The only caveat is Outlook does not automatically discover these properties—you need to add your custom properties using Outlook's Field Chooser, and then drag and drop the properties into your view. You should then see the value you created programmatically. For example, if you create a custom property called My Custom String in the Outlook Outbox and dragged it into the view, you would see Test as the value.

Set oOutbox = oSession.Outbox Set oMessages = oOutbox.Messages Set oNewMsg = oMessages.Add oNewMsg.Fields.Add "My Custom String", vbString, "Test" oNewMsg.Fields.Add "My Custom String2", vbString, "Test2" oNewMsg.Update

Delete Method

The Delete method deletes all the Field objects in the collection. You should be careful when using this method since it is irreversible.

SetNamespace Method

The SetNamespace method selects the default property set to use when accessing named properties in the Fields collection. The most common use for this method is setting your namespace so that it uses the Outlook property set for the Fields collection. In this way, you can access Contact properties from CDO.

Before showing you how to set the namespace so that you can access Outlook contact information from CDO, I must give you ample warning. First, Microsoft will not support you if you use the SetNamespace method to access contact information in your own custom applications. Currently, Outlook Web Access uses the SetNamespace method to get at contacts, so you can look at the Outlook Web Access code for more information on doing this. Second, the properties in the next code example talk directly to the Outlook contact schema, so they can and probably will change in the future. Third, if you do not correctly set these properties, you can crash Outlook because you are not setting them by using Outlook's object model.

If you who can live with these caveats, take a look at the next sample. It sets the namespace of the Fields collection to the Outlook property namespace. Then it retrieves the business and home address of the contact item using custom properties, and displays these values in a message box.

'$ Outlook Contact props Const AMPidTag_FileUnder = "0x8005" Const AMPidTag_FileUnderId = "0x8006" Const AMPidTag_SelectedOriginalDisplayName = "0x8009" Const AMPidTag_YomiFirstName = "0x802C" Const AMPidTag_YomiLastName = "0x802D" Const AMPidTag_YomiCompanyName = "0x802E" Const AMPidTag_FileUnderList = "0x8026" Const AMPidTag_EmailDisplayName = "0x8080" Const AMPidTag_EmailEntryID = "0x8081" Const AMPidTag_EmailAddrType = "0x8082" Const AMPidTag_EmailEmailAddress = "0x8083" Const AMPidTag_EmailOriginalDisplayName = "0x8084" Const AMPidTag_EmailOriginalEntryID = "0x8085" Const AMPidTag_EmailEmailType = "0x8087" Const AMPidTag_Email2DisplayName = "0x8090" Const AMPidTag_Email2EntryID = "0x8091" Const AMPidTag_Email2AddrType = "0x8092" Const AMPidTag_Email2EmailAddress = "0x8093" Const AMPidTag_Email2OriginalDisplayName = "0x8094" Const AMPidTag_Email2OriginalEntryID = "0x8095" Const AMPidTag_Email2EmailType = "0x8097" Const AMPidTag_Email3DisplayName = "0x80A0" Const AMPidTag_Email3EntryID = "0x80A1" Const AMPidTag_Email3AddrType = "0x80A2" Const AMPidTag_Email3EmailAddress = "0x80A3" Const AMPidTag_Email3OriginalDisplayName = "0x80A4" Const AMPidTag_Email3OriginalEntryID = "0x80A5" Const AMPidTag_Email3EmailType = "0x80A7" Const AMPidTag_EmailList = "0x8027" Const AMPidTag_ABPEmailList = "0x8028" Const AMPidTag_ABPArrayType = "0x8029" Const AMPidTag_WebPage = "0x802B" Const AMPidTag_HomeAddress = "0x801A" Const AMPidTag_BusinessAddress = "0x801B" Const AMPidTag_OtherAddress = "0x801C" Const AMPidTag_BusinessAddressCity = "0x8046" Const AMPidTag_BusinessAddressStreet = "0x8045" Const AMPidTag_BusinessAddressState = "0x8047" Const AMPidTag_BusinessAddressCountry = "0x8049" Const AMPidTag_BusinessAddressPostalCode = "0x8048" Const AMPidTag_BusinessAddressPostOfficeBox = "0x804A" On Error Resume Next Set oContacts = oContactFldr.Messages Set oContact = oContacts.GetFirst Set oFields = oContact.Fields 'Set the namespace to the correct Propset oFields.SetNamespace "0420060000000000C000000000000046" strBusinessAddress = oFields(AMPidTag_BusinessAddress) strHomeAddress = oFields(AMPidTag_HomeAddress) MsgBox "Contact Name: " & oContact.Subject & vbLf _ & "Contact Home Address: " & vbLf _ & strHomeAddress & vbLf & "Contact Business Address: " & vbLf _ & strBusinessAddress

Fields Collection Properties

The Fields collection contains both the Count and Item properties. The Count property returns the number of Field objects in the collection. The Item property can be used to retrieve a specific Field object by specifying either the index, the property tag, or the name of the Field object. When you pass the name of a Field object, you can optionally specify the property set ID so that you can search for the property. Most times you will not use this parameter—if you are going to pull information from other property sets, use the SetNameSpace method to set the property set ID instead.

Field Object Methods

The Field object represents a MAPI property on a CDO object. The Field object supports multiple types of data as well as multivalued properties.

Delete Method

This method deletes the current Field object. Be careful when working with this method because the delete action is irreversible once committed.

Field Object Properties

ID Property

The property returns the MAPI tag for the Field object. You can store this tag so that you can use it later in your application to quickly retrieve your field by ID.

Name Property

This property returns the name of the Field object as a string. You can use this property to display the friendly names for all the Fields of an item by scrolling through the Fields collection, as shown in this example:

Set oItem = oSession.Inbox.Messages.GetFirst Set oFields = oItem.Fields For Each oField In oFields MsgBox "Name: " & oField.Name & vbLf & "Type: " _ & oField.Type & vbLf & "Value: " & oField.Value Next

Type Property

The Type property returns the data type for the Field object. You can use this property to check for certain types of properties in the custom fields of an item.

Value Property

The Value property returns or sets the actual value stored in the Field object. This property is the default property for the Field object.

Attachments Collections and Attachment Object

To create and add attachments to your Message objects, you must use the Attachments collection, which contains Attachment objects. These attachments can be the contents of a file copied into the message, a link to a file on a file server, an OLE object, or a message embedded in your current message. The only methods you will probably use on the Attachments collection are Add and Delete, and the only property you will use is Count. The Count property returns the number of attachment objects in the collection. The Add,and Delete methods are described in the next sections.

Add Method

The Add method on the Attachments collection allows you to quickly add new attachments to your messages. This method takes four optional parameters:

The type of the attachment can have four different values:

The following example uses all four attachment types in a single message:

Set oOutbox = oSession.Outbox Set oInbox = oSession.Inbox Set oInboxMsgs = oInbox.Messages 'Get the first message to embed it later Set oMsg = oInboxMsgs.GetFirst Set oMessage = oOutbox.Messages.Add Set oRecips = oMessage.Recipients Set oRecip = oRecips.Add With oRecip .Name = "Tom Rizzo" .Address = "SMTP:thomriz@microsoft.com" End With oRecips.Resolve False oMessage.Subject = "Hello Mutliple Attachments!" oMessage.Text = "Check the position of the attachments!" oMessage.Attachments.Add "My BMP", 0, CdoFileData, _ "c:\winnt\winnt256.bmp" oMessage.Attachments.Add "My Link", 10, CdoFileLink, _ "\\webdata\public\exch.doc" oMessage.Attachments.Add "My Ole", 15, CdoOle, _ "c:\culinary.doc" oMessage.Attachments.Add "My Msg", 25, CdoEmbeddedMessage, _ oMsg.ID oMessage.Send

Delete Method

The Delete method removes all the Attachment objects from the collection. Like other collections, this deletion is irreversable, so you should be careful when using the method. Most times, you will use it when the user decides she no longer wants to keep any of the attachments created on the message.

Attachment Object Methods

The only method that you probably will ever use on the Attachment object is the Delete method.

Delete Method

The Delete method deletes the current Attachment object from the Attachments collection. You must call the Update method on the parent object in order for the deletion to take effect.

Attachment Object Properties

The following sections describe the Attachment object properties.

Fields Property

The Fields property returns a Fields collection for the current Attachment object. The only time you are likely to use this property when retrieving specific MAPI properties on an Attachment object for which CDO does not have an object. The following example shows you some of these Attachment object properties and how to use the Fields property for the Attachment object. First, the example filters all the messages with attachments. Then it grabs the first message with an attachment from the filtered collection. It prints out some information about the first attachment on the item using the Fields property.

Const CdoPR_ATTACH_EXTENSION = &H3703001E Const CdoPR_ATTACH_FILENAME = &H3704001E Const CdoPR_ATTACH_SIZE = &HE200003 Const CdoPR_HASATTACH = &HE1B000B Const CdoPR_ATTACH_LONG_FILENAME = &H3707001F On Error Resume Next Set oFolder = oSession.Inbox Set oMessages = oFolder.Messages Set oMessages.Filter = Nothing Set oFilter = oMessages.Filter oFilter.Fields.Add CdoPR_HASATTACH, True Set oMessage = oMessages.GetFirst Set oAttach = oMessage.Attachments.Item(1) strExtension = oAttach.Fields(CdoPR_ATTACH_EXTENSION) strFileName = oAttach.Fields(CdoPR_ATTACH_FILENAME) strlFileName = oAttach.Fields(CdoPR_ATTACH_LONG_FILENAME) strSize = oAttach.Fields(CdoPR_ATTACH_SIZE) MsgBox "The subject of the message is " & oMessage.Subject MsgBox "The short filename for the attachment is: " & strFileName MsgBox "The long filename for the attachment is: " & strlFileName MsgBox "The extension for the attachment is: " & strExtension MsgBox "The size of the attachment is: " & strSize

Name Property

The Name property returns or sets the display name of the attachment.

Position Property

The Position property returns or sets the position of the attachment within the text of the message. This property takes a long value. A value of 0 indicates that the attachment is present but should not be made visible. A value of –1 indicates that the attachment is not handled using the Position property. Any other positive value indicates an index that specifies the the number of characters into the message text where you want the attachment to appear. Note that positioning of attachments in the text of a message works only with RTF messages and not with HTML messages.

Source Property

The Source property returns or sets the source for the attachment. For file link attachments, the Source property contains the full path and filename for the source of the attachment. For OLE attachments, it returns the OLE class name for the attachment. For embdedded messages, it returns a message object that is the message embedded as an attachment. If you set this property for an embedded message, you must pass the ID of the message you want to embed rather than a message object.

Type Property

This property describes the type of attachment. This property can have four values: CdoFileData(1), CdoFileLink(2), CdoOLE(3), or CdoEmbeddedMessage(4).

Категории