Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

.NET

Examining the Microsoft Mail SDK


APR91: EXAMINING THE MICROSOFT MAIL SDK

EXAMINING THE MICROSOFT MAIL SDK

Mail APIs can hide the complexity of network programming

Bruce D. Schatzman

Bruce has worked in the computer industry for more than ten years, holding a variety of positions at corporations including General Dynamics, Tektronix, and Xerox. He is currently an independent consultant and can be reached at P.O. Box 5703, Bellevue, WA 98006.


Whether you are implementing a terminal emulator, a client-server application, or a peer-to-peer file transfer mechanism, serious communications programming can mean hundreds of hours of low-level coding and debugging. Many programmers, in fact, feel that the only "right" way to develop communications software is at a low level, controlling the environment directly through transport or session-layer APIs. A limited selection of general-purpose network APIs such as Apple's Comm Toolbox (see "The Macintosh Communications Toolbox" DDJ, December issue 1990) do exist to make this job easier, but many of the details are left to the programmer. However, there are ways to implement network solutions that require no previous network development experience and considerably less work. One such solution is the mail API included in the Microsoft Mail SDK for the Macintosh. This may sound strange at first, but you can use mail APIs to build an almost unlimited number of peer-to-peer and client-server network applications that have little or nothing to do with mail.

An advantage of using a mail API is that it hides virtually all of the lower-level routines and data structures usually associated with network programming. A good mail API is typically simple and designed for developers who have little experience with network programming. In addition, a transport system consisting of thousands of networks and millions of users is often already in place for your application to use. Also available are third-party mail servers, gateways, bridges, and routers that allow your application to send information around the globe to virtually any PC, Mac, VAX, Unix workstation, IBM mainframe, or fax device.

When to Use a Mail API

If you want to move bits on a cable and maintain a high degree of control over network addressing, performance, routing, or data formatting, you need a general-purpose communications API. You can use a mail API if you don't need this degree of control and if your application deals with files or messages (rather than bytes or packets) as the basic unit of transmission.

When choosing a mail API, you must be willing to accept the architecture, security, fault tolerance, and transport mechanisms that are offered through the mail product. Any application that you build with Microsoft Mail, for example, moves data from point A to point B via MS Mail's store-and-forward scheme. With this in mind, you clearly could not use a mail API to implement "real-time" applications such as a terminal emulation program, but you could certainly use it to develop an automated document management system for workgroups.

Overview of the MS Mail C API (MAPILib.h)

The best way to introduce the MS Mail API is through its most basic service--what Microsoft calls "standard integration with MS Mail." This is a relatively simple job and involves putting appropriate function calls in your application that enable users to retrieve and send mail directly from your application using the MS Mail desk accessory (DA) as the user interface.

When standard integration is implemented, two commands--Open Mail and Send Mail--are dynamically added or removed from an application's File menu each time the user logs into or out of MS Mail through the MS Mail DA. In this way, a user simply chooses the Send Mail command from the File menu and enters the name(s) of the addressee(s) in the default mail form to send the currently open file (spreadsheet, drawing, database table, document, and so on) to another user's mailbox on the server.

Implementing standard integration with MS Mail is straightforward, usually requiring only a couple of days for coding and debugging. Listing One, page 100, presents a code extract (generously provided by Darryl Lovato of Aladdin Systems), from Stuffit, Aladdin's widely used file compression utility.

Beginning with SendMail() in Listing One, a call is made to msmSession-Established(), which returns zero if the workstation is already logged onto a mail account, or a nonzero error code that is sent to MSMError() for handling. Note that MSMError() is not a MAPILib function. If msmSessionEstablished() does not return an error, it is assumed everything is OK and a message is created using msmCreateMess(). msmCreateMess() allocates and initializes a message structure, returning a handle in the variable messh (declared as type MessHdl). All message fields are automatically set to their initial default values, if any. The second parameter states the message type as Mess, which indicates a standard note message. msmCreateMess() returns 0 if it was successful.

Next, a subject is added to the message using msmAddMessSubject(). This function simply sets the subject of message messh to the text in datah. Note that datah is a handle to pure text: It is not a null-terminated C string or a length-encoded Pascal string. Therefore, the size of datah must be less than 255 characters.

