CDOLive LLC The Premier Resource for Microsoft Collaboration Data Objects             


Common Tasks | Special Tasks
Tips and Tricks - Common Tasks

This tips and tricks section provides some basic and also special tasks for CDO. Note that the following examples should serve only as an idea to create your own solution and are not checked for proper function. You can find a lot of checked and proper running samples at the CDOLive Code Sample Library.

For a list of the most common MAPI property tags, please take a look at 'Digging deeper into CDO', Property Tags and Types or use the CDO.HLP file, which is located on the Microsoft Exchange Server 5.5 CD-ROM. Note, that an updated version is included with the Microsoft Exchange Server 5.5 Service Pack 1 (or higher). You can also download the most current version from CDOLive cdo.zip (1,065 Kbyte).

Common Tasks
Task Description
Variable declaration and explicit variable checking Always use:
Option Explicit

and then:
Dim Variable

Note that only VBA And VB supports explicit variable declaration like this:

Dim Variable As Type

statement to prevent problems with non declared variables. Make your variables self explaining (e. g. objSession, objAddressList, objAddressEntries) to avoid unreadable code.
Work with objects and collections Before and after you use an object or a collection in VBScript or JScript, always use:
Set Object = Nothing

to free up used system resources. Note that this is not required when working with Visual Basic or VBA.

Task Description
Implement a detailed error trapping Because CDO does not display any dialog boxes, logon screens, or error messages, it is a good choice to implement a detailed error trapping. Each time you use:
Err.Clear
On Error Resume Next
Set Object = Expression

check if an error is returned with:

If Err.Number <> 0 Then
   ' Your error handling here, use Err.Number and Err.Description to identify the problem
Else
  ' Your code here ...
End If
Frequently object creation needs more system resources Because CDO internally creates a temporary object for each period that appears in the statement, reuse objects whenever possible. Do not use:
Subject = objSession.Inbox.Messages.Item(1).Subject

Create intermediary objects by using:

Set objMessages = objSession.Inbox.Messages
Set objMessage = objMessages.Item(1)
strSubject = objMessage.Subject

once and use it within the rest of your code.
Use the 'For Each', 'Next' statement to loop through a collection If you need to loop through a collection, you can use the 'For Each', 'Next' statement:
For Each objMessage In objMessages
  strSubject = objMessage.Subject
Next
Do not use the 'For Each', 'Next' statement to loop through a collection and add or delete objects of the collection at the same time Be aware of that if you want to add or delete objects from a collection while looping through the same collection, do not use the 'For Each', 'Next' statement. This will end up in an undefined behavior. Always use the 'For', 'Next' statement to accomplish that:
For intCounter = 1 To objFields.Count
  Set objField = objFields(intCounter).Value
  Debug.Print objField.Value
Next

Note that you cannot use the Count property with so called large collections. For a large collection the service provider cannot always maintain an accurate count of member objects. Large collections support Get methods that enable you to access individual members of the collection. The following collections are large collections:

Messages; AddressEntries; Folders

For a so called small collection the service provider maintains an accurate count of member objects. You can directly access individual members of the collection using an index. The following collections are small collections:

AddressLists; Attachments; InfoStores; Recipients; Fields; Columns; Formats; Patterns; Views
Loop through a large collection As mentioned above you cannot loop through a so called large collection using the 'For', 'Next' statement if you want to modify the collection at the same time. Instead of use the following code:
' Get first element
Set objMessage = objMessages.GetFirst()

' Loop through the collection
Do While Not objMessage Is Nothing

   ' Pull out subject of next message
  Debug.Print objMessage.Subject

  ' Get next message
   Set objMessage = objMessages.GetNext()
Loop
Delete items of a large collection If you want to delete items of a collection you must use a reversal loop, because the count of the collection is not updated at the same time. Use the following code:
' Get message collection
Set objMessages = objSession.Inbox.Messages

' Loop recursive through the message collection
While objMessages.Count <> 0
 objMessages(intCounter).Delete
Wend
Check the version of the CDO library You can check the version of the installed CDO library by using the following code in your Outlook form:
' Create MAPI session
Set objSession = CreateObject("MAPI.Session")

' Logon using an existing MAPI session
objSession.Logon "", "", False, False, 0

' Check the version of the CDO library
strVersion = objSession.Version

If you have Microsoft Outlook 98 installed, but the result of the check above is "1.1" or "1.0a", it is possible that CDO 1.21 is not installed properly on your machine. If you are running Microsoft Windows NT open a command window, change to your windows\system directory (e. g. 'C:\WINNT\SYSTEM32') and run 'REGSVR32.EXE CDO.DLL' to re-register the CDO library.

