|
|||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
If
you’ve been following along, you’ll see a few differences
between this script and the Outlook version.
The main one is the logon line:
MailSession.Logon ShowDialog:=False, NewSession:=True, _
Profileinfo:="ExchangeServer" & Chr$(10) &
"AutoSend" This
is the line that lets us connect and send mail from any mailbox on
any Exchange Server that we have permission to access.
In this case, the computer actually running the Exchange
Server mail software is called “ExchangeServer”.
It has a mailbox called “AutoSend” to which we happen to
have access. In your
case, you’ll need to change these names.
One source of problems is making sure the user has access to
the AutoSend mailbox. How
this is done varies depending on which version of Exchange you’re
using and it gets much too complicated to go into here. Another
difference is in the attachment code:
Set Attachment = MailItem.Attachments.Add
Attachment.Type = 1 'ActMsgFileData
Attachment.Source = "c:\test.txt"
Attachment.ReadFromFile "c:\test.txt" With
Outlook, we just had to add the attachment and give it a file name
as part of the Add method. With
CDO, we have to first create the attachment then specify the Type,
Source, and ReadFromFile properties.
They are all required. This
is a good example of how CDO is more complicated than outlook.
Another important difference is the requirement to Logoff at
the end. Ok,
here’s the PICK subroutine for CDO: SUBROUTINE
AT.SEND.EMAIL.CDO(MAILSERVER, MAILBOX, ADDRESS, SUBJECT, MESSAGE,
ATTACH) *Send
email from host using AccuTerm and CDO *
ADDRESS:
recipient's email address *
SUBJECT:
one-line email subject *
MESSAGE:
multi-line email message (lines separated by AM) *
ATTACH:
optional attachment file name EQU
AM TO CHAR(254), VM TO CHAR(253), SVM TO CHAR(252) EQU
ESC TO CHAR(27), STX TO CHAR(2), CR TO CHAR(13) EQU
EM TO CHAR(25) SCRIPT
= '' SCRIPT
= SCRIPT : 'Dim MailSession As Object' : EM SCRIPT
= SCRIPT : 'Dim MailItem As Object' : EM SCRIPT
= SCRIPT : 'Dim Recipient As Object' : EM SCRIPT
= SCRIPT : 'Dim Attachment As Object' : EM SCRIPT
= SCRIPT : 'Dim Body As String' : EM SCRIPT
= SCRIPT : 'Set MailSession = CreateObject("MAPI.Session")'
: EM SCRIPT
= SCRIPT : 'MailSession.Logon ShowDialog:=False, NewSession:=True,' SCRIPT
= SCRIPT : ' Profileinfo:="' : MAILSERVER : '" & Chr$(10)
& "' : MAILBOX : '"' : EM SCRIPT
= SCRIPT : 'Set MailItem = MailSession.Outbox.Messages.Add' : EM *ADD
THE RECIPIENTS ARG
= ADDRESS; GOSUB 100 N
= DCOUNT(ARG, AM) FOR
I = 1 TO N
SCRIPT = SCRIPT : 'Set Recipient = MailItem.Recipients.Add' :
EM
SCRIPT = SCRIPT : 'Recipient.Name = "' : ARG<I> :
'"' : EM
SCRIPT = SCRIPT : 'On Error Resume Next' : EM
SCRIPT = SCRIPT : 'Recipient.Resolve False' : EM
SCRIPT = SCRIPT : 'If Err.Number <> 0 Then' : EM
SCRIPT = SCRIPT : '
MsgBox "The recipient did not check out!"' : EM
SCRIPT = SCRIPT : '
Exit Sub' : EM
SCRIPT = SCRIPT : 'End If' : EM NEXT
I ARG
= SUBJECT; GOSUB 100 SCRIPT
= SCRIPT : 'MailItem.Subject = "' : ARG : '"' : EM ARG
= MESSAGE; GOSUB 100 SCRIPT
= SCRIPT : 'Body = "' : ARG<1> : '"' : EM N
= DCOUNT(ARG, AM) FOR
I = 2 TO N
SCRIPT = SCRIPT : 'Body = Body & Chr$(13) & Chr$(10)
& "' : ARG<I> : '"' : EM NEXT
I SCRIPT = SCRIPT :
'MailItem.Text = Body' : EM IF ATTACH <> ''
THEN ARG = ATTACH; GOSUB 100 N = DCOUNT(ARG, AM) FOR I = 1 TO N SCRIPT = SCRIPT
: 'Set Attachment = MailItem.Attachments.Add' : EM SCRIPT = SCRIPT
: 'Attachment.Type = 1' : EM SCRIPT = SCRIPT
: 'Attachment.Source = "' : ARG<I> : '"' : EM SCRIPT = SCRIPT
: 'Attachment.ReadFromFile "' : ARG<I> : '"' :EM NEXT I END SCRIPT = SCRIPT :
'MailItem.Send' : EM SCRIPT = SCRIPT :
'Set Attachment = Nothing' : EM SCRIPT = SCRIPT :
'Set Recipient = Nothing' : EM SCRIPT = SCRIPT :
'Set MailItem = Nothing' : EM SCRIPT = SCRIPT :
'MailSession.Logoff' : EM SCRIPT = SCRIPT :
'Set MailItem = Nothing' PRINT ESC : STX : 'P'
: SCRIPT : CR : RETURN 100 * Local
subroutine to fixup embedded double-quote marks K = 1 LOOP J = INDEX(ARG, '"', K) WHILE J DO ARG = ARG[1, J] : ARG[J,
99999] K = K + 2 REPEAT RETURN It works the same as the Outlook routine except that
it has two additional parameters - MAILSERVER and MAILBOX. There are many other differences that make CDO harder
to work with.
If you need to use CDO, this is how you do it. My advice is
to avoid it if you can.
The MAPI method MAPI is one of the oldest technologies available to
send Email in Windows. While not as robust as Outlook or CDO,
MAPI services are available when using alternative Email clients such as
Outlook Express and Eudora. Unlike Outlook and CDO, which supply a rich object
model, Simple MAPI is an API – not an object model. What is an API? API stands for
“Application Programming Interface”, and is simply a set of subroutine and
data structure definitions that can be called from a programming
language.
API’s are usually implemented in DLL’s and in order to use API
functions in a DLL, the functions must be DECLAREd. Once a
function is declared, it becomes available to your routine as if it was a
built into the language. The syntax for declaring functions is
the subject of an entire book. It’s too big to address in this
article.
For more information on accessing the Windows API functions, you
might want to check out Dan Appleman’s book, “The Visual Programmer’s
Guide to the Win32 API” ISBN 0-672-31590-4. For now, just take the declaration of
the MAPISendMail function
below on faith. While it is possible to declare DLL functions in
AccuTerm’s scripting language (when invoked from the host), it is not
straightforward.
The problem is that before AccuTerm can execute a script which is
transmitted from the host, it must enclose the script between Sub Main() and
End Sub
statements.
But all DECLARE statements are “global” and need to reside outside
of any Subs or Functions. They normally occur before the first Sub
or Function. When executing the script from the host you have no
access to code before the Sub Main()
statement, so where do you place the DECLARE statements? The answer
relies on knowing that AccuTerm will add Sub Main() to the
beginning of the code you send and End Sub to the
end.
There is nothing that prevents you from adding your own
End Sub and
Sub xxxx
statements into the code. For example, look at the following
script: Sub Main () Call
AnotherSub End Sub Sub AnotherSub () ' Do
something End Sub If you want AccuTerm to execute the preceding script
from the host, you need to send all lines between (but not including) the
first and last lines. Using this information, we now know that we can insert
any DECLARE statements between our own End Sub /
Sub xxxx
statements. Sending Email using Simple MAPI requires only one
function: MAPISendMail, which has the following definition: Declare Function MAPISendMail Lib "MAPI32.DLL" (ByVal
Session&, ByVal UIParam&, Message As MapiMessage, ByVal
Flags&, ByVal Reserved&) As Long Reviewing the MAPI documentation reveals that for
automated Email messages only the Message parameter is required. All other
parameters can be left as zero. However, the Message parameter is a
structure, which in VBA, is defined using a “User Defined Type”, or
UDT. A
user defined type is just a fancy way of saying “there’s a block of memory
and it breaks down into individual pieces like this”. As an example,
the definition of MapiMessage looks 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 Originator As MapiRecip
'address of originator structure RecipCount As Long Recipients As MapiRecip
'address of first element of array of recipients FileCount As Long Files As MapiFile 'address
of first element of array of attached files End
Type To use the UDT, we declare a variable of that type.
For example, we can use the MapiMessage structure above to create a
variable that holds all the information with this statement: Dim msg As MapiMessage This creates a variable “msg” of type
MapiMessage. We can
reference the different elements of the type by using the dot
operator.
To change the Subject of the msg variable,
we’d do this: Msg.Subject = "This is the subject" There are some other structures we’ll need:
MapiRecip and
MapiFile. They’re
defined as: Type MapiRecip Reserved As Long RecipClass As Long Name As String Address As String EIDSize As Long EntryID As String End Type
Type MapiFile Reserved As Long Flags As Long Position As Long PathName As String FileName As String FileType As String End
Type Some of the things passed to the MapiSendMail function
are passed by reference. That means that the memory address of
the variable is passed to the routine, not the value of the variable
itself. So the next “trick” we need to employ is how to get the address of
a variable or array? The answer is the undocumented
VarPtr() function,
which returns the address of its argument. For this to work, we need to change the
data type in the MapiMessage definition
for the Originator, Recipients and Files members from MapiRecip or
MapiFile to Long,
the data type returned by the VarPtr() function. Finally, we need to translate any strings in
MapiRecip or
MapiFile
structures from Unicode to ANSI before calling MAPISendMail. The reason for
this is very complex and stems from the fact that AccuTerm’s scripting
environment uses Unicode to represent strings, and most API’s (including
Simple MAPI) expect strings in ANSI. Conversion of strings in the
MapiMessage structure
is handled automatically. But as far as our scripting environment
is concerned, the Originator, Recipients and Files elements are simply
Long integers and no conversion of their members will take place
automatically. So how do you use MAPISendMail to send
an Email message to a recipient? Basically, all you need to do is fill in
the MapiMessage structure
and call MAPISendMail. The
MapiMessage structure
has elements (fields) for Subject, NoteText (message body), Originator,
Recipients and Files (among others). It turns out that we usually don't need
to specify the Originator (this will be filled in by your MAPI Email
client program (Outlook Express, Eudora, etc.) The Recipients
element is the address of an array of MapiRecip structures; RecipientCount
is the number of items in the Recipients array. The Files
element is the address of an array of MapiFile structures; FileCount is
the number of items in the Files array. In our sample Email routine we will assume a single
recipient and optionally one attachment, which simplifies the Recipients
and Files arrays to be a single variable each. It is trivial
to fill in the Subject and NoteText fields. As described above, we use the
VarPtr() function
to fill in the address of the Recipients and Files fields. Which leaves
only the task of filling in the one MapiRecip and
possibly the MapiFile
structures. To initialize the MapiRecip structure,
we need to fill in three fields: Name, Address and RecipClass. The Name field
is the “display” name, and is required for some versions of Outlook. In this
sample, we simply use the first part of the Email address for the
name. The
Address field is the recipient’s Email address, preceded by the string
“smtp:”.
Remember that both Name and Address need to be converted to
ANSI.
Finally, the RecipClass field is set to 1, designating the
recipient as the “To recipient” (as opposed to CC, BCC and Sender
recipient classes). If an attachment is to be included with the Email
message, a MapiFile structure
is required.
The MapiFile structure
requires only two fields: Position and PathName. Set Position
to –1 and PathName to the full path of the attachment file (converted to
ANSI). Based on the preceding discussion, here is a sample
VBA script to send an Email message using Simple MAPI: Sub Main() SendMessage End Sub
Type MapiMessage Reserved As Long Subject As String NoteText As String MessageType As String DateReceived As String ConversationID As String Flags As Long Originator As Long 'address
of Originator RecipCount As Long Recipients As Long 'address
of Recipients array FileCount As Long Files As Long 'address of
Files array End Type
Type MapiRecip Reserved As Long RecipClass As Long Name As String Address As String EIDSize As Long EntryID As String End Type
Type MapiFile Reserved As Long Flags As Long Position As Long PathName As String FileName As String FileType As String End Type
Declare Function
MAPISendMail Lib "MAPI32.DLL" (ByVal Session&, ByVal UIParam&,
Message As MapiMessage, ByVal Flags&, ByVal Reserved&) As Long
Sub SendMessage()
Dim rc As Long Dim msg As MapiMessage Dim recip(1) As MapiRecip Dim attach(1) As MapiFile recip(1).Name =
StrConv("pjs",vbFromUnicode) recip(1).Address =
StrConv("smtp:pjs@asent.com", vbFromUnicode) recip(1).RecipClass = 1 'To attach(1).Position = -1 'end? attach(1).Pathname =
StrConv("c:\temp\test.txt",vbFromUnicode) msg.Subject = "test subject" msg.NoteText = "this is the
message body" msg.RecipCount = 1 msg.Recipients =
VarPtr(recip(1)) msg.FileCount = 1 msg.Files = VarPtr(attach(1)) rc = MAPISendMail(0, 0, msg, 0,
0) If rc <> 0 Then MsgBox "Email
failed; code = " & CStr(rc) End If End
Sub We can now test the above code by pasting it into the AccuTerm 2K2 (or 2000) scripting window and stepping through the code. Be sure to change the Email addresses and the attached file name to something appropriate to your system. Here's the Pick subroutine for the MAPI version: SUBROUTINE
AT.SEND.EMAIL.MAPI(ADDRESS, SUBJECT, MESSAGE, ATTACH) * 05/14/01 02:00PM * ADDRESS: recipient's email address * SUBJECT: one-line email subject * MESSAGE: multi-line email message (lines
separated by AM) * ATTACH: optional attachment file name EQU AM TO CHAR(254), VM TO
CHAR(253), SVM TO CHAR(252) EQU ESC TO CHAR(27), STX TO
CHAR(2), CR TO CHAR(13) EQU EM TO CHAR(25) RECIP.CNT = DCOUNT(ADDRESS,
AM) ATTACH.CNT = DCOUNT(ATTACH,
AM) * SCRIPT = '' SCRIPT = SCRIPT : 'SendMessage' :
EM SCRIPT = SCRIPT : 'End Sub' :
EM SCRIPT = SCRIPT : 'Type
MapiMessage' : EM SCRIPT = SCRIPT : 'Reserved As
Long' : EM SCRIPT = SCRIPT : 'Subject As
String' : EM SCRIPT = SCRIPT : 'NoteText As
String' : EM SCRIPT = SCRIPT : 'MessageType As
String' : EM SCRIPT = SCRIPT : 'DateReceived As
String' : EM SCRIPT = SCRIPT : 'ConversationID
As String' : EM SCRIPT = SCRIPT : 'Flags As Long'
: EM SCRIPT = SCRIPT : 'Originator As
Long' : EM SCRIPT = SCRIPT : 'RecipCount As
Long' : EM SCRIPT = SCRIPT : 'Recipients As
Long' : EM SCRIPT = SCRIPT : 'FileCount As
Long' : EM SCRIPT = SCRIPT : 'Files As Long'
: EM SCRIPT = SCRIPT : 'End Type' :
EM SCRIPT = SCRIPT : 'Type MapiRecip'
: EM SCRIPT = SCRIPT : 'Reserved As
Long' : EM SCRIPT = SCRIPT : 'RecipClass As
Long' : EM SCRIPT = SCRIPT : 'Name As String'
: EM SCRIPT = SCRIPT : 'Address As
String' : EM SCRIPT = SCRIPT : 'EIDSize As
Long' : EM SCRIPT = SCRIPT : 'EntryID As
String' : EM SCRIPT = SCRIPT : 'End Type' :
EM SCRIPT = SCRIPT : 'Type MapiFile'
: EM SCRIPT = SCRIPT : 'Reserved As
Long' : EM SCRIPT = SCRIPT : 'Flags As Long'
: EM SCRIPT = SCRIPT : 'Position As
Long' : EM SCRIPT = SCRIPT : 'PathName As
String' : EM SCRIPT = SCRIPT : 'FileName As
String' : EM SCRIPT = SCRIPT : 'FileType As
String' : EM SCRIPT = SCRIPT : 'End Type' :
EM SCRIPT = SCRIPT : 'Declare
Function MAPISendMail Lib "MAPI32.DLL"' SCRIPT = SCRIPT : ' (ByVal
Session&, ByVal UIParam&,' SCRIPT = SCRIPT : ' Message As
MapiMessage, ByVal Flags&,' SCRIPT = SCRIPT : ' ByVal
Reserved&) As Long' : EM SCRIPT = SCRIPT : 'Sub
SendMessage()' : EM SCRIPT = SCRIPT : 'Dim rc As Long'
: EM SCRIPT = SCRIPT : 'Dim msg As
MapiMessage' : EM IF RECIP.CNT = 1 THEN SCRIPT = SCRIPT
: 'Dim recip As MapiRecip' : EM END ELSE SCRIPT = SCRIPT
: 'Dim recip(':RECIP.CNT - 1:') as MapiRecip' :EM END IF ATTACH <> "" THEN IF ATTACH.CNT =
1 THEN
SCRIPT = SCRIPT : 'Dim attach As MapiFile' : EM END ELSE
SCRIPT = SCRIPT : 'Dim attach(': ATTACH.CNT - 1:') as MapiFile':
EM END END SCRIPT = SCRIPT : 'Dim body As
String' : EM IF RECIP.CNT = 1 THEN SCRIPT = SCRIPT
: 'recip.Name = StrConv("' : FIELD(ADDRESS,'@',1) : '",vbFromUnicode)' :
EM SCRIPT = SCRIPT
: 'recip.Address = StrConv("smtp:' : ADDRESS : '", vbFromUnicode)' :
EM SCRIPT = SCRIPT
: 'recip.RecipClass = 1' : EM END ELSE FOR I=1 TO
RECIP.CNT
SCRIPT = SCRIPT : 'recip(':I-1:').Name = StrConv("' :
ADDRESS<I> : '",vbFromUnicode)' : EM
SCRIPT = SCRIPT : 'recip(':I-1:').Address = StrConv("smtp:' :
ADDRESS<I> : '", vbFromUnicode)' : EM
SCRIPT = SCRIPT : 'recip(':I-1:').RecipClass = 1' : EM NEXT I END IF ATTACH <> '' THEN IF ATTACH.CNT =
1 THEN
SCRIPT = SCRIPT : 'attach.Position = -1' : EM
SCRIPT = SCRIPT : 'attach.Pathname = StrConv("' : ATTACH :
'",vbFromUnicode)' : EM END ELSE
FOR I = 1 TO ATTACH.CNT
SCRIPT = SCRIPT : 'attach(':I-1:').Position = -1' : EM
SCRIPT = SCRIPT : 'attach(':I-1:').Pathname = StrConv("' :
ATTACH<I> : '",vbFromUnicode)' : EM
NEXT I END END ARG = SUBJECT; GOSUB 100 SCRIPT = SCRIPT : 'msg.Subject =
"' : ARG : '"' : EM ARG = MESSAGE; GOSUB 100 SCRIPT = SCRIPT : 'Body = "' :
ARG<1> : '"' : EM N = DCOUNT(ARG, AM) FOR I = 2 TO N SCRIPT = SCRIPT
: 'Body = Body & Chr$(13) & Chr$(10) & "' : ARG<I> : '"'
: EM NEXT I SCRIPT = SCRIPT : 'msg.NoteText =
Body' : EM SCRIPT = SCRIPT : 'msg.RecipCount
= ':RECIP.CNT : EM IF RECIP.CNT = 1 THEN SCRIPT = SCRIPT
: 'msg.Recipients = VarPtr(recip)' : EM END ELSE SCRIPT = SCRIPT
: 'msg.Recipients = VarPtr(recip(0))' : EM END IF ATTACH <> '' THEN SCRIPT = SCRIPT
: 'msg.FileCount = ':ATTACH.CNT : EM If ATTACH.CNT =
1 THEN
SCRIPT = SCRIPT : 'msg.Files = VarPtr(attach)' : EM END ELSE
SCRIPT = SCRIPT : 'msg.Files = VarPtr(attach(0))' : EM END END SCRIPT = SCRIPT : 'rc =
MAPISendMail(0, 0, msg, 0, 0)' : EM SCRIPT = SCRIPT : 'If rc <>
0 Then' : EM SCRIPT = SCRIPT : 'MsgBox "Email
failed; code = " & CStr(rc)' : EM SCRIPT = SCRIPT : 'End If' * PRINT ESC : STX : 'P' : SCRIPT :
CR : RETURN * 100 * Local
subroutine to fixup embedded double-quote marks K = 1 LOOP J = INDEX(ARG,
'"', K) WHILE J DO ARG = ARG[1, J]
: ARG[J, 99999] K = K + 2 REPEAT RETURN This script has been tested with Outlook Express, Outlook 98, Outlook 2000 and Eudora Pro. You may need to adjust you Email client configuration to get things working the way you want. Summary
That's it, three different ways to accomplish the same
exact thing. Of course it’s never that simple. There are lots of little
quirks.
In writing this article we found differences between the way
Outlook Express and Outlook 98/2000 handle Email addresses using the MAPI
routine.
There were also differences between the way Eudora Express and Pro
handled them.
Development these days seems to require a lot of trial and error
but with a little experimentation and a little luck you should be Emailing
from you MultiValue application in no time! Since the original publication of this article, Microsoft has increased security in Outlook, which causes a security warning whenever an external application uses the Outlook object model to send an email. One possible solution to this annoying enhancement is a product called Advanced Security for Outlook from MapiLab (www.mapilab.com/outlook/security). If you have questions or comments on this article, please send them to Peter Schellenbach, pjs@asent.com. The sample Pick/BASIC routines may be downloaded from www.asent.com. Follow the Support link to the Code Samples page. |
||||||||||||||||||||||||||||
|
Questions? comments? webmaster@asent.com © 2003 AccuSoft Enterprises All rights reserved Please see our disclaimer | |||||||||||||||||||||||||||||