Immediately after completion of msmAddMessSubject(), datah is disposed to free its memory. Failure to dispose of handles properly is one of the few areas that can cause problems in your program. Keep this in mind. In this case, the memory is released with the standard Mac Toolbox call to DisposHandle().

Because SendMail() is embedded within a compression application, it assumes that you are sending an archive as a file enclosure. To access the archive file, the appropriate parameter block fields are specified. The parameter block is then passed to the Toolbox routine PBOpenWD(), which opens the file's data fork.

Next, msmAddMessEnclosure() associates the selected archive file with the message messh, which must reference an unsent message. It now must be determined where the message will be sent. To accomplish this, the routine msmDisplaySendDocScreen() puts a standard mail form on the screen for the user to interactively edit, address, and send the message.

Because this application was written with version 2.0 of the MS Mail SDK, msmDisplaySendDocScreen() has only one parameter--the message handle. The recently introduced MS Mail SDK 3.0 has an improved msmDisplaySendDocScreen() that includes two additional parameters: allowEnclAccess, which allows the user to add or remove enclosures before sending; and send, which checks to see whether the user pressed the Send or Cancel button.

It is important to note that the 2.0 version of msmDisplaySendDocScreen() automatically frees the message's memory, regardless of whether Send or Cancel was pressed. In 3.0, it will dispose of the handle only if send is true. This is a good illustration of why you must always keep disposing of handles in mind, being especially diligent when upgrading your application to the 3.0 SDK.

Like SendMail, OpenMail() demonstrates the use of the MAPILib routines. msmDisplayMessageCenter, for instance, displays a Message Center dialog box similar to that used by the MS Mail DA, allowing the user to browse through mail messages and open any, if desired. Another routine, msmGetListNumEnclosures returns the number of file enclosures selected in the mail message list specified by its one parameter, which is a handle to a message list.

Next, msmGetListEnclosureName returns the name of the jth file enclosure referenced in the mail message list, and msmGetListEnclosure saves the jth file enclosure to the disk in the current working directory under a specified file name. Finally, the MAPILib routine, msmGetListEnclosureComments, displays a mail form that contains the message body and addressing information of the specified mail message.

MAPILib 3.0

MAPILib 3.0 contains about 110 functions that can be grouped into categories, a few of which are shown in Figure 1. Some function prototypes are also listed to show what the routines in the API look like.

Figure 1: Function categories in MAPILib with sample function prototypes

  Accessing Mail Accounts
  Examples: msmGetUserName (Boolean fullname, Handle *namehp)
            msmGetServerName (Handle *namehpo)

  Creating Mail Messages
  Examples: msmCreateMess (MessHdl *messhp, long type)
            msmCreateReplyMess (MessHdl *messhp, MessHdl original,
                               Boolean replyAll, Boolean copyText)

  Addressing Mail Messages
  Examples: msmAddMessRecipient (MessHdl messh, Handle datah)
            msmAddMessCcRecipient (MessHdl messh, Handle datah)

  Sending Mail Messages
  Examples: msmSendMess (MessHdl messh)
            msmAddMessCcRecipient (MessHdl messh, Handle datah)

  Reading Mail Messages
  Examples: msmReadMess (MessHdl *messhp, long type, long id, short fldrld)
            msmGetMessSubject (MessHdl messh, Handle *datahp)
            msmGetMessSender (MessHdl messh, Boolean fullname, Handle
                              *namehp)

There are also sets of routines for printing messages, manipulating forms, accessing a mail database, adding file enclosures to messages, accessing mail folders, and a variety of other functions. These are all documented within the MAPILib header file itself. A few of the new routines included within MAPILib 3.0 are listed in Figure 2.

Figure 2: MAPILib 3.0 functions

  Function                     Description
  -------------------------------------------------------------------------

  msmEnableDialog()            Disables the looking for server, progress,
                               and notify dialog boxes
  msmChooseServer()            "Chooses" to another server
  msmGetListField()            Extracts information from mail lists
  msmGetMultiListEnclosures()  Reads multiple file enclosures in one step
  msmPrintMessList()           Prints multiple messages in a single job
  msmMoveMess()                Moves a message between folders
  msmCopyMess()                Copies a message between folders
  msmCreateFldr()              Creates a user folder