A complete list of the CDO library version numbers can be found at 'Digging deeper into CDO', Versions.
Log on, send a message and log off Sending a message using CDO is very simple. In VBScript use:
' Create MAPI session
Dim objSession
Set objSession = CreateObject("MAPI.Session")

In VB and VBA use:
Dim objSession As MAPI.Session
Set objSession As New MAPI.Session

' Logon using an existing MAPI session
objSession.Logon "", "", False, False, 0

' Or, logon using an existing MAPI profile with a new session
objSession.Logon "<Profile Name>", "", False, True, 0

' Or, logon using an new MAPI session with a dynamically created profile
strProfileInfo = "<Your Servername>" & vbLf & "<Your Mailbox>"
objSession.Logon "", "", False, True, 0, False, strProfileInfo

' Create a new message
Set objNewMessage = objSession.Outbox.Messages.Add

' Add a subject
objNewMessage.Subject = "This is a message created with CDO"

' Add text to the message body
' Note that CDO 1.x cannot add text formatted with RTF or HTML
' Only Plain Text is supported in the current version
objNewMessage.Text = "Welcome to the world of CDO. Enjoy your life!"

' Add recipient and resolve against the directory
Set objRecipient = objNewMessage.Recipients.Add
objRecipient.Name = "John Doe"
objRecipient.Resolve

' Send message
objNewMessage.Update
objNewMessage.Send

' Logoff from MAPI Session
objSession.Logoff

Note that if you want to logon using the default MAPI profile, you must determine the name of the MAPI profile which is stored in the registry. There is an article 'Logging on to Active Messaging Session w/ Default Profile' at the Microsoft Knowledge Base about that behavior. For more information, please take a look at Knowledge Base Articles about CDO & Scripting and Routing
Post a message to a folder You can post a message to a particular folder using CDO, not necessary if it is a public or private folder:
' Set the destination folder, in this sample the inbox
Set objFolder = objSession.Inbox

' Create a new message in that folder
Set objNewMessage = objFolder.Messages.Add

' Add content
objNewMessage.Subject = "This is a message created with CDO"
objNewMessage.Text = "Welcome to the world of CDO. Enjoy your life!"
objNewMessage.ConversationTopic = objNewMessage.Subject
objNewMessage.TimeReceived = Now
objNewMessage.TimeSent = Now
objNewMessage.Sent = True
objNewMessage.Submitted = False
objNewMessage.Unread = True

' Post (NOT sent) message
objNewMessage.Update

Note that in Microsoft Visual Basic you can use the With statement to speed up your code like this:

' Set the destination folder, in this sample the inbox
Set objFolder = objSession.Inbox

' Create a new message in that folder
Set objNewMessage = objFolder.Messages.Add

' Add content
With objNewMessage
  .Subject = "This is a message created with CDO"
  .Text = "Welcome to the world of CDO. Enjoy your life!"
  .ConversationTopic = objNewMessage.Subject
  .TimeReceived = Now
  .TimeSent = Now
  .Sent = True
  .Submitted = False
  .Unread = True
End With

' Post (NOT sent) message
objNewMessage.Update
Get an Outlook default folder Microsoft Outlook maintains a set of default folders to store different type of data. This folder can be accessed by the following way:
' CDO 1.x folder constants
Public Const CdoDefaultFolderCalendar = 0
Public Const CdoDefaultFolderContacts = 5
Public Const CdoDefaultFolderDeletedItems = 4
Public Const CdoDefaultFolderInbox = 1
Public Const CdoDefaultFolderJournal = 6
Public Const CdoDefaultFolderNotes = 7
Public Const CdoDefaultFolderOutbox = 2
Public Const CdoDefaultFolderSentItems = 3
Public Const CdoDefaultFolderTasks = 8

' Get the default calendar folder
Set objFolder =_
objSession.GetDefaultFolder(CdoDefaultFolderCalendar)

Note that you can not use this method to access other users folder. To access other users folder you need to logon to the mailbox of the user in question and open the appropriate folder. Logon to the appropriate mailbox is only possible if the Microsoft Windows NT account in question does have the required permission.

Also it is not possible to access the "Drafts" folder with this method, because it is not supported by the current version of CDO. An alternate way is to get the "Drafts" folder by using the following method:

' MAPI property to access the drafts folder
Public Const CdoPR_DRAFTS_FOLDER = &H36D70102

' Get inbox folder
Set objInbox = objSession.Inbox

' Get entry ID of drafts folder
strDraftsEntryID = objInbox.Fields.Item(CdoPR_DRAFTS_FOLDER)

