Search Site

 
 

 

 

 

 
 

Manuals

FAQs

Downloads

Technical Articles

Code Samples

Support Request

Products

 

page < 1 - 2 >

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.

page < 1 - 2 >

 

Questions? comments? webmaster@asent.com © 2003 AccuSoft Enterprises All rights reserved  Please see our disclaimer