Using Mail for General Network Communications

Once you are familiar with the MAPILib library, you will begin to see possibilities that go beyond standard integration with mail. The key is to view the API routines not as a mail interface, but as a general message-passing interface that interacts transparently with a network.

From this perspective, each mail message can be thought of as a "generic" network message that can contain any data and be acted upon in any way. Figure 3 shows a standard mail message. The message consists of a variable number of objects known as "message fields." Each message field contains an identifier, type, and N bytes of associated data, where N is limited only by the space available on the mail server and by the memory available on the client. Very long messages can be encoded as file attachments to get around memory limitations.

An application can use the standard (default) message formats, or build custom message types (using MAPILib functions) to suit specific application requirements. Standard mail message types include those shown in Figure 4.

Figure 4: Some of the standard mail message types

  Function              Description
  ---------------------------------------------------------------------

  smMessTypeNote        Simple text-only message
  msmMessTypeImage      A graphic image, such as TIFF
  msmMessTypePhone      A phone message
  msmMessTypeAssist     A request for network administrator assistance
  msmMessTypeAppleLink  AppleLink message
  msmMessTypeVoice      Voice messages (works with a variety of third
                         party sound drivers)
  msmMessType80Col      A message in 80 column format for use with PCs

Once the appropriate message formats are identified or built, you can use MAPILib functions to manipulate them, with or without interaction, through the MS Mail DA user interface. The msmLogOn() function can set up a logon session with a server that either goes through the DA or through your own application-specific session. Consider the statement in Example 1.