' Get drafts folder
Set objFolder = objSession.GetFolder(strDraftsEntryID, Null)

' Finally display the name of the folder
MsgBox objFolder.Name

If CDO 1.2.1 and Microsoft Outlook 98 are sharing a stored profile, GetDefaultFolder returns CdoE_NO_SUPPORT if the ObjectType parameter specifies CdoDefaultFolderCalendar. To avoid this behavior, the recommended procedure is to use different profiles for CDO and Outlook 98. You can do this by specifying separate stored profiles for each, or by supplying the ProfileInfo parameter in the Session object's Logon method.
Create a new Outlook folder While it is possible with CDO 1.x to add folders there seems to be no way to create Outlook standard folders, like contacts, tasks, calendar etc. However, you can use the following code to define the folder type when creating the new folder:
' MAPI properties used
Const CdoPR_CONTAINER_CLASS = &H3613001E

' Create new folder
Set objFolder = objFolders.Add("MyFolder")

' Get fields collection
Set objFields = objFolder.Fields

' Set folder type field
objFields.Add CdoPR_CONTAINER_CLASS, "IPF.Contact"

' Save changes
objFolder.Update
Get a valid AppointmentItems collection With CDO 1.2x Microsoft added support for appointments. Unfortunately they are only supported in the default calendar folder of the primary delivery location. Use the following code to get a valid AppointmentItems collection:
' Get the default calendar folder
Set objFolder =_
objSession.GetDefaultFolder(CdoDefaultFolderCalendar)

' Get the Appointment Items collection
Set ojbAppointmenItems = objFolder.Messages

' Loop through the AppointmentItems collection
For Each objAppointment In objAppointmentItems

  ' Display start time of each appointment
  MsgBox objAppointment.StartTime
Next

Note that you can't retrieve a valid appointment item object from an Exchange Server public folder or any other folder than your default calendar. This is a design limitation of CDO 1.2x.
Get the parent of the current folder Sometimes it is necessary to determine what the parent folder is. Use the following code to get the parent folder as CDO folder object:
' Constant for default calendar folder
Public Const CdoDefaultFolderCalendar = 0

' Get default calendar folder
Set objFolder = objSession.GetDefaultFolder(0)

' Get parent folder entry ID
strParentID = objFolder.FolderID

' Get parent folder as CDO object
Set objParentFolder = objSession.GetFolder(strParentID, Null)
Get the sender information of an Outlook item If you want to get the information of the a sender of a message within an Outlook form use the following code:
' Create MAPI session
Set objSession = CreateObject("MAPI.Session")

' Logon using an existing MAPI session
objSession.Logon "", "", False, False, 0

' Get folder where the current Outlook item lives
Set objFolder = Item.Parent

' Get the Outlook item with CDO
Set objMessage = objSession.GetMessage(Item.EntryID, objFolder.StoreID)

' Get the sender of the message
Set objSender = objMessage.Sender

' Pull out some properties of the sender
MsgBox "Sender name: " objSender.Name
MsgBox "Sender address: " objSender.Address

' Close MAPI session
objSession.Logoff
Populate a combo box of an Outlook form with the Global Address List If you want to populate a combobox with all names of the global address list use the following code:
' Constant for global address list
Public Const CdoAddressListGAL = 0

' Create object reference to the combo box
Set objList = Item.GetInspector.ModifiedFormPages("Message").ComboBox1
objList.Clear

' Get global address list
Set objAddressList = objSession.GetAddressList(CdoAddressListGAL)

' Get address entries of the global address list
Set objAddressEntries = objAddressList.AddressEntries

' Loop through the address entries collection
For Each objAddressEntry In objAddressEntries

  ' Add each address entry to the combo box
  objList.AddItem objAddressEntry.Name
Next
Add an attachment to a message You can add four different types of attachments to a message, but you must take care of the different properties which must be set:
'  Adding an attachment
Set objAttachment = objMessage.Attachments.Add

' Setting the position where the attachment should be placed
' In this case at the beginning of the message, use -1 to place the
' attachment at the end of the message body. Note that this is only
' necessary for RTF enabled messages.
objAttachment.Position = 0

' Setting the attachment type and add it as a file
' Note that a list of possible attachment types is shown in the table below
objAttachment.Type = CdoFileData
objAttachment.ReadFromFile "c:\autoexec.nt"
objAttachment.Source = "c:\autoexec.nt"

' Or, add the attachment as link to a file
objAttachment.Type = CdoFileLink
objAttachment.Source = "\\Server\Share\autoexec.nt"

