Cover | Table of Contents | Colophon
http://www.microsoft.com/exchange/downloads/intro.htm.
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Messaging Subsystem
"1" for any entry means that the
corresponding component is present on the computer.
|
Messaging Component
|
Registry Entry Name
|
|---|---|
|
MAPI
|
"
MAPIX" |
|
Simple MAPI
|
"
MAPI" |
|
CMC
|
"
CMC" |
|
CDO
|
"
OLEMessaging" |
HKEY_CURRENT_USER\Software\Microsoft\Windows Messaging Subsystem\Profiles
HKEY_CURRENT_USER\Software\Microsoft\WINDOWS NT\CURRENTVERSION\Windows Messaging Subsystem\Profiles
DefaultProfile that contains the name of the
user's default MAPI profile (if the user has a default
profile). In addition, the key has a subkey for each profile that has
been set up for the user. The name of each subkey corresponds to the
name of the associated profile. Example 2-3 shows
two functions for accessing this information on a Windows 9x machine.
GetDefaultProfileName returns the user's
default profile name, and GetProfileNames
returns a string array containing the names of the user's
profiles. As in Example 2-2, several
registry-related constants and API functions must be declared.
' Place these declarations at the module level.
Public Const HKEY_LOCAL_MACHINE = &H80000002
Public Const HKEY_CURRENT_USER = &H80000001
Public Const KEY_QUERY_VALUE = &H1
Public Const ERROR_SUCCESS = 0&
Public Const ERROR_FILE_NOT_FOUND = 2&
Public Type FILETIME
dwLowDateTime As Long
dwHighDateTime As Long
End Type
Public Declare Function RegOpenKeyEx Lib "advapi32.dll" _
Alias "RegOpenKeyExA" ( _
ByVal hKey As Long, _
ByVal lpSubKey As String, _
ByVal ulOptions As Long, _
ByVal samDesired As Long, _
phkResult As Long _
) As Long
Public Declare Function RegQueryValueEx Lib "advapi32.dll" _
Alias "RegQueryValueExA" ( _
ByVal hKey As Long, _
ByVal lpValueName As String, _
ByVal lpReserved As Long, _
lpType As Long, _
lpData As Any, _
lpcbData As Long _
) As Long
Public Declare Function RegCloseKey Lib "advapi32.dll" ( _
ByVal hKey As Long) As Long
Public Declare Function RegQueryInfoKey Lib "advapi32.dll" _
Alias "RegQueryInfoKeyA" ( _
ByVal hKey As Long, _
ByVal lpClass As String, _
lpcbClass As Long, _
ByVal lpReserved As Long, _
lpcSubKeys As Long, _
lpcbMaxSubKeyLen As Long, _
lpcbMaxClassLen As Long, _
lpcValues As Long, _
lpcbMaxValueNameLen As Long, _
lpcbMaxValueLen As Long, _
lpcbSecurityDescriptor As Long, _
lpftLastWriteTime As FILETIME _
) As Long
Public Declare Function RegEnumKeyEx Lib "advapi32.dll" _
Alias "RegEnumKeyExA" ( _
ByVal hKey As Long, _
ByVal dwIndex As Long, _
ByVal lpName As String, _
lpcbName As Long, _
ByVal lpReserved As Long, _
ByVal lpClass As String, _
lpcbClass As Long, _
lpftLastWriteTime As FILETIME _
) As Long
Public Function GetDefaultProfileName( ) As String
' Returns the name of the user's default profile. If none, an empty
' string is returned.
Dim hKey As Long ' handle to registry key
Dim nResult As Long ' results from API function calls
Dim nType As Long ' type of data read from registry
Dim strData As String ' data read from registry
Dim nBufferSize As Long ' size of data buffer
' Initialize data buffer.
strData = String(256, 0)
nBufferSize = Len(strData)
' Open the registry key where the entry resides.
nResult = RegOpenKeyEx(HKEY_CURRENT_USER, _
"SOFTWARE\Microsoft\Windows Messaging Subsystem\Profiles", _
0, KEY_QUERY_VALUE, hKey)
If nResult <> ERROR_SUCCESS Then
Err.Raise 335, App.Title, "Could not access system registry"
End If
' Read the desired entry.
nResult = RegQueryValueEx(hKey, "DefaultProfile", 0, nType, _
ByVal strData, nBufferSize)
' Done with the registry.
RegCloseKey hKey
' Did we find the desired registry entry?
If nResult = ERROR_SUCCESS Then
' Truncate the returned data at the terminating null.
strData = Left(strData, InStr(strData, Chr(0)) - 1)
' Return result.
GetDefaultProfileName = strData
' else was the registry entry not found?
ElseIf nResult = ERROR_FILE_NOT_FOUND Then
GetDefaultProfileName = ""
Else ' else there was some error
Err.Raise 335, App.Title, "Could not access system registry"
End If
End Function ' GetDefaultProfileName
Public Sub GetProfileNames(ByRef astr( ) As String)
' Loads astr with the names of the profiles that have been
' set up for the user.
Dim hKey As Long ' handle to registry key
Dim nResult As Long ' results from API function calls
Dim strData As String ' data read from registry
Dim nSubkeys As Long ' number of subkeys
Dim nMaxBufferSize As Long ' length of longest subkey name + 1
Dim nBufferSize As Long ' size of data buffer
Dim nIndex As Long ' for enumerating the subkeys
Dim astrRetVal( ) As String ' for gathering results
Dim ft As FILETIME ' dummy required for registry api calls
' Open the registry key where the subkeys reside.
nResult = RegOpenKeyEx(HKEY_CURRENT_USER, _
"SOFTWARE\Microsoft\Windows Messaging Subsystem\Profiles", _
0, KEY_QUERY_VALUE, hKey)
If nResult <> ERROR_SUCCESS Then
Err.Raise 335, App.Title, "Could not access system registry"
End If
' Determine the number of keys to enumerate, and the size of the buffer
' needed for the longest subkey name.
nResult = RegQueryInfoKey(hKey, 0, 0, 0, nSubkeys, _
nMaxBufferSize, 0, 0, 0, 0, 0, ft)
' Allocate results array.
ReDim astrRetVal(1 To nSubkeys) As String
' Retrieve subkeys.
For nIndex = 1 To nSubkeys
' Initialize data buffer.
strData = String(nMaxBufferSize, 0)
nBufferSize = Len(strData)
' Get the subkey name.
nResult = RegEnumKeyEx(hKey, nIndex - 1, strData, nBufferSize, _
0, 0, 0, ft)
' Everything OK?
If nResult = ERROR_SUCCESS Then
' Truncate the returned data at the terminating null.
strData = Left(strData, InStr(strData, Chr(0)) - 1)
' Return result.
astrRetVal(nIndex) = strData
Else ' else there was some error
Err.Raise 335, App.Title, "Could not access system registry"
End If
Next nIndex
' Done with the registry.
RegCloseKey hKey
' Return the array of names.
astr = astrRetVal
End Sub ' GetProfileNamesDeclare statement to make the Visual Basic
compiler aware of the function that is to be called.
Declare statement has two forms, depending on
whether the statement declares a subroutine or a function. The form
for a subroutine is:
[Public | Private] Declare Sub name Lib "libname" [Alias "aliasname"] [([arglist])]
[Public | Private] Declare Function name Lib "libname" [Alias "aliasname"] [([arglist])] [As type]
Dim nMAPISession As Long ' Initiate a MAPI session. nRetVal = MAPILogon(0, "MyProfile", "", MAPI_NEW_SESSION, _ 0, nMAPISession)
Declare statement looks like this:
Public Declare Function MAPILogon Lib "MAPI32.DLL" ( _ ByVal UIParam As Long, _ ByVal User As String, _ ByVal Password As String, _ ByVal Flags As Long, _ ByVal Reserved As Long, _ Session As Long _ ) As Long
MAPI_LOGON_UI flag in the
Flags parameter.
MAPIMessage and set the
values of its members.
MAPIRecip and call
MAPIResolveName on each element.
MapiFile and set each element appropriately.
Sending attachments is covered later in this
chapter.
Dim nRetVal As Long Dim MyMessage As MAPIMessage Dim MyRecips( ) As MapiRecip Dim MyFiles( ) As MapiFile ' Set the subject and body text. MyMessage.Subject = "Test message from Simple MAPI." MyMessage.NoteText = "This is the body text of the message." ' Add a recipient. MyMessage.RecipCount = 1 ReDim MyRecips(1 To MyMessage.RecipCount) As MapiRecip nRetVal = MAPIResolveName(nMAPISession, 0, "Annemarie", 0, 0, MyRecips(1)) ' Send the message. nRetVal = MAPISendMail(nMAPISession, 0, MyMessage, MyRecips, MyFiles, 0, 0)
MAPIMessage
datatype is defined in mapi32.txt like this:
Type MAPIMessage
Reserved As Long
Subject As String
NoteText As String
MessageType As String
DateReceived As String
ConversationID As String
Flags As Long
RecipCount As Long
FileCount As Long
End Type
Reserved
SubjectMAPIMessage to
represent the message (as already described in this chapter).
FileCount member of the
MAPIMessage variable equal to the desired number
of attachments.
MapiFile elements with as
many elements as there are files to attach.
Dim nRetVal As Long Dim MyMessage As MAPIMessage Dim MyRecips( ) As MapiRecip Dim MyFiles( ) As MapiFile ' Set the subject and body text. MyMessage.Subject = "Test message from Simple MAPI, with attachment." MyMessage.NoteText = "This is the body text of the message." ' Add a recipient. MyMessage.RecipCount = 1 ReDim MyRecips(1 To MyMessage.RecipCount) As MapiRecip nRetVal = MAPIResolveName(nMAPISession, 0, "Annemarie", 0, 0, MyRecips(1)) ' Add an attachment. MyMessage.FileCount = 1 ReDim MyFiles(1 To MyMessage.FileCount) As MapiFile MyFiles(1).PathName = "c:\autoexec.bat" ' Send the message. nRetVal = MAPISendMail(nMAPISession, 0, MyMessage, MyRecips, MyFiles, 0, 0)
Dim nRetVal As Long
Dim nRetValFindNext As Long
Dim strSeedMessageID As String
Dim strMessageID As String
Dim nMsg As Long
Dim MyMessage As MAPIMessage
ReDim MyRecips(0 To 0) As MapiRecip
ReDim MyFiles(0 To 0) As MapiFile
Dim recipOriginator As MapiRecip
Dim nRecipients As Long
Dim nFiles As Long
' This tells MAPIFindNext to start with the first message.
strSeedMessageID = ""
Do ' for each message in the user's Inbox
' Get the next message ID.
strMessageID = String(512, 0)
nRetValFindNext = MAPIFindNext(nMAPISession, 0, "", _
strSeedMessageID, 0, 0, strMessageID)
' if there was another message
If nRetValFindNext = SUCCESS_SUCCESS Then
' Fetch message associated with the message ID.
nRetVal = BMAPIReadMail(nMsg, nRecipients, nFiles, nMAPISession, 0, _
strMessageID, MAPI_ENVELOPE_ONLY Or MAPI_PEEK, 0)
Debug.Assert nRetVal = SUCCESS_SUCCESS
' Prepare the MyRecips and MyFiles arrays to receive
' the recipient and attachment information.
If nRecipients > 0 Then
ReDim MyRecips(0 To nRecipients - 1) As MapiRecip
End If
If nFiles > 0 Then
ReDim MyFiles(0 To nFiles - 1) As MapiFile
End If
' Read the fetched message.
nRetVal = BMAPIGetReadMail(nMsg, MyMessage, MyRecips, MyFiles, _
recipOriginator)
Debug.Assert nRetVal = SUCCESS_SUCCESS
' Write the subject line to the immediate window.
Debug.Print MyMessage.Subject
' The message ID becomes the seed for the next call to MAPIFindNext.
strSeedMessageID = strMessageID
End If
Loop While nRetValFindNext = SUCCESS_SUCCESS' Read the fetched message. nRetVal = BMAPIGetReadMail(nMsg, MyMessage, MyRecips, MyFiles, _ recipOriginator)
MapiFile user-defined type, shown earlier
in this chapter and repeated here:
Type MapiFile
Reserved As Long
Flags As Long
Position As Long
PathName As String
FileName As String
FileType As String
End Type
FileCopy statement to copy
the file from its temporary location to a destination specified by
the user. Assuming that MyFiles is defined as in
Example 3-4, that
BMAPIGetReadMail has been called as already
described, that nIndex identifies the
attachment to be copied, and that
strCopyTo holds the destination path and
filename, the following statement does the job:
FileCopy MyFiles(nIndex).PathName, strCopyTo
PathName member of the
MapiFile type contains the full path of the file
attachment, including the filename.
MapiRecip records representing the address entries
selected from the address book. This array can then be passed to the
MAPISendMail function. Example 3-5 demonstrates this process. Figure 3-4 shows the address book dialog box displayed by
the MAPI system when BMAPIAddress is
called.
' Assume that a MAPI session has already been established, and that
' nMAPISession holds the session handle.
Dim nRetVal As Long
Dim MyMessage As MAPIMessage
Dim MyRecips( ) As MapiRecip
Dim MyFiles( ) As MapiFile
Dim nInfo As Long
Dim nRecipients As Long
' Set the subject and body text of the message.
MyMessage.Subject = "Test message from Simple MAPI."
MyMessage.NoteText = "This is the body text of the message."
' Show the address book to allow the user to select recipients.
nRetVal = BMAPIAddress(nInfo, nMAPISession, 0, "Select Names", 4, "", _
nRecipients, MyRecips, 0, 0)
If nRetVal = SUCCESS_SUCCESS Then
' Load the recipients into MyRecip( ).
ReDim MyRecips(0 To nRecipients - 1)
nRetVal = BMAPIGetAddress(nInfo, nRecipients, MyRecips)
If nRetVal = SUCCESS_SUCCESS Then
' Send the message.
MyMessage.RecipCount = nRecipients
nRetVal = MAPISendMail(nMAPISession, 0, MyMessage, MyRecips, _
MyFiles, 0, 0)
End If
End If
nRetVal = MAPIDetails(nMAPISession, 0, MyRecip, 0, 0)
Public Declare Function MAPIDetails Lib "MAPI32.DLL" Alias "BMAPIDetails" ( _ ByVal Session As Long, _ ByVal UIParam As Long, _ Recipient As MapiRecip, _ ByVal Flags As Long, _ ByVal Reserved As Long _ ) As Long
http://msdn.microsoft.com.) The article
provides MAPIReadMail, which wraps
BMAPIReadMail and
BMAPIGetReadMail, and
MAPIAddress, which wraps
BMAPIAddress and
BMAPIGetAddress.
Function MAPIReadMail(Session As Long, UIParam AsLong, MessageID As String, Flags As Long, Reserved As Long, Message As MAPIMessage, Orig As MapiRecip,RecipsOut( ) As MapiRecip, FilesOut( ) As MapiFile) As Long
Declare statement
to gain access to this API (and others) and how to use the API to
read and send messages, with or without attachments. You also learned
a couple of nice extras, such as showing the address book.
With MAPISession1 .UserName = "MyProfile" .SignOn End With
With MAPISession1 .DownLoadMail = True .LogonUI = True .NewSession = False .Password = "" .UserName = "MyProfile" .SignOn End With
False if you want to allow the user to manipulate
messages in the Inbox while offline. After working offline, you can
force a dial out to send messages from the Outbox and to receive new
messages. This is done by logging out of the MAPI Subsystem (calling
the SignOff method), then setting the
DownLoadMail property to MAPIMessages1.SessionID = MAPISession1.SessionID
(MsgCount - 1). It is an error to set the
MsgIndex property to a value higher than MsgCount -
1. To give an example, the following code loops through all
messages, adding each message's subject line to a list box
named With MAPIMessages1 .Compose .MsgSubject = "This is the subject." .MsgNoteText = "This is the message body." .RecipIndex = 0 .RecipDisplayName = "Dave" .Send End With
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
"MyResume.doc", is helpful.
mapData, which indicates that the attached file is
a data file (as opposed to an embedded OLE object, to be discussed
shortly).
Len(MAPIMessages1.MsgNoteText) - 1.
mapData. There are two
other legal values for this property: mapEOLE and
mapSOLE. Either value can be used to send an OLE
object. The difference is that mapEOLE is for an
"editable" OLE object, and mapSOLE is
for a "static" (i.e., non-editable) OLE object.
mapEOLE, the user can double-click the object to
begin editing it. For this to work, the user must have software on
his or her system that knows how to edit the kind of object that was
sent. For example, if you send an OLE object that contains a range of
cells from a Microsoft Excel spreadsheet, the receiving user must
have Excel in order to edit the object. However, it is not necessary
to have Excel simply to view the object. (OLE objects carry inside
them a static visual representation of their data.)