Sending Mail

Sending a text message programmatically is simple. Assuming that you’ve already signed on with the MAPISession control and that you’ve set the MAPIMessages control’s SessionID property equal to the MAPISession control’s SessionID property, the following code does the job:

With MAPIMessages1
   .Compose
   .MsgSubject = "This is the subject."
   .MsgNoteText = "This is the message body."
   .RecipIndex = 0
   .RecipDisplayName = "Dave"
   .Send
End With

Calling the MAPIMessages control’s Compose method tells the control that you are about to set some properties for a new outgoing message. Unlike incoming messages, there can never be more than one outgoing message at a time. The value of the MsgIndex property for the outgoing message is -1.

The MsgSubject and MsgNoteText properties are self-explanatory, being the subject and body portions of the message, respectively.

Unlike composing a message, there is no explicit method to call for adding a recipient, and the RecipIndex property is never -1. The number of recipients is controlled by how you set the RecipIndex property. In the code shown previously, the act of setting RecipIndex to automatically causes RecipCount to become 1. RecipCount is always automatically one greater than the highest value to which you have set RecipIndex. You never set RecipCount directly. The previously shown code can be modified to send to two recipients as follows:

With MAPIMessages1
   .SessionID = MAPISession1.SessionID
   .Compose
   .MsgSubject = "This is the subject."
   .MsgNoteText = "This is the message body."
   .RecipIndex = 0
   .RecipDisplayName = "Dave"
   .RecipIndex = 1
   .RecipDisplayName = "Annemarie"
   .Send
End With

The second recipient was added by incrementing RecipIndex and setting RecipDisplayName to a new name. Note that you can’t assign a variant string array to RecipDisplayName to reduce coding; the control’s type library identifies this property as a String, so if you try to assign anything else, Visual Basic generates a Type Mismatch error.

The MAPIMessages control also supports a RecipAddress property, which allows you to specify a recipient’s email address. But in the previous code fragment, I used the RecipDisplayName property instead. The reason is that MAPI requires all recipients of a message to go through address resolution, and it uses the display name for this purpose. Address resolution is the process of comparing an email address in string form to the recipients stored in the user’s address books. When a matching entry is found, the system associates the address book entry with the message. When the message ultimately is sent, the system reads critical information from the address book entry, such as the address, which transport provider to use, and whether to send to that address in Microsoft Exchange Rich Text Format (RTF). Again, this matching is based on the recipient’s display name, not his or her address.

If the value in RecipDisplayName is in a format that indicates that it is itself a valid address, the system creates a temporary address book entry to associate with the message, a so-called one-off recipient. For example, strings of the form user@company.com are valid Simple Mail Transport Protocol (SMTP) addresses, allowing you to write something like this:

With MAPIMessages1
   .SessionID = MAPISession1.SessionID
   .Compose
   .MsgSubject = "This is the subject."
   .MsgNoteText = "This is the message body."
   .RecipIndex = 0
   .RecipDisplayName = "jsmith@FictitiousCompany.com"
   .Send
End With

When the Send method is called, MAPI attempts to resolve the name jsmith@FictitiousCompany.com. During this process, MAPI discovers that this is a valid format for SMTP email addresses, so a one-off recipient is created and associated with the message. Note that even if there is already an address book entry with that address, that entry will not be matched during the address resolution process. To match an existing entry, the display name must be used.

If MAPI can’t resolve a name, or if the name resolves to more than one address, the MAPIMessages control raises an error. If the name can’t be resolved at all, the control raises a mapUnknownRecipient error. In the case of resolving to more than one name, the control raises a mapAmbiguousRecipient error.

You can force more sophisticated behavior by explicitly calling the control’s ResolveName method. This method resolves the name of the currently indexed recipient only. (Again, the currently indexed recipient is defined by the value of the MsgIndex and the RecipIndex properties.) If the address is not found or is ambiguous, the control does one of two things, depending on how you’ve set the AddressResolveUI property. If this property is set to False, the control behaves as described in the previous paragraph (i.e., it raises an error). However, if the AddressResolveUI property is set to True, the control triggers the address book’s address resolution dialog box, allowing the user to choose an appropriate address.