' Setting the attachment name
objAttachment.Name = "autoexec.nt"

' Or, add the attachment as OLE object
objAttachment.Type = CdoOLE
objAttachment.Source = "Word.Document"

' Setting the attachment name
objAttachment.Name = "doc1.doc"

' Or, add the attachment as MAPI message
objAttachment.Type = CdoEmbeddedMessage
objAttachment.Source = <ID property of the message object>

' Finally update the message to add the attachment
objMessage.Update

The attachment overwrites the placeholder character at the position specified by the attachment's Position property. A space is normally used for the placeholder character.

The CDO Library does not actually place the attachment within the message; that is the responsibility of the messaging client application. You can also use the value –1 for the Position property, which indicates that the attachment should be sent with the message, but should not be placed by the Position property.

The CDO Library supports several different kinds of attachments: files, links to files, OLE objects, and embedded messages. An attachment's type is specified by its Type property. To add an attachment, use the related Attachment object property or method appropriate for that type, as shown in the following table:
Type Object Property/Method Description
CdoFileData (or 1) ReadFromFile method Attachment is a file (Default value)
CdoFileLink (or 2) Source property Attachment is a link to a file
CdoOLE (or 3) ReadFromFile method Attachment is an OLE object
CdoEmbeddedMessage (or 4) ID property of the Message object to be embedded Attachment is an embedded message

More information can be found in the CDO.HLP file, which is located on the Microsoft Exchange Server 5.5 CD-ROM. Note, that an updated version is included with the Microsoft Exchange Server 5.5 Service Pack 1 (or higher). You can also download the most current version from CDOLive cdo.zip (1,065 Kbyte).
Extract all attachments of a messageYou can extract all attachments from a message using the following code:
' Set the destination folder
Set objFolder = objSession.Inbox

' Get message collection of the inbox
Set objMessages = objSession.Inbox.Messages

' Get first message of inbox
Set objMessage = objMessages.Item(1)

' Loop through the attachments collection
For Each objAttachment In objMessage.Attachments

  ' Extract all attachments to the filesystem
  strAttachName = objAttachment.Name
  objAttachment.WriteToFile("c:\" & strAttachName)
Next

The WriteToFile method overwrites the file without warning if a file of that name already exists. It operates differently, depending on the value of the Attachment object's Type property. The following table describes its operation:
Type Description
CdoFileData (or 1) Copies the contents of the attachment to the specified file
CdoFileLink (or 2) (Not supported)
CdoOLE (or 3) Writes the attachment to the specified file in OLE docfile format. The file can subsequently be read by the ReadFromFile method with an CdoOLE type setting.
CdoEmbeddedMessage (or 4) (Not supported)

Note that the current version of CDO does not support WriteToFile for CdoFileLink or CdoEmbeddedMessage attachments.
Copy/Move a messageYou can copy a message using the CopyTo method of CDO:
' Set the destination folder
Set objFolder = objSession.Outbox

' Get message collection of the inbox
Set objMessages = objSession.Inbox.Messages

' Get first message of inbox
Set objMessage = objMessages.GetFirst()

' Create a copy of the message in the inbox
Set objCopyMessage = objMessages.CopyTo(objFolder.ID)

' Update copied message
objCopyMessage.Update
Set objCopyMessage = Nothing

Note that the message body will retain the rich text formatting (RTF) if you use the CopyTo method. You can also move a message using the MoveTo method:

Set objNewMessage = objMessage.MoveTo(objSession.Inbox.ID,_
objSession.Inbox.StoreID)

All properties that have been set on this message are moved, whether they have read-only or read/write access. Each property is moved with its value and access unchanged.

Note that the current version of CDO does not support the MoveTo method on AppointmentItem objects.
Send a message with high priority While it is possible with CDO 1.x to send a message with a particular importance, it is not possible to set the message priority directly. This is especially useful if you want to have your Exchange Server MTA handle the message with a higher priority. You can use the following code to set the message priority:
' MAPI property used
Const CdoPR_PRIORITY = &H00260003

' Message priority options
Const CdoPR_PRIORITY_LOW = -1
Const CdoPR_PRIORITY_NORMAL = 0
Const CdoPR_PRIORITY_HIGH = 1

' Create new message
Set objMessage = objSession.Outbox.Messages.Add

' Set message priority
objMessage.Fields.Add CdoPR_PRIORITY, CdoPR_PRIORITY_HIGH

' Set recipient
objMessage.Recipients.Add("John Doe", "John@Doe.net", "SMTP")

' Update and send message
objMessage.Update
objMessage.Send