Example 1: Using MsmLogOn() to log onto a server either through the DA (if fDA is true, or forcing the application to directly log into the mail system (if fDA is false).

  msmLogOn (Boolean fDA, StringPtr stUsername, StringPtr stPassword,
                                                StringPtr stPrompt);

If fDA is false, the DA is bypassed and the application logs into the mail system directly. You can then scan for new messages in the background, retrieve them, and act on them without user intervention. In other words, because the content of a message is arbitrary (it can easily include binary data) and MAPILib gives you a general message-handling interface, you can build a broad range of fairly sophisticated network applications.

Custom Forms

The standard form that comes with MS Mail handles only mail applications. If you build a more general application that involves any type of user interaction, you will usually create custom forms that are designed to obtain input and display messages in an application-specific format. This can be done in one of two ways. The first and easiest involves no programming at all. Use the HyperCard-based MS Mail Form Designer to create new forms interactively and store them on the server. This is another advantage of using a mail program to build network applications--user interface screens can be designed and stored without writing a single line of code.

For the most sophisticated applications, MS Mail supports special Form Control Procedures (FCPs) that allow developers to control forms programmatically and extend the functionality of the mail system. FCPs have their own API that is separate from the functions provided in MAPILib and is useful for interfacing to specialized hardware drivers. Be aware, however, that the FCP API is a lower-level interface that is much more difficult to use than MAPILib. In general, the recommendation is to use the Form Designer and MAPILib whenever possible.

As an example of an FCP, consider a special "sound" form that is custombuilt and installed on the server under a unique form ID number. When the user clicks a sound icon in their Mail DA, the form and its associated FCP are automatically downloaded from the server. The FCP contains the information to display the form to the user as well as the code that deals with processing the form. The form would then appear and display "play" and "record" buttons that interface with Apple or third-party sound drivers, allowing you to record your voice and send it to anyone as compressed digital voice mail. In this case, the message would be of type "Voice" and would be in binary format, perhaps along with other nonbinary enclosures.

Other Tools

The C SDK is one of several MS Mail programming environments. Excel and HyperCard have macro and script equivalents for most of the MAPILib functions. C programmers should note that either of these programs can act as a prototyping tool by which you can build your application and test it at a very high level before you begin coding in C. Since Excel and HyperCard have user interface tools as well, you can also experiment quickly with different forms and dialog boxes before spending time with C libraries. Even if you have no intention of using Excel or HyperCard, you should be aware that the documentation provided with these two SDKs is more complete than the C SDK and can provide better insight into the operation of each MAPILib function. Naturally, you can use both the Excel and HyperCard SDKs as full-blown development environments, not just as prototyping tools. However, Excel and HyperCard are not well-suited for applications that involve large amounts of data and/or transactions.

The MS Mail Excel Macro Library is actually an Excel Macro sheet which includes about 65 macro functions that may also make calls to external ("registered") code that is supplied with Mail's file resources fork. Programming with Excel macros does not give you the flexibility that programming directly with MAPILib does, but you can access virtually all of the mail functionality.

Mail messages that are related to specific information stored in an Excel spreadsheet or database can be created, sent, or read by users and/or other applications. For example, if a finance department keeps their company-wide budget allocations in an Excel spreadsheet, a report with a corresponding chart and message could be automatically generated and sent to each department head at the end of the month to inform them of their budget status. The macro program would be initiated by including the macro shown in Example 2 in the Excel application which opens and initializes the Excel Mail library. The macro could also store monthly budget results in the mail database for each of the department heads. The four macro calls shown in Example 3 would be the foundation for the routine.

Example 2: Macro to open and initialize the Excel Mail library.

  =IF (ISERROR (OPEN ("Mail", FALSE, TRUE))
   ,ALERT ("Cannot find the 'Mail'
   library in this directory",2))

  =IF (Mail!INIT.MAIL.LIBRARY)()<0,ALERT
   ("Could not initialize the Mail
   library", 2))

Example 3: Macro calls to steup[SP] a store monthly budget results in the mail

  =GET.USER.LIST (Mail!msmLocalList, "user name sub-string" ,1,1)
  =GET.LIST.ELEMENT.FIELD (AValidHandle,1,MAIL!msxUserLEF ID)
  =SET.APPSIG(0,0)
  =ADD.DATABASE.ELEMENT (0, Local_user_key,user_list)

Each of the above calls (as well as the other macro functions) have C language equivalents in MAPILib. The HyperCard SDK extends the HyperTalk script language in a similar manner, and is suitable for applications that involve a high degree of user interaction.

Products Mentioned

Microsoft Mail Version 3.0 SDK Microsoft Corporation One Microsoft Way Redmond, WA 98052-6399 206-882-8080 MPW C SDK: $445.00 System Requirements: MPW 3.0 Note: HyperCard SDK is available free over AppleLink.

Conclusion

Mail APIs are important tools for developers who want to quickly implement network applications without the traditional hassle of low-level communications programming. They are simple, robust, high-level, and provide great flexibility for applications that are file or message-oriented. While Apple's Comm Toolbox or other low-level API is the only choice for some situations, a mail API can save hundreds of hours of development time yet allow you to build extremely sophisticated applications.

Acknowledgments

I would like to thank the people at Information Research Corporation, which markets a workgroup-oriented project tracking application called Syzygy, for their assistance and comments while I was preparing this article.


_EXAMINING THE MICROSOFT MAIL SDK_
by Bruce D. Schatzman

[LISTING ONE]
<a name="00f2_0014">

#include <MacHeaders>
#include "MAPILib.h"
#include "MAPIErrs.h"
#include "stuffit.h"

extern char string[256];
extern arcRecord *myArc;
extern WindowPtr windows[8];
extern EventRecord pss;
extern HParamBlockRec HRec;

SendMail()
{
    int i, j;
    MessHdl messh;
    Handle datah;
    SFReply myReply;

    i = msmSessionEstablished ();
    if ( i )
    {
        MSMError ( i );
        return;
    }

    i = msmCreateMess ( &messh, 'Mess' );

    /*  the message was successfully created */
    if ( i == 0 )
    {
        i=PtrToHand ( &myArc->arcName[1], &datah, myArc->arcName[0] );
                    /* Add archive name as subject */
        if ( i == 0 )
        {
            i = msmAddMessSubject ( messh, datah );
            DisposHandle ( datah );
        }
        if( i== 0)     /*add default body */
        {
           GetIndString ( string, 260, 1 );
           i = PtrToHand ( &string[1], &datah, string[0] );
           if ( i == 0 )  i = msmAddMessBody( messh, datah );
           DisposHandle ( datah );
        }

        /*  Now add the archive as an enclosure */
        HRec.wdParam.ioNamePtr = 0L;
        HRec.wdParam.ioVRefNum = myArc->arcVol;
        HRec.wdParam.ioWDProcID = 'SIT! ';
        HRec.wdParam.ioWDDirID = myArc->arcDir;

        PBOpenWD ( &HRec, FALSE );

        i = msmAddMessEnclosure ( messh, myArc->arcName,
                         j = HRec.wdParam.ioVRefNum, 0L );

        /*  Now find out where to send it */
        if ( i == 0 )
        {
            i = msmDisplaySendDocScreen ( messh );
            /*  this now disposes of messh.. */
        }
        else
        {
        /*  we're done with it  */
        msmDisposeMess ( messh );
        }
        HRec.wdParam.ioVRefNum = j;
        PBCloseWD ( &HRec, FALSE );
    }
    if ( i == msmTErrCancelled )  i = 0;
    if ( i )  MSError ( i );
}

OpenMail ()
{
    int i, numItems, j, vol;
    long dir;
    SFTypeList myList;
    Handle resh;
    SFReply myReply;
    char str[32];
    int numOpen = 0;
    Point startPt;

    i = msmSessionEstablished ();
    if ( i )
        MSMError ( i );
        return;
    }

/*  How many archives are open now ? */
    for ( i = 0; i < 8 ; i++)
    {
        if ( windows[i] ) numOpen++;
    }
    myList[0] = 'SIT! ';
    myList[1] = 'SIT2 ';
    myList[2] = 'SITD ';

    i = msmDisplayMessageCenter (3, myList, 0L, 0, &resh);

    if ( i == 0)
    {
        EventAvail ( 0, &pss );
        numItems = msmGetListNumEnclosures ( resh );
        for ( j = 0; j < numItems && i == 0; ++j )
            {
                i = msmGetListEnclosureName (resh, j ,
                            (StringPtr) string );
                if ( i == 0 )
                {
            GetIndString (str, 260, 2); /* "Save enclosure as :" */
            startPt.h = (screenBits.bounds.right -
                        screenBits.bounds.left) / 2 - 158;
            startPt.v = 80;
            SFPutFile ( startPt, (StringPtr) str, (StringPtr)
                         string, (ProcPtr) oL, &myReply );
            if ( myReply.good )
            {
            if ( i == 0 )
            i = msmGetListEnclosure ( resh, j, TRUE,
                     myReply.fName, myReply.vRefNum, &myReply);
            if (( i == 0 ) && ( numOpen < 8 ))
                       /* open archive if room permits */
              {
            StartSpinCursor ();
            GetWDVolDir ( myReply.vRefNum, &vol, &dir );
            if ( !ActualOpen (vol, dir, myReply.fName, 0L, 0,
                              0L, pss.modifiers ))
            break;
            if ( pss.modifiers & shiftKey )
            DoAutoUnsit ( FALSE );
            AutoView ();
            numOpen++;
            StopSpinCursor ();
               }
               if ( i == 0)
                i = msmGetListEnclosureComments ( resh, j );
                }
              }
            }
            StopSpinCursor ();
                DisposHandle ( resh );
            }

            if ( i == msmTErrCancelled )  i = 0;
            if ( i )  MSMError ( i );
}

MSMError ( i )
{
    char str2[256];
    NumToString ( i, (StringPtr) string );
    switch ( i )
    {
        case msmNoDriver:
            GetIndString ( str2, 259, 3 );
            /*  "MS Mail was not loaded at startup. " */
            break;
        case msmNoServer:
            GetIndString (str2, 259, 1 );
            /*  "You are not connected to a MS Mail server. "  */
            break;
        case msmDErrNotLoggedOn:
            GetIndString (str2, 259, 13 );
            /*  "You are not logged onto the MS Mail server. " */
            break;
        default:
            GetIndString ( str2, 259, 2 );

            /* " Miscellaneous MS Mail Error. "  */
            break;
    }
    ParamText (str2, string, 0, 0 );
    StopCAlert ( 296, 0L );
}


Copyright © 1991, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.