Tip

The address resolution dialog box may contain a Cancel button which, if pressed, causes a mapUserAbort error to be raised in your code. Be prepared to handle this error if you allow the address resolution dialog box to be shown. Example 4-2 shows one way such an error handler could be written.

Example 4-2. Handling the mapUserAbort Error During Address Resolution

Private Sub Send( )

   ' This procedure assumes that a message is being composed, that the
   ' recipients have already been set, and that all that needs to be done
   ' at this point is to resolve the recipient names and send the message.
   
   ' This procedure could be called in response to the user clicking a
   ' Send button on a form.
   
   Dim nRecip As Long
   
   On Error GoTo ErrorHandler
   
   For nRecip = 0 To MAPIMessages1.RecipCount - 1
      MAPIMessages1.RecipIndex = nRecip
      MAPIMessages1.ResolveName
   Next nRecip
   
   MAPIMessages1.Send
   
   Exit Sub
   
ErrorHandler:

   ' Did the user press Cancel on the address resolution dialog box?
   ' If so, just exit without sending the email. However, if a different
   ' error occurred, then re-raise that error so that it can be handled
   ' by the calling procedure.
   If Err.Number <> mapUserAbort Then
      Err.Raise Err.Number
   End If

End Sub ' Send

If it’s successful, the ResolveName method uses the RecipDisplayName property and sets the RecipAddress property equal to the email address of the recipient. Further, if the recipient’s display name (as configured in the user’s address book) is different from or more complete than the name in RecipDisplayName, the ResolveName method sets RecipDisplayName to the name as it is stored in the address book.

To delete a recipient from the current message, call the Delete method, passing the mapRecipientDelete constant, as in the following code fragment:

' Delete current recipient.
MAPIMessages1.Delete mapRecipientDelete

This statement deletes the recipient in the position defined by the RecipIndex property in the message defined by the MsgIndex property, and it decrements the indices of the recipients that followed the deleted recipient in the recipient set. It also decrements the RecipIndex property if the deleted recipient was the last one in the recipient set.

MAPI distinguishes between primary, copy, and blind copy recipients. When you add recipients to your outgoing message, they are flagged by default as primary. You can change the type of a given recipient (which is defined, again, by the value of the RecipIndex property) by setting the RecipType property. To set the current recipient to be a copy recipient, set the RecipType property to mapCcList. For a blind copy recipient, set the RecipType property to mapBccList. The default value is mapToList. When messages are sent, the receiving parties can see the primary and copy recipients but not the blind copy recipients.

Calling the Send method instructs MAPI to send the message to the mail server for distribution. Note that the Send method does not set the MsgSent property to True. That only happens when the message has actually been moved to the mail server.

The Send method has an optional parameter that you’ll find useful when you want to present the user with a standard message composition dialog box. Rather than developing the dialog box yourself, you can simply pass a value of True as an argument to the Send method. This way, it takes only two lines of code to allow the user to enter and send a standard email message:

MAPIMessages1.Compose
MAPIMessages1.Send True

The resulting dialog box is shown in Figure 4-5.

Using the mail system’s dialog box for composing a message

Figure 4-5. Using the mail system’s dialog box for composing a message

If your code sets other properties prior to calling Send True, those settings are reflected in the dialog box shown to the user. For example, you may wish to set a recipient and a subject programmatically, allowing the user simply to enter the message text. However, while the dialog box is shown, the user can make any changes he or she likes. If you need total control over what the user enters, you’ll need to create your own dialog box.

If the user cancels the dialog box, a mapUserAbort error is raised in your code. Your code should trap and handle this error.

Get CDO & MAPI Programming with Visual Basic: now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.