0% found this document useful (0 votes)
64 views858 pages

MAPI, SAPI, and TAPI Developer's Guide

MAPI,SAPI, and TAPI Developer's Guide

Uploaded by

heemsoft
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
64 views858 pages

MAPI, SAPI, and TAPI Developer's Guide

MAPI,SAPI, and TAPI Developer's Guide

Uploaded by

heemsoft
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 858

MAPI,

SAPI, and
TAPI Developer's Guide

by Michael C. Amundsen

C O N T E N T S

Introduction to MAPI, SAPI, and TAPI Developer's Guide

Chapter 1 Windows Communications Overview

● MAPI Message Services


● TAPI Telephony Services
● SAPI Speech Services
● MCI Multimedia Services
● Applications Covered in This Book
● Development Tools
● Hardware and Software Tools
● What's Not Covered in This Book
● How to Use This Book

Chapter 2 Introduction to Windows Open Services Architecture (WOSA)

● What Is WOSA?
● The WOSA Model
❍ The Client API Makes Requests

❍ The Server SPI Responds to Requests

❍ The Interface DLL Talks to Both the API and SPI

● WOSA Services
❍ Common Application Services

❍ Communication Services

❍ Vertical Market Services

● Benefits of WOSA
❍Isolated Development
❍ Multivendor Support

❍ Upgrade Protection

❍ Leveraging WOSA in Your Own Applications

● Summary

Chapter 3 What is MAPI

● MAPI Services and Windows


❍ Flexibility

❍ Consistency

❍ Portability

● Messages
❍ Text Messages

❍ Formatted Documents and Binary Files

❍ Control Messages

● MAPI Applications
❍ Electronic Mail Clients

❍ Message-Aware Applications

❍ Message-Enabled Applications

❍ Other Types of Message Applications

● Summary

Chapter 4 MAPI Architecture

● The MAPI Client


❍ Messages and Attachments

❍ Storage Folders

❍ Addresses

● The MAPI Server


❍ Message Transport

❍ Message Stores

❍ Address Books

● The MAPI Spooler


● Summary

Chapter 5 Using the Microsoft Exchange Forms Designer

● Introduction
● What Is the Microsoft Exchange Forms Designer?
❍ EFD Design Wizards

❍ The QuickHelp Feature

❍ Extending EFD with Visual Basic

● Using the Microsoft Exchange Forms Designer to Create a Custom Form


❍ Using the Microsoft Exchange Forms Designer Wizard

❍ Modifying the Job Request Form

❍ Completing the Job Request Form Fields

❍ Setting Form and Window Properties of the Job Request Form


❍ Adding Online Help to the Job Request Form
❍ Installing the Job Request Form

❍ Testing the Job Request Form

❍ More Forms Designing Techniques

● Designing Microsoft Exchange Folders


❍ Creating and Managing Folder Views

❍ Installing Forms in Folders

● Summary

Chapter 6 Creating MAPI-Aware Applications

● The Simple MAPI API Calls


❍ The User-Defined Types

❍ The API Functions

● Creating Mail-Aware Applications


❍ Creating QIKMAIL.XLS with Excel

❍ Adding MAPI Services to Existing Visual Basic 4.0 Programs

● Summary

Chapter 7 Creating a Simple MAPI Client with the MAPI Controls

● The Visual Basic MAPI Controls


❍ The MAPISession Control

❍ The MAPIMessage Control

● Building a Simple MAPI Client Application


❍ Laying Out the Forms

❍ Coding the Main Support Routines

❍ Coding the MAPIMAIN.FRM Events

❍ Coding the Main Button Bar

❍ Coding the Reader Form

❍ Running the Simple Mail Client

❍ Additional Features

● Summary

Chapter 8 The OLE Messaging Library

● Introduction
● The Session Object
❍ The Session Object Methods

❍ The Session Object Properties

● The InfoStore Objects and Collections


❍ The InfoStores Collection

❍ The InfoStore Object

● The Folder Objects and Collections


❍ The Folders Collection Object

❍ The Folder Object

❍ The Inbox and OutBox Folders

● The Message Objects and Collections


❍ The Messages Collection Object
❍ The Message Object

● The Recipient Objects and Collections and the Address Object


❍ The Recipients Collection Object

❍ The Recipient Object

❍ The AddressEntry Object

● The Attachment Objects and Collections


❍ The Attachments Collection Object

❍ The Attachment Object

● Summary

Chapter 9 Creating a MAPI Mailing List Manager with the OLE Messaging
Library

● Introduction
❍ Laying Out the MLM Form

❍ Coding the Support Routines

❍ Coding the Edit Routines

❍ Coding the MAPIStart and MAPIEnd routines

❍ Coding the SendMail Routines

❍ Coding the Inbox Routines

❍ Adding New Subscribers

❍ Dropping Subscribers

❍ Listing Archives

❍ Sending Requested Archives

● Running the MLM Application


❍ Testing the MLM Application

● Summary

Chapter 10 Building a MAPI-Enabled Fourm Tool

● Discussion Groups versus E-Mail


❍ Folders as Destinations

❍ Using the ConversationTopic and ConversationIndex Properties

❍ Update versus Send

● The Discuss Project


● The MAPIPost Code Library
● The Discuss and Msgs Forms
❍ Laying Out the Discuss Form

❍ Coding the Discuss Form

❍ Laying Out and Coding the Msgs Form

● Building the Other Forms


❍ The Note Form

❍ The Options Form

❍ The About Dialog Box

● Testing the Discuss Forum Tool


● Summary
Chapter 11 Creating a MAPI Email Agent

● Designing the Email Agent


❍ Features of the MAPI Email Agent

❍ Storing the Rules in a Control File

● Coding the MAPI Email Agent Forms


❍ The Main Form

❍ The Add Rule Form

❍ The Setup Form

● Coding the Support Routines


❍ The Initialization Routines

❍ The List-Handling Routines

❍ The Message Processing Routines

● Installing and Testing the MAPI Email Agent


❍ Setting Up Your Email Folders

❍ Building MAPI Email Agent Actions, Tests, and Rules

❍ Running the MAPI Email Agent

● Summary

Chapter 12 Creating Windows Messaging Client Extensions

● What Are Exchange Client Extensions?


❍ How Microsoft Exchange Client Extensions Work

❍ Advantages of Microsoft Exchange Client Extensions

❍ The Microsoft Exchange Client Contexts

❍ The Microsoft Exchange COM Interface

❍ Mapping Contexts to COM Interfaces

❍ Message Event Extensions

❍ Property Extensions

❍ Registering Extensions

● Creating the Message Signing Extension


❍ Building the Initial Header File

❍ Coding the Main DLL Routines

❍ Laying Out and Coding the Property Sheet Dialog Box

● Installing and Testing the Message Signing Extension


❍ Running the Message Signing Extension

● Summary

Chapter 13 Part II Summary-The Messaging API

● Chapter 3, "What Is MAPI?"


● Chapter 4, "MAPI Architecture"
● Chapter 5, "Using the Microsoft Exchange Forms Designer"
● Chapter 6, "Creating MAPI-Aware Applications"
● Chapter 7, "Creating a Simple MAPI Client with the MAPI Controls"
● Chapter 8, "The OLE Messaging Library"
● Chapter 9, "Creating a MAPI Mailing List Manager with the OLE Messaging Library"
● Chapter 10, "Building a MAPI-Enabled Forum Tool"
● Chapter 11, "Creating a MAPI E-Mail Agent"
● Chapter 12, "Creating Microsoft Exchange Client Extensions"

Chapter 14 What Is SAPI

● Speech Recognition
❍ Word Separation

❍ Speaker Dependence

❍ Word Matching

❍ Vocabulary

❍ Text-to-Speech

❍ Voice Quality

❍ Phonemes

❍ TTS Synthesis

❍ TTS Diphone Concatenation

● Grammar Rules
❍ Context-Free Grammars

❍ Dictation Grammars

❍ Limited Domain Grammars

● Summary

Chapter 15 SAPI Architecture

● Introduction
● High-Level SAPI
❍ Voice Command

❍ Voice Text

● Low-Level SAPI
❍ Speech Recognition

❍ Text-to-Speech

● Speech Objects and OLE Automation


❍ OLE Automation Speech Recognition Services

❍ OLE Automation Text-to-Speech Services

● Summary

Chapter 16 SAPI Basics

● SAPI Hardware
❍ General Hardware Requirements

❍ Software Requirements-Operating Systems and Speech Engines

❍ Special Hardware Requirements-Sound Cards, Microphones, and Speakers

● Technology Issues
❍ SR Techniques

❍ SR Limits

❍ TTS Techniques

❍ TTS Limits

● General SR Design Issues


● Voice Command Menu Design
● TTS Design Issues
● Summary

Chapter 17 SAPI Tools-Using SAPI Objects with Visual Basic 4.0

● OLE Voice Text Object


❍ Using the Visual Basic Object Browser

❍ Using the Register Method to Connect to the TTS Engine

❍ Using the Enable Property to Start and Stop the TTS Engine

❍ Using the Speak Method to Play Text

❍ Adjusting the Speed of Voice Playback

❍ Adding Playback Controls for TTS Services

❍ Getting TTS Status Reports with the IsSpeaking Property

❍ Establishing a TTS Callback in Visual Basic 4.0

● OLE Voice Command Objects


❍ Creating the Voice Command Menu Object

❍ Using the Register Method to Connect to the SR Engine

❍ Using the Awake Property to Start and Stop SR Processing

❍ Creating the Menu Object

❍ Adding Commands to the Voice Menu Object

❍ Using the CommandSpoken Property to Respond to Menu Commands

❍ Establishing an SR Callback in Visual Basic 4.0

❍ Creating List Commands for the Voice Menu Object

❍ Removing Commands from the Voice Menu Object

● Summary

Chapter 18 SAPI Behind the Scenes

● Control Tags
❍ The Voice Character Control Tags

❍ The Phrase Modification Control Tags

❍ The Low-Level TTS Control Tags

● Grammar Rules
❍ General Rules for the SAPI Context-Free Grammar

❍ Creating and Compiling a SAPI Context-Free Grammar

❍ Loading and Testing SAPI Context-Free Grammars

● International Phonetic Alphabet


● Summary

Chapter 19 Creating SAPI Applications with C++

● The TTS Demo Project


● The VCMD Demo Project
● Summary

Chapter 20 Building the Voice-Activated Text Reader

● Designing the Application


❍ Adding TTS Services
❍ Adding SR Services

● Coding the MDISpeech Module


❍ Declaring the Global Variables

❍ Coding the InitSAPI and UnInitSAPI Routines

❍ Coding the InitVoice Routine

❍ Coding the InitVText Routine

❍ Coding the VTextAction Routine

● Modifying the MDINote Forms


❍ Modifying the MDI Form

❍ Modifying the NotePad Form

● Testing the SAPI-Enabled MDI NotePad


● Summary

Chapter 21 Part III Summary - The Speech API

● Chapter 14, "What Is SAPI?"


● Chapter 15, "SAPI Architecture"
● Chapter 16, "SAPI Basics"
● Chapter 17, "SAPI Tools-Using SAPI Objects with Visual Basic 4.0"
● Chapter 18, "SAPI Behind the Scenes"
● Chapter 19, "Creating SAPI Applications.with C++"
● Chapter 20, "Building the Voice-Activated Text Reader"
● The Future of SAPI

Chapter 22 What Is TAPI?

● The Telephony API Model


❍ Lines

❍ Phones

● TAPI and the WOSA Model


● Typical Configurations
❍ Phone-Based Configurations

❍ pc-Based Configurations

❍ Shared or Unified Line Configurations

❍ Multiline Configurations

● Telephone Line Services


❍ The Telephone Switching Network

❍ Plain Old Telephone Service (POTS)

❍ Digital T1 Lines

❍ Integrated Services Digital Network (ISDN)

❍ Private Branch Exchange (PBX)

● Summary

Chapter 23 TAPI Architecture

● Assisted Telephony Services


● Basic Telephony Services
❍ The Basic Telephony Line Device API Set
❍ The Basic Telephony Line Device Structures

❍ Basic Telephony Line Device Messages

● Supplemental Telephony Services


❍ Supplemental Telephony API for Line Devices

❍ Supplemental Telephony API for Phone Devices

❍ The Supplemental Telephony Phone Device Structures

❍ The Supplemental Telephony Phone Device Messages

● Extended Telephony Services


● Summary

Chapter 24 TAPI Basics

● Using TAPI to Place Outbound Calls


❍ Calling lineInitialize to Start the TAPI Session

❍ Calling lineNegotiateAPIVersion to Check TAPI Services

❍ Using lineOpen to Locate an Appropriate TAPI Line Device

❍ Setting Call Parameters with the LINECALLPARAMS Structure

❍ Using lineMakeCall to Place the Call

● The TAPIOut Project


❍ The Initial Declarations

❍ The User Dialog Box and the WinMain Procedure

❍ The PlaceCall Function

❍ The ShowProgress and SetVarProps Procedures

❍ The lineCallBackProc Procedure

● Testing the TAPIOut Project


● Summary

Chapter 25 TAPI Hardware Considerations

● Modems and the UniModem Drivers for Win95 and WinNT


❍ A Quick Review of How Modems Work

❍ The Universal Modem Drivers and TAPI Service Providers

● Basic Data Modems


● Data Modems with Voice
● Telephony Cards
● Summary

Chapter 26 TAPI Tools-Using the TAPILINE Control

● The TAPILINE Control


❍ Installing the TAPILINE Control

❍ The TAPI Control's TapiCallBack Event

❍ The TAPILINE.OCX Properties

❍ The TAPILINE.OCX Methods

❍ The TAPILINE DLL Functions

● The TAPILINE Test Project


❍ The DLL Declarations and General TAPI Constants
❍ The Line Structures and Constants
● The TAPILine Function Module
❍ The TAPICallBackHandler Function

❍ A Sample CallState Message Function

❍ The Clean and OffSet Functions

● Laying Out the TAPILine Form


● Coding the TAPILine Form
❍ Adding the Support Routines

❍ Adding the Form Event Code

❍ Adding the Button Event Code

❍ Displaying Call Information and Address Status

❍ Displaying TAPI Dialog Boxes

● Summary

Chapter 27 TAPI Behind the Scenes--The TELEPHON.INIFile

● Building the TAPI Dialog Utility Program


❍ Laying Out the TAPI Dialog Utility Form

❍ Coding the TAPI Dialog Utility Project

● The TELEPHON.INI File


● TAPI Service Provider Information
❍ The Providers Section

❍ The Provider-Specific Section

● Handoff Priorities Information


● Dialing Location Information
❍ Modifying the Location Values with the lineTranslateDialog Method

● Credit Card Dialing Instructions


❍ Understanding the TAPI Dialing Rules

❍ Testing the Calling Card Settings of TAPIDLG.EXE

● Summary

Chapter 28 Using TAPI to Handle Incoming Calls

● Process Flow for Inbound Calls


● The TAPIANS Project
❍ Creating the TAPIANS Form

❍ Coding the TAPIANS Form

❍ Testing TAPIANS

● Summary

Chapter 29 Writing TAPI-Assisted Applications

● The Assisted Telephony API Calls


❍ Testing the Assisted TAPI Functions

● Creating the QikDial Application Using Excel 95


● Writing the TeleBook Application Using Visual Basic 4.0
❍ Creating the TeleBook Class Module

❍ Coding the TeleBook Main Form


❍Coding the TeleBook Phone Entry Form
❍ Coding the Phone Log Form for TeleBook

❍ Coding the Support Routines for the TeleBook Application

❍ Running the TeleBook TAPI Application

● Summary

Chapter 30 Creating TAPI-Enabled Applications

● Designing the TAPIFONE Application


● The libTAPI Module
● frmTAPI-The Main Form
❍ Laying Out the frmTAPI Form

❍ The frmTAPI Menu

❍ Coding the frmTAPI Form

● The Call and About Dialog Boxes


❍ Laying Out and Coding the Call Dialog Box

❍ Laying Out and Coding the About Dialog Box

● Testing TAPIFONE
● Summary

Chapter 31 Third Party TAPI Tools

● The Visual Voice Telephony Toolkit for Windows


❍ The Voice Control

❍ The Test Control

❍ The Vlink Control

❍ The Voice Workbench

❍ The Voice Monitor

❍ The TAPI Examiner and the Stylus Trace Applications

● Pronexus' VBVoice Development Kit


❍ The VBFrame Control

❍ The VBVoice Controls

❍ The Process of Building a VBVoice Application

❍ VBVoice and Voice Files

❍ VBVoice Test Mode

● Microsoft Phone
❍ Adding Announcement, Message, and AutoFax Mailboxes

❍ Configuring the Microsoft Phone Answering Machine

● Summary

Chapter 32 Part IV Summary-The Telephony API

● Chapter 22, "What Is TAPI?"


● Chapter 23, "TAPI Architecture"
● Chapter 24, "TAPI Basics"
● Chapter 25, "TAPI Hardware Considerations"
● Chapter 26, "TAPI Tools-Using the TAPILINE Control"
● Chapter 27, "TAPI Behind the Scenes-The TELEPHON.INI File"
● Chapter 28, "Using TAPI to Handle Incoming Calls"
● Chapter 29, "Writing TAPI-Assisted Applications"
● Chapter 30, "Creating TAPI-Enabled Applications"
● Chapter 31, "Third-Party TAPI Tools"
● The Future of Telephony and TAPI

Chapter 33 Design Considerations for Integrated Communications


Applications

● Introduction
● General Considerations
❍ Rules of Complexity

❍ Native API versus High-Level Tools

❍ Standalone versus Extensions and Add-ins

● MAPI versus Comm API


● Assisted TAPI versus Full TAPI
● When to Use the Speech API
● Summary

Chapter 34 Building the FaxBack Application

● Introduction
● Project Resources
❍ The VBVoice Controls

❍ The FAX.VAP Voice Prompt File

❍ The SMAPI Support Modules

● Starting the FaxBack Project


● Coding the FaxBack Support Module
● The FaxBack Form
❍ Laying Out the FaxBack Form

❍ Setting the VBVoice Control Properties

❍ Coding the FaxBack Form

● The About Dialog Box


● Testing the FaxBack Application
● Summary

Chapter 35 Creating the Voice Phone Application

● Project Resources
● Coding the Library Modules
❍ The AssistedTAPI Module

❍ The CallBack Modules

● Building the LibVPhone Module


❍ Adding the Voice Command Routines

❍ Adding the Voice Text Routines

❍ Adding the Database Engine Routines

❍ Adding the TAPI and Form Support Routines

● Laying Out the VPhone Form


● Coding the VPhone Form
● Laying Out the Support Forms
❍ The VRec Form

❍ The VHelp Form

❍ The VAbout Form

● Testing Voice Phone


● Summary

Chapter 36 The Talk Mail Project

● Design Considerations
❍ Project Forms and Resources

● Coding the LibTalkMail Module


● The tmView Form
❍ Laying Out tmView

❍ Coding tmView

❍ Coding the Form Events

❍ The Control Building Code

❍ The Menu Support Routines

❍ Coding the Control Events

● The tmNew and tmRead Forms


❍ Laying Out tmNew

❍ Coding tmNew

❍ Laying Out tmRead

❍ Coding tmRead

● The tmAbout Box


● Testing Talk Mail
● Summary

Chapter 37 Integration Summary

● Design Issues
● The FaxBack Application
● The Voice Phone Application
● The Talk Mail Project
● Some Final Remarks

appendix A MAPI Resources

● Books
● Web Links
● Other Online Resources

appendix B SAPI Resources

● Books
● Web Links
● Other Online Resources
● Software and Hardware Resources

appendix C TAPI Resources

● Books
● Web Links
● Other Online Resources
● Software and Hardware Resources

appendix D The CD-ROM Contents

This book is dedicated to Joe and Ida LaSala in thanks for their love, support, and
generosity over the last twenty years.

Copyright © 1996 by Sams Publishing

FIRST EDITION

All rights reserved. No part of this book shall be reproduced, stored in a retrieval system, or transmitted by any means,
electronic, mechanical, photocopying, recording, or otherwise, without written permission from the publisher. No patent
liability is assumed with respect to the use of the information contained herein. Although every precaution has been
taken in the preparation of this book, the publisher and author assume no responsibility for errors or omissions. Neither
is any liability assumed for damages resulting from the use of the information contained herein. For information, address
Sams Publishing, 201 W. 103rd St., Indianapolis, IN 46290.

International Standard Book Number: 0-672-30928-9

HTML conversion by :
M/s. LeafWriters (India) Pvt. Ltd.
Website : http://leaf.stpn.soft.net
e-mail : [email protected]

Trademarks

All terms mentioned in this book that are known to be trademarks or service marks have been appropriately capitalized.
Sams Publishing cannot attest to the accuracy of this information. Use of a term in this book should not be regarded as
affecting the validity of any trademark or service mark.

President and Publisher Richard K. Swadley


Publishing Team Leader Greg Wiegand
Managing Editor Cindy Morrow
Director of Marketing John Pierce
Assistant Marketing Managers Kristina Perry, Rachel Wolfe

Tony Amico, Andrew


Acquisitions Editor Sharon Cox Development Editor
Fritzinger
Software Development
Steve Straiger Production Editor Mary Ann Faughnan
Specialist
Indexer Chris Barrick Technical Reviewer John Charlesworth
Technical Edit
Editorial Coordinator Bill Whitmer Lynette Quinn
Coordinator
Resource Coordinator Deborah Frisby Editorial Assistants Carol Ackerman, Andi
Richter, Rhonda Tinch-Mize
Cover Designer Tim Amrhein Book Designer Alyssa Yesh
Copy Writer Peter Fuller Production Team Brad Chinn
Supervisor
Production Stephen Adams, Debra Bolhuis, Mona Brown, Michael Dietsch, Jason Hand, Daniel
Harris, Susan Knose, Casey Price, Laura Robbins, Susan Van Ness

Acknowledgments

Putting this book together took lots of help from many talented people. Although I can't list them all, I want to take a
moment to single out a few of the individuals who made this work possible.

First, I want to thank Jefferson Schuler and Bill Zembrodt of Pioneer Solutions. They accepted my challenge to build a
simple TAPI OCX tool that would allow Visual Basic programmers virtually the same access to Microsoft's Telephony
API services as C++ programmers. The result is the TAPILINE.OCX that is included on the CD-ROM that
accompanies this book. They spent several long days and late nights developing this handy tool and I thank them for all
their work and assistance.

Next, I must thank all those in cyberspace who answered my queries about telephony, speech systems, and electronic
mail. Many of the concepts that appear in this book were hashed out in extensive messages over the Internet, and I thank
all those who assisted me in my efforts. I could name many who helped, but I will refrain from doing so lest they be
blamed for any of my mistakes within these pages.

I also want to thank the people at Sams Publishing. It takes a great number of talented individuals to get a book from the
idea stage to the store shelves, and I consider it a privilege to be able to work with the folks at Sams. Completing this
book took more time and effort than any of us originally suspected and more than once it seemed like the book would
never be done. I am especially indebted to Sharon Cox for her continued help and support. I doubt this book would be in
your hands today were it not for her assistance.

Finally, I need to acknowledge the special contributions made by my family. Without their support, patience, and
understanding, I could not have completed this book. (And now that I have completed it, I have a long list of promises
that I must live up to!)
About the Author

Mike Amundsen works as an IS consulting and training specialist for Design-Synergy Corporation, a consulting and
project management firm specializing in information technology services. He has earned Microsoft certifications for
Windows operating systems, Visual Basic, SQL Server, and Microsoft Exchange Server. Mike's work takes him to
various locations in the U.S. and Europe where he teaches Windows programming and helps companies develop and
manage Windows-based client/server solutions.

He is co-author of Teach Yourself Database Programming with Visual Basic 4 in 21 Days, published by Sams, and was a
contributing author for Visual Basic 4 Unleashed and Visual Basic 4 Developer's Guide from Sams Publishing. Mike is
the contributing editor for Cobb's "Inside Visual Basic for Windows" newsletter, and his work has been published in
"Visual Basic Programmer's Journal" magazine, "VB Tech" magazine, and "Access Developer's Journal."

When he's not busy writing or traveling to client sites, Mike spends time with his family at his home in Kentucky. You
may write to Mike at his CompuServe address 102461,1267, at [email protected] on the Internet, or you
can visit his Web site at www.iac.net/~mamund/.

Tell Us What You Think!

As a reader, you are the most important critic and commentator of our books. We value your opinion and want to know
what we're doing right, what we could do better, what areas you'd like to see us publish in, and any other words of
wisdom you're willing to pass our way. You can help us make strong books that meet your needs and give you the
computer guidance you require.

Do you have access to CompuServe or the World Wide Web? Then check out our CompuServe forum by typing GO
SAMS at any prompt. If you prefer the World Wide Web, check out our site at http://www.mcp.com.

Note
If you have a technical question about this book, call the technical support
line at (800) 571-5840, ext. 3668.

As the team leader of the group that created this book, I welcome your comments. You can fax, e-mail, or write me
directly to let me know what you did or didn't like about this book-as well as what we can do to make our books
stronger. Here's the information:

FAX: 317/581-4669
E-mail: [email protected]
Mail: Greg Wiegand
Comments Department
Sams Publishing
201 W. 103rd Street
Indianapolis, IN 46290
Introduction to MAPI, SAPI, and TAPI Developer's Guide

This book covers the three most exciting programming services available on the Microsoft Windows platform-messaging
(MAPI), speech (SAPI), and telephony (TAPI). Each of these APIs provides a specialized set of services that expand the
reach of the Windows operating system in a way that makes it easier to write programs that work without having to deal
with the differences between hardware provided from third parties.

The addition of these services as part of the basic operating system not only is a boon to programmers-it is of great
interest to users, too. Computers that can handle messages and telephones, and that can generate and understand simple
speech, are computers that, ultimately, are easier to use. Learning how you add these vital features to your applications
will give your software a greater reach and appeal that can make a real difference to your target audience.

This book is arranged in the following parts:

● Part I-Introduction covers some preliminary issues regarding the Windows Open Services Architecture
(WOSA) upon which all three of the API sets are based.
● Part II-The Messaging API (MAPI) contains chapters that describe the MAPI service model, review existing
client and server software that implements the MAPI model, and show you how to use common developer tools
for building MAPI-compliant applications. There are also several chapters devoted to creating commonly used
MAPI-based programs, including e-mail clients, a broadcast mailing list manager, an e-mail-based discussion
forum tool, and an e-mail agent. You'll also learn how to use the Microsoft Exchange Forms designer and
discover how you can use C++ to create built-in extensions to the Windows Messaging client interface.
● Part III-The Speech API (SAPI) covers the Microsoft Voice product available for Windows 95. You'll learn
the details of the API model and how you can use it to create applications that use Text-to-Speech (TTS) and
Speech Recognition (SR) engines to add a voice to your pc applications. You'll use both C++ and Visual Basic
to build programs that respond to voice commands and read printed text back to users.
● Part IV-The Telephony API (TAPI) outlines the API set that allows Windows programmers to add inbound
and outbound telephony features to their applications. You'll learn about the telephony object model, and how
to build simple dialing applications and basic inbound call handlers. Along the way you'll learn how to select
telephony hardware and third-party TAPI development tools that will make it easier to build and maintain TAPI-
compliant applications.
● Part V-Creating Integrated Applications covers design issues you need to keep in mind when designing
Windows applications that combine messaging, telephony, and speech services. You'll learn how to build a
FaxBack service using MAPI and TAPI; an integrated voice response system that uses TAPI to allow users to
call in and request data from the computer and have the results spoken over the phone; and an application that
combines all three extension services to create an integrated voice and telephony application that uses voice
commands to place outbound telephone calls.
● Part VI-appendixes contains lists of third-party vendors for each of the three API sets and pointers to printed
and online documentation sources, along with a handful of e-mail and Web addresses that you can use to keep
current on these three technologies.

I encourage you to contact me via the Internet or through my Web site. I hope you enjoy this book, and I look forward to
hearing from you soon.

Mike Amundsen
[email protected]
www.iac.net/~mamund/
Chapter 1

Windows Communications Overview

CONTENTS

● MAPI Message Services


● TAPI Telephony Services
● SAPI Speech Services
● MCI Multimedia Services
● Applications Covered in This Book
● Development Tools
● Hardware and Software Tools
● What's Not Covered in This Book
● How to Use This Book

This developer's guide is designed to show you how to design, develop, and deploy applications that use messaging,
telephony, and speech services available within the Windows 95 operating system. The primary focus is on developing
client-side applications. Several chapters in this book cover the server-side aspects of these APIs, too. However, the
emphasis is on the client or desktop.

Note
The primary focus of this book is Windows 95, and all examples are
therefore in 32 bits. The same examples will work for Windows NT. The
ideas can be applied to 16-bit Win31, but no 16-bit examples are given in
this book.

One book cannot begin to cover all the possible development tools and API sets that provide message, telephony, and
speech services for desktop pcs. The approach taken here is to concentrate on the Microsoft API sets. This offers several
advantages: First, by using all Microsoft API models, the programmer gets to work with a consistent set of interfaces
that work well together. Second, there is no denying that Microsoft's presence in the marketplace adds strength to any
service model they develop. The time you invest in learning a set of API calls to provide advanced Windows services is
usually time well spent. Using Microsoft API models assures you that your knowledge will not be useless in a year or so
when some other API model is abandoned due to lack of use. And third, the Microsoft approach to service models is
designed to allow third-party vendors to contribute to the growth and development of the service model. This open
approach means you're really learning how to create programs that work with a variety of hardware/software
combinations and not tying your efforts to a single hardware or software system.

MAPI Message Services

The messaging system explored in this book is the Microsoft Messaging API (MAPI). The MAPI system is one of the
most widely used message models available for the pc environment. While there are other message models in use today,
they are not covered here just because one book can't cover everything. If you are familiar with other message models,
your knowledge will help you gain a greater understanding of the MAPI system.

TAPI Telephony Services

The telephony model covered here is the Microsoft Telephony API (TAPI). Just as with message services, there are
competing telephony service models, too. The primary advantage of TAPI to the programmer is that Microsoft has
committed to providing the TAPI model as part of the basic operating system for all future versions of Windows. For
example, TAPI is shipped as part of every Windows 95 operating system. Version 4.0 of Windows NT is also designed
to contain TAPI services as a fundamental part of the operating system.

Another major advantage of the TAPI model is that it is designed to work with multiple hardware products.
Programmers who use TAPI can be assured that their programs will work the same from one installation to the next as
long as TAPI-compliant hardware is used at all locations.

SAPI Speech Services

The speech service discussed here is Microsoft's Speech API (SAPI). There are several vendor-specific speech APIs. As
in the TAPI model, the real advantage of Microsoft's SAPI is that it is designed to support multiple third-party vendor
products. By writing to the SAPI model, you can potentially increase the reach of your speech-enabled software to all
SAPI-compliant hardware.

MCI Multimedia Services

Some of the examples in this book also use the Microsoft Multimedia Communications Interface (MCI) to add audio and
video services. This is a well-established interface that is supported by almost all existing audio and video hardware.

Applications Covered in This Book

The programming examples in this book move from simple, high-level examples and progress to more complex, full-
featured applications. Each section of the book has four basic parts:

● General theory
● Creating simple service-aware applications
● Building full-featured service implementation examples
● Designing applications that use the service in unique ways

Each section starts with the general theory of the API service. This includes coverage of the general design of the service
model and its objects, methods, and properties. You'll also get information about the various developer tools available
for building programs that use the target service.

You'll then learn how to create service-aware applications. This usually involves adding the target service to existing
applications, such as adding Send, Dial, or Speak buttons to a form. Service-aware applications use the target service
as an added feature instead of as a basic part of the program. You'll see how you can add service features using high-
level code routines.

The next level of programming examples shows you how to build applications that take advantage of the target service
as a basic part of the program design. Examples would be an e-mail message reader, an online address book that can dial
the selected address at the push of a button, or a program that allows users to drag and drop text documents onto a palette
and then reads a selected document aloud.
Lastly, you'll learn how to create applications that use the service in unique ways. For example, you could use the
message service to send data between two programs running on different machines on the same network, or use TAPI
services to create an answering machine application.

The final section of the book contains examples of applications that provide integrated services: using MAPI and TAPI
to build a dial-up FAX-back application; building a program that allows users to dial in, request information from a
database, and hear the results spoken to them over the phone; and creating a single application that combines messaging,
telephony, and speech into a single voice-mail system.

While the examples in this book don't cover every possible use of the target APIs, you'll find enough examples here to
give you the techniques and skills you'll need to build your own Windows applications to exploit the features of the
MAPI, TAPI, and SAPI services.

Development Tools

In the past, if you wanted to access advanced Windows services like MAPI, SAPI, and TAPI, you had to use C or C++
developer tools. But now, with the introduction of OLE libraries and the Microsoft OCX add-in control specification,
Microsoft has made it possible to access these advanced services from high-level developer tools like Visual Basic 4.0
and Microsoft FoxPro. You can even complete a number of the programming examples included in this book using the
VBA-compatible Microsoft Office tools like Excel, Access, Word, and others.

Most of the programming examples presented in this book are done in the 32-bit version of Microsoft Visual Basic 4.0
Professional Edition. Several examples are done using Microsoft's Excel 95 and Microsoft Access 95. However, a few of
the services are only available via C++ programming. You'll therefore see some short programming examples here using
Microsoft's Visual C++ 4.0. If you do not own a copy of Visual C++, you can still get a lot out of the C++ examples
simply by reading them to get the general concepts. None of the major projects in this book require coding in C++. In the
cases where C++ code is needed to gain access to a specific feature in the Windows service, you'll find compiled OLE
libraries and/or OCX tools included on the accompanying CD-ROM. You can use these compiled tools as part of your
high-level language projects.

All of the programming concepts described here are applicable to non-Microsoft programming environments, too. If you
are using products such as Borland's Delphi, dBASE, Borland C++, and so on, you can still get a lot out of this book.
You'll need to re-create the detail of the code examples in code usable in your programming environment, but the
Windows API calls will be exactly the same.

Hardware and Software Tools

The programming examples in this book cover a wide range of hardware and software requirements, and it's no small
matter to equip your workstation with all the tools necessary to access all the Windows services described within these
pages. The following list summarizes the tools used in developing the programs presented here:

● Windows 95 operating system-Most of the examples require Windows 95. Some of the examples will run under
Windows 3.1 and most will run without problems under Windows NT (WinNT). However, all of the
programming examples in this book were developed under Windows 95. You'll avoid lots of difficulty by
sticking with Windows 95.
● Messaging API tools-MAPI services are accessed via the MAPI OCX controls, the OLE Message Library, and
the MAPI 1.0 API set. The MAPI OCXs ship with the Professional Edition of Visual Basic 4.0. The OLE
Message Library and the MAPI 1.0 API developer tools can be found on the Microsoft Developer's Network
(MSDN) Level II CD-ROMs. You'll need access to MSDN CD-ROMs to complete the advanced examples in
this book.
● Telephony API tools-The examples in this book were written using TAPI v1.4 for Windows 95. The v1.4 run-
time files are shipped as part of the Windows 95 operating system. TAPI services are accessed via a custom
OCX that comes on the CD-ROM shipped with this book and via calls directly through the API set. TAPI
developer tools are found on the MSDN Professional Level CD-ROMs, too. It is important to note that all the
examples in this book were developed as 32-bit applications for Windows 95. There is a 16-bit version of TAPI
(v1.3), and a new version of TAPI (v2.0) for WinNT will be released with NT v4.0. To keep frustration to a
minimum, stick with Win95 and TAPI 1.4.
● Speech API tools-The release version of SAPI developer tools and run-time modules are available from
Microsoft and are included on the CD-ROM that ships with this book. However, a major piece of the SAPI kit
is not included automatically as part of the SAPI developer kit. You'll need a text-to-speech engine supplied by
a third-party vendor in order to run the SAPI examples in this book. Check the accompanying CD-ROM for
demo versions of Text-to-Speech (TTS) engines. You'll also find a list of vendors of SAPI tools in appendix B,
"SAPI Resources."
● Microsoft Exchange E-Mail-Almost all the examples in this book that involve e-mail clients were created using
the Microsoft Exchange client for Windows 95. Some were created using the Microsoft Exchange Server or
Microsoft Mail Server clients. MAPI services are pretty much the same regardless of the client installed on your
workstation. Where the differences are important, they are noted in the text of the book.
● Sound cards, speakers, and microphones-Some of the examples here require the use of a pc audio system. Just
about any WAV-compatible audio card will work fine. Some examples use voice input and playback. You can
accomplish this with an attached microphone and speakers. In some cases, you can even use an attached
telephone handset, too.
● Data modems-You'll need at least a basic data/fax modem to run the examples in this book. Both the TAPI and
the MAPI examples require a data modem. For some of the TAPI examples, you can perform all the functions
using a simple data modem. For others, you'll need a data modem that supports the Unimodem/V
communications driver. Not all modems support Unimodem/V even though they offer voice-mail or telephony
features. Consult the appendix at the back of the book and the accompanying CD-ROM for a list of vendors
who offer data modems that support TAPI and the Unimodem/V standard.
● Telephony cards-While you can get through almost all the TAPI examples in this book without a telephony card
in your workstation, you'll get a lot more out of the TAPI section if you have access to one. Telephony cards
provide a much more advanced set of telephony features than do TAPI-compliant data modems. The CD-ROM
that comes with this book contains some demo tools that can be used to mimic the presence of a telephony card.
These demo tools can be used during development and testing, but they will not work in a production setting.
Consult appendix C, "TAPI Resources," and the CD-ROM for information on third-party developers who offer
full-featured TAPI-compliant telephony hardware. When shopping for telephony cards, be sure to confirm that
they are TAPI-compliant and that there are TAPI drivers (service providers) available for your telephony card.
● Access to live phone service-You'll need access to a live phone line to run most of the programs in this book.
All the examples will work with a simple analog phone line like the line available to most U.S. households. The
nature of the TAPI services makes it very easy to use digital lines such as T1 and ISDN to run these examples,
too. However, if you use digital lines, be sure your hardware (data modem and/or telephony card) is compatible
with the type of phone service you're using.

What's Not Covered in This Book

Even though this book covers a lot of territory, it leaves a lot out, too. The main focus of this book is the client desktop.
However, there's another set of APIs designed for building server-side applications for Windows. Microsoft provides
developer kits for the creation of MAPI message servers like transport providers, address books, and message storage
systems. Every TAPI client depends on a server-side interface that talks directly to the vendor hardware to translate the
requests from the client into something the hardware understands. Microsoft publishes the Telephony Service Provider
Interface (TSPI) as a set of API calls for handling the server-side aspects of Windows telephony. Finally, the Speech API
is designed to allow third-party vendors to create separate text-to-speech and speech recognition engines that work
within the SAPI system.

Covering the details of each of these server-side models could fill an entire book. For our purposes, you'll learn the
general facts about how the clients and servers relate to each other, but you'll need to look elsewhere if you want to learn
how to build server-side components.

It is also important to keep in mind that all the material here was built for the Windows 95 platform. Even though many
of the examples will run on other Windows platforms, not much will be said about Windows 3.1 or even Windows NT.
This is mainly to keep the book clear of confusing exceptions and special notations for cases where the platforms behave
in different ways. If you're unlucky enough to have the responsibility of deploying advanced Windows services on
multiple platforms, you'll need to supplement this text with copious experimenting and tweaking.

How to Use This Book

Since this book is divided into very clear sections, you can use it in a number of ways:

● Start from the beginning-The chapters of the book are arranged to start with relatively simple issues and
progress through more complex topics toward the end of the book. If you're new to programming with WOSA
and APIs, you'll get the most out of the book by following along in chapter order.
● Focus on a target service-If you have a good understanding of WOSA and the use of Windows services, you
can jump right into the API sections that interest you most. The three service sections (MAPI, SAPI, and TAPI)
are each self-contained. You can read these sections in any order without missing any vital material.
● Focus on integrating services-If you already know one or more of the APIs and are mainly interested in
building advanced integration applications, you can skip the API sections you have had previous experience
with and move directly to the section on building integrated applications. If you take this approach, you may
run into areas of the book that refer to previous programming examples or concepts discussed in previous
sections of the book. Be prepared to do a bit of skipping around at times to follow some of these threads back to
their source. In this way, you can use this book as more of a reference guide than a tutorial.

No matter how you use this book, by the time you complete the examples and absorb the basic concepts explained here,
you'll have a solid understanding of some of the most advanced Windows extension services available.

That's about it for the preliminaries-now it's time to get started!


Chapter 2

Introduction to Windows Open Services Architecture (WOSA)

CONTENTS

● What Is WOSA?
● The WOSA Model
❍ The Client API Makes Requests

❍ The Server SPI Responds to Requests

❍ The Interface DLL Talks to Both the API and SPI

● WOSA Services
❍ Common Application Services

❍ Communication Services

❍ Vertical Market Services

● Benefits of WOSA
❍ Isolated Development

❍ Multivendor Support

❍ Upgrade Protection

❍ Leveraging WOSA in Your Own Applications

● Summary

Before we jump into the details of the three API sets covered in this book, it is a good idea to spend some time reviewing
the overall architecture upon which these APIs are built. Although these API sets are only slightly related in their
function, they are all closely related in their construction and implementation. It is the construction and implementation
of the APIs that is the topic of this chapter.

All of the API sets covered in this book are part of the Windows Open Services Architecture (WOSA). This chapter
offers a brief overview of the WOSA model and describes how it works and the benefits of the WOSA model for
developers. Once you understand the general notions behind the creation and deployment of API services via the WOSA
model, you will also have a good understanding of how to build well-designed end-user applications that use WOSA-
based API services. The WOSA model APIs offer clear benefits to developers who understand the model. Those who
fail to grasp the basic theories behind WOSA may end up using the APIs incorrectly, thus ruining their chances of taking
advantage of the real benefits of the WOSA model.

What Is WOSA?

The Windows Open Services Architecture was developed by Microsoft to "...provide a single, open-ended interface to
enterprise computing environments." The concept of WOSA is to design a way to access extended services from the
Windows operating system that require having only a minimum amount of information about the services. For example,
the MAPI (Message API) model is designed to allow programmers to develop applications that use the message services
without having to understand the complexities of the hardware and software routines that implement messaging on
various Windows platforms. The same is true for the Speech (SAPI) and Telephony (TAPI) services we will discuss in
this book.
The WOSA model goes beyond the idea of exposing services in a uniform way across Windows operating systems.
WOSA is also designed to work in a mixed operating system environment. For example, the Microsoft Rpc (Remote
Procedure Call) interface is a WOSA service that is designed to work with the Open Software Foundation's DCE
(Distributed Computing Environment) Rpc model. The design of Microsoft Rpc allows programmers to design software
that will safely interface with any product that uses the DCE model, regardless of the operating system with which the
software must interact.

In order to attain this flexibility, the WOSA model defines two distinct interfaces-the Client API and the Server SPI.
These interfaces are linked by a single interface module that can talk to both API and SPI applications. As a result, all
client applications need to do is conform to the API rules and then to the universal interface. All server applications need
to do is conform to the SPI rules and then to the universal interface. No matter what changes are made to the client or
server applications, both software modules (client and server) will be compatible as long as they both continue to
conform to the API/SPI model and use the universal interface.

The next two sections of this chapter outline the WOSA model in detail and give several examples of WOSA services
currently available.

The WOSA Model

The WOSA model consists of three distinct pieces. Each of these pieces plays an important, and independent, role in
providing programming services to your applications. The three WOSA components are

● The Client API-the application programming interface used by the program requesting the service.
● The Server SPI-the service provider interface used by the program that provides the extended service (for
example, e-mail, telephony, speech services, and so on).
● The API/SPI Interface-the single module that links the API and SPI calls. This is usually implemented as a
separate DLL in the Windows environment.

Figure 2.1 shows the relationship between the three WOSA components.

Figure 2.1 : The three components of the WOSA model.

Each of the components has an important job to do. Even though they perform their tasks independently, the components
work together to complete the service interface. This is the key to the success of the WOSA model-distinct, independent
roles that together provide the whole interface.

The Client API Makes Requests

The Client API is the interface for the application requesting the service. API sets are usually implemented at the
Windows desktop. The Message API (MAPI) is a good example of a WOSA client API. Each client API defines a stable
set of routines for accessing services from the back-end service provider. For example, the operations of logging into an
e-mail server, creating an e-mail message, addressing it, and sending it to another e-mail client are all defined in the
MAPI set. These services are requested by the client. The actual services are provided by the server-side application.

The key point is that the client application interface allows programs to request services from the server-side service
provider but does not allow the client software to access the underlying services directly. In fact, the request is not even
sent directly to the server-side application. It is sent to the DLL interface that sits between the API and SPI (see Figure
2.2).

Figure 2.2 : Client APIs talk directly to the DLL interface.


This isolates API calls from the SPI and limits the possibility that future changes to one or the other component will
adversely affect the interface.

The Server SPI Responds to Requests

The Server SPI (Service Provider Interface) accepts requests for services and acts upon those requests. The SPI is not
designed to interface directly with a client application. Most SPI programs are implemented on network servers or as
independent services running on desktop pcs. Users rarely interact with these service providers, except through the client
API requests.

A good example of an SPI implementation is the Open Database Connectivity (ODBC) interface. Even though
programmers use API calls (or some other method of requesting ODBC services) in their programs, these calls merely
request services from an external program. ODBC calls to Microsoft's SQL Server, for example, are simply requests to
SQL Server to perform certain database operations and to return the results to the client application. When making an
ODBC request to SQL Server, the client actually performs very few (if any) database operations. It is SQL Server that
performs the real database work.

As mentioned earlier, service providers rarely display interfaces directly to client applications. Their job is to respond to
requests. These requests do not even come directly from the client program. In the WOSA model, all requests come
directly from the interface DLL. The SPI talks only to the interface DLL. Any information that the SPI needs to supply
as a part of the response to the request is sent directly to the interface DLL. It is the DLL's job to send the information on
to the client that made the initial request.

Another important point should be highlighted here. The service provider portion of the WOSA model allows for
multiple clients to request services (see Figure 2.3).

Figure 2.3 : SPIs must handle requests from multiple clients.

The DLL interface tells the SPI which client is making the request. It is the SPI's responsibility to keep the various
clients straight. SPIs must have the ability to handle multiple requests from the same client and from multiple clients.

The Interface DLL Talks to Both the API and SPI

Since a key design aspect of WOSA is the isolation of the client API and the server SPI, a single interface between the
two components is required. This single interface is usually implemented as a Windows dynamic link library (DLL) that
allows programs to link to existing services at run-time instead of at compile time. The advantage of using DLLs is that
programs need not know everything about an interface at compile time. Thus, programmers can upgrade DLL modules
without having to recompile the applications that access the interface.

The primary role of the DLL is to broker the requests of the client API and the responses of the server SPI. The DLL
does not actually perform any real services for the client and makes no requests to the SPI.

Note
Actually, the interface DLL may request basic information from the SPI at
startup about the underlying SPI services, their availability, and other
information that may be needed to support requests from clients.

The interface DLL is the only application in the Windows environment that actually "speaks" both API and SPI. It is the
DLL's job to act as translator between the client and server applications.
In the past (before WOSA), these DLLs were written as translators from the client API directly to a specific back-end
product. In other words, each DLL interface understood how to talk to only one back-end version of the service. For
example, early implementations of the MAPI interface involved different DLLs for each type of MAPI service provider.
If a program needed to talk to a Microsoft MAPI service provider, the MAPI.DLL was used as an interface between the
client and server. If, however, the program needed to talk to another message server [such as Lotus Vendor Independent
Messaging (VIM) or the universal Common Message Calls (CMC) service provider], another DLL had to be used to link
the client request with the back-end provider.

In the WOSA world, interface DLLs can speak to any service provider that understands the SPI call set. This is an
important concept. Now, a single interface DLL can be used for each distinct service. This single DLL is capable of
linking the client application with any vendor's version of the service provider. This is possible because the service
provider speaks SPI rather than some proprietary interface language (see Figure 2.4).

Figure 2.4 : Interface DLLs can talk to multiple service providers.

WOSA Services

Microsoft has been championing the WOSA model for several years and promotes three types of WOSA services:

● Common Application Services


● Communication Services
● Vertical Market Services

Each type has its own purpose and its own core of services. The following sections describe the WOSA service types
and give examples of services currently available for each.

Common Application Services

Common Application Services allow applications to access services provided by more than one vendor. This
implementation of WOSA focuses on providing a uniform interface for all Windows applications while allowing
programmers and/or users to select the vendor that provides the best service option for the requirement. In this way,
Microsoft can encourage multiple (even competing) vendors to provide their own versions of key service components for
the Windows operating system.

By defining a single set of APIs to access the service, all third-party vendors are assured equal access to the Windows
operating environments. Since the interface is stable, vendors can concentrate on building service providers that expose
the services that customers request most often. These vendors can also be confident that, as Windows operating systems
change and evolve, the basic model for service access (the WOSA model) will not change.

The list of Common Application Services available for Windows operating systems is constantly growing and changing.
Here is a list of some of the services provided under the WOSA model:

● License Service Application Program Interface (LSAPI) provides access to software license management
services.
● Messaging Application Program Interface (MAPI) provides access to e-mail and other message services.
● Open Database Connectivity (ODBC) provides access to database services.
● Speech Application Program Interface (SAPI) provides access to speech and speech recognition services.
● Telephony Application Program Interface (TAPI) provides access to telephone services.

Communication Services

Communication Services provide access to network services. This set of WOSA services focuses on gaining uniform
access to the underlying network on which the Windows pc is running. The Communications Services also provide
uniform access to all the network resources exposed by the underlying network. By defining a universal interface
between the pc and the network, Windows applications are able to interact with any network operating system that
conforms to the WOSA model.

The following list shows some examples of WOSA implementations of Communication Services:

● Windows SNA Application Program Interface provides access to IBM SNA services.
● Windows Sockets provide access to network services across multiple protocols, including TCP/IP, IPX/SPX,
and AppleTalk.
● Microsoft Rpc provides access to a common set of remote procedure call services. The Microsoft Rpc set is
compatible with the Open Software Foundation Distributed Computing Environment (DCE) model.

Vertical Market Services

The last category of WOSA services defined by Microsoft is Vertical Market Services. These are sets of API/SPI calls
that define an interface for commonly used resources in a particular vertical market. By defining the interfaces in this
way, Microsoft is able to work with selected vertical markets (banking, health care, and so on) to develop a standard
method for providing the services and functions most commonly used by a market segment. In effect, this allows users
and programmers to invent Windows-based solutions for an entire market segment without having to know the particular
requirements of back-end service provider applications.

As of this writing, Microsoft has defined two Vertical Market Services under the WOSA umbrella:

● WOSA Extensions for Financial Services provide access to common services used in the banking industry.
● WOSA Extensions for Real-Time Market Data provide access to live stock, bond, and commodity tracking data
for Windows applications.

Benefits of WOSA

There are several benefits for both users and programmers in the WOSA model. The three key benefits worth
mentioning here are

● Isolated development
● Multivendor support
● Upgrade protection

The next three sections describe the details of the benefits of the WOSA model as it relates to both client and server
programmers and application users.

Isolated Development

In one way or another, all WOSA benefits are a direct result of the model's ability to separate the details of service
providers from the application running on users' desktops. By keeping the details of hardware and software interface
locked away in the SPI-side, programmers can concentrate on providing a consistent interface to the services, rather than
concerning themselves with the low-level coding needed to supply the actual services.

The isolation of services from user applications has several other benefits. With WOSA services, developers can limit
their investment in understanding the details of a service technology, where appropriate. Those focused on developing
client-side applications can leave the details of server-side development to others. They can concentrate their efforts on
developing quality client-side software knowing that, as long as the service providers maintain WOSA compatibility, the
client software will be able to take advantage of new services as they become available.
Of course, this works the same for developers of server-side software. They can concentrate their efforts on providing
the most efficient and effective means for exposing and supporting requested services and leave the client interface
details to others. Service provider developers are assured equal access to all Windows clients because the WOSA model
ensures that all links to the services are the same, regardless of the client software used.

Multivendor Support

In addition to allowing programmers to focus on the client-side of the equation instead of the server-side, WOSA
implementations provide benefits to application programmers. With a common interface for the service, application
programmers can build software solutions that are independent of vendor-specific implementations. The WOSA model
allows programmers to build programs that interact with any vendor's service implementation as long as that vendor
adheres to the WOSA model.

This is a key benefit for both client-side and provider-side developers. Now service provider vendors can be assured that,
as client-side applications change, the interface to their services will remain the same. At the same time, client-side
developers can be assured that as service providers upgrade their software, client applications need not be rewritten
except to take advantage of new services. This feature allows client-side and service-side development to go forward
independently. The result is greater freedom to advance the technology on both sides of the interface.

Upgrade Protection

Another benefit of WOSA-compliant systems is the protection it provides during service or application upgrades or
platform migrations. Users can more easily plan and implement software and hardware upgrades when the service access
to uniform calls is isolated to a single DLL. Since the WOSA model ensures that service provision and access is
standardized across vendors and platforms, changing software and hardware has a minimal effect on users who access
services from WOSA-compliant applications.

Thus, when users decide to move their primary database services from an IBM VSAM system to a DB2 or SQL Server
environment, the client applications see minimal change as long as the WOSA model was implemented for database
access. This protects users' software investment and allows greater flexibility when selecting client and server software.

At the same time, this approach provides protection for commercial software providers because a single application can
be designed to work with multiple service providers. Developers can focus on creating full-featured applications without
tying their software to a single service provider. As the market grows and changes, and service providers come and go,
client-side applications can remain the same when the WOSA model is used as the route for service access.

Leveraging WOSA in Your Own Applications

Now that you understand the concepts behind the WOSA model, you can use this information in your own development
efforts. For example, when accessing WOSA services from your client applications, isolate those calls in your code. This
will make it easier to modify and enhance your application's WOSA interface in the future. As the services available
change and grow, you'll need only to make changes in a limited set of your code.

Also, when designing your application, plan for the future from the start. Assume that the various program services will
be provided via WOSA-compliant tools-even if they are not currently available as true WOSA components. This
approach will make it much easier to add true WOSA components to your client application once they become available
and will increase the life and flexibility of your software.

The same holds true for back-end service provider developers. If there is a WOSA SPI defined for the services you are
providing, use it. This will make your software client-ready for virtually all Windows desktops. If no WOSA interface is
yet defined for your service, code as if there is one. Limit the code that talks directly to hardware to a single area in your
program. If you are using vendor-specific calls, isolate these, too. This way, when a WOSA model is defined for your
service, you'll have an easier time converting your software to comply with the WOSA model.

As you work through the book, you learn about the specific API calls that are used to access extension services. You'll
see a consistent pattern throughout all three extensions. Each of the extension services is divided into layers. The first
layer provides a simple interface-sometimes as simple as a single function call-to the target service. The second level
provides a more extensive access to a complete feature set. Finally, you'll find a third level of service that allows
sophisticated programmers access to all the nifty little details of the extension service. When you're coding your own
applications, it is a good idea to use this same approach. Where possible, give users a single function or subroutine that
provides access to the extension service. For more advanced applications, create a function library or class object that
encapsulates all the extension service calls. By doing this, you'll make it easier to make modifications to your program
logic without touching the extension routines. It will also be easier to update the extension routines in the future without
damaging your local program logic.

Summary

In this chapter, you learned that the WOSA model was developed by Microsoft to "... provide a single, open-ended
interface to enterprise computing environments." You learned that the WOSA model has three key components:

● The Client API usually resides on the desktop workstation and makes service requests.
● The Server SPI usually resides on a network server and receives and responds to service requests made by
clients.
● The interface DLL acts as a broker between the client API and the server SPI. The API and SPI never talk
directly to each other. They both talk only to the interface DLL.

You learned that there are three types of WOSA implementations. Each of these implementations focuses on a different
aspect of service implementation. The three WOSA types are

● Common Application Services provide common access to system-level services, independent of vendor
implementations. Examples of WOSA Common Application Services are ODBC, MAPI, SAPI, TAPI, and
LSAPI.
● Communication Services provide standard access to network services, independent of the underlying network
available to the workstation. Examples of Communication Services are Windows SNA API, Windows Sockets
API, and the Microsoft Rpc interface.
● Vertical Market Services provide access to common services used in a particular vertical market, such as
banking, health care, and so on. Examples of this type of WOSA service are the WOSA Extensions for
Financial Services and the WOSA Extensions for Real-Time Market Data.

We also covered some of the key benefits of using WOSA-defined services in developing Windows applications. The
benefits mentioned are

● Isolated development-Using WOSA-compliant services isolates access to external services. As these services
change, the impact on your application is limited.
● Multivendor support-WOSA-compliant services are vendor neutral. If your application uses WOSA-defined
service calls, they can be provided by any vendor's service provider if it is also WOSA-compliant.
● Upgrade protection-When you use WOSA components, you can easily plan and implement software and
hardware upgrades because the WOSA model is consistent across platforms and operating systems.

Finally, you learned how you will use the concepts behind the WOSA model in your own software development to
improve compatibility and ensure easy future upgrades.
Chapter 3

What is MAPI

CONTENTS

● MAPI Services and Windows


❍ Flexibility

❍ Consistency

❍ Portability

● Messages
❍ Text Messages

❍ Formatted Documents and Binary Files

❍ Control Messages

● MAPI Applications
❍ Electronic Mail Clients

❍ Message-Aware Applications

❍ Message-Enabled Applications

❍ Other Types of Message Applications

● Summary

Before getting into the details of how the Messaging Application Programming Interface (MAPI) works and how to
write MAPI applications, we'll take a moment to review the general architecture of Microsoft's messaging API and how
this set of message services fits into the overall Windows operating system. As you will see a bit later in this chapter,
MAPI is more than a handful of e-mail APIs-it is a defined set of message services available to all programs that run in
the Windows operating environment.

We'll also discuss the various kinds of applications and message types commonly used under MAPI services. In this
chapter, you'll learn about the three general types of MAPI messages: text messages, formatted documents and binary
files, and control messages. Each of these message types has a distinct set of properties and uses within the MAPI
framework. This chapter describes each of the message types and shows how you can use them within the MAPI
architecture.

There are also three common types of MAPI applications: electronic mail clients, message-aware applications, and
message-enabled applications. Each of these application types is defined and illustrated in this chapter. You'll also learn
the relative strengths of each type of MAPI application.

MAPI Services and Windows

The MAPI service set is more than a set of API commands and functions that you can use to send e-mail from point to
point. The MAPI interface is actually a carefully defined set of messaging services available to all Windows programs.
This pre-defined set has three key attributes:
● Flexibility
● Consistency
● Portability

Because the MAPI service set contains these three characteristics, it has become the de facto messaging interface
standard for Windows applications.

Access to MAPI services is the same for all versions of the Windows operating system. But even though your Windows
programs use the same methods for accessing MAPI services, MAPI services can vary from system to system. Also,
MAPI architecture allows software designers to create their own service providers (SPs) to support the MAPI service set.

These services are also available within all existing flavors of the Windows operating system. Even more important, the
methods of access to these message services is the same, regardless of the version of Windows you are working with.
This means that programs using MAPI services that were written under Windows 3.1 will still be able to access those
same MAPI services under Windows 95.

Flexibility

Probably the most important aspect of the MAPI service architecture is its flexibility. Microsoft has implemented MAPI
within the Windows Open Systems Architecture (WOSA). This architecture is designed to allow customization at both
the client (user) side and the server (provider) side. In other words, you can use MAPI not only to create your own end-
user software to read, write, create, and send messages, but also to construct custom server-side software to store and
transport those same messages. As part of the WOSA model, MAPI services are implemented in a tiered format (see
Figure 3.1).

Figure 3.1 : The tiered implementation of MAPI services.

The first layer is the client layer. This is what the end-user most often sees. At this level a set of well-defined services are
available. These services are accessed when the client layer makes a service request to the second layer-the MAPI DLL.
The MAPI DLL takes the service request from the client and forwards it on to the third layer in the tier-the service
provider. The service provider is responsible for fulfilling the client request and sending the results of that request back
to the DLL where it is then forwarded to the client that made the initial request. Throughout the process, the DLL layer
acts as a broker between the client side and the server side.

The primary advantage of this layered implementation is the ease with which users can interchange client and server
components. Since the only constant required in the mix is the DLL layer, any client can be matched with any server
component to provide a working message service. It is very common to switch client-side components in a messaging
service. Each e-mail reader is a MAPI client application. Any application that sports a send button is actually a MAPI
client. Any specialized program written to manipulate messages at the end-user level is a MAPI client.

While interchanging MAPI clients is rather commonplace, interchanging MAPI service providers is not. In a network
environment, a single service provider will usually be designated as the default provider of MAPI services. It is no
longer rare to have several MAPI service providers available at the same time, however. In fact, the Microsoft Mail
Exchange client that ships with Windows 95 is specifically designed to be able to access more than one service provider.
It is possible to use the Windows Exchange client to access messages via Microsoft Mail Server, Microsoft FAX, or
through the Microsoft Network (MSN). You can even install third-party service providers into the Exchange client (such
as the one provided by CompuServe) to access messages stored in other mail systems (see Figure 3.2).

Figure 3.2 : Microsoft Exchange can link to multiple service providers.

Consistency

The MAPI service set contains a set of services that encompasses all the basic messaging tasks:
● Message service logon and logoff
● Reading, creating, and deleting text messages
● Adding and deleting message binary file attachments
● Addressing and transporting the completed messages

The exact behavior and properties of each of these services are defined as part of MAPI. All vendors who supply a
MAPI-compliant set of tools provide these services in the exact same manner. This way, any program that uses MAPI
services can be assured that there are no special API variations to deal with when moving from one vendor's MAPI
product to another. This means the programs you write today using your current implementation of MAPI services will
function under other implementations of the MAPI service set (see Figure 3.3).

Figure 3.3 : Consistency of MAPI services across vendor implementations.

Portability

This leads to the second strength of Microsoft's MAPI service set-portability. Microsoft supports MAPI services on all
versions of its Windows operating systems. If you write a program for the Windows 3.1 version of MAPI services, that
same program can still access the MAPI services under any other version of Windows that supports your executable
program. This is a key issue when you consider how many versions of Windows are currently in use and how quickly
new versions of the operating system are developed and deployed.

Not only will you be able to move your MAPI-related programs to various Windows platforms, you can also allow
programs to access MAPI services from more than one platform at once. In other words, users of more than one version
of Windows can all be accessing MAPI services from a central location at the same time (see Figure 3.4).

Figure 3.4 : Portability of MAPI across Windows platforms.

Microsoft has announced plans to move several of its service sets (MAPI included) beyond the Windows operating
environment, too. As this happens, Microsoft has pledged that the same set of functions and routines used under the
Windows environment will be available in other operating systems.

Messages

The primary role of the MAPI service is to transport and store messages. This section identifies three common message
types supported by MAPI services:

● Text messages
● Formatted documents or binary files
● Control messages

The most basic message form is the text message, commonly thought of as e-mail. Most electronic message systems also
support the second type of message-formatted documents or binary files. These are usually included as attachments to a
text message.

The third message type is a control message. Control messages are usually used by the operating system to pass vital
information such as system faults, potential failure conditions, or some other type of status information. Control
messages can also be passed between programs in order to implement a level of distributed processing in a computer
network.

The following sections review each message type in more detail.


Text Messages

The text message is the most common MAPI message. In fact, all MAPI messages have a default text message
component. A text message contains the letters and words composed by users to communicate with other message
system users.

All MAPI service providers must supply a simple text message editor as part of their MAPI implementation. All MAPI
message providers support plain ASCII text characters as a message body. Many also support rich-text messages that
contain formatting characters such as font and color. The Microsoft Mail client supplied for Windows 3.11 and Windows
for Workgroups supports plain ASCII text. The Microsoft Mail Exchange client supplied for Windows 95 supports rich-
text formatting.

Formatted Documents and Binary Files

The second MAPI message type is the formatted document or binary file, which is usually a file containing non-printable
characters such as a spreadsheet, a word-processing file, graphics, or even an executable program. Binary files are
supported by MAPI services as attachments to text messages. The MAPI service set allows multiple attachments to a
single text message. This means you can send several binary files to the same e-mail address using a single message
body.

All MAPI service providers support the use of binary attachments to a message body. However, the transport of binary
attachments across multiple message servers may not be supported. For example, if you compose a message that
contains attached binary files, address it to an associate at a distant location, and attempt to send the message using a
service provider that supports only Simple Mail Transfer Protocol (SMTP) format, your attached binary files will not be
successfully transported to the recipient.

Control Messages

The third type of MAPI message is the control message. Control messages are usually used by the operating system to
deliver system status information, such as a component failure or other system-related problem. These messages are
usually addressed directly to the system administrator.

Control messages can also be used to pass data or other control information between programs. Control messages of this
type can contain requests for information that is to be collected by one program and returned to another for further
processing. Or the control message can contain actual data to be manipulated by another program. Since MAPI services
can stretch across the LAN or across the globe, control messages can be passed to systems halfway around the globe as
easily as to systems across the room.

It is possible to designate one or more workstations on a network as batch job computers. These machines wait for
control messages that direct them to perform time-consuming tasks, such as extended database searches or generating
long reports, thus freeing up users' workstations for more immediate business. Once the task is complete, the batch job
machine can send a completion notice via e-mail to the user who sent the original request. While it is true that OLE
Automation servers are beginning to replace batch job computers that are controlled by MAPI messages, MAPI services
are still a very powerful alternative.

MAPI Applications

Just as there are three types of MAPI messages, there are three general types of MAPI applications:

● Electronic mail clients


● Message-aware applications
● Message-enabled applications
Electronic Mail Clients

Electronic mail (e-mail) clients are the most common form of MAPI application. An e-mail client allows end-users direct
access to the MAPI services supported by the back-end service provider. Figure 3.5 shows the Microsoft Exchange
electronic mail client.

Figure 3.5 : The Microsoft Exchange Mail Client.

Typical services provided by a MAPI e-mail client include

● Message service logon and logoff


● Reading, creating, and deleting text messages
● Adding and deleting binary file message attachments
● Addressing and transporting completed messages

Electronic mail clients can also provide additional services to make it easy to manipulate, store, and retrieve text
messages and binary file attachments. Electronic mail clients may also have additional features for addressing and
transporting messages, including the use of defined mailing lists and the capability to address messages as cc (courtesy
copies) or Bcc (blind courtesy copies).

Message-Aware Applications

Message-aware applications are non-MAPI programs that allow users access to MAPI services. Typically, this access is
implemented through the addition of a send option in a menu or button bar. Figure 3.6 shows the Microsoft Word 95
main menu with the Send menu item highlighted.

Figure 3.6 : Microsoft Word 95 is a message-aware application.

Message-aware applications usually treat e-mail services just like any other storage or output location, such as disk
drives, printers, or modems. In these cases, the ability to send the standard output as an electronic mail message is an
added feature for the application. As MAPI services become a standard part of the Windows operating system, message-
aware applications will become the norm instead of the exception.

Message-Enabled Applications

The last category of MAPI applications is message-enabled applications. Message-enabled applications are programs
that offer message services as a fundamental feature. While message-aware applications provide message services as an
additional feature and can operate well without them, message-enabled applications are specifically designed to use
message services and most will not run properly unless message services are available on the workstation.

Here are some examples of message-enabled applications:

● Computerized service dispatch-Customer calls are handled by representatives at pc workstations where they fill
out data entry forms outlining the repair needs and the location of the service call. When the data entry is
complete, the program analyzes the information and, based on the parts needed and the service location, routes
an instant electronic message containing the service request and a list of needed parts to the repair office nearest
to the customer.
● Online software registration-When a user installs a new software package, part of the installation process
includes an online registration form that already contains the unique software registration code along with a
data entry form for the user to complete. Once the form is completed, the results are placed in the user's e-mail
outbox to be sent directly to the software company to confirm the user's software registration.
● End-user support services-When network end-users have a question about a software package or need to report
a problem with their workstation or the network, they call up a data entry form prompting them to state the
nature of the problem. This program will also automatically load the user's system control files and add them as
attachments to the incident report. Once the form is complete, it is sent (along with the attachments) to the
appropriate network administrator for prompt action.

It is important to note that, in some cases, users of message-enabled applications may not even be aware that they are
using the e-mail system as part of their application. MAPI services define properties and methods for logging users in
and out of the message server without using on-screen prompts. MAPI also provides options for addressing and sending
messages without the use of on-screen prompts or user confirmation. By using these features of MAPI services, you can
design a program that starts a message session, reads mail, composes replies, addresses the new mail, and sends it to the
addressee without ever asking the user for input.

Other Types of Message Applications

There are two more types of message-enabled applications that deserve comment here. These two application types are

● Electronic forms applications


● Message-driven applications

Electronic forms applications display a data entry screen that contains one or more data fields for the user to complete.
These data fields act as full-fledged windows controls and can support all the events normally supported by Windows
data entry forms. Once the form is completed, the data, along with additional control information, is sent to the addressee
through MAPI services. When the addressee opens the new mail, the same formatted data entry form appears with the
fields filled in (see Figure 3.7).

Figure 3.7 : The filled-in electronic form.

The message-driven application looks for data contained in a message and acts based on the data it finds. Message-
driven applications can use any aspect of the message as control information for taking action. Message-driven
applications can inspect the message body or subject line for important words or phrases, check the sender's name or the
date and time the message was sent, or even scan attachments for important data. These applications can then use the
data to forward messages to another person automatically, to set alerts to notify the user of important messages, or to
start other programs or processes at the workstation.

Below are some examples of message-driven applications:

● Message filtering agent-Users can enter a list of important keywords into a list box. This list is used to scan all
incoming text messages automatically. If the message contains one or more of the keywords, the user is notified
immediately that an important message has arrived. Users could also set values to scan for the message sender's
name. For example, if the message came from the user's boss, an alert could sound to warn the user that an
urgent message has arrived. The same technique can be used to automatically forward specific messages when
the user is away on a trip.
● File transfer database update application-This program could be used by outlying sales offices to update a
central database automatically. Each day the remote offices would enter sales figures in a database, then attach
the binary database file to an e-mail message, and send the message to the corporate headquarters. There, a
special workstation (logged in as the addressee for all sales database updates) would receive the message and
automatically run a program that takes the binary database file and merges it into the central database. This
same program could then provide summary data back to the remote offices to keep them up to date on their
progress.
● Electronic database search tool-Many companies have large libraries of information on clients, products,
company regulations, policies and procedures, and so on. Often users would like to run a search of the
information but don't have time to physically visit the library and pour through thousands of pages in search of
related items. If the information is kept in online databases, users at any location around the world could
formulate a set of search criteria to apply against the databases and then submit these queries, via MAPI
messages, to one or more workstations dedicated to performing searches. After the search is completed, the
resulting data set could be returned to the user who requested the data.

Filtering agents, remote update routines, and long-distance search tools are all examples of how MAPI services can be
used to extend the reach of the local workstation to resources at far-away locations. The Windows MAPI services
provide excellent tools for building programs that enable users to collect and/or disseminate data over long distances or
to multiple locations. The next several chapters will explore the details of MAPI architecture, teach you how to
incorporate MAPI services into your programs, and show you examples of real-life programs that take advantage of
Windows MAPI services.

Summary

In this chapter, you learned that the Messaging Application Programming Interface (MAPI) is a part of the Windows
Open Systems Architecture (WOSA) model. MAPI is designed to offer three key benefits over other messaging services:

● Flexibility-Since MAPI is implemented within the WOSA model, there are three distinct layers:
● The client layer (the end-user software)
● The MAPI DLL layer (the MAPI service broker)
● The service layer (the actual message service provider)
● Because the MAPI DLL layer acts as the service request broker between the MAPI client and the MAPI server,
you can interchange servers and clients without having to modify your MAPI software modules.
● Consistency-MAPI services and the methods for accessing them are the same no matter what vendor you are
using to provide the message services.
● Portability-MAPI services are available on all supported versions of Windows (Win3.11, WFW, WinNT, and
Win95). As Microsoft moves WOSA services to non-Windows platforms, MAPI services will be available
within those operating systems, too.

You also learned the three general types of MAPI messages:

● Text messages-These are the standard plain ASCII text messages commonly known as e-mail. Some MAPI
service providers support the use of rich-text formatted messages (for example, the Microsoft Exchange Mail
client).
● Formatted documents and binary files-These are word-processing documents, graphics files, databases, and so
on. MAPI enables you to send these binary files as attachments to the body of a text message.
● Control messages-These are used by operating systems and specialized batch programs to send information
about the operating system, or send commands to tell remote machines how to process attached data or run
special jobs.

Finally, you learned about the various types of MAPI applications. These application types are

● Electronic mail clients-The sole purpose of these programs is to give users direct access to the available MAPI
services (for example, the Microsoft Mail Exchange client that ships with Windows 95).
● Message-aware applications-These are programs that provide MAPI services as an added feature. These
programs usually offer users a send button or menu option. The standard output of the program can then be
routed to another location through MAPI. The Send menu option of Microsoft Word 95 is an example of a
message-aware application.
● Message-enabled applications-These programs offer MAPI services as a basic part of their functionality.
Usually, message-enabled applications will not operate properly unless MAPI services are available to the
workstation. Examples of message-enabled applications include data entry forms that collect data and
automatically route the information to the appropriate e-mail address, sometimes without asking the user for
MAPI logons or addresses.

Two more application types reviewed in this chapter are


● Electronic forms applications-These programs are fully functional data entry forms that are MAPI-enabled.
Users treat the form like any Windows program. Once the data entry is completed and the form sent, the
addressee can open the message and see the same data form.
● Message-driven applications-These are programs that can inspect portions of the message (body, header,
attachments) and perform requested actions based on the contents of the message parts. Examples of message-
driven applications include e-mail filtering agents, file transfer and update routines, and long-distance data
search and retrieval programs.

Now that you know the common types of MAPI messages and applications, it's time to review the details of MAPI
architecture. That is the subject of the next chapter.
Chapter 4

MAPI Architecture

CONTENTS

● The MAPI Client


❍ Messages and Attachments

❍ Storage Folders

❍ Addresses

● The MAPI Server


❍ Message Transport

❍ Message Stores

❍ Address Books

● The MAPI Spooler


● Summary

In Chapter 3, "What Is MAPI?," you learned the general outline of the messaging API and how MAPI fits into
Microsoft's Windows Open Systems Architecture (WOSA) scheme. You also learned about the three most common
types of MAPI messages (ASCII text, formatted documents or binary files, and control messages). Lastly, Chapter 3
described the three major categories of MAPI applications (e-mail, message-aware, and message-enabled).

In this chapter, you'll learn about the basic conceptual components of MAPI-the MAPI
Client and the MAPI Server. These two components work together to create, transport, and store messages within the
MAPI system.

MAPI clients deal with three main objects:

● Messages and attachments


● Storage folders
● Addresses

Each of these objects is represented slightly differently in the various versions of MAPI implementations you'll learn
about in this book. For example, the MAPI OCX tools that ship with Visual Basic allow only limited access to folders
and address information. The Messaging OLE layer (provided through the MAPI 1.0 SDK) provides increased access to
folders and addresses, but only the full MAPI 1.0 functions allow programmers to add, edit, and delete folders and
addresses at the client level. This chapter will cover all three objects in a general sense. Later chapters will provide
detailed programming examples.

MAPI servers also deal with three main objects:

● Message transport
● Message stores
● Addresses books
As you can see, the server side of MAPI is quite similar to the client side (see Figure 4.1).

Figure 4.1 : Client and server aspects of the MAPI system.

Where the client is concerned with creating and manipulating messages, the server component is concerned with the
transporting of those same messages. Where the client side is accessing storage folders, the server side is dealing with
message storage, and both client and server must deal with message addresses. However, the MAPI server has the
responsibility of managing the transport, storage, and addressing of messages from any number of client applications.

In addition to maintaining the message base for all local clients, MAPI servers also have the task of moving messages to
and from remote servers and clients. The final section of this chapter talks briefly about a special MAPI component that
handles this task-the MAPI Spooler.

When you complete this chapter, you'll understand the fundamental tasks that must be performed by all MAPI clients
and MAPI servers and the basic objects maintained and manipulated by both client and server applications. Once you
have a grasp of the general operations of a MAPI system, you'll be ready to explore MAPI functions from a Windows
programming perspective in the coming chapters.

The MAPI Client

The MAPI Client is the application that runs on the user's workstation. This is the application that requests services from
the MAPI Server. As mentioned in the preceding chapter, client applications can be generic e-mail tools such as
Microsoft's Microsoft Mail or Exchange Mail Client for Windows 95. Client applications can also be message-aware
applications like the Microsoft Office Suite of applications. Each of these applications provides access to the message
server via a send menu option or command button. Lastly, message-enabled applications, ones that use MAPI services as
a primary part of their functionality, can be built to meet a specific need. These programs usually hide the MAPI services
behind data entry screens that request message-related information and then format and send messages using the
available message server.

All MAPI clients must, at some level, deal with three basic objects:

● Messages and attachments


● Storage folders
● Addresses

Depending on the type of client application, one or more of these MAPI objects may be hidden from the client interface.
However, even though they may not be visible to the application user, all three objects are present as part of the MAPI
architecture. The next three sections describe each of the MAPI client objects and their functions in the MAPI model.

Messages and Attachments

The MAPI system exists in order to move messages from one location to another. Therefore, the heart of the system is
the MAPI message object. All message objects must have two components-a message header and a message body. The
message header contains information used by MAPI to route and track the movement of the message object. The
message body contains the actual text message portion of the message object. Even though every message object must
have a message body, the body can be left blank. In addition to the two required components, message objects can also
have one or more attachments. Attachments can be any valid operating system file such as an ASCII text file, a binary
image, or an executable program.

The Message Header


The Message Header contains all the information needed to deliver the associated message body and attachments. Data
stored in the message header varies, depending on the messaging service provider. While the exact items and their names
and values differ between messaging systems (CMC, MAPI, OLE Messaging), there is a basic core set that appears in all
message headers. Examples of basic data items that can be found in a message header are listed in Table 4.1.

Note
The item names given in Table 4.1 do not necessarily correspond to a valid
property or variable name in programming code. The actual property names
for each item can vary from one code set to another. Also, the order in
which these properties appear differs greatly for each MAPI
implementation. The chapters following this one focus on different code
sets (CMC API, MAPI Controls, OLE Messaging) and detail the exact
keywords and property names used to access the items described here.

Table 4.1. Basic data items found in a message header.


Property Name Type Description
E-mail address of the person who will
receive the message. This could be a
Recipients Recipients object
single name, a list of names, or a group
name.
E-mail address of the person who sent the
Sender AddressEntry object
message.
Subject String A short text line describing the message.
The date and time the message was
TimeReceived Variant (Date/Time)
received.
TimeSent Variant (Date/Time) The date and time the message was sent.

Along with this basic set, additional data items may appear or be available to the programmer. These additional items
add functionality to the MAPI interface, but because they are not part of the core set, you cannot expect them to be
available to you when you write your programs. Table 4.2 contains a list of additional header data items.

Table 4.2. Optional items that may be found in the message header.
Property Name Type Description
Flag that indicates the sender asked for a return
receipt message upon either delivery of the message
DeliveryReceipt Boolean
to the recipient or the reading of the message by the
recipient.
A value that indicates the relative importance of the
Importance Long message. Currently, Microsoft Mail clients recognize
three priorities: High, Medium, and Low.
Flag that indicates the item has been sent to the
Submitted Boolean
recipient.
Sent Boolean Read/write.
Signed Boolean Read/write.
Value that identifies this message as one of a class of
messages. Currently, Microsoft Mail systems
recognize the IPM (Interpersonal Message) type for
Type String sending messages read by persons. Microsoft has
defined the Ipc (Interprocess Communication) type
for use between program processes. Other types can
be defined and used by other programs.
Flag that indicates whether the message has been
Unread Boolean
received and/or read by the recipient.

The Message Body

The message body contains the text data sent to the recipient from the sender. For most systems, this is a pure ASCII text
message. However, some service providers can handle rich-text format message bodies, which allows for additional
information such as font, color, and format codes to be included in the message body (see Figure 4.2).

Figure 4.2 : Using Microsoft Exchange to send a rich-text message.

The availability and support of rich-text message bodies vary between service providers. Some service providers allow
you to create rich-text message bodies but translate the information into simple ASCII upon delivery. For example,
Microsoft Exchange allows users to build rich-text message bodies but translates that message into simple ASCII text
when using the SMTP service provider. All messages received by the Microsoft Mail system are converted into simple
text as well. This behavior ensures that the message will be delivered but may result in surprise or even unreadable
material at the recipient's end (see Figure 4.3).

Figure 4.3 : Receiving a rich-text message that has been changed to simple ASCII.

Other transport providers may transmit the rich-text and allow the receiving message provider to handle the translation to
simple ASCII, if needed. This option allows for the most flexibility but can result in undeliverable messages. For
example, Microsoft Exchange users have the option of sending rich-text messages through the CompuServe message
transport. The CompuServe transport supports rich-text messages. Rich-text messages sent from one Windows
Messaging Client to another by way of the CompuServe transport retain their original layout and look. However,
recipients using something other than Microsoft Exchange may see something different. For example, the WinCIM
client provided by CompuServe treats rich-text messages as binary attachments to a simple ASCII text message.
WinCIM clients would receive the message shown in Figure 4.2 as an attachment to view with Microsoft Word or some
other viewer.

If, however, the CompuServe recipient is defined as an Internet user through the CompuServe address (for example,
[email protected]), the CompuServe message transport reports the message as undeliverable when it is
discovered that the message body contains anything other than simple ASCII text (see Figure 4.4).

Figure 4.4 : Reporting an undeliverable rich-text message.

Message Attachments

Message attachments are supported by all forms of Microsoft MAPI. A MAPI attachment can be any data file, of any
type (text, binary programs, graphics, and so on). These attachments are sent to the recipient along with the message
header and body. Upon receipt of the message, the recipient can, depending on the features of the message client
software, view, manipulate, and store the attachments on the local workstation. The MAPI system keeps track of
attachments with a set of properties. Table 4.3 lists an example set of attachment properties. The actual properties and
their names can differ between program code sets.
Table 4.3. Example MAPI attachment properties.
Property Name Type Description
Each message object can contain more than one
Index Long
attachment. Attachments are numbered starting with 0.
The name to display in a list box or in the client message
Name String
area (for example, "June Sales Report").
A value that indicates where in the message body the
attachment is to be displayed. Microsoft Mail and
Windows Messaging clients display an icon representing
Position Long
the attachment within the message body. Other clients
may ignore this information, show an icon, or show ASCII
text that represents the attachment.
The exact filename used by the operating system to locate
Source String and identify the attachment (for example, "\\Server1\Data
\Accounting\JuneSales.xls").
A value that indicates the type of attachment. Microsoft
defines three attachment types:
Type Long Data-A direct file attachment
Embedded OLE-An embedded OLE object
Static OLE-A static OLE object

Attachments can be handled differently by the message transport provider. Microsoft Mail and Windows Messaging
clients display attachments within the message body and transport the attachments as part of the message body, too.
Microsoft Mail and Microsoft Exchange recipients see the attachment within the message body and can use the mouse to
select, view, and save the attachment when desired. Other transports may handle the attachment differently.

SMTP transports will report a message that contains attachments as undeliverable unless the transport supports MIME or
some other binary transfer protocol. The Internet transport that ships with the Windows Messaging Client does support
MIME protocol.

The CompuServe service provider for Microsoft Exchange will transport the attachments as additional messages
addressed to the same recipient. For example, a single ASCII text message with one attachment would be received as
two messages when sent via the CompuServe transport.

Note
The CompuServe message transport for Microsoft Exchange supports only
attach-ments for native, or CompuServe, addresses. You cannot use the
CompuServe transport to send attachments to an SMTP-defined Internet
account (such as [email protected]).

Storage Folders

MAPI messages can be saved in storage folders. The MAPI model defines several storage folders. Figure 4.5 shows a set
of folders as viewed from the Windows Messaging Client.

Figure 4.5 : Viewing the folders from the Windows Messaging Client.

The defined storage folders within MAPI are


● Inbox-This is the place where all incoming messages first appear.
● Outbox-This is the place where all outgoing messages are placed before they are sent to their destination.
● Sent-This is the place where all outgoing messages are placed after they are sent to their destination. This is, in
effect, a set of message copies that can be referenced after the original has been sent.
● Deleted-This is the place where all messages are placed once they have been marked for deletion.
● User-defined folders-This can be one or more folders defined by the user. Each folder can hold messages that
have been received and copies of messages that have been sent.

Not all implementations of MAPI support all of the folders listed above. For example, the Simple MAPI interface allows
access to the Inbox (by way of the .Fetch method of the MAPISession Control) and the Outbox (by way of the .
Send method of the MAPISession Control). Simple MAPI allows no other folder access. Programmers cannot inspect
the contents of the Sent folder or move messages from the Inbox to other user-defined storage folders.

The OLE Messaging library offers a more complete access to the MAPI storage system. The Inbox and Outbox
folders are accessible by name (that is, Session.Inbox and Session.OutBox). The Sent and Deleted folders
are available. The OLE MAPI implementation allows access to all folders through the InfoStore object. An
Infostore object contains all folder and message objects. All MAPI service providers can implement their own
message stores, and a single client can have access to more than one message store at the same time. Therefore, a single
MAPI session can involve attaching to several message stores and presenting their contents to the user for handling.

Note
The OLE Messaging library does not allow programmers to create or delete
folders from the InfoStore collection. Only the MAPI 1.0 API set
available from C++ allows programmers to create and delete folders from
the storage set.

The MAPI storage folders are defined as a hierarchy (as seen in Figure 4.5). The MAPI model allows for the creation of
multiple levels of the hierarchy at any time. The MAPI model allows for the creation of subfolders at any level and on
any folder. For example, the Inbox folder can have several subfolders such as UnRead, Read, Urgent, and Unknown.
The addition of subfolders allows users to organize message stores based on use and preference.

The MAPI model defines a handful of properties for storage folders. These properties are available through the OLE
Messaging library and C++. Table 4.4 lists some of the more commonly used properties of the storage folder.

Table 4.4. Example MAPI storage folder properties.


Property Name Type Description
This is a string value that uniquely
FolderID String
identifies this folder.
This is the set of folder objects
contained by the current folder. Any
Folders Folders collection object
folder can contain one or more sublevel
folders.
This is the set of messages stored in this
Messages Messages collection object
folder.
A unique user-defined string that
Name String
identifies the storage folder.
This contains the name of the parent
Parent Object folder or InfoStore to which the
current folder belongs.
Note
You can find additional information on Folder and InfoStore objects,
their properties, and their methods in Chapter 8, "The OLE Messaging
Library," and Chapter 9, "Creating a MAPI Mailing List Manager with the
OLE Messaging Library."

Physical organization of the storage folders can differ greatly depending on the service provider. For example, the
Microsoft Mail Server storage arrangement involves a single directory on the disk that identifies the post office and
subsequent directories underneath that correspond to an individual user's InfoStores (and subfolders within the
storage). However, the physical organization of storage folders for the Windows Messaging Client that ships with
Windows 95 involves a single data file (usually found in \Exchange\mailbox.pst). This single file is used to store
all the personal messages maintained on the user's workstation. It is up to the service provider to establish the storage
details and to support access to the physical storage using the pre-defined InfoStore, Folder, and Message
objects.

Addresses

Addresses are the last class of objects dealt with at the client level. Every electronic message has at least two address
objects: the sender object and the recipient object. MAPI allows you to add several recipient address objects to the same
message. Each address object has several properties. Table 4.5 shows a set of sample properties for the MAPI Address
object.

Table 4.5. MAPI Address object properties.


Property Name Type Description
This is the unique electronic address for this address
object. The combination of the Type property (see
below) and the Address property creates the complete
Address String
MAPI address. Sample address properties are
[email protected] address
/MailNet1/PostOfc9/MCA-MS Mail address
The MAPI service allows programmers to define
addresses by type. This means you can sort or filter
messages using the DisplayType property. Sample
address types are
DisplayType Long
mapiUser-Local user
mapiDistList-Distribution list
mapiForum-Public folder
mapiRemoteUser-Remote user
This is the name used in the Address book. Usually, this
Name String is an easy-to-remember name such as "Fred Smith" or
"Mary in Home Office."
This value contains the name of the message transport
type. This allows MAPI to support the use of external
message transport services. Sample address types are
Type String
MS:-Microsoft Mail transport
SMTP:-Simple Mail Transport Protocol
MSN:-Microsoft Network transport

MAPI address objects are a part of every MAPI message and are stored in the MAPI address book. You'll learn more
about the address book in the following section on MAPI Server objects.
The MAPI Server

The MAPI Server handles all the message traffic generated by MAPI clients. The MAPI Server usually runs on a
standalone workstation connected to the network, but this is not a requirement. There are versions of user-level MAPI
servers that can be used to handle message services.

Microsoft supports two standalone MAPI servers:

● Microsoft Mail Server (for both pcs and Apple workstations)


● Microsoft Exchange Server (for NT Server workstations)

The Microsoft Mail Server runs standalone on both Intel pcs or Apple workstations. It provides direct MAPI services for
all connected MAPI users and also provides gateway MAPI services for remote users. The Microsoft Mail Server has,
until recently, been Microsoft's primary electronic mail server. Even though Microsoft is stressing the early adoption of
the new Microsoft Exchange Server for NT, the Microsoft Mail Server will continue to be the primary mail server for
thousands of users. All MAPI Clients can share information with connected Microsoft Mail Servers regardless of the
client platform (Win31, WinNT, or Win95).

The Microsoft Exchange Server runs as a service on an NT Server workstation. It provides MAPI services to all MAPI
users. Unlike the Microsoft Mail Server, which distinguishes between local and remote users, the Microsoft Exchange
Server treats all MAPI users as remote users. This simplifies several aspects of MAPI administration. Unlike the
Microsoft Mail Server, which only supports Microsoft Mail format messages, the Microsoft Exchange Server supports
multiple message formats and services, including Microsoft Mail. This also means that the administration of gateways
and remote transports is quite different for Microsoft Exchange.

Microsoft also supports two peer-to-peer message servers. These servers run on the user's workstation, usually as a part
of the MAPI client software. The two client-level MAPI servers provided by Microsoft are

● WorkGroup Post Office for Windows for WorkGroups


● Windows Messaging Client for Windows 95

The WorkGroup Post Office runs as a limited version of the Microsoft Mail Server. Clients that use Microsoft's peer-to-
peer networking are able to establish a post office on a single workstation and then share the post office directories with
other peers on the network. The design and operation are very much like the Microsoft Mail Server system, but it runs on
an individual pc. The primary advantage of the peer-to-peer version is that a single user can set up a WorkGroup Post
Office and use that as a base for adding remote mail connections and fax support. The main disadvantage of the peer-to-
peer client is that users were not able to attach to both the WorkGroup Post Office and an existing Microsoft Mail Server
post office.

With the introduction of Windows 95, Microsoft introduced a client version of Microsoft Exchange that provides the
same features as the Microsoft Exchange Server version. Users are able to install and share a WorkGroup Post Office
and are also able to attach to existing Microsoft Mail post offices. In addition, users can connect using other mail
transports as they become available.

Regardless of the actual server application used, the same basic processes must occur for all MAPI server systems. The
three main tasks of all MAPI servers are

● Message transport-Moving the message from location to location.


● Message storage-Providing a filing system for the storage and retrieval of received messages.
● Address book services-Providing centralized addressing and verification services that can be used by all MAPI
clients.
The next three sections discuss each of these processes in greater detail.

Message Transport

Message Transport is the process of moving messages from one place to another. Under the MAPI model, message
transport is a distinct, and often separate, process. MAPI 1.0 allows for the use of external message transports. In other
words, programmers can write software that knows how to handle a particular type or types of message formats and
register this transport mechanism as part of the MAPI system. This allows third-party vendors to create format-specific
transports that can be seamlessly integrated into the MAPI system.

It is the message transport that knows just how to format and, if necessary, pre-process messages for a particular
messaging format. The message transport knows exactly what information must be supplied as part of the message
header and how it needs to be arranged. The message transport also knows what types of message bodies are supported.
For example, SMTP format allows only text message bodies. However, the Microsoft Network message format allows
rich-text message bodies. It is the job of the message transport to keep track of these differences, modify the message
where appropriate, or reject the message if modification or pre-processing is not possible.

One of the key features of the MAPI model is the provision for multiple message transports within the MAPI system.
Once message transports are installed (or registered) with a MAPI client application, they are called into action
whenever the pre-defined message type is received by the MAPI client software. Since MAPI is designed to accept the
registration of multiple transports, the MAPI Client is potentially capable of handling an unlimited number of vendor-
specific message formats.

Note
Message types are stored as part of the address. These types were discussed
earlier in this chapter in the "Addresses" section.

Figure 4.6 shows how the multiple message transports are used when messages are received by the MAPI Client.

Figure 4.6 : Handling incoming messages with multiple message transports.

Under the MAPI system, message transports provide another vital service. It is the responsibility of the message
transport to enforce any security features required by the message format. For example, the MSN mail transport is
responsible for prompting the user for a username and password before attempting to link with the MSN mail system.

It is important to note that the message transport is not responsible for storing the messages that have been received. The
transport is only in charge of moving messages from one location to another. Message storage is discussed in the next
section.

Message Stores

Message stores are responsible for providing the filing system for the messages received via the message transport. The
MAPI model dictates that the message store must be in a hierarchical format that allows multilevel storage. In other
words, the system must allow users to create folders to hold messages, and these folders must also be able to hold other
folders that hold messages. Under the MAPI model, there is no limit to the number of folder levels that can be defined
for a message store (see Figure 4.7).

Figure 4.7 : MAPI message stores are hierarchical filing systems.

Under the MAPI model, storage folders can have properties that control how they are used and how they behave. For
example, storage folders can be public or private. Folders can have properties that make the contained messages read-
only to prevent modification. The options available depend on the implementation of the message store. In other words,
the programmer who designs the message store can establish the scope of storage options and the MAPI Client will
comply with those rules.

As in the case with message transports, MAPI clients can access more than one message store. Figure 4.8 shows the
Windows Messaging Client that is currently accessing two different message stores. You can see that each store has its
own set of folders and its own set of messages.

Figure 4.8 : Accessing multiple message stores at the same time.

The Windows Messaging Client that ships with Microsoft Exchange Server also allows you to create folder column,
grouping, sort, and filter rules for personal and public folders. By doing this, you can create storage views that reflect the
course of an ongoing discussion and allow for easy search and retrieval of data kept in the message store (see Figure
4.9).

Figure 4.9 : A Microsoft Exchange folder with discussion properties.

Address Books

The last of the main MAPI server objects is the address book. The MAPI address book contains all the directory
information about a particular addressee. The book can contain data for individual users or groups of users (a distribution
list). The minimum data stored in the address book is the user's display name, the transport type, and the user's e-mail
address.

Additional information such as mailing address, telephone number, and other data may be available depending on the
design of the address book.

Address books, like the other objects described in this chapter, work independently under the MAPI model. Also, the
MAPI client can access more than one address book at a time. This means that several address books of various formats
can all be viewed (and used) at the same time when composing messages (see Figure 4.10).

Figure 4.10 : Accessing multiple address books at the same time.

Along with storing addresses, the address book interface also acts to resolve display names used in the MAPI interface
with the actual e-mail addresses and transport types for those display names. To do this, MAPI offers a ResolveName
service that performs lookups upon request. The ResolveName service is able to look at all address books (regardless of
their storage format) in order to locate the proper e-mail address.

Users are also able to designate one of the address books as the default or personal address book. This is the first address
book in which new addresses are stored and the first address book that is checked when resolving a display name. The
Windows Messaging Client and the Microsoft Mail client both ship with default personal address books. The Windows
Messaging Client allows users to add new address books and designate their own personal address book container.

The MAPI Spooler

The MAPI Spooler is a special process that interacts with both message stores and message transports. It is the spooler's
job to route messages from the client to the proper transport and from the transport to the client. The spooler is the direct
link between the client and the transport. All messages go through the MAPI Spooler.

Note
Actually there are some cases in which a message moves directly from the
message store to the message transport. This occurs when service providers
offer both message store and message transport. E-mail service providers
that offer these features are known as tightly coupled service providers.

Figure 4.11 illustrates the different paths messages can take when the MAPI Spooler is used.

Figure 4.11 : The MAPI Spooler passes messages between the message store and the message transport.

As each message is moved from the message store (the "outbox") to the transport, the MAPI Spooler checks the address
type to see which transport should be used. Once this is determined, the spooler notifies the transport and attempts to
pass the message from the message store to the message transport. If the transport is not currently available, the MAPI
Spooler holds onto the message until the transport is free to accept messages. This allows transport providers to act as
remote connections without any additional programming or addressing on the client side.

Note
In fact, the implementation used in the Microsoft Exchange version of
MAPI treats all connections as if they were remote-even when the message
is moved from one user's Microsoft Exchange outbox to another Microsoft
Exchange user's inbox on the same network.

In the case of messages that move along a constantly connected transport (that is, between two addresses on the same
Microsoft Exchange Server), the spooler notifies the transport (Microsoft Exchange) and the transport accepts the
message almost immediately. Often the user is not aware of any delay in the handling of the message.

In the case of messages that move from the Windows Messaging Client by way of an SMTP transport through a dial-up
connection to the Internet, the MAPI Spooler holds onto the message until the user connects to the Internet account
through Win95 Dial-Up Networking. Once the connection is made, the MAPI Spooler sends all local messages on to the
Internet mail server, retrieves any waiting mail from the mail server, and passes these new messages to the appropriate
message store.

The MAPI Spooler is also able to move a single message to several recipients when some of those recipients are not
using the same message transport. For example, users can build distribution lists that contain names of users on the local
Microsoft Exchange Server, users who have addresses on a local Microsoft Mail Server, and users who can only be
contacted through a fax address. When the message is sent, it moves from the message store to the spooler, which then
sorts out all the transports needed and passes the messages on to the correct transports at the first available moment.

The MAPI Spooler is also responsible for marking messages as read or unread, notifying the sender when a message has
been successfully passed to the transport, and, when requested, notifying the sender when the recipient has received (or
read) the message. The MAPI Spooler also reports when messages cannot be sent due to unavailable transports or other
problems.

Summary

In this chapter, you learned about the general architecture of the MAPI system. You learned that there are two main
components to the system:

● The MAPI Client


● The MAPI Server

You learned that the MAPI Client resides on the user's desktop and handles three main MAPI objects:

● Messages and attachments


● Storage folders
● MAPI addresses

This chapter also reviewed the basic properties and features of MAPI messages, including message headers, folders, and
address objects.

You learned that the MAPI Server usually resides on a standalone workstation connected to the network (although not
always). Like the MAPI Client, the MAPI Server handles three main objects:

● Message transports
● Message stores
● Address books

You learned that the MAPI model allows users to use multiple versions of message transports (such as Microsoft
Exchange Server messages and SMTP Internet messages), message storage, and address books. You also learned about
the MAPI Spooler. The MAPI Spooler is the process that moves items from the message store to the appropriate
provider.

Now that you know the basic architecture of the MAPI system, it's time to build some applications that use the system.
In the next chapter, you'll learn how to use the Microsoft Exchange Forms Designer to build MAPI-enabled applications
that work within the Microsoft Exchange interface.
Chapter 8

The OLE Messaging Library

CONTENTS

● Introduction
● The Session Object
❍ The Session Object Methods

❍ The Session Object Properties

● The InfoStore Objects and Collections


❍ The InfoStores Collection

❍ The InfoStore Object

● The Folder Objects and Collections


❍ The Folders Collection Object

❍ The Folder Object

❍ The Inbox and OutBox Folders

● The Message Objects and Collections


❍ The Messages Collection Object

❍ The Message Object

● The Recipient Objects and Collections and the Address Object


❍ The Recipients Collection Object

❍ The Recipient Object

❍ The AddressEntry Object

● The Attachment Objects and Collections


❍ The Attachments Collection Object

❍ The Attachment Object

● Summary

Introduction

One of the new features of Microsoft Exchange is the creation of the OLE Messaging library. This set of OLE objects,
properties, and methods allows any VBA-compliant development tool to gain access to MAPI services and incorporate
them into desktop applications. This chapter shows you how the OLE Messaging library works and how you can use the
OLE objects to create MAPI-enabled programs.

This chapter provides an overview of all the OLE Messaging library objects and gives examples of their use. You'll learn
about the following objects:

● The Session object


● The InfoStore objects and collections
● The Folder objects and collections
● The Message objects and collections
● The Recipient objects and collections
● The Address objects
● The Attachment objects and collections

You'll also learn how these objects interact with each other and how to use them to perform several advanced MAPI
tasks, including:

● Moving messages from one folder to another


● Renaming existing folders
● Modifying and deleting address entries
● Switching between message stores

When you complete this chapter, you'll understand the OLE Messaging library objects and how to use them to build
MAPI-compliant e-mail applications with any VBA-compatible development tool.

The Session Object

The Session object of the OLE Messaging library is the top-most object in the hierarchy. You must create an instance
of a MAPI Session object before you can gain access to any other aspects of the MAPI system. The Session object
has several properties (including subordinate objects) and a handful of methods that you can invoke.

The Session Object Methods

Table 8.1 shows the Session Object Methods along with a list of parameters and short descriptions.

Table 8.1. Session object methods.


Methods Parameters Description
(opt) title as String,
(opt) oneAddress as Boolean,
(opt) forceResolution as Boolean,
(opt) recipLists as long, Access to session
AddressBook
(opt) toLabel as String, addressbook.
(opt) ccLabel as String,
(opt) bccLabel as String,
(opt) parentWindow as Long
Direct access to a
GetAddressEntry entryID as String single entry in the
address book.
Direct access to one
GetInfoStore storeID as String of the Message
storage objects.
Direct access to a
folderID as String, single folder object
GetFolder
storeID as String in the folders
collection.
Direct access to a
messageID as String, single message in
GetMessage
storeID as String the Messages
collection.
End the current
Logoff (none)
MAPI Session.
(opt) profileName as String,
(opt) profilePassword as String,
Start new MAPI
Logon (opt) showDialog as Boolean,
Session.
(opt) newSession as Boolean,
(opt) parentWindow as Long

The most used of these methods are the Logon and Logoff methods. You use these to start and end MAPI sessions.
You will also use the AddressBook and GetInfoStore methods frequently in your programs.

Using the MAPI Logon and Logoff Methods

If you have not done so yet, load Visual Basic 4.0 and start a new project. Place a single button on the form. Set its index
property to 0 and its caption to MAPI &Start. Copy and paste a second command button onto the form and set its
caption property to MAPI &End. Now add the code in Listing 8.1 to the Command1_Click event of the buttons.

Listing 8.1. Adding Code to the Command1_Click event.

Private Sub Command1_Click(Index As Integer)


'
' handle user selections
Select Case Index
Case 0 ' mapi start
MAPIStart
Case 1 ' mapi end
MAPIEnd
End Select
'
End Sub

The code in Listing 8.1 calls two subroutines-MAPIStart and MAPIEnd. They each use a form-level variable called
objSession. Add this variable to the general declarations section of the form.

Option Explicit
'
Dim objSession As Object ' for mapi session

Now create a new subroutine called MAPIStart and add the code shown in Listing 8.2. This routine initializes the
session object and calls the MAPI logon dialog box.

Listing 8.2. Adding the MAPIStart routine.

Public Sub MAPIStart()


'
' start a mapi session
'
On Error GoTo MAPIStartErr
'
Set objSession = CreateObject("MAPI.Session")
objSession.logon
Exit Sub
'
MAPIStartErr:
MsgBox Error$, vbCritical, "MAPIStartErr [" & CStr(Err) & "]"
'
End Sub

It is important that you formally end each MAPI session you begin. This will ensure that you do not have any stray
sessions running in the background.

Warning
By default, the MAPI Logon method will attempt to connect you to the
first available active session currently running on your workstation. For
example, if you have started a MAPI session with your e-mail client and
you then run this sample code, MAPI will attach this program to the same
MAPI session started by the MAPI client. This could give you unexpected
results. That is another reason why you should always close your MAPI
sessions when you are exiting your programs.

Now add a new subroutine to the project called MAPIEnd and add the code shown in Listing 8.3. Notice that this routine
sets the objSession variable to Nothing. This is done to clear Visual Basic's memory storage and conserve RAM
space.

Listing 8.3. Adding the MAPIEnd routine.

Public Sub MAPIEnd()


'
' end the current session
'
On Error Resume Next
'
objSession.logoff
Set objSession = Nothing
'
End Sub

Save the form as OML.FRM and the project as OML.VBP. You can now run the project and log into and out of a MAPI
session. You won't see much, but it works!

Accessing the MAPI Address Book


You will often need to give your users access to the MAPI Address Book. This is achieved with the AddressBook
method of the Session object. Access to the address book gives users the ability to look up names in the address book;
add, edit, and delete names from the book; and select one or more addresses as recipients of a message.

Add another command button to the array (Edit | Copy, Edit | Paste) and modify the Command1_Click event
to match the code in Listing 8.4. This will call up the MAPI address book.

Listing 8.4. Modifying the Command1_Click event to call the address book.

Private Sub Command1_Click(Index As Integer)


'
' handle user selections
Select Case Index
Case 0 ' mapi start
MAPIStart
Case 1 ' mapi end
MAPIEnd
Case 2 ' call address book
MAPIAddrBook
End Select
'
End Sub

Now add the MAPIAddrBook routine to the form and enter the code in Listing 8.5.

Listing 8.5. Adding the MAPIAddrBook routine.

Public Sub MAPIAddrBook()


'
' call the address book
'
On Error Resume Next
'
objSession.AddressBook
'
End Sub

Save and run the project. After clicking the MAPI Start button, press the Address Book button. Your screen
should look like the one in Figure 8.1.

Figure 8.1 : Displaying the address book.

You can set several parameters when you call the Address Book method. For example, you can set the title of the
address book using the Title property. You can also control the number and caption of the recipient selection buttons
that appear on the address book. You can even set the address book dialog so that the user can review the addresses but
cannot select one.

Listing 8.6 shows you how to modify the code in the MAPIAddrBook routine to set the title and remove all recipient
selection buttons.

Listing 8.6. Controlling the appearance of the MAPI address book.

Public Sub MAPIAddrBook()


'
' call the address book
'
On Error Resume Next
'
objSession.AddressBook recipLists:=0, Title:="For Viewing Only"
'
End Sub

When you run the project, your address book will look something like the one in Figure 8.2.

Figure 8.2 : Modified Address Book dialog box.

Automating the Session Logon

You can also control the Logon method behavior by passing selected parameters. The most common use for this is
automatically logging a user into MAPI without the use of the Logon dialog box. To do this you need to pass the user
profile and password and set the ShowDialog flag to false.

Listing 8.7 shows you how to modify the MAPIStart routine to perform an automatic logon. You should change the
ProfileName parameter to match your personal Microsoft Exchange logon.

Listing 8.8. Creating an automatic MAPI session logon.

Public Sub MAPIStart()


'
' start a mapi session
'
On Error GoTo MAPIStartErr
'
Set objSession = CreateObject("MAPI.Session")
objSession.logon ProfileName:="MCA", ShowDialog:=False
Exit Sub
'
MAPIStartErr:
MsgBox Error$, vbCritical, "MAPIStartErr [" & CStr(Err) & "]"
'
End Sub

The Session Object Properties

Now let's look at some of the Session object properties. After reviewing the properties, you can add another button to
the form to display the properties of your MAPI session. Table 8.2 has a list of the Session object properties, their
type, and a short description.

Table 8.2. Session object properties.


Properties Type Description
Name of the library. Always set to
Application String
OLE/Messaging.
Internal identifying code for all
Class Long MAPI objects. Always set to 0 for
Session objects.
Address object of current user (see
CurrentUser AddressEntry object "Address Objects" later in this
chapter).
Folder object where all new
unread messages are placed (see
Inbox Folder object
"Folder Objects" later in this
chapter).
InfoStores object collection for
InfoStores InfoStores object this session (see "InfoStore
Objects" later in this chapter).
Session name. Always set to the
Name String current profile name on Microsoft
Exchange system.
OperatingSystem String Name of operating system in use.
Folder object where all outgoing
Outbox Folder object messages are placed (see "Folder
Objects" later in this chapter).
Version number of OLE Messaging
Version String
library. Current version is 1.00.

The Session object has several properties, many of them objects themselves. Using these object properties allows you
to gain access to more complex data than standard strings or numbers. You'll inspect the object properties later in the
chapter.

Now add another button to the control array (use Edit | Copy, Edit | Paste), set its Caption to "MapiProps" and
modify the code in the Command1_Click event to look like the code in Listing 8.9.

Listing 8.9. Updated Command1_Click event.


Private Sub Command1_Click(Index As Integer)
'
' handle user selections
Select Case Index
Case 0 ' mapi start
MAPIStart
Case 1 ' mapi end
MAPIEnd
Case 2 ' call address book
MAPIAddrBook
Case 3 ' session properties
SessionProps
End Select
'
End Sub

Now create a new subroutine called SessionProps and enter the code that appears in Listing 8.10. This routine
creates a message box that displays several of the Session object's properties.

Listing 8.10. Adding the SessionProps code.

Public Sub SessionProps()


'
' get basic session properties
'
On Error GoTo SessionPropsErr
'
Dim cMsg As String
'
cMsg = "Application: " & objSession.Application & Chr(13)
cMsg = cMsg & "Operating System: " & objSession.OperatingSystem
& Chr(13)
cMsg = cMsg & "Session Name: " & objSession.Name & Chr(13)
cMsg = cMsg & "Version: " & objSession.Version & Chr(13)
cMsg = cMsg & "Object Class: " & CStr(objSession.Class)
'
MsgBox cMsg
'
Exit Sub
'
SessionPropsErr:
MsgBox Error$, vbCritical, "SessionPropsErr [" & CStr(Err) & "]"
'
End Sub

Save the project again and run it. After clicking on the MAPI Start button, click on the MapiProps button. Your
screen should look like the one in Figure 8.3.
Figure 8.3 : Displaying the Session properties.

Now it's time to start inspecting the subordinate objects in the OLE Messaging library.

The InfoStore Objects and Collections

The first-level subordinate object of the Session object is the InfoStore object. Each InfoStore object
represents a separate message store. The MAPI model allows clients to access more than one message storage system at
the same time. For example, the Microsoft Exchange shared folders are a separate message store (represented by a
separate InfoStore object). The Microsoft Exchange Personal Folders are another separate message store. Users can
have any number of message stores connected to their MAPI client.

The InfoStores collection object is an OLE Messaging library object that allows you to view all the connected
message stores for the logged-in user. You can use the InfoStores object to locate a particular message store and
then access that message store using the InfoStore object.

Note
Be sure not to confuse the InfoStore object with the InfoStores
object. The InfoStore object is the OLE Messaging library object that
you use to gain access to the contents of a single message store. The
InfoStores object is the OLE Messaging library object you use to gain
access to the collection of message stores. In Microsoft OLE naming rules,
collection objects are plural (InfoStores) and single objects are not
(InfoStore).

The InfoStores Collection

The InfoStores collection has only a few properties and no methods at all. You cannot add, modify, or delete
InfoStore objects using the OLE Messaging library interface. Table 8.3 shows the InfoStores properties with
their type and a short description.

Table 8.3. The InfoStores collection object properties.


Property name Type Description
Name of the library. Always set to OLE/
Application String
Messaging.
Internal identifying code for all MAPI objects.
Class Long
Always set to 17 for InfoStores objects.
The total number of InfoStore objects in the
Count Long
collection. The count starts at 1.
Allows access to one of the member
InfoStore objects. The Item property
Item InfoStore object
accepts an Index value between 1 and the
value of Count.

To test the InfoStores object, add another button to the control array on the form with the caption of
InfoStoreColl and modify the Command1_Click event as shown in Listing 8.11.
Listing 8.11. Updated Command1_Click event.

Private Sub Command1_Click(Index As Integer)


'
' handle user selections
'
Select Case Index
Case 0 ' mapi start
MAPIStart
Case 1 ' mapi end
MAPIEnd
Case 2 ' call address book
MAPIAddrBook
Case 3 ' session properties
SessionProps
Case 4 ' show infostores collection
SessionInfoStoreColl
End Select
'
End Sub

Next you'll need to add two new form-level variables to the general declaration section of the form. Your general
declaration section should now look like this:

Option Explicit
'
Dim objSession As Object ' for mapi session
Dim objInfoStoreColl As Object ' collection of stores
Dim objInfoStore As Object ' single info store

Now add the SessionInfoStoreColl routine shown in Listing 8.12. This routine gets the InfoStores
properties and displays the names of all the message stores in the collection.

Listing 8.12. Adding the SessionInfoStoreColl routine.

Public Sub SessionInfoStoreColl()


'
' show list of available infostores
'
Dim cMsg As String
Dim nCount As Integer
Dim x As Integer
Dim cStoresList As String
'
Set objInfoStoreColl = objSession.InfoStores
cMsg = "Application: " & objInfoStoreColl.Application & Chr(13)
cMsg = cMsg & "Class: " & CStr(objInfoStoreColl.Class) & Chr(13)
cMsg = cMsg & "Count: " & CStr(objInfoStoreColl.Count) & Chr(13)
'
nCount = objSession.InfoStores.Count
cStoresList = "List:" & Chr(13)
For x = 1 To nCount
cStoresList = cStoresList & " " & objSession.InfoStores.Item
(x).Name
cStoresList = cStoresList & Chr(13)
Next x
'
MsgBox cMsg & Chr(13) & cStoresList, vbInformation, "InfoStores
Collection ➂Object"
'
End Sub

Warning
The OLE Messaging library does not require message stores to have unique
names. If you are using the InfoStores collection object to locate a
particular message store, you must be sure that there is not more than one
store with that name in the collection! InfoStore objects are assigned a
unique unchanging ID value. Once you know the ID value of an
InfoStore object, you can locate it using the GetInfoStore method
of the Session object.

Save and run this project. After logging in, press the InfoStoreColl button. Your screen should look similar to the
one in Figure 8.4.

Figure 8.4 : Viewing the InfoStores object properties.

Now that you know how to review the InfoStore collection, it's time to learn more about the individual InfoStore
objects.

The InfoStore Object

The InfoStore object contains all the folders and messages defined for a single message store. InfoStore objects
have several properties and no methods. You cannot use the OLE Messaging library to add, modify, or delete
InfoStore objects. Table 8.4 shows the important InfoStore object properties, their types, and their descriptions.

Table 8.4. The InfoStore object properties.


Property name Type Description
Name of the library. Always set to OLE/
Application String
Messaging.
Internal identifying code for all MAPI objects.
Class Long
Always set to 1 for InfoStore objects.
A unique value that never changes. It is assigned
ID String
by MAPI when the store is created.
The count position of the InfoStore in the
Index Long InfoStores collection. This can be used with
the Item property of the InfoStores object.
Name String The display name of the message store.
The name of the message store vendor or
ProviderName String
programmer.
RootFolder Folder object The starting folder of the message store.

Now add some code to view the properties of an InfoStore object. First, add another command button to the control
array and set its caption to InfoStore. Then modify the Command1_Click routine so that it matches the one in
Listing 8.13.

Listing 8.13. Updated Command1_Click event.

Private Sub Command1_Click(Index As Integer)


'
' handle user selections
'
Select Case Index
Case 0 ' mapi start
MAPIStart
Case 1 ' mapi end
MAPIEnd
Case 2 ' call address book
MAPIAddrBook
Case 3 ' session properties
SessionProps
Case 4 ' show infostores collection
SessionInfoStoreColl
Case 5 ' show infostore properties
InfoStoreProps
End Select
'
End Sub

Now add the InfoStoreProps subroutine and enter the code shown in Listing 8.14.

Listing 8.14. Adding the InfoStoreProps routine.

Public Sub InfoStoreProps()


'
' show the infostore object properties
'
Dim cMsg As String
'
Set objInfoStoreColl = objSession.InfoStores
For Each objInfoStore In objInfoStoreColl
cMsg = "Application: " & objInfoStore.Application & Chr(13)
cMsg = cMsg & "Class: " & CStr(objInfoStore.Class) & Chr(13)
cMsg = cMsg & "ID: " & objInfoStore.ID & Chr(13)
cMsg = cMsg & "Name: " & objInfoStore.Name & Chr(13)
cMsg = cMsg & "ProviderName: " & objInfoStore.ProviderName &
Chr(13)
cMsg = cMsg & "RootFolder: " & objInfoStore.RootFolder.Name
'
MsgBox cMsg, vbInformation, "InfoStore Object Properties"
Next
'
End Sub

Note that the InfoStore object is part of what is called a small collection. OLE object collections that are considered
to have a limited number of members are called small collections. All small collection objects have an Index property
and a Count property. Most of them also have an Item property. Small collection objects support the use of the For
Each...Next programming construct.

Save and run the project. After clicking the InfoStore button, you should see a series of dialog boxes showing the
properties of each message store available to your client (see Figure 8.5).

Figure 8.5 : Viewing the InfoStore properties.

The next object to review is the Folder object and the Folders collection object.

The Folder Objects and Collections

One of the first level objects below the InfoStore object is the Folder object. The Folder object can hold
messages and other folders. Each InfoStore object also has a Folders collection object that contains a list of all the
Folder objects in the message store.

There is no limit to the number of messages or folders a Folder object can have. For this reason it is called a large
collection object. large collection objects do not have an Index property or a Count property. The only
way you can locate all the folders in a message store is to "walk through" the store using a set of methods to get each
item. All large collection objects support the use of the GetFirst, GetNext, GetPrevious, and
GetLast methods to provide a way to navigate through the collection. You'll use these methods in the next few
examples.

The Folders Collection Object

The Folders collection object has only a few properties and methods. Table 8.5 shows the important Folders
collection object properties and Table 8.6 shows the Folders collection object methods.

Table 8.5. The Folders collection object properties.


Property name Type Description
Application String Name of the library. Always set to OLE/Messaging.
Internal identifying code for all MAPI objects. Always set to
Class Long
18 for Folders objects.

Table 8.6. The Folders collection object methods.


Method name Parameters Description
GetFirst (none) Points to the first Folder object in the collection.
GetLast (none) Points to the last Folder object in the collection.
GetNext (none) Points to the next Folder object in the collection.
GetPrevious (none) Points to the previous Folder object in the collection.

To test the Folders object, add two new variables to the general declaration area of the form. Your form-level variable
list should now look like this:

Option Explicit
'
Dim objSession As Object ' for mapi session
Dim objInfoStoreColl As Object ' collection of stores
Dim objInfoStore As Object ' single info store
Dim objFolderColl As Object ' collection of folders
Dim objFolder As Object ' single folder

Now add another command button to the control array and set its Caption property to FolderColl. Then modify the
Command1_Click event so that it matches the code in Listing 8.15.

Listing 8.15. Updated Command1_Click event.

Private Sub Command1_Click(Index As Integer)


'
' handle user selections
'
Select Case Index
Case 0 ' mapi start
MAPIStart
Case 1 ' mapi end
MAPIEnd
Case 2 ' call address book
MAPIAddrBook
Case 3 ' session properties
SessionProps
Case 4 ' show infostores collection
SessionInfoStoreColl
Case 5 ' show infostore properties
InfoStoreProps
Case 6 ' folders collection
FoldersColl
End Select
'
End Sub

Now add the new subroutine called FoldersColl and enter the code from Listing 8.16.

Listing 8.16. Adding the FoldersColl routine.


Public Sub FoldersColl()
'
' show the folders collection
'
Dim cMsg As String
'
Set objFolderColl = objSession.InfoStores.Item(1).RootFolder.
Folders
'
cMsg = "Application: " & objFolderColl.Application & Chr(13)
cMsg = cMsg & "Class: " & CStr(objFolderColl.Class) & Chr(13)
'
cMsg = cMsg & Chr(13) & "Folders:" & Chr(13)
Set objFolder = objFolderColl.GetFirst
Do Until objFolder Is Nothing
cMsg = cMsg & objFolder.Name & Chr(13)
Set objFolder = objFolderColl.GetNext
Loop
'
MsgBox cMsg, vbInformation, "Folders Collection Object"
'
End Sub

The FoldersColl routine shows the application and class property of the object and then lists all the folder names in
the collection. Note that you cannot determine the folder hierarchy from the list returned by the Get methods. The Get
methods traverse the folder collection in the order the folders were created, not in the order they are displayed or
arranged within the MAPI client.

Save and run the program. Clicking the FoldersColl button should give you a display similar to the one in Figure
8.6.

Figure 8.6 : Viewing the Floders collection object properties.

The Folder Object

The Folder object has several properties and one method. The OLE Messaging library allows you to modify the Name
property of a Folder object, but you cannot add or delete a Folder object from the message store. Table 8.7 contains
the list of important Folder object properties.

Table 8.7. The Folder object properties.


Property name Type Description
Name of the library. Always set to
Application String
OLE/Messaging.
Internal identifying code for all MAPI
Class Long objects. Always set to 2 for Folder
objects.
A collection of user-defined fields
Fields Fields collection object
added to the folder object.
A unique and permanent value that
identifies the Folder object in the
FolderID String
message store. This value is set by
MAPI when the folder is created.
A collection of subfolders that are
Folders Folders collection object
members of this folder object.
ID String The same as the FolderID property.
A collection of messages that are
Messages Messages collection object
members of this Folder object.
Display name of the folder. This name
does not have to be unique and can be
Name String modified through the OLE Messaging
library using the Update method of
the Folder object.
A unique and permanent value that is
the same as the ID value of the
StoreID String
InfoStore object in which this
folder resides.

Add a new button to the command array and set its Caption property to Fo&lder Object. Then modify the
Command1_Click event to match the one in Listing 8.17.

Listing 8.17. Updated Command1_Click event.

Private Sub Command1_Click(Index As Integer)


'
' handle user selections
'
Select Case Index
Case 0 ' mapi start
MAPIStart
Case 1 ' mapi end
MAPIEnd
Case 2 ' call address book
MAPIAddrBook
Case 3 ' session properties
SessionProps
Case 4 ' show infostores collection
SessionInfoStoreColl
Case 5 ' show infostore properties
InfoStoreProps
Case 6 ' folders collection
FoldersColl
Case 7 ' folder object
FolderProps
End Select
'
End Sub
Now create a new subroutine called FolderProps and add the code shown in Listing 8.18.

Listing 8.18. Adding the FolderProps routine.

Public Sub FolderProps()


'
' inspect the folder object properties
'
Dim cMsg As String
'
Set objFolder = objSession.InfoStores.Item(1).RootFolder
'
cMsg = "Application: " & objFolder.Application & Chr(13)
cMsg = cMsg & "Class: " & CStr(objFolder.Class) & Chr(13)
cMsg = cMsg & "FolderID: " & objFolder.folderID & Chr(13)
cMsg = cMsg & "ID: " & objFolder.ID & Chr(13)
cMsg = cMsg & "Name: " & objFolder.Name & Chr(13)
cMsg = cMsg & "StoreID: " & objFolder.storeID
'
MsgBox cMsg, vbInformation, "Folder Object Properties"
'
End Sub

Save and run the project. Then click the Folder Object button and compare your results with those shown in Figure
8.7.

Figure 8.7 : Viewing the Folder object properties.

The only method available for Folder objects is the Update method. You can use this method to save changes made
to the Folder object properties. The only property you can modify is the Name property. If you wish to change the
name of an existing folder you can use the following line of code:

objFolder.Name = "New Name" ' modify name


objFolder.Update ' save changes

Note
The MAPI system will not let you modify the name of the Inbox,
Outbox, Sent Items, or Deleted Items folders. Attempting to do
this will cause MAPI to return an error to your program.

The Inbox and OutBox Folders

There are two folders that are used for almost every MAPI message transaction:

● The InBox, where new messages are received;


● The OutBox, where composed messages are placed when they are to be sent out.
Because these two folders are used so often, the OLE Messaging library has defined them as a property of the
InfoStore object. This means you can access the InBox and OutBox folders directly from the InfoStore object.

Note
The InfoStore object was discussed in the earlier section of this chapter
titled "The InfoStore Objects and Collections."

You can modify the FolderProp routine to access the properties of the Inbox by changing the line that sets the
objFolder object.

'
'Set objFolder = objSession.InfoStores.Item(1).RootFolder
Set objFolder = objSession.Inbox
'

Now when you click the Folder Object button, you'll get data on the Inbox folder in message store #1. This works
the same way for the Outbox.

The Message Objects and Collections

The Message object and Messages collection object are the heart of the OLE Messaging library. These objects hold
the actual messages composed and received by the MAPI client. You will use the Message objects to read, modify,
create, and delete messages from the message store.

The Messages Collection Object

The Messages collection object has very few properties and a number of methods. Because the Message collection is
a large collection (that is, it has an unlimited number of members), you must use the GetFirst, GetNext,
GetPrevious, and GetLast methods to retrieve messages from the collection. You can also add and delete
messages within the collection.

Table 8.8 shows the properties for the Messages collection object.

Table 8.8. The Messages collection object properties.


Property name Type Description
Application String Name of the library. Always set to OLE/Messaging.
Internal identifying code for all MAPI objects. Always set to
Class Long
19 for Messages objects.

Table 8.9 shows the list of methods for the Messages collection object.

Table 8.9. The Messages collection object methods.


Method name Parameters Description
(optional) subject as String,
(optional) text as String, Adds a new, null, message to the
Add
(optional) type as String, collection.
(optional) importance as Long
Deletes all messages from the
Delete (none)
collection.
Returns the first message in the
collection. The filter value allows
GetFirst (optional) filter as Variant
you to collect only messages with a
specified Type value.
Returns the last message in the
collection. The filter value allows
GetLast (optional) filter as Variant
you to collect only messages with a
specified Type value.
Returns the next message in the
GetNext (none)
collection.
Returns the previous message in the
GetPrevious (none)
collection.
Sorts the message collection based
on the following values:
Sort sortOrder as Long None = 0,
Ascending = 1
Descending = 2

To test the Messages collection object, you first need to add two new variables to the general declaration area of the
form. Make sure your variables match the ones in Listing 8.19.

Listing 8.19. Declaring the form-level variables.

Option Explicit
'
Dim objSession As Object ' for mapi session
Dim objInfoStoreColl As Object ' collection of stores
Dim objInfoStore As Object ' single info store
Dim objFolderColl As Object ' collection of folders
Dim objFolder As Object ' single folder
Dim objMessageColl As Object ' messages collection
Dim objMessage As Object ' single message

Next add a new button to the control array and set its caption to M&essageColl. Then modify the
Command1_Click event to match the code shown in Listing 8.20.

Listing 8.20. The updated Command1_Click event.


Private Sub Command1_Click(Index As Integer)
'
' handle user selections
'
Select Case Index
Case 0 ' mapi start
MAPIStart
Case 1 ' mapi end
MAPIEnd
Case 2 ' call address book
MAPIAddrBook
Case 3 ' session properties
SessionProps
Case 4 ' show infostores collection
SessionInfoStoreColl
Case 5 ' show infostore properties
InfoStoreProps
Case 6 ' folders collection
FoldersColl
Case 7 ' folder object
FolderProps
Case 8 ' messsage collection
MessagesColl
End Select
'
End Sub

Then add the new MessagesColl subroutine to the project and enter the code from Listing 8.22.

Listing 8.22. Adding the MessagesColl routine.

Public Sub MessagesColl()


'
' inspect the messages collection
'
Dim cMsg As String
Dim nCount As Integer
'
Set objMessageColl = objSession.Inbox.Messages ' get inbox
messages
'
cMsg = "Application: " & objMessageColl.Application & Chr(13)
cMsg = cMsg & "Class: " & CStr(objMessageColl.Class) & Chr(13)
'
cMsg = cMsg & Chr(13) & "Inbox Messages:" & Chr(13)
Set objMessage = objMessageColl.GetFirst
Do Until objMessage Is Nothing
nCount = nCount + 1
cMsg = cMsg & CStr(nCount) & " - " & objMessage.subject & Chr
(13)
Set objMessage = objMessageColl.GetNext
Loop
'
MsgBox cMsg, vbInformation, "Messages Collection Object"
End Sub
The MessagesColl routine reads the Application and Class properties of the object and then uses the Get
methods to build a list of messages currently in the message store inbox.

Save and run the project. Your screen should look similar to the one in Figure 8.8.

Figure 8.8 : Viewing the Messages collection object properties.

You can sort the messages in the collection using the Sort method. Setting the sort order affects how the Get methods
access the messages in the collection. The sort order is based on the TimeReceived property of the messages. The sort
method can be set for None(0), Ascending(1), or Descending(2). The default SortOrder is None(0). If
there is no sort order specified, the Get methods access the messages in the order in which they were added to the
message collection.

Tip
Because the sort order affects the way in which the Get methods access the
file, setting the SortOrder to Ascending(1) and performing the
GetFirst method on the messages collection will return the same
message as setting the SortOrder to Descending(2) and performing
the GetLast method on the messages collection.

Modify the MessageColl routine to ask you for a sort order. Add the lines of code to the routine as indicated in
Listing 8.23.

Listing 8.23. Adding the SortOrder lines to the MessageColl routine.

cMsg = cMsg & "Class: " & CStr(objMessageColl.Class) & Chr(13)


' *** add these lines to the routine ***************
nCount = InputBox("Enter Sort Order (0=None, 1=Ascending,
2=Descending):",
➂Message Collection Sort")
objMessageColl.Sort nCount
cMsg = cMsg & "Sort Order: " & CStr(nCount) & Chr(13)
nCount = 0 ' clear value
' *** end of added lines ***************************
cMsg = cMsg & Chr(13) & "Inbox Messages:" & Chr(13)

Now when you press the MessageColl button you'll be asked to supply a sort order and then be presented with a
different view of the message collection list.

You can delete all the messages in a message collection by invoking the Delete method followed by the Update
method on the parent object to save the change. The code below shows how this is done.
Set objMessageColl = objSession.Inbox.Messages ' get inbox
messages
objMessageColl.Delete ' dump all messages in the inbox
objSession.Inbox.Update ' make the modification permanent

You'll learn about the Add method in the section on the Message object.

The Message Object

The Message object is the richest object in the OLE Messaging library system. It has only a few methods, but they are
all quite powerful. They allow you to delete the message from the collection (Delete), display a message option dialog
box (Options), display a send dialog box (Send), and save the modified message properties (Update). These
properties range from handling attachments to flagging a message as read or unread. Table 8.10 shows the Message
object methods with their parameters and a short description.

Table 8.10. The Message object methods.


Method name Parameters Description
Removes a message from the
Delete (none) collection. This is a non-recoverable
delete.
Displays a dialog box that allows
users to modify message properties.
Options (optional) parentWindow as Long
Not all properties may appear on the
dialog box.
(optional) saveCopy as Boolean, Sends the message to the recipient
(optional) showDialog as (s). Optionally displays a dialog box
Send
Boolean, for the composition and ending of
(optional) parentWindow as Long the message.
Saves all changes to message
properties. Any modifications to
Update (none)
properties are not permanent until
the Update method is invoked.

The Message object properties are extensive and powerful. The properties range from simple Boolean flags to indicate
the importance of a message, to the actual text body of the message, to the list of binary attachments to the message.
Table 8.11 shows the important Message object properties, their type, and a short description.

Table 8.11. The Message object properties.


Property name Type Description
Name of the library. Always
Application String
set to OLE/Messaging.
Attachments collection The collection of binary
Attachments
object attachments to the message.
Internal identifying code for
Class Long all MAPI objects. Always set
to 3 for Message objects.
This is a value that shows the
order of the message within
the conversation topic.
ConversationIndex String Microsoft Exchange uses an
eight-byte value that
expresses the time stamp of
the message.
An identifying string. All
messages with the same
ConversationTopic
value can be grouped together
to show relationships. The
ConversationTopic String
Windows Messaging client
supports the
ConversationTopic
property in its Folder
Views options.
A flag that indicates the
sender wants to receive
notification when the
DeliveryReceipt Boolean
message is delivered to the
recipient. The default value is
False.
A flag to indicate the message
has been encrypted. The
support of encrypted
Encrypted Boolean messages is dependent on the
message store. MAPI does
not perform message
encryption.
A collection of custom, user-
defined fields added to the
Fields Fields collection object message. You can add any
number of fields to your
messages.
The unique MAPI-assigned
FolderID String value that identifies the folder
in which this message resides.
The unique MAPI-assigned
value that identifies this
message in the message store.
ID String
This value is assigned when
the message is first created
and cannot be changed.
A flag to indicate the relative
importance of this message.
Microsoft Exchange supports
the following values for this
Importance Long
property:
Low(0)
Normal(1)-the default
High(2)
Flag to indicate that the
sender wants to be notified
ReadReceipt Boolean
when the recipient reads
(opens) the message.
A collection of recipient
objects. Each recipient object
represents one e-mail address
to which this message has
Recipients Recipients object been sent. A message can
have any number of
recipients. These recipients
do not have to be found in the
MAPI address book.
The AddressEntry object
of the user that sent this
Sender AddressEntry object message. This points to an
entry in the Microsoft
Exchange address book.
A flag that indicates that the
message has been sent to the
recipient(s). When sending a
message, this flag is set by
MAPI. However, under some
cases the programmer can set
Sent Boolean
this value as part of an
Update operation on
messages sent to shared
folders or saved in the
message store in some other
way.
A flag indicating that the
message has been marked
with a digital signature.
Signed Boolean Message signing is not
supplied by MAPI, but is
provided by the message
store.
The approximate size of the
Size Long
message (in bytes).
The unique MAPI-assigned
value that indicates the
StoreID String
message store to which this
message belongs.
The subject line of the
Subject String
message.
A flag that indicates this
message has been submitted
for viewing. This property is
related to (but not the same
as) the Sent property. The
Submitted Boolean
submitted property must be
set to True for messages sent
to shared folders in order for
some clients to be able to see
them.
The actual body of the
Text String
message.
The date and time the
TimeReceived Variant (Date/Time)
message was received.
The date and time the
message was sent. These
TimeSent Variant (Date/Time) values must be set by the
programmer for messages
placed in public folders.
Microsoft message type in the
form "XXX.?" There are two
main message types:

IPM-Interpersonal messages.
These are recognized by the
MAPI client and displayed in
Type String
the Inbox when received.

Ipc-Interprocess
communications. These are
not recognized by the MAPI
client, but can be processed
behind the scenes.
Flag indicating whether the
Unread Boolean
message has been read.

Now add another button to the control array and set its caption to MessageProps. Then modify the
Command1_Click event as shown in Listing 8.24.

Listing 8.24. Modifying the Command1_Click event.

Private Sub Command1_Click(Index As Integer)


'
' handle user selections
'
Select Case Index
Case 0 ' mapi start
MAPIStart
Case 1 ' mapi end
MAPIEnd
Case 2 ' call address book
MAPIAddrBook
Case 3 ' session properties
SessionProps
Case 4 ' show infostores collection
SessionInfoStoreColl
Case 5 ' show infostore properties
InfoStoreProps
Case 6 ' folders collection
FoldersColl
Case 7 ' folder object
FolderProps
Case 8 ' messsage collection
MessagesColl
Case 9 ' message properties
MessageProps
End Select
'
End Sub

Next add a new subroutine called MessageProps to the form and enter the code from Listing 8.25.

Listing 8.25. Adding the MessageProps routine.

Public Sub MessageProps()


'
' display message properties
'
Dim cMsg As String
'
' get the inbox message collection
Set objMessageColl = objSession.Inbox.Messages
' get the first message in the inbox
Set objMessage = objMessageColl.GetFirst
'
On Error Resume Next ' when property is missing
'
cMsg = "Application: " & objMessage.Application & Chr(13)
cMsg = cMsg & "Class: " & objMessage.Class & Chr(13)
cMsg = cMsg & "ConversationIndex: " & objMessage.
ConversationIndex & Chr(13)
cMsg = cMsg & "ConversationTopic: " & objMessage.
conversationtopic & Chr(13)
cMsg = cMsg & "DeliveryReceipt: " & objMessage.DeliveryReceipt &
Chr(13)
cMsg = cMsg & "Encrypted: " & objMessage.Encrypted & Chr(13)
cMsg = cMsg & "FolderID: " & objMessage.folderID & Chr(13)
cMsg = cMsg & "ID: " & objMessage.ID & Chr(13)
cMsg = cMsg & "Importance: " & CStr(objMessage.importance) & Chr
(13)
cMsg = cMsg & "ReadReceipt: " & objMessage.ReadReceipt & Chr(13)
cMsg = cMsg & "Sent: " & objMessage.Sent & Chr(13)
cMsg = cMsg & "Signed: " & objMessage.Signed & Chr(13)
cMsg = cMsg & "Size: " & CStr(objMessage.Size) & Chr(13)
cMsg = cMsg & "StoreID: " & objMessage.storeID & Chr(13)
cMsg = cMsg & "Subject: " & objMessage.subject & Chr(13)
cMsg = cMsg & "Submitted: " & objMessage.Submitted & Chr(13)
' cMsg = cMsg & "Text: " & objMessage.Text & Chr(13)
cMsg = cMsg & "TimeReceived: " & Format(objMessage.TimeReceived,
"general ➂date") & Chr(13)
cMsg = cMsg & "TimeSent: " & Format(objMessage.TimeSent,
"general date") & ➂Chr(13)
cMsg = cMsg & "Type: " & objMessage.Type & Chr(13)
cMsg = cMsg & "Unread: " & objMessage.Unread
'
MsgBox cMsg, vbInformation, "Message Object Properties"
'
End Sub

The MessageProps routine first selects the session's Inbox message collection and then selects the first message in
the collection.

Warning
This routine crashes with an error if no message is present in the Inbox. If
you do not currently have at least one message in the Inbox, fire up an e-
mail client and place one there before running this routine. In production
applications, you should make sure you trap for such an error to prevent
needless (and annoying) crashes.

Notice the addition of the On Error Resume Next just before the series of statements that retrieve the message
properties. This is done in case one or more properties are missing in the selected message. Since MAPI supports
messages from several different providers, it is quite likely that some of the properties will contain garbage or nothing at
all. You should keep this in mind when writing routines that read Message object properties.

Save and run the project. After starting MAPI and pressing the MessageProps button, you'll see something like the
data shown in Figure 8.9. Of course, the actual values of the properties will vary depending on the message you are
reading.

Figure 8.9 : Viewing the Message object properties.

Adding a New Message

It's easy to add new messages to the message collection. The simplest method is to use the Send method of the
Message object to invoke the MAPI Send dialog box. From this dialog box, the user can compose, address, and send
the message without any additional coding.

Add a new button to the control array and set its caption property to MessageAdd. Then update the
Command1_Click event so that it matches the one in Listing 8.26.
Listing 8.26. Updating the Command1_Click event.

Private Sub Command1_Click(Index As Integer)


'
' handle user selections
'
Select Case Index
Case 0 ' mapi start
MAPIStart
Case 1 ' mapi end
MAPIEnd
Case 2 ' call address book
MAPIAddrBook
Case 3 ' session properties
SessionProps
Case 4 ' show infostores collection
SessionInfoStoreColl
Case 5 ' show infostore properties
InfoStoreProps
Case 6 ' folders collection
FoldersColl
Case 7 ' folder object
FolderProps
Case 8 ' messsage collection
MessagesColl
Case 9 ' message properties
MessageProps
Case 10 ' add a message w/ dialogs
MessageAdd
End Select
'
End Sub

Now add the MessageAdd subroutine and enter the code from Listing 8.27. This will create a new Message object in
the message collection and then bring up the MAPI Send dialog box.

Listing 8.27. Adding the MessageAdd routine.

Public Sub MessageAdd()


'
' add a message w/ dialog
'
On Error Resume Next ' skip errors
'
Set objMessageColl = objSession.Outbox.Messages
Set objMessage = objMessageColl.Add
objMessage.Send showdialog:=True
'
End Sub
You can also build the message using Visual Basic code and then call the Send method to address and ship the message.
Listing 8.28 shows a new routine MessageMake that codes this.

Listing 8.28. Adding the MessageMake routine.

Public Sub MessageMake()


'
' make a message from code
' then show send dialog
'
On Error Resume Next
'
Set objMessageColl = objSession.Outbox.Messages
Set objMessage = objMessageColl.Add ' add a new message
objMessage.subject = "Creating a Message in Code"
objMessage.Text = "This message was written with VB code."
objMessage.Update ' save the message properties
objMessage.Send showdialog:=True ' show dialog
'
End Sub

After adding this routine, add a new button to the array and set its caption property to MessageMake. Also add another
line to the Select Case structure to handle the button selection (see Listing 8.29).

Listing 8.29. Updating the Command1_Click event.

Private Sub Command1_Click(Index As Integer)


'
' handle user selections
'
Select Case Index
Case 0 ' mapi start
MAPIStart
Case 1 ' mapi end
MAPIEnd
Case 2 ' call address book
MAPIAddrBook
Case 3 ' session properties
SessionProps
Case 4 ' show infostores collection
SessionInfoStoreColl
Case 5 ' show infostore properties
InfoStoreProps
Case 6 ' folders collection
FoldersColl
Case 7 ' folder object
FolderProps
Case 8 ' messsage collection
MessagesColl
Case 9 ' message properties
MessageProps
Case 10 ' add a message w/ dialogs
MessageAdd
Case 11 ' make a message in code
MessageMake
End Select
'
End Sub

You can also set several message options by calling the Options method to bring up the Options dialog box. Modify
the code in the MessageMake routine to call the Options method before the Send method. Insert the line shown
below into the code just before the objMessage.Send line.

objMessage.Options ' call options dialog

When you save and run the project, press the MessageMake button. You'll see the Options dialog box before you see
the Send dialog box. The exact layout and contents of the Options dialog box depend on the type of message you are
working with and the available message transports at your workstation (see Figure 8.10).

Figure 8.10 : Viewing the Options dialog box.

You can also compose a complete message, address it, and send it without the use of dialog boxes. However, to do that
you need to create at least one recipient object. You learn how to do that in the next section.

The Recipient Objects and Collections and the Address Object

The OLE Messaging library defines two address objects-the Recipient object and the AddressEntry object. The
two objects are related, but serve different purposes. The purpose of the Recipient object is to provide valid
addressing information for the message. The purpose of the AddressEntry object is to gain access to individual
records in the MAPI address book.

The next several sections outline the properties and methods of both the Recipient and AddressEntry objects.

The Recipients Collection Object

The Recipients collection object holds a list of all recipients for a message. Every message has a recipients
collection-even if the message has only one recipient in the collection. There are three methods for the collection and a
handful of properties. Because the Recipients collection is a small collection, it has Count and Item properties.
This also means that the Recipient object supports an Index property.

Table 8.12 shows the Recipients object methods and Table 8.13 shows the object's properties.

Table 8.12. The Recipients collection object methods.


Method name Parameters Description
(optional) name as String,
(optional) address as String, Used to add a new recipient to the
Add
(optional) type as Long, collection.
(optional) entryID as String
Used to delete all recipients from the
Delete (none)
collection.
Used to validate all Recipient
(optional) showDialog as objects in the collection. Can optionally
Resolve
Boolean show a dialog box to help resolve
ambiguous entries.

Table 8.13. The Recipients collection object properties.


Property name Type Description
Name of the library. Always set to OLE/
Application String
Messaging.
Internal identifying code for all MAPI objects.
Class Long
Always set to 20 for Recipients objects.
Total number of Recipient objects in the
Count Long
collection.
Used to gain access to a particular
Item Recipient object Recipient object in the collection (using the
Recipient Index property).
Flag to indicate that all Recipient objects in
the collection contain valid addressing
Resolved Boolean
information. Set when you call the Resolve
method of the object.

To test the properties of the Recipients collection object, add a new button to the control array and set its caption to
RecipColl. Then add a new line in the Command1_Click event to match the code in Listing 8.30.

Listing 8.30. Updating the Command1_Click event.

Private Sub Command1_Click(Index As Integer)


'
' handle user selections
'
Select Case Index
Case 0 ' mapi start
MAPIStart
Case 1 ' mapi end
MAPIEnd
Case 2 ' call address book
MAPIAddrBook
Case 3 ' session properties
SessionProps
Case 4 ' show infostores collection
SessionInfoStoreColl
Case 5 ' show infostore properties
InfoStoreProps
Case 6 ' folders collection
FoldersColl
Case 7 ' folder object
FolderProps
Case 8 ' messsage collection
MessagesColl
Case 9 ' message properties
MessageProps
Case 10 ' add a message w/ dialogs
MessageAdd
Case 11 ' make a message in code
MessageMake
Case 12 ' recipients collection
RecipColl
End Select
'
End Sub

Before coding the collection routine, you must first add three new variables to the general declaration area of the form.
Modify the declaration list to match the code in Listing 8.31.

Listing 8.31. Modifying the form-level declarations.

Option Explicit
'
Dim objSession As Object ' for mapi session
Dim objInfoStoreColl As Object ' collection of stores
Dim objInfoStore As Object ' single info store
Dim objFolderColl As Object ' collection of folders
Dim objFolder As Object ' single folder
Dim objMessageColl As Object ' messages collection
Dim objMessage As Object ' single message
Dim objRecipColl As Object ' recipient collection
Dim objRecipient As Object ' single recipient
Dim objAddrEntry As Object ' addressentry object

Next add the RecipColl subroutine to the project and enter the code in Listing 8.32.

Listing 8.32. Adding the RecipColl routine.

Public Sub RecipColl()


'
' show the recipient collection properties
'
Dim cMsg As String
'
' get the recipient collection
' from the first message in the inbox
Set objMessageColl = objSession.Inbox.Messages
Set objMessage = objMessageColl.GetFirst
'
' get its recipient collection
Set objRecipColl = objMessage.Recipients
'
' get the properties
cMsg = "Application: " & objRecipColl.Application & Chr(13)
cMsg = cMsg & "Class: " & CStr(objRecipColl.Class) & Chr(13)
cMsg = cMsg & "Count: " & CStr(objRecipColl.Count) & Chr(13)
cMsg = cMsg & "Item (Name): " & objRecipColl.Item(1).Name & Chr
(13)
cMsg = cMsg & "Resolved: " & objRecipColl.Resolved
'
MsgBox cMsg, vbInformation, "Recipients Collection Object"
'
End Sub

The RecipColl routine locates the first message in the Inbox collection and then displays the Recipients
collection object properties. Notice that the routine calls the Name property of the Item object in the collection. The
Item(1) property points to the first recipient object in the collection.

In Figure 8.11 you can see the results of a message with three recipients. Your exact results will vary based on the
message you select.

Figure 8.11 : Viewing the Recipients collection object properties.

Using the Delete method will remove all the Recipient objects from the collection. After calling the Delete
method, you must call the Update method on the Message object to make the changes permanent. Once the recipients
are deleted, they cannot be recovered. The following code sample illustrates how to remove all recipients from a
collection:

objMessage.Recipients.Delete
objMessage.Update

You can use the Add method to add a new recipient to a message, as explained in the next section.

The Recipient Object

The Recipient object holds all the information needed to address a message to its destination. Along with the usual
properties, the Recipient object has an AddressEntry property. This property points to a valid AddressEntry
object. You'll learn more about the AddressEntry object in the next section. The methods for the Recipient object
allow you to delete the recipient from the collection and validate the address before attempting to send the message.

Table 8.14 shows the Recipient object methods, and Table 8.15 shows the Recipient object properties.

Table 8.14. The Recipient object methods.


Method name Parameters Description
Used to delete the selected recipient
Delete (none)
from the collection.
Used to validate the recipient entry
(optional) showDialog as against the MAPI address book. Can
Resolve
Boolean optionally show a dialog box to help
resolve ambiguous entries.

Table 8.15. The Recipient object properties.


Property name Type Description
The complete routing address for
the target recipient. In the format:

TransportType:
EmailAddress. The
Address String TransportType is taken from
the Type property of the child
AddressEntry object. The
EmailAddress is taken from the
Address property of the
AddressEntry object.
The underlying object that contains
detailed information on the
AddressEntry AddressEntry object recipient, including the exact
address and message transport
type.
Name of the library. Always set to
Application String
OLE/Messaging.
Internal identifying code for all
Class Long MAPI objects. Always set to 4 for
Recipient objects.
A value that indicates the type of
recipient. This is used to control
how the message is displayed by
client applications. Valid display
types are

Local User
Distribution List
Shared Folder
Agent
DisplayType Long
Organization
Private Distribution
List
Remote User

MAPI performs no special


processing based on the
DisplayType property. It is up
to the programmer to use this
property where appropriate.
A value that indicates the position
Index Long of the Recipient object in the
collection.
The displayable Name property of
the underlying AddressEntry
Name String
object. This is the value shown in
the MAPI address book.
A value indicating the type of
recipient for this message. Valid
types are
Type Long
To-Primary addressee
cc-Courtesy copy addressee
BC-Blind courtesy copy addressee

To view the properties of a Recipient object, add another button to the control array and set its caption property to
RecipProps. Then modify the Command1_Click event as shown in Listing 8.33.

Listing 8.33. Updating the Command1_Click event.

Private Sub Command1_Click(Index As Integer)


'
' handle user selections
'
Select Case Index
Case 0 ' mapi start
MAPIStart
Case 1 ' mapi end
MAPIEnd
Case 2 ' call address book
MAPIAddrBook
Case 3 ' session properties
SessionProps
Case 4 ' show infostores collection
SessionInfoStoreColl
Case 5 ' show infostore properties
InfoStoreProps
Case 6 ' folders collection
FoldersColl
Case 7 ' folder object
FolderProps
Case 8 ' messsage collection
MessagesColl
Case 9 ' message properties
MessageProps
Case 10 ' add a message w/ dialogs
MessageAdd
Case 11 ' make a message in code
MessageMake
Case 12 ' recipients collection
RecipColl
Case 13 ' recipient object
RecipProps
End Select
'
End Sub
Now add the RecipProps subroutine to the form and enter the code from Listing 8.34.

Listing 8.34. Adding the RecipProps routine.

Public Sub RecipProps()


'
' show the recipient object properties
'
Dim cMsg As String
'
' select a message and recipient
Set objMessageColl = objSession.Inbox.Messages
Set objMessage = objMessageColl.GetFirst
Set objRecipient = objMessage.Recipients.Item(1)
'
' now get properties
cMsg = "Address: " & objRecipient.address & Chr(13)
cMsg = cMsg & "Application: " & objRecipient.Application & Chr
(13)
cMsg = cMsg & "Class: " & CStr(objRecipient.Class) & Chr(13)
cMsg = cMsg & "DisplayType: " & CStr(objRecipient.DisplayType) &
Chr(13)
cMsg = cMsg & "Index: " & CStr(objRecipient.Index) & Chr(13)
cMsg = cMsg & "Name: " & objRecipient.Name & Chr(13)
cMsg = cMsg & "Type: " & CStr(objRecipient.Type)
'
MsgBox cMsg, vbInformation, "Recipient Object Properties"
'
End Sub

Adding a Recipient Object to a Collection

You can use the Add method of the Recipients collection object to add a valid recipient to a message. To do this you
must have a Message object, access the recipients collection for that message, execute the Add method, and then
populate the new Recipient object properties. Once that is done, you must execute the Update method of the
message to save all changes.

Add a new button to the control array and set its caption property to RecipAdd. Be sure to modify the
Command1_Click event to match the code in Listing 8.35.

Listing 8.35. Modifying the Command1_Click event.

Private Sub Command1_Click(Index As Integer)


'
' handle user selections
'
Select Case Index
Case 0 ' mapi start
MAPIStart
Case 1 ' mapi end
MAPIEnd
Case 2 ' call address book
MAPIAddrBook
Case 3 ' session properties
SessionProps
Case 4 ' show infostores collection
SessionInfoStoreColl
Case 5 ' show infostore properties
InfoStoreProps
Case 6 ' folders collection
FoldersColl
Case 7 ' folder object
FolderProps
Case 8 ' messsage collection
MessagesColl
Case 9 ' message properties
MessageProps
Case 10 ' add a message w/ dialogs
MessageAdd
Case 11 ' make a message in code
MessageMake
Case 12 ' recipients collection
RecipColl
Case 13 ' recipient object
RecipProps
Case 14 ' add a recipient to a message
RecipAdd
End Select
'
End Sub

Now add the RecipAdd subroutine to the form and enter the code from Listing 8.36.

Listing 8.36. Adding the RecipAdd routine.

Public Sub RecipAdd()


'
' adding a new recipient to a message
'
' add a new message first
Set objMessageColl = objSession.Outbox.Messages
Set objMessage = objMessageColl.Add
objMessage.subject = "New Message"
objMessage.Text = "Added this using VB code"
'
' now add a recipient
Set objRecipColl = objMessage.Recipients
Set objRecipient = objRecipColl.Add
objRecipient.Name = "MCA" ' select a good name
objRecipient.Resolve ' force validation
'
' update and send
objMessage.Update
objMessage.Send showdialog:=True
'
End Sub

The RecipAdd routine first creates a new message in the outbox and then adds a new recipient to the message. Finally,
after validating the Recipient object, the routine updates all the changes and calls the Send dialog box (see Figure
8.12).

Figure 8.12 : Viewing the results of adding a recipient to a message.

This routine could easily be modified to complete the send operation without presenting any dialog boxes. If you
change the ShowDialog parameter to False, the OLE Messaging library will post the message without interfacing
with the user.

The AddressEntry Object

The AddressEntry object is a child object of the Recipient object. The AddressEntry object contains all the
valid addressing information for a message system. The AddressEntry object is also the object that represents an
entry in the MAPI address book. In this way, the AddressEntry object provides a link between the MAPI address
book and MAPI messages.

The OLE Messaging library interface allows you to modify or delete AddressEntry objects from the MAPI address
book. However, there is no Add method for the AddressEntry object. You cannot use the OLE Messaging library to
create new entries in the MAPI address book. Table 8.16 shows the AddressEntry object methods, their parameters,
and brief descriptions.

Table 8.16. The AddressEntry object methods.


Method name Parameters Description
Removes the AddressEntry
Delete (none) object from the MAPI address
book.
Displays a dialog box that allows
the user to modify published
Details (opt) parentWindow as Long
properties of the
AddressEntry.
(opt) makePermanent as Boolean, Make changes to the object's
Update
(opt) refreshObject as Boolean properties permanent.

The AddressEntry object has a handful of properties. These properties identify the message transport used to send
messages to this location, the actual address, and other internal properties. Table 8.17 shows the properties of the
AddressEntry object.
Table 8.17. The AddressEntry object properties.
Property name Type Description
The electronic mail address of the
Address String
record.
Name of the library. Always set to
Application String
OLE/Messaging.
Internal identifying code for all
Class Long MAPI objects. Always set to 8 for
AddressEntry objects.
A value that indicates the type of
recipient. This is used to control
how the message is displayed by
client applications. Valid display
types are

Local User
Distribution List
Shared Folder
Agent
DisplayType Long
Organization
Private Distribution
List
Remote User

MAPI performs no special


processing based on the
DisplayType property. It is up
to the programmer to use this
property where appropriate.
A collection of user-defined
properties. This is used by
Fields collection
Fields programmers to add additional
object
unique information to
AddressEntry objects.
A MAPI-assigned unique value that
ID String
identifies this address object.
The displayable Name property of
the AddressEntry object. This
Name String
is the value shown in the MAPI
address book.
A value indicating the type of
recipient for this message. Valid
types are
Type Long
To-Primary addressee
cc-Courtesy copy addressee
BC-Blind courtesy copy addressee

Add a new button to the control array and set its caption to AddrProps. Then modify the Command1_Click event to
match the code in Listing 8.37.
Listing 8.37. Modifying the Command1_Click event.

Private Sub Command1_Click(Index As Integer)


'
' handle user selections
'
Select Case Index
Case 0 ' mapi start
MAPIStart
Case 1 ' mapi end
MAPIEnd
Case 2 ' call address book
MAPIAddrBook
Case 3 ' session properties
SessionProps
Case 4 ' show infostores collection
SessionInfoStoreColl
Case 5 ' show infostore properties
InfoStoreProps
Case 6 ' folders collection
FoldersColl
Case 7 ' folder object
FolderProps
Case 8 ' messsage collection
MessagesColl
Case 9 ' message properties
MessageProps
Case 10 ' add a message w/ dialogs
MessageAdd
Case 11 ' make a message in code
MessageMake
Case 12 ' recipients collection
RecipColl
Case 13 ' recipient object
RecipProps
Case 14 ' add a recipient to a message
RecipAdd
Case 15 ' address entry object
AddrProps
End Select
'
End Sub

Now add the AddrProps subroutine and enter the code in Listing 8.38.

Listing 8.38. Adding the AddrProps routine.

Public Sub AddrProps()


'
' show address entry properties
'
Dim cMsg As String
'
' start a new message
Set objMessageColl = objSession.Outbox.Messages
Set objMessage = objMessageColl.Add
objMessage.subject = "Testing AddressEntry Objects"
objMessage.Text = "Testing the AddrProps routine"
'
' add a recipient
Set objRecipient = objMessage.Recipients.Add
objRecipient.Name = "MCA"
objRecipient.Resolve ' validate it
'
' set addressentry object
Set objAddrEntry = objRecipient.AddressEntry
'
' now get properties of address entry object
cMsg = "Address: " & objAddrEntry.address & Chr(13)
cMsg = cMsg & "Application: " & objAddrEntry.Application & Chr
(13)
cMsg = cMsg & "Class: " & CStr(objAddrEntry.Class) & Chr(13)
cMsg = cMsg & "DisplayType: " & CStr(objAddrEntry.DisplayType) &
Chr(13)
cMsg = cMsg & "ID: " & objAddrEntry.ID & Chr(13)
cMsg = cMsg & "Name: " & objAddrEntry.Name & Chr(13)
cMsg = cMsg & "Type: " & objAddrEntry.Type
'
MsgBox cMsg, vbInformation, "Address Entry Object Properties"
'
End Sub

When you save and run the project, then click AddrProps, you'll see a set of property information for the message
recipient. Although the details will vary, your screen should look like the one in Figure 8.13.

Figure 8.13 : Viewing the AddressEntry properties.

Accessing the AddressEntry Details Dialog Box

You can use the Details method to invoke a Details dialog box for an AddressEntry object. This is handy for
looking up additional information on a known AddressEntry object. Add another button to the control array and set
its caption to AddrDetails. Modify the Command1_Click event as shown in Listing 8.39.

Listing 8.39. Updated Command1_Click event.

Private Sub Command1_Click(Index As Integer)


'
' handle user selections
'
Select Case Index
Case 0 ' mapi start
MAPIStart
Case 1 ' mapi end
MAPIEnd
Case 2 ' call address book
MAPIAddrBook
Case 3 ' session properties
SessionProps
Case 4 ' show infostores collection
SessionInfoStoreColl
Case 5 ' show infostore properties
InfoStoreProps
Case 6 ' folders collection
FoldersColl
Case 7 ' folder object
FolderProps
Case 8 ' messsage collection
MessagesColl
Case 9 ' message properties
MessageProps
Case 10 ' add a message w/ dialogs
MessageAdd
Case 11 ' make a message in code
MessageMake
Case 12 ' recipients collection
RecipColl
Case 13 ' recipient object
RecipProps
Case 14 ' add a recipient to a message
RecipAdd
Case 15 ' address entry object
AddrProps
Case 16 ' address entry details dialog
AddrDetails
End Select

'
End Sub

Now add the AddrDetails subroutine and enter the code from Listing 8.40.

Listing 8.40. Adding the AddrDetails routine.

Public Sub AddrDetails()


'
' show the details of an address entry
'
' first get an existing message
Set objMessageColl = objSession.Inbox.Messages
Set objMessage = objMessageColl.GetFirst
'
' now get the recipient/address entry objects
Set objRecipient = objMessage.Recipients.Item(1)
Set objAddrEntry = objRecipient.AddressEntry
'
' now show the details of the address entry
objAddrEntry.Details
'
End Sub

After accessing the first message in the inbox and getting the AddressEntry of the first Recipient object of the
message, this routine invokes the Details method to show a dialog box that allows users to modify the fields of the
address book entry (see Figure 8.14).

Figure 8.14 : Viewing the Details dialog box for the AddressEntry object.

You can modify AddressEntry properties without invoking the Details method. For example, you can change the
Name property of an existing AddressEntry object. The following code fragment shows how this is done:

objAddrEntry.Name = "New Name"


objAddrEntry.Update(True, True) ' update object and reload data

You can also delete an entry from the MAPI address book by using the Delete method. The following code example
shows how this can be done:

objAddrEntry.Delete ' mark for delete


objAddrEntry.Update(True, True) ' update the changes

Warning
Once a Delete/Update method pair is invoked on an AddressEntry
object, it is permanently removed from the address book and cannot be
recovered. Use this method sparingly. And when you do provide delete
services in your programs, be sure to add a confirmation option dialog box
before permanently deleting the object.

The Attachment Objects and Collections

The Attachment objects contain non-text data that is to be sent along with a standard message. Often this is a binary
graphic or formatted document file that is shipped from one user to another. The Attachment object methods and
properties allow you to read and write attachments to your messages.

There can be multiple attachments for each message. For this reason, the message object has an Attachments
collection object associated with it. All attachment objects are part of the Attachments collection.

The Attachments Collection Object

The Attachments collection object is a child of the Message object and holds all the associated attachments for that
message. The Attachments collection is a small collection, so it supports the Count and Item properties and the
Attachment object supports the Index property.
Table 8.18 shows the Attachments collection object methods, and Table 8.19 shows the Attachments collection
object properties.

Table 8.19. The Attachments collection object methods.


Method name Parameters Description
(optional) name as String,
(optional) position as Long, Adds a new attachment to the
Add
(optional) type as Long, attachments collection.
(optional) source as String.
Removes all attachments
Delete (none)
from the collection.

Table 8.20. The Attachments collection object properties.


Property name Type Description
Application String Name of the library. Always set to OLE/Messaging.
Internal identifying code for all MAPI objects. Always set to
Class Long
21 for Attachments objects.
Count Long The total number of attachments in this collection.
Used to gain access to one of the attachments in the
Item Object collection based on the Index property of the
Attachment object.

To test the Attachments collection object, add a new command button to the control array and set its caption to
AttachColl. Then modify the Command1_Click event to match the code in Listing 8.41.

Listing 8.41. Modifying the Command1_Click event.

Private Sub Command1_Click(Index As Integer)


'
' handle user selections
'
Select Case Index
Case 0 ' mapi start
MAPIStart
Case 1 ' mapi end
MAPIEnd
Case 2 ' call address book
MAPIAddrBook
Case 3 ' session properties
SessionProps
Case 4 ' show infostores collection
SessionInfoStoreColl
Case 5 ' show infostore properties
InfoStoreProps
Case 6 ' folders collection
FoldersColl
Case 7 ' folder object
FolderProps
Case 8 ' messsage collection
MessagesColl
Case 9 ' message properties
MessageProps
Case 10 ' add a message w/ dialogs
MessageAdd
Case 11 ' make a message in code
MessageMake
Case 12 ' recipients collection
RecipColl
Case 13 ' recipient object
RecipProps
Case 14 ' add a recipient to a message
RecipAdd
Case 15 ' address entry object
AddrProps
Case 16 ' address entry details dialog
AddrDetails
Case 17 ' attachment collection
AttachColl
End Select
'
End Sub

Next you need to add two new variables to the general declaration section of the form. Modify your declarations to
match the code in Listing 8.42.

Listing 8.42. Modifying the form-level declarations.

Option Explicit
'
Dim objSession As Object ' for mapi session
Dim objInfoStoreColl As Object ' collection of stores
Dim objInfoStore As Object ' single info store
Dim objFolderColl As Object ' collection of folders
Dim objFolder As Object ' single folder
Dim objMessageColl As Object ' messages collection
Dim objMessage As Object ' single message
Dim objRecipColl As Object ' recipient collection
Dim objRecipient As Object ' single recipient
Dim objAddrEntry As Object ' addressentry object
Dim objAttachColl As Object ' attachment collection
Dim objAttachment As Object ' single attachment

Now add the AttachColl subroutine to the form and enter the code that appears in Listing 8.43.

Listing 8.43. Adding the AttachColl routine.


Public Sub AttachColl()
'
' inspect the attachment collection
' of a message object
'
Dim cMsg As String
Dim x As Integer
'
' first get a message
Set objMessageColl = objSession.Inbox.Messages
Set objMessage = objMessageColl.GetFirst
'
' next get attachment collection of message
Set objAttachColl = objMessage.Attachments
'
' now get properties of the collection
cMsg = "Application: " & objAttachColl.Application & Chr(13)
cMsg = cMsg & "Class: " & CStr(objAttachColl.Class) & Chr(13)
cMsg = cMsg & "Count: " & CStr(objAttachColl.Count) & Chr(13)
'
' now list out attachments
cMsg = cMsg & Chr(13) & "Attachments:" & Chr(13)
For x = 1 To objAttachColl.Count
cMsg = cMsg & CStr(x) & " - " & objAttachColl.Item(x).Name &
Chr(13)
Next x
'
MsgBox cMsg, vbInformation, "Attachment Collection Object
Properties"
'
End Sub

Warning
The AttachColl routine will crash if there are no attachments on the first
message in the inbox. Be sure to add a single message in the inbox with at
least one attachment before you run this routine. You can use your MAPI
client to create a new message with attachments and send it to yourself.

The AttachColl routine accesses the attachments collection of the first message in the inbox and displays its
properties. Your output may be different, but it should look something like Figure 8.15.

Figure 8.15 : Viewing the Attachments collection object properties.

You can delete all the attachments in the collection by invoking the Delete method on the Attachments collection
object. The delete is not complete until you invoke the Update method on the parent object. And once the delete is
complete, you cannot recover the data.

You'll learn about the Add method in the next section on Attachment objects.

The Attachment Object


The Attachment object contains all the information about the data file attached to a message. In fact, the
Attachment object includes the actual data file itself. There are several properties and three methods to the
Attachment object. Table 8.21 shows you the Attachment object methods and Table 8.22 shows you the
Attachment object properties.

Table 8.21. The Attachment object methods.


Method name Parameters Description
Removes the attachment from the attachment
Delete (none)
collection.
Copies the operating system file into the
ReadFromFile fileName as String
Attachment object.
Copies the data from the Attachment
WriteToFile fileName as String
object to an operating system file.

Table 8.22. The Attachment object properties.


Property name Type Access
Application String Name of the library. Always set to OLE/Messaging.
Internal identifying code for all MAPI objects. Always set to
Class Long
5 for Attachment objects.
A value that indicates the position of the attachment in the
Index Long
attachments collection.
Name String The display name of the attachment.
A value indicating the character position in the message body
Position Long
where the attachment should be displayed.
For OLE-type data, contains the OLE object name (for
Source String example, Word.Document). For File-type data, contains a
blank.
The value that indicates the type of attachment. MAPI
Type Long
supports two attachment types: File(0) and OLE(2).

To review the attachment properties, add a new button to the control array and set its caption to AttachProps. Then
modify the Command1_Click event to match the code in Listing 8.44.

Listing 8.44. The modified Command1_Click event.

Private Sub Command1_Click(Index As Integer)


'
' handle user selections
'
Select Case Index
Case 0 ' mapi start
MAPIStart
Case 1 ' mapi end
MAPIEnd
Case 2 ' call address book
MAPIAddrBook
Case 3 ' session properties
SessionProps
Case 4 ' show infostores collection
SessionInfoStoreColl
Case 5 ' show infostore properties
InfoStoreProps
Case 6 ' folders collection
FoldersColl
Case 7 ' folder object
FolderProps
Case 8 ' messsage collection
MessagesColl
Case 9 ' message properties
MessageProps
Case 10 ' add a message w/ dialogs
MessageAdd
Case 11 ' make a message in code
MessageMake
Case 12 ' recipients collection
RecipColl
Case 13 ' recipient object
RecipProps
Case 14 ' add a recipient to a message
RecipAdd
Case 15 ' address entry object
AddrProps
Case 16 ' address entry details dialog
AddrDetails
Case 17 ' attachment collection
AttachColl
Case 18 ' attachment object properties
AttachProps
End Select
'
End Sub

Now add the AttachProps subroutine and enter the code from Listing 8.45.

Listing 8.45. Adding the AttachProps routine.

Public Sub AttachProps()


'
' view the attachment object properties
'
Dim cMsg As String
'
' select a message with an attachment
Set objMessageColl = objSession.Inbox.Messages
Set objMessage = objMessageColl.GetFirst
Set objAttachColl = objMessage.Attachments
Set objAttachment = objAttachColl.Item(1)
'
' now get properties
cMsg = "Application " & objAttachment.Application & Chr(13)
cMsg = cMsg & "Class: " & CStr(objAttachment.Class) & Chr(13)
cMsg = cMsg & "Index: " & CStr(objAttachment.Index) & Chr(13)
cMsg = cMsg & "Name: " & objAttachment.Name & Chr(13)
cMsg = cMsg & "Position: " & CStr(objAttachment.position) & Chr
(13)
cMsg = cMsg & "Source: " & objAttachment.Source & Chr(13)
cMsg = cMsg & "Type: " & CStr(objAttachment.Type)
'
MsgBox cMsg, vbInformation, "Attachment object Properties"
'
End Sub

Save and run this routine. When you click on the AttachProps button, you will see the properties of the attachment.
Refer to Figure 8.16 for an example of the output.

Figure 8.16 : Viewing the Attachment object properties.

Adding an Attachment to a Message

You can use Visual Basic code to add an attachment directly to a message. This involves setting three properties and
invoking the ReadFromFile method. First, add another button to the control array and set its caption to AttachAdd.
Then modify the Command1_Click event to match the code in Listing 8.46.

Listing 8.46. Updated Command1_Click event.

Private Sub Command1_Click(Index As Integer)


'
' handle user selections
'
Select Case Index
Case 0 ' mapi start
MAPIStart
Case 1 ' mapi end
MAPIEnd
Case 2 ' call address book
MAPIAddrBook
Case 3 ' session properties
SessionProps
Case 4 ' show infostores collection
SessionInfoStoreColl
Case 5 ' show infostore properties
InfoStoreProps
Case 6 ' folders collection
FoldersColl
Case 7 ' folder object
FolderProps
Case 8 ' messsage collection
MessagesColl
Case 9 ' message properties
MessageProps
Case 10 ' add a message w/ dialogs
MessageAdd
Case 11 ' make a message in code
MessageMake
Case 12 ' recipients collection
RecipColl
Case 13 ' recipient object
RecipProps
Case 14 ' add a recipient to a message
RecipAdd
Case 15 ' address entry object
AddrProps
Case 16 ' address entry details dialog
AddrDetails
Case 17 ' attachment collection
AttachColl
Case 18 ' attachment object properties
AttachProps
Case 19 ' add attachment
AttachAdd
End Select
'
End Sub

Next, add a new subroutine called AttachAdd and enter the code shown in Listing 8.47.

Listing 8.47. Adding the AttachAdd routine.

Public Sub AttachAdd()


'
' add an attachment to a message
'
' first create a new message
Set objMessageColl = objSession.Outbox.Messages
Set objMessage = objMessageColl.Add
objMessage.subject = "Here's the WIN.INI File"
objMessage.Text = " Here's a copy of the Windows INI File you
asked for."
'
' now add an attachment
Set objAttachColl = objMessage.Attachments
Set objAttachment = objAttachColl.Add
objAttachment.Name = "WIN.INI"
objAttachment.position = 0 ' place at start of message
objAttachment.ReadFromFile "C:\WINDOWS\WIN.INI"
'
' now address the message
Set objRecipColl = objMessage.Recipients
Set objRecipient = objRecipColl.Add
objRecipient.Name = "MCA" ' a valid name
objRecipient.Resolve ' validate name
'
' now update and send message
objMessage.Update ' save it all
objMessage.Send showdialog:=True ' let's see it
'
End Sub

Warning
Be sure to address the message to a valid person in your MAPI address
book. It is a good idea to address this message to yourself since you will be
reading and saving an attachment in the next section.

The AttachAdd routine creates a new message, adds the WIN.INI file as an attachment (at the first position in the
file), and then addresses the message and sends it. Your screen should look something like the one in Figure 8.17.

Figure 8.17 : Viewing the attachment to the message.

Tip
You could easily send the message without ever seeing the dialog box by
just setting the ShowDialog parameter to False.

Saving a Message Attachment to Disk

Now you can read the message you just sent yourself. Add one more button to the command array and set its caption to
AttachSave. Then modify the Command1_Click event to match the code in Listing 8.48.

Listing 8.48. Updating the Command1_Click event.

Private Sub Command1_Click(Index As Integer)


'
' handle user selections
'
Select Case Index
Case 0 ' mapi start
MAPIStart
Case 1 ' mapi end
MAPIEnd
Case 2 ' call address book
MAPIAddrBook
Case 3 ' session properties
SessionProps
Case 4 ' show infostores collection
SessionInfoStoreColl
Case 5 ' show infostore properties
InfoStoreProps
Case 6 ' folders collection
FoldersColl
Case 7 ' folder object
FolderProps
Case 8 ' messsage collection
MessagesColl
Case 9 ' message properties
MessageProps
Case 10 ' add a message w/ dialogs
MessageAdd
Case 11 ' make a message in code
MessageMake
Case 12 ' recipients collection
RecipColl
Case 13 ' recipient object
RecipProps
Case 14 ' add a recipient to a message
RecipAdd
Case 15 ' address entry object
AddrProps
Case 16 ' address entry details dialog
AddrDetails
Case 17 ' attachment collection
AttachColl
Case 18 ' attachment object properties
AttachProps
Case 19 ' add attachment
AttachAdd
Case 20 ' save attachment
AttachSave
End Select
'
End Sub

Now add the AttachSave subroutine and enter the code from Listing 8.49.

Listing 8.49. Adding the AttachSave routine.

Public Sub AttachSave()


'
' read a message and save the attachment
'
Dim cMsg As String
'
' get the last message in the inbox
Set objMessageColl = objSession.Inbox.Messages
Set objMessage = objMessageColl.GetLast
'
' now save the attachment
Set objAttachColl = objMessage.Attachments
Set objAttachment = objAttachColl.Item(1)
objAttachment.WriteToFile App.Path & "\saved.txt"
'
cMsg = "Attachment: " & objAttachment.Name & Chr(13)
cMsg = cMsg & "Saved as: " & App.Path & "\saved.txt"
'
MsgBox cMsg, vbInformation, "Save Attachment"
'
End Sub

The AttachSave routine reads the last message added to the inbox collection (the one you sent just a moment ago)
and retrieves the attachment from the message's attachment collection. The attachment is then saved in the local disk
folder as SAVED.TXT (see Figure 8.18).

Figure 8.18 : Saving the attached file.

In production applications, you should add code to the routine to prompt the user for a folder and filename for storing the
attachment.

Summary

In this chapter, you learned how to use the OLE Messaging library to access features of MAPI. You learned all the major
objects, their methods, and their properties.

● The Session object


● The InfoStore objects and collections
● The Folder objects and collections
● The Message objects and collections
● The Recipient objects and collections
● The Address objects
● The Attachment objects and collections

You also wrote several code examples that inspected and modified objects and their properties. You learned to use the
OLE Messaging library to

● Read existing messages.


● Create new messages.
● Add recipients to messages.
● Add attachments to messages.
● Modify records in the MAPI address book.

The OLE Messaging library is a rich and powerful set of commands. In the next several chapters, you'll use the OLE
Messaging library to build powerful desktop applications that use the advanced features of MAPI 1.0.
Chapter 7

Creating a Simple MAPI Client with the MAPI Controls

CONTENTS

● The Visual Basic MAPI Controls


❍ The MAPISession Control

❍ The MAPIMessage Control

● Building a Simple MAPI Client Application


❍ Laying Out the Forms

❍ Coding the Main Support Routines

❍ Coding the MAPIMAIN.FRM Events

❍ Coding the Main Button Bar

❍ Coding the Reader Form

❍ Running the Simple Mail Client

❍ Additional Features

● Summary

In this chapter, you'll learn how to use the MAPI controls from Visual Basic to create a simple program that can read and
reply to all e-mail sent to the logon ID. You'll also learn how to write routines to access all the electronic mail services
available to a basic MAPI client application on a desktop workstation. This includes creating new e-mail messages,
checking the inbox for new mail, and accessing and updating the e-mail address book. When you complete this chapter,
you will have a fully functional e-mail client application.

You'll also learn the details of using the MAPISession and MAPIMessage controls with Visual Basic 4.0. The Visual
Basic MAPI controls offer a quick way to build Simple MAPI applications. The MAPI controls allow you full access to
the Inbox object, to the MAPI Compose, Fetch, Read, Delete, and Send services, and limited access to the
address book.

Note
Simple MAPI is sometimes called MAPI 0 to indicate that it precedes the
MAPI 1.0 release that matches Microsoft Exchange Server. Throughout this
book, you'll see Simple MAPI instead of MAPI 0.

When you complete the coding examples in this chapter, you will understand the basics of Simple MAPI, and you'll
know how to use Visual Basic and the MAPI controls to read, compose, address, send, and delete MAPI messages.

The Visual Basic MAPI Controls


Note
Throughout the rest of this chapter, there are sample programs that illustrate
the use of the MAPI controls to create mail-aware applications with Visual
Basic 4.0. These programs must be run on a workstation that has access to
simple MAPI services through a MAPI-compliant mail server. MAPI-
compatible servers from Microsoft include the Microsoft Mail Server for
DOS, Microsoft Exchange Server for NT, Microsoft Mail for Windows for
Workgroups, Windows 95 Exchange Mail, or any other fully MAPI-
compliant system (such as WordPerfect Office).

Visual Basic 4.0 Professional Edition ships with two OCX controls that provide access to all the MAPI services you'll
need to create fully functional electronic mail applications using Visual Basic. The MAPISession control provides
access to everything you'll need to sign on and sign off any MAPI-compliant server. The MAPIMessage control
provides access to the MAPI routines that allow you to read, compose, address, and send messages using the session
established with the MAPISession control. This section of the chapter will review the MAPI-related properties and
methods of each of these controls.

The MAPISession Control

The MAPISession control is used to establish a link between your Visual Basic program (the mail client) and the
electronic mail server. The MAPI-compliant mail server must be installed and available to the Visual Basic client
program. If your program will run on a traditional network server, the mail server may be Microsoft Mail installed on a
network workstation. If you are running Windows for Workgroups or Windows 95, Microsoft Mail or Microsoft
Exchange can act as a mail server. There are other mail servers available for both file server and peer-to-peer networks.

There are two methods and seven properties for the MAPISession control that are directly MAPI related. The two
following sections identify these components of the MAPISession control, and describe their meaning and their use in
Visual Basic programs.

Methods of the MAPISession Control

There are only two MAPISession methods: SignOn and SignOff. The SignOn method is used to begin a MAPI
session. By default, the SignOn method provides a logon dialog box that prompts the user for valid logon information.
The exact nature of the logon dialog box depends on the mail server. The Microsoft Mail logon dialog box prompts the
user for a valid username and password (see Figure 7.1).

Figure 7.1 : Default logon dialog box for Microsoft Mail.

The default logon dialog box for the Microsoft Exchange Mail product simply asks the user to select the desired mail
services profile (see Figure 7.2).

Figure 7.2 : Default logon dialog box for Microsoft Exchange Mail.

If a valid username and password are supplied by way of the SignOn method, the MAPISession control returns a
unique value in the SessionID property. This unique value is used in all message transactions to identify the link
between the client application and the mail server. We'll talk more about the SessionID property in the next section of
this chapter.

The SignOff method of the MAPISession control does just what you'd expect-it safely ends your link to the mail
server. There is no dialog box associated with the SignOff method.
In order to use the MAPISession control methods, you must first place the MAPISession control on a Visual Basic
form. The form is invisible at run-time and is only used to provide the methods and properties needed to establish a
MAPI session between your Visual Basic program and the mail server.

As an example of the MAPISession control, start a Visual Basic project. Use the Tools | Custom Controls
menu item to add the Microsoft MAPI controls to your project. Place a MAPISession control on the form and add the
three lines of code in Listing 7.1 to the Form_Load event.

Listing 7.1. A tiny e-mail client.

Private Sub Form_Load()


'
MAPISession1.SignOn
MAPISession1.SignOff
End
'
End Sub

Save the form as CDG0701.FRM and the project as CDG0701.VBP, and run the project. You'll see the default logon
dialog box provided by your mail server. Once you sign in, the Visual Basic program immediately signs you out and
ends. You'll add more features as we go along.

Properties of the MAPISession Control

The MAPISession control has seven MAPI-related properties. These properties all deal with options that you can set
to control the behavior of the control at logon time.

The Action property can be used to invoke the sign-on or sign-off methods. This property was present in Visual Basic
3.0 and has been replaced by the SignOn and SignOff methods discussed earlier.

The DownloadMail property is used to control whether new messages are downloaded from the mail server at logon
time. By default, all new messages are forced into the user's mailbox at logon time. You can set this property to False
to prevent this from happening. In the code example in Listing 7.2, the DownloadMail property has be set to False
before invoking the SignOn method.

Listing 7.2. Setting the DownloadMail property of the MAPISession control.

Private Sub Form_Load()


'
MAPISession1.DownloadMail = False ' don't get new mail
MAPISession1.SignOn
MAPISession1.SignOff
End
'
End Sub
The LogonUI property can be used to turn off the default logon screen provided by the mail server. You can set this
property to False to suppress the mail server from presenting the logon screen when you invoke the SignOn method.
If you set this property to False, you must supply valid data in the UserName and Password properties, or an error
will be reported.

You can use the NewSession property to force the creation of a new MAPI session, even if a MAPI session already
exists for this workstation. By default, the SignOn method will attempt to locate any current MAPI interface sessions
before attempting to log on to the mail server. When you set the NewSession parameter to True, the SignOn
method will create a second MAPI session link with the mail server. The code in Listing 7.3 shows how this is done.

Listing 7.3. Setting the NewSession property of the MAPISession control.

Private Sub Form_Load()


'
MAPISession1.DownloadMail = False ' dont get new mail
MAPISession1.NewSession = True ' force a new session
MAPISession1.SignOn
MAPISession1.SignOff
End
'
End Sub

The Password and UserName properties should be set to valid values if you want to bypass the default logon screen.
If you supply a UserName but leave the Password property blank, the SignOn method will force the logon dialog
box to appear and prompt for the missing information. If, however, the LogonUI property is set to False, no dialog
box will appear, and an error will be returned.

If you are using the Microsoft Exchange Mail Client, you only need to provide a valid Profile Name. Microsoft
Exchange Mail will ignore any value in the Password property. If you are using the Microsoft Mail client (network or
workgroup version), you will need to supply both the UserName and the Password properties if you want to bypass
the default logon dialog box. Refer to Listing 7.4 for an example.

Listing 7.4. Getting the username and password at sign-on.

Private Sub Form_Load()


'
MAPISession1.DownloadMail = False ' don't get new mail
MAPISession1.NewSession = True ' force a new session
MAPISession1.UserName = "MCA" ' username for mail
MAPISession1.Password = "PASSWORD" ' users password
MAPISession1.SignOn
MAPISession1.SignOff
End
'
End Sub

The SessionID property is a read-only property available only at run-time that contains the unique session ID number
of a completed link between your Visual Basic program and the mail server. This session ID value must be used in all
transactions to the mail server. Its value is used to set the SessionID of the MAPIMessage control before attempting
to access the message services provided by the mail server. The code in Listing 7.5 displays the value of the
SessionID after a successful logon of the user.

Listing 7.5. Displaying the SessionID property.

Private Sub Form_Load()


'
MAPISession1.DownloadMail = False ' dont get new mail
MAPISession1.NewSession = True ' force a new session
MAPISession1.UserName = "VBU Profile" ' username for mail
MAPISession1.Password = "" ' user password
MAPISession1.SignOn
MsgBox Str(MAPISession1.SessionID) ' show unique session
handle
MAPISession1.SignOff
End
'
End Sub

Modify the UserName and, if needed, the Password properties to contain valid logon data for your mail server. Then
save and run the project. You will see a message box that shows the unique session handle for the MAPI session (see
Figure 7.3).

Figure 7.3 : Displaying the SessionID of a MAPI session.

The MAPIMessage Control

The MAPIMessage control gives your Visual Basic program access to all the message services available from the mail
server with which your program has established a session. The MAPIMessage control provides services for reading,
composing, addressing, and sending mail messages. You can perform all major mail operations by requesting the service
from the server.

The MAPIMessage control has four primary functions:

● Creating and sending messages


● Reading any new messages
● Deleting any old messages
● Maintaining the address list

There are 11 methods and over 30 properties of the MAPIMessage control that are MAPI-related. We'll review these
methods and properties briefly. You can find additional documentation on each of the MAPIMessage properties in the
Visual Basic online help files.

Methods

There are 11 methods for the MAPIMessage control:

● To create and send messages, the control uses the Compose, Copy, Forward, Reply, ReplyAll, and
Send methods.
● To read messages, the control uses the Fetch method.
● To manage old messages, the control uses the Save and Delete methods.
● To maintain the address list, the control uses the Show and ResolveName methods.

The message-creation methods account for six of the eleven methods. Of these six, you only need to know two before
you can create a functional e-mail application. The Compose method clears out the edit buffer area and prepares a clean
slate for the creation of a new message. The Send method attempts to send the new message to the address supplied. If
an address or message is not supplied, the Send method forces the mail server to present the default message compose
form to the user.

In other words, you can create a complete e-mail composer by adding only two lines to the code we started earlier in this
chapter. The code shown in the following listing is all you need to create a Visual Basic application that can compose
and send e-mail messages. Start a new Visual Basic project and add the MAPISession and the MAPIMessage
controls to the form. Then add the code shown in Listing 7.6 to the Form_Load event.

Listing 7.6. Creating a MAPIMessage.

Private Sub Form_Load()


'
MAPISession1.SignOn ' log into MAPI server
MAPIMessages1.SessionID = MAPISession1.SessionID
'
MAPIMessages1.Compose ' clear buffers
Listing 7.6. continued
MAPIMessages1.Send True ' show default form
'
MAPISession1.SignOff ' exit MAPI server
End
'
End Sub

Now save the form as CDG0702.FRM and the project as CDG0702.VBP, and run the program. You'll be prompted for
a logon, and then you'll see the default compose form. Figure 7.4 shows what comes up if you are using Microsoft
Exchange Mail.

Figure 7.4 : The default compose form for Microsoft Exchange.

Notice that you have a highly functional e-mail program with very little coding. Use this form to send yourself a
message. We'll use other Visual Basic code to read the message later in this chapter.
You can use the other message creation methods (Copy, Forward, Reply, and ReplyAll) to load the compose
buffer with messages that have been sent to you by others. You can then use the default compose dialog box to edit the
old message and send it just as you would a new message you created from scratch. The Forward, Reply, and
ReplyAll methods also alter the subject line to reflect the action. For example, when you use the Forward method,
the mail server will add "FW:" to the beginning of the subject line. The Reply methods place "RE:" at the start of the
subject line.

The code example in Listing 7.7 takes the first message in the user's inbox and copies it to the compose buffer with
"FW:" on the subject line.

Listing 7.7. Forwarding a message.

Private Sub Form_Load()


'
MAPISession1.SignOn ' log into MAPI server
MAPIMessages1.SessionID = MAPISession1.SessionID
'
MAPIMessages1.Fetch ' get messages
MAPIMessages1.Forward ' forward the current message
MAPIMessages1.Send True ' send it
'
MAPISession1.SignOff ' exit MAPI server
End
'
End Sub

You can use the Fetch method to tell the mail server to send your Visual Basic program all the messages that are
currently on file in the inbox for the logged-on user. The code example shown in Listing 7.8 fetches all the messages for
the user and then uses the MsgCount property to find out how many messages are actually on file.

Listing 7.8. Displaying the MsgCount property.

Private Sub Form_Load()


'
MAPISession1.SignOn ' log into MAPI server
MAPIMessages1.SessionID = MAPISession1.SessionID
'
MAPIMessages1.Fetch ' get all messages into inbox
MsgBox Str(MAPIMessages1.MsgCount) ' show number of msgs
'
MAPISession1.SignOff ' exit MAPI server
End
'
End Sub
You can use the Delete method to remove messages from the user's collection. The Delete method can also be used
to remove a single recipient from a list of addresses or to remove a single attachment from a list of currently attached
files. The code in Listing 7.9 removes the current message from the user's collection.

Listing 7.9. Deleting a message from the user's storage.

Private Sub Form_Load()


'
MAPISession1.SignOn ' log into MAPI server
MAPIMessages1.SessionID = MAPISession1.SessionID
'
MAPIMessages1.Fetch ' get all messages into inbox
MsgBox Str(MAPIMessages1.MsgCount)
MAPIMessages1.Delete ' get rid of first message
MsgBox Str(MAPIMessages1.MsgCount)
'
MAPISession1.SignOff ' exit MAPI server
End
'
End Sub

Finally, you can tell the mail server to show you a list of all the mail server addresses on file by invoking the Show
method. The Show method can also be used to display details of the current message recipient (if this is supported by the
MAPI mail server). The example code shown in Listing 7.10 will display the current mail server address list.

Listing 7.10. Showing the MAPI address book.

Private Sub Form_Load()


'
MAPISession1.SignOn ' log into MAPI server
MAPIMessages1.SessionID = MAPISession1.SessionID
'
MAPIMessages1.Show ' show the addresss list
'
MAPISession1.SignOff ' exit MAPI server
End
'
End Sub

Properties

The MAPIMessage control has more than thirty MAPI-related properties. We will quickly review them here. You can
find more detailed information on each of the MAPIMessage control properties by consulting the topic
"MAPIMessage Control" in the Visual Basic online help file.
The MAPIMessage control properties can be divided into several groups. Each group contains a set of properties that
all deal with the same aspect of the message services provided by the mail server. Table 7.1 shows the MAPI-related
properties, divided by service group.

Table 7.1. MAPI-related properties of the MAPIMessage control.


Group Property Description
Allows you to set the caption of the
Address AddressCaption
Address Book dialog box.
Use this property to set the number
AddressEditFieldCount of edit buttons available on the
Address Book dialog box.
If you use only one edit button
AddressLabel (To:), you can set the button label
with this property.
When set to True, allows user to
AddressModifiable modify the contents of the address
book; the default is False.
When set to True, presents a
dialog box to help users resolve
rejected addresses. Use this
AddressResolveUI
property in conjunction with the
ResolveName method. The
default is False.
Contains the number of
Attachment AttachmentCount attachments for the current
message.
Pointer to the current attachment in
AttachmentIndex
the list.
Filename of the attachment pointed
AttachmentName to by the AttachmentIndex
property.
Contains full pathname of the
AttachmentPathName
current attachment.
Position (in characters) where the
AttachmentPosition attachment is located within the
message body.
Indicates the type of the current
attachment. Valid values are DATA
AttachmentType (data file), EOLE (embedded OLE
object), and SOLE (static OLE
object).
Contains the type of message to
look for when executing the
Fetch FetchMsgType
Fetch method. Not commonly
used by current products.
Controls sort order when building
message collection. Set to True
FetchSorted for First In, First Out sort order. Set
to False to sort by user's InBox
setting. The default is False.
Set to True to get only the unread
messages into the user's collection.
FetchUnreadOnly If False, all messages for the
user's ID are loaded into the local
set. The default is True.
Contains internally generated ID
that keeps track of all messages in
Message MsgConversationID a related "thread." You can use this
value to collect all related
messages into a single group.
Contains the total number of
MsgCount
messages in the set.
Date the current message was
MsgDateReceived
received.
Internally generated unique ID
MsgID
value.
Pointer to the current message in
MsgIndex
the message collection.
This is the actual body of the
MsgNoteText
message (less any attachments).
Address of the originator (sender)
MsgOrigAddress
of the current message.
Contains the display name
MsgOrigDisplayName associated with the
MsgOrigAddress property.
Flag to indicate that the message
has been read by the user to whom
MsgRead it was addressed. When set to
True, the user has read the
message.
Flag to indicate that a return receipt
has been requested by the sender.
When set to True, the sender
MsgReceiptRequested
wants a confirmation that the
message has been successfully
delivered.
Flag to show that the message has
been sent to the mail server for
MsgSent
delivery. This flag is updated by
the server, not the client.
The text line that identifies the
MsgSubject
subject of the message.
Contains the message type code.
Currently, all MsgType codes are
MsgType
left null or empty to indicate an
Interpersonal Message (IPM).
Address of the current recipient
Recipient RecipAddress
(could be a list).
Contains the number of recipients
RecipCount
for the current piece of mail.
The display name associated with
RecipDisplayName
an address in the address book.
Points to the current recipient in
RecipIndex
the list.
Shows what type of recipient is
currently pointed to by the
RecipIndex property. A value
of 0 = OrigList (used only by
the message editor); 1 = ToList
RecipType
(a person in a "To" address list); 2
= CcList (a person on the
courtesy copy list); 3 = BccList
(a person on the blind courtesy
copy list).
Carryover from Visual Basic 3.0-
Other Action
use the new methods instead.
Contains the value of the
SessionID generated by the
SessionID MAPISession control. You must
update this property before you can
perform any Message operations.

You can use these properties to modify the behavior of the MAPI dialog boxes supplied by the mail server. For example,
you can change the caption of the address list, change the number of buttons that appear on the list, even change the
caption of the To: button. You can also use these properties to add increased functionality to your Visual Basic mail
applications by honoring attached documents, allowing the use of blind courtesy copy recipients, and so on.

While there are a number of things you can do with the numerous properties of the MAPIMessage control, one of the
most useful is the ability to create e-mail attachments. That is the subject of the next section.

Creating E-Mail Attachments

Creating e-mail attachments is very useful and very easy to do with the MAPIMessage control and Visual Basic.
Attachments can be simple text files, word processing documents, or even databases. For the next example, you'll attach
a system file from a user's workstation and prepare a message to send to the help desk at a large corporation.

There are four property values you need to set in order to successfully create an e-mail attachment:

● AttachmentPathName-This contains the complete filename as it is used by the operating system (for
example, c:\config.sys or \\server\directory1\file.ext).
● AttachmentName-This is a text string that appears underneath the attachment icon in the message.
● AttachmentType-This flag tells the server what type of attachment you are using. Visual Basic constants for
this property are mapData (a data file), mapEOLE (an embedded OLE object), and mapSOLE (a static OLE
object).
● AttachmentPosition-This is an integer value that contains the exact character position where the
attachment icon is to appear in the message text.

Note
If you plan on adding multiple attachments to the message, you'll also need
to use the AttachmentCount and AttachmentIndex properties to
fetch the attachments when you read the message.

You can add an attachment to any valid message in the compose buffer. The code example below creates a new message
and adds the workstation's CONFIG.SYS file as an attachment. Modify the Form_Load event of CDG0702.FRM to
match the code in Listing 7.11.

Listing 7.11. Creating an e-mail attachment.

Private Sub Form_Load()


'
MAPISession1.SignOn ' log into MAPI server
MAPIMessages1.SessionID = MAPISession1.SessionID
'
MAPIMessages1.Compose ' clear buffer
MAPIMessages1.MsgSubject = "User's CONFIG.SYS File"
MAPIMessages1.MsgNoteText = "Here is the CONFIG.SYS File" + Chr
(13) + " "
'
MAPIMessages1.AttachmentPosition = Len(MAPIMessages1.
MsgNoteText)
MAPIMessages1.AttachmentType = mapData
MAPIMessages1.AttachmentName = "System Configuration File"
MAPIMessages1.AttachmentPathName = "C:\CONFIG.SYS"
'
MAPIMessages1.Send True ' send it
'
MAPISession1.SignOff ' exit MAPI server
End
'
End Sub

Now save and run the CDG0702.VBP project. After logging onto the mail server, you'll see a form appear with the C:
\CONFIG.SYS file already attached to the message (see Figure 7.5).

Figure 7.5 : Viewing an added attachment to the message.

Building a Simple MAPI Client Application

The rest of this chapter covers the creation of a complete e-mail client for Simple MAPI access. This example uses all of
the default MAPI services supplied by the installed MAPI service provider. The look and feel of the sign-in dialog box,
the compose form, and the address book are completely controlled by the underlying MAPI engine running on the
workstation. As you saw earlier in the chapter, each of these forms looks different depending on whether you are running
Microsoft Mail, Microsoft Exchange, or any other MAPI-compliant messaging provider (such as Perfect Office). By
using the services already available on the workstation, you can reduce your coding and maintain a familiar look and feel
for your users' e-mail applications.
You will notice that one of the services unavailable within this client is the ability to store and retrieve old messages
within the existing private and public mail folders. Simple MAPI services do not include access to the message store.
You can only read information from the inbox using Simple MAPI. The Send method automatically writes messages to
the outbox, but the Simple MAPI client cannot view messages in the outbox once they are placed there.

Note
The ability to access mail folders is available through MAPI OLE
Messaging. You'll learn how to use OLE Messaging in Chapter 8, "The
OLE Messaging Library."

Laying Out the Forms

The simple mail client project requires two forms. The first form (MAPIMAIN.FRM) is the main form in the application.
This form will consist of a single list box that shows all the messages in the user's e-mail inbox and a set of command
buttons that can be used to perform e-mail activities such as creating new messages, reading e-mail, deleting old
messages, saving messages to text files, and replying to existing messages. This form will also hold the MAPI controls
and a common dialog box control that will be used to save e-mail messages as text files. Refer to Figure 7.6 when laying
out the form.

Figure 7.6 : Laying out the e-mail forms.

Listing 7.12 shows the Visual Basic form code for MAPIMAIN.FRM. When you build the MAPIMAIN.FRM, be sure to
add the picture control first; then add a single command button to the form by selecting the command button from the
toolbox and drawing it onto the picture control rather than double-clicking the command button from the toolbox. By
drawing it on the picture control, you establish the command button as a child control of the picture box. Now, whenever
you move the picture box, the command button will move with it.

Listing 7.12. The MAPIMAIN.FRM layout code.

VERSION 4.00
Begin VB.Form MapiMain
Caption = "Form1"
ClientHeight = 1710
ClientLeft = 1875
ClientTop = 1725
ClientWidth = 6345
Height = 2115
Left = 1815
LinkTopic = "Form1"
ScaleHeight = 1710
ScaleWidth = 6345
Top = 1380
Width = 6465
Begin VB.ListBox List1
BeginProperty Font
name = "Courier"
charset = 0
weight = 400
size = 9.75
underline = 0 'False
italic = 0 'False
strikethrough = 0 'False
EndProperty
Height = 840
Left = 120
TabIndex = 1
Top = 540
Width = 4695
End
Begin VB.PictureBox Picture1
Align = 1 'Align Top
Height = 375
Left = 0
ScaleHeight = 315
ScaleWidth = 6285
TabIndex = 0
Top = 0
Width = 6345
Begin VB.CommandButton Command1
Caption = "Command1"
Height = 315
Index = 9
Left = 900
TabIndex = 11
Top = 0
Width = 375
End
Begin VB.CommandButton Command1
Caption = "Command1"
Height = 315
Index = 8
Left = 1380
TabIndex = 10
Top = 60
Width = 375
End
Begin VB.CommandButton Command1
Caption = "Command1"
Height = 315
Index = 7
Left = 1800
TabIndex = 9
Top = 60
Width = 375
End
Begin VB.CommandButton Command1
Caption = "Command1"
Height = 315
Index = 6
Left = 2280
TabIndex = 8
Top = 0
Width = 375
End
Begin VB.CommandButton Command1
Caption = "Command1"
Height = 315
Index = 5
Left = 2820
TabIndex = 7
Top = 0
Width = 375
End
Begin VB.CommandButton Command1
Caption = "Command1"
Height = 315
Index = 4
Left = 540
TabIndex = 6
Top = 0
Width = 375
End
Listing 7.12. continued
Begin VB.CommandButton Command1
Caption = "Command1"
Height = 315
Index = 3
Left = 3240
TabIndex = 5
Top = 0
Width = 375
End
Begin VB.CommandButton Command1
Caption = "Command1"
Height = 315
Index = 2
Left = 3720
TabIndex = 4
Top = 0
Width = 375
End
Begin VB.CommandButton Command1
Caption = "Command1"
Height = 315
Index = 1
Left = 4200
TabIndex = 3
Top = 0
Width = 375
End
Begin VB.CommandButton Command1
Caption = "Command1"
Height = 315
Index = 0
Left = 120
TabIndex = 2
Top = 0
Width = 375
End
End
Begin MSComDlg.CommonDialog CommonDialog1
Left = 5280
Top = 900
_Version = 65536
_ExtentX = 847
_ExtentY = 847
_StockProps = 0
End
Begin MSMAPI.MAPISession MAPISession1
Left = 5880
Top = 540
_Version = 65536
_ExtentX = 741
_ExtentY = 741
_StockProps = 0
End
Begin MSMAPI.MAPIMessages MAPIMessages1
Left = 5100
Top = 480
_Version = 65536
_ExtentX = 741
_ExtentY = 741
_StockProps = 0
End
End

Also, you'll notice that the command buttons are in the form of a control array. Control arrays are easier to work with
than a set of individually named controls. Control arrays also consume fewer Windows resources than a set of
individually named controls. You create the array by clicking on the first command button and setting its Index
property to 0. Then highlight the control and select Edit | Copy from the Visual Basic main menu. Select Edit |
Paste from the same menu. You'll see a dialog box asking you to confirm that you want to create a control array-click
Yes. Visual Basic will then place a copy of the control on the form. Continue cutting and pasting until you have ten
command buttons in the picture control.

The second form (MAPIREAD.FRM) will be used to display messages sent to you by other users. This form will have a
label control to display the e-mail header information, a text box control to display the actual body of the message, and a
single command button to close the form. Use the Visual Basic form code in Listing 7.13 and Figure 7.6 as a reference
when building the MAPIREAD.FRM.

Listing 7.13. The MAPIREAD.FRM control table.

Begin VB.Form MapiRead


Caption = "Form2"
ClientHeight = 4575
ClientLeft = 1140
ClientTop = 1515
ClientWidth = 6690
Height = 4980
Left = 1080
LinkTopic = "Form2"
ScaleHeight = 4575
ScaleWidth = 6690
Top = 1170
Width = 6810
Begin VB.CommandButton cmdClose
Caption = "&Close"
Height = 495
Left = 5340
TabIndex = 2
Top = 3900
Width = 1215
End
Begin VB.TextBox Text1
Height = 2415
Left = 120
MultiLine = -1 'True
ScrollBars = 2 'Vertical
TabIndex = 1
Listing 7.13. continued
Text = "MapiRead.frx":0000
Top = 1320
Width = 6435
End
Begin VB.Label Label1
BorderStyle = 1 'Fixed Single
Caption = "Label1"
BeginProperty Font
name = "MS LineDraw"
charset = 2
weight = 400
size = 9.75
underline = 0 'False
italic = 0 'False
strikethrough = 0 'False
EndProperty
Height = 1215
Left = 120
TabIndex = 0
Top = 60
Width = 6435
End
End

Coding the Main Support Routines

There are four support routines that you need to add to the MAPIMAIN.FRM form. The first routine, AdjustForm (see
Listing 7.14), will do all the work to set the proper size and location for all the controls on the form, including the ten
command buttons.

Listing 7.14. Coding the AdjustForm routine.

Public Sub AdjustForm()


'
' set tool bar buttons
'
Dim x As Integer
Dim cBtnCaption(9) As String
'
cBtnCaption(0) = "&Read"
cBtnCaption(1) = "&New"
cBtnCaption(2) = "&Save"
cBtnCaption(3) = "&Del"
cBtnCaption(4) = "&Addr"
cBtnCaption(5) = "&Fwd"
cBtnCaption(6) = "&Reply"
cBtnCaption(7) = "A&ll"
cBtnCaption(8) = "&InBox"
cBtnCaption(9) = "E&xit"
'
For x = 0 To 9
Command1(x).Caption = cBtnCaption(x)
Command1(x).Width = (Picture1.Width / 10) * 0.9
Command1(x).Height = 300
Command1(x).Top = 0
Command1(x).Left = (x * (Picture1.Width / 10))
Next x
'
Picture1.Height = 360
'
' set list box dimensions
'
List1.Left = 120
List1.Width = Me.Width - 360
List1.Top = Picture1.Height + 120
List1.Height = Me.Height - (List1.Top + 600)
'
End Sub

This routine is called at the very start of the program and also each time the user resizes the form during run-time. When
you add this routine to the Form_Resize event, all controls will be adjusted automatically each time the user changes
the size of the form.

The next support routine you need to add is the MAPIStart routine (see Listing 7.15). This is the routine that attempts
to log the user onto the mail server. Calling the MAPISession1.Signon method forces the mail server to display the
default logon screen for the e-mail system.

Listing 7.15. Coding the MAPIStart routine.

Public Sub MAPIStart()


'
' attempt to start a mapi session
'
On Error GoTo MAPIStartErr ' error trap
'
MAPISession1.SignOn ' start session
MAPIMessages1.SessionID = MAPISession1.SessionID ' pass ID
'
Exit Sub
'
MAPIStartErr:
MsgBox Error$, vbCritical, "Error " + Str(Err)
'
End Sub

Tip
Notice the use of the error-handling routine. You'll see these throughout the
application. Adding error handlers is always a good idea. Error handlers are
even more critical whenever your programs are calling for system services
like e-mail, since unexpected errors in the e-mail system can affect your
Visual Basic program, too.

Once your user has successfully logged onto the mail server, you need to check for any messages in the user's inbox. The
MAPIFetch routine (see Listing 7.16) does this. The MAPIMessages1.Fetch method brings all messages from the
inbox into the MAPI read buffer. You can then use a For...Next loop to "walk" through this buffer and read each
message. This program pulls the messages into the read buffer and then copies the message header information (message
subject, the sender's name, and the date and time the message was received) into a list box on the main form.

Listing 7.16. Coding the MAPIFetch routine.

Public Sub MAPIFetch()


'
' load all messages from 'inbox'
'
On Error GoTo MAPIFetchErr ' error trap
'
Dim x As Integer
Dim cLine As String
'
MAPIMessages1.Fetch ' get stuff from mail server
'
' now load into list box
'
List1.Clear
For x = 0 To MAPIMessages1.MsgCount - 1
MAPIMessages1.MsgIndex = x
cLine = MAPIMessages1.MsgSubject
cLine = cLine + Space(30)
cLine = Left(cLine, 30) + Space(2)
cLine = cLine + MAPIMessages1.MsgOrigDisplayName
cLine = cLine + Space(20)
cLine = Left(cLine, 52) + Space(2)
cLine = cLine + MAPIMessages1.MsgDateReceived
List1.AddItem cLine
Next x
'
Exit Sub
'
MAPIFetchErr:
MsgBox Error$, vbCritical, "Error " + Str(Err)
'
End Sub

The last support routine you need to add to the MAPIMAIN.FRM form is MAPISave (see Listing 7.17). This routine is
used to save selected messages to your local workstation for later use. The common dialog box control is used to provide
the standard save file dialog box. Once a valid name is given for the file, the routine writes out the message header
information followed by the actual message text. This is saved as an ASCII file that can be loaded by Notepad or any
standard text editor.

Listing 7.17. Coding the MAPISave routine.

Public Sub MAPISave()


'
' save the selected message as a text file
'
On Error GoTo MAPISaveErr ' error trap
'
' set dialog parms and show SaveAs box
'
CommonDialog1.Filter = "Text File|*.txt" ' file types to view/
save
CommonDialog1.DialogTitle = "Save Mail Message" ' dialog caption
CommonDialog1.filename = "mailmsg.txt" ' suggest savename
CommonDialog1.Flags = &H2 ' force overwrite confirmation
CommonDialog1.ShowSave ' pop up the dialog
'
' if user picked a real name, save the message
'
If Len(CommonDialog1.filename) > 0 Then
Open CommonDialog1.filename For Output As 1
Print #1, "DATE:" + Chr(9) + MAPIMessages1.MsgDateReceived
Print #1, "FROM:" + Chr(9) + MAPIMessages1.MsgOrigDisplayName
Print #1, "TO:" + Chr(9) + MAPIMessages1.RecipDisplayName
Print #1, "SUBJ:" + Chr(9) + MAPIMessages1.MsgSubject
Print #1, "" ' blank line
Print #1, MAPIMessages1.MsgNoteText
Close #1
End If
'
Exit Sub
'
MAPISaveErr:
MsgBox Error$, vbCritical, "Error " + Str(Err)
'
End Sub

Now that you have coded the support routines, you need to add code for four Visual Basic events on the MAPIMAIN.
FRM form.
Coding the MAPIMAIN.FRM Events

The MAPIMAIN.FRM form has only five events that require code. Four of them are described here. The last event is
described in the next section. The four events covered here are

● Form_Load
● Form_Unload
● Form_Resize
● List1_DblClick

The code for these events can be found in Listing 7.18.

Listing 7.18. Code for the Form and List1 events.

Private Sub Form_Load()


'
On Error GoTo FormLoadErr ' error trap
'
Me.Width = 9000 ' set starting width
Me.Height = 4500 ' set starting height
Me.Left = (Screen.Width - Me.Width) / 2
Me.Top = (Screen.Height - Me.Height) / 2
Me.Caption = "Simple Mail Client"
'
AdjustForm ' fix up display form
MAPIStart ' start MAPI Session
MAPIFetch ' get any new messages
'
Exit Sub
'
FormLoadErr:
MsgBox Error$, vbCritical, "Error " + Str(Err)
Unload Me
End Sub

Private Sub Form_Resize()


AdjustForm ' resize all controls as needed
End Sub

Private Sub Form_Unload(Cancel As Integer)


'
On Error Resume Next ' ignore error
MAPISession1.SignOff ' end mapi session
'
End Sub

Private Sub List1_DblClick()


'
' read the selected message
MAPIMessages1.MsgIndex = List1.ListIndex
MapiRead.Show vbModal
'
End Sub

The Form_Load event first sets the form size and location and then calls the support routines AdjustForm,
MAPIStart, and MAPIFetch. If any of these routines returns an error, the program is halted. The Form_Resize
event calls the AdjustForm routine. This automatically resizes all controls each time the user changes the size of the
form during run-time. The Form_Unload event invokes the MAPISession1.SignOff method to end the e-mail
session and log the user out of the message server. The List1_DblClick event gets the currently selected message
and calls the MAPIREAD.FRM form to display the e-mail note. You'll code the MAPIREAD.FRM form later on.

Coding the Main Button Bar

The last code routine needed for the MAPIMAIN.FRM form is the code used for the Command1_Click event (see
Listing 7.19). Since you built a command button array, all button clicks are sent to the same routine. The program will be
able to tell which button was pressed by checking the Index parameter passed to the Click routine.

Listing 7.19. Coding the Command1_Click event.

Private Sub Command1_Click(Index As Integer)


'
' main command loop
'
On Error GoTo CommandErr ' error trap
'
Dim nTemp As Integer
'
Select Case Index
Case Is = 0
' open the selected message
If List1.ListIndex <> -1 Then
MAPIMessages1.MsgIndex = List1.ListIndex
MapiRead.Show vbModal
Else
MsgBox "No Message Selected", vbCritical, "Read
Message"
End If
Case Is = 1
' start a new message
MAPIMessages1.Compose ' point to compose buffer
MAPIMessages1.Send True ' start new message dialog
Case Is = 2
' save the selected message
If List1.ListIndex <> -1 Then
MAPIMessages1.MsgIndex = List1.ListIndex
MAPISave
Else
MsgBox "No Message Selected", vbCritical, "Save
Message"
End If
Case Is = 3
' delete the selected message
If List1.ListIndex <> -1 Then
nTemp = MsgBox("Delete Selected Message?",
vbInformation + vbYesNo, ✆✡✣✩✲✣✛"Delete Message")
If nTemp = vbYes Then
MAPIMessages1.MsgIndex = List1.ListIndex
MAPIMessages1.Delete
MAPIFetch
End If
Else
MsgBox "No Message Selected", vbCritical, "Delete
Message"
End If
Case Is = 4
' open the address book
MAPIMessages1.Show ' show address book
Case Is = 5
Listing 7.19. continued
' forward the selected message
If List1.ListIndex <> -1 Then
MAPIMessages1.MsgIndex = List1.ListIndex
MAPIMessages1.Forward
MAPIMessages1.Send True
Else
MsgBox "No Message Selected", vbCritical, "Forward
Message"
End If
Case Is = 6
' reply to the originator of selected message
If List1.ListIndex <> -1 Then
MAPIMessages1.MsgIndex = List1.ListIndex
MAPIMessages1.Reply
MAPIMessages1.Send True
Else
MsgBox "No Message Selected", vbCritical, "Reply
Message"
End If
Case Is = 7
' reply to all of selected message
If List1.ListIndex <> -1 Then
MAPIMessages1.MsgIndex = List1.ListIndex
MAPIMessages1.ReplyAll
MAPIMessages1.Send True
Else
MsgBox "No Message Selected", vbCritical, "Reply All
Message"
End If
Case Is = 8
' check inbox for new messages
MAPIFetch ' call fetch routine
Case Is = 9
' exit program
nTemp = MsgBox("Exit Program?", vbInformation + vbYesNo,
"Exit")
If nTemp = vbYes Then
Unload Me
End If
End Select
'
Exit Sub
'
CommandErr:
MsgBox Error$, vbCritical, "Error " + Str(Err)
'
End Sub

Each time a user presses one of the command buttons, this routine will execute the desired function. For most routines,
the user must first select one of the messages in the inbox. If no message is selected, a dialog box pops up. Notice also
that the only function that will require much code is the message-reading routine. You need to provide your own MAPI
message reader (you'll use the MAPIREAD.FRM for that). All other e-mail services (New, Delete, Forward, Reply,
ReplyAll, and AddressBook) are all provided by the message server your user logged onto at the start of the
program.

Note
There is, of course, a downside to this "easy-code" access to MAPI services.
Simple MAPI does not allow you to read, write, or search the MAPI address
book via code. You can only gain access to the address book by starting the
address book dialog box with the MAPIMessages.Show method. Also,
you cannot see any existing MAPI storage folders except the inbox. This
means you cannot save messages to an existing folder, and you cannot read
old messages stored in other folders in the past. These limitations make the
Simple MAPI interface less than ideal for building robust MAPI clients.
However, Simple MAPI is very good for general access to MAPI messages
and for creating very simple e-mail interfaces.

Coding the Reader Form

You need to code the MAPIREAD.FRM form to complete the project. This form has two support routines and four events
that require code. The first support routine, AdjustReader, handles the resizing and locating of controls on the reader
form. The second support routine, LoadReader, loads the message from the read buffer onto the form for display. This
routine also creates a message header to display in the label control of the form. The code for these two support routines
is shown in Listing 7.20.

Listing 7.20. Code for the MAPIREAD.FRM support routines.

Public Sub AdjustReader()


'
' arrange controls on the form
'
On Error Resume Next ' ignore any errors
'
' locate & size header label
Label1.Top = 120
Label1.Left = 120
Label1.Width = Me.Width - 360
Label1.Height = Me.Height * 0.3
'
' locate & size close button
cmdClose.Top = Me.Height - 840
cmdClose.Left = Me.Width - 1440
cmdClose.Width = 1200
cmdClose.Height = 300
'
' locate & size message text box
Text1.Top = Label1.Height + 240
Text1.Left = 120
Text1.Width = Me.Width - 360
Text1.Height = cmdClose.Top - (Text1.Top + 120)
'
End Sub

Listing 7.20. continued


Public Sub LoadReader()
'
' fill in message header and body
'
Label1.Caption = ""
Label1.Caption = "DATE: " + MapiMain.MAPIMessages1.
MsgDateReceived + Chr(13)
Label1.Caption = Label1.Caption + "FROM: " + ✆✡✣✩✲✣✛MapiMain.
MAPIMessages1.MsgOrigDisplayName + Chr(13)
Label1.Caption = Label1.Caption + "TO: " + ✆✡✣✩✲✣✛MapiMain.
MAPIMessages1.RecipDisplayName + Chr(13)
Label1.Caption = Label1.Caption + "SUBJ: " + MapiMain.
MAPIMessages1.MsgSubject
'
Text1.Text = MapiMain.MAPIMessages1.MsgNoteText
End Sub

The Form_Load event contains code to set the form size and then call the AdjustReader and LoadReader
routines. The Form_Resize event calls the AdjustReader routine to resize the controls. The cmdClose_Click
event unloads the MAPIREAD.FRM form, and the Text1_KeyPress event contains a single line of code that tells
Visual Basic to ignore any key presses that occur within the text box control. This is a simple way to make a text box
control read-only. Each time a user presses a key, the KeyPress event will zero out the ASCII key value before it gets
typed to the screen. Listing 7.21 contains the code for the MAPIREAD.FRM form events.

Listing 7.21. Code for the MAPIREAD.FRM events.

Private Sub Form_Load()


'
Me.Width = 7500
Me.Height = 3750
Me.Left = (Screen.Width - Me.Width) / 2
Me.Top = (Screen.Height - Me.Height) / 2
Me.Caption = "Read Mail [" + MapiMain.MAPIMessages1.MsgSubject +
"]"
'
AdjustReader ' arrange controls on form
LoadReader ' fill in header and body
'
End Sub

Private Sub Form_Resize()


AdjustReader ' arrange controls on form
End Sub

Private Sub Text1_KeyPress(KeyAscii As Integer)


KeyAscii = 0 ' ignore all key presses
End Sub

Private Sub cmdClose_Click()


Unload Me
End Sub

Running the Simple Mail Client

After you add all the code, save the project (MAPICLN.VBP) and run it. When the program first starts, you'll see a
prompt to log onto your e-mail server. After you log on, you'll see the main form of the application (see Figure 7.7).

Figure 7.7 : The main form of the simple e-mail client.

This form shows all the e-mail messages that are currently in your inbox. Notice that you can resize the form, and all the
controls will adjust to the new form dimensions. If you double-click an item in the list box or select an item with a single
click and press the Read button, you'll see the MAPIREAD.FRM form with the message header and selected e-mail
message (see Figure 7.8).

Figure 7.8 : Displaying the message reader form.

If you press the New button, you'll see the default message compose dialog box. This box will look different depending
on the e-mail system you are using. Figure 7.9 shows how the Microsoft Exchange compose dialog box looks in
Windows 95.

Figure 7.9 : The Microsoft Exchange compose dialog box.

You can get direct access to the e-mail address book by clicking the Addr command button on the MAPIMAIN.FRM
form. Figure 7.10 shows what the Microsoft Exchange address book looks like in Windows 95.

Figure 7.10 : The Microsoft Exchange address book.

You can test the application by sending yourself e-mail and then using the various command buttons to forward, reply,
save, and delete e-mail messages. You now have a completed e-mail client application!

Additional Features

Even though this project is fairly complete, there are a few features you might consider adding to improve the program.
Some possible enhancements are

● Adding a print button to print the selected message. This involves adding a print button to the control button
array and setting up the common dialog box properties to access the printer.
● Adding the ability to read and save e-mail attachments. This involves adding the ability to collect and list
attachments to the MAPIREAD.FRM form. The Visual Basic text box will not allow you to render an icon
within the message body as is done by the Microsoft Mail and Windows Messaging clients. However, VB4-
32bit does contain a rich-text edit box that will allow you to render icons. If you wish, you can collect the
attachment properties and add them to a list box at the end of the MAPIREAD form, or to a list on another form
launched from MAPIREAD.FRM. After viewing the attachment list, you'll need to offer the user the chance to
save the attachments via the file options of the Visual Basic common dialog box control.
● Replacing the command buttons with icon buttons using the Sheridan 3-D command button that ships with the
Professional Edition of Visual Basic. If you are working in the 32-bit version of Visual Basic, use the new
Toolbar control instead.
● Adding a set of menu items to the forms to match the functions of the command buttons.

Summary

This chapter covered two main topics:

● The Visual Basic MAPI controls


● Building a Simple MAPI client application

You learned the properties and methods of the two Visual Basic MAPI controls. The MAPISession control is used to
gain access to MAPI service providers through the SignOn and SignOff methods. The MAPIMessages control is
used to read, create, delete, address, and send MAPI messages.

The Simple MAPI client detailed in this chapter showed you how to build a complete MAPI-enabled application with a
minimum of coding. This sample application shows how you can use the Visual Basic MAPI controls to create a fully
functional e-mail client that can read and delete incoming messages, compose new messages, address them, and send
them to their recipients.

You also learned that Simple MAPI services allow only limited access to the MAPI address book. You can search and
edit the address book only through the pre-defined MAPI.Show method. You cannot directly search for existing
addresses or add, edit, or delete addresses without using the address dialog box supplied by the MAPI service provider.

You also learned that Simple MAPI does not allow you to access any of the existing mail folders. You can only see the
inbox folder and its contents.

Now that you know how to use the Visual Basic MAPI controls to create a simple e-mail client, you're ready to tackle
OLE messaging. In the next chapter, you'll learn how to build a mailing list server application that is capable of
managing large mailing lists.
Chapter 6

Creating MAPI-Aware Applications

CONTENTS

● The Simple MAPI API Calls


❍ The User-Defined Types

❍ The API Functions

● Creating Mail-Aware Applications


❍ Creating QIKMAIL.XLS with Excel

❍ Adding MAPI Services to Existing Visual Basic 4.0 Programs

● Summary

The most basic MAPI service is the ability to provide a send feature to a program. Almost all Windows programs have a
print feature to send output to a print device. The send feature works basically the same way. It provides the user a way
to send output to some other e-mail address. Adding this most basic form of MAPI service to your Windows applications
makes it "MAPI-aware." MAPI-aware applications do not have e-mail services as a basic part of their functionality (that
is, an e-mail client) but provide it as an added feature. For example, most office suite applications (word processing,
spreadsheet, and so on) provide a send feature on the main menu of all their programs. Basically, whatever documents
you can create with the program can be sent to other locations using the mail services available on the network.

In this chapter, you'll learn how to make your programs MAPI-aware using the Simple MAPI API call set. This API set
provides very easy, very quick access to the most needed MAPI services. Another advantage of using the API set is that
it is available to any program that supports DLL calls. This means it is quite easy to add MAPI services to most any
Windows application.

In the first half of this chapter, you'll get a quick overview of the API calls themselves. You'll learn about the eleven API
calls and three user-defined types that comprise the Simple MAPI interface, and you'll build a set of examples that
illustrate the use of Simple MAPI services.

In the second half of the chapter, you'll create some real-world examples of Windows applications that have MAPI-
aware features. You'll create a quick spreadsheet routine that can send its results using MAPI. You'll also modify an
existing Visual Basic 4.0 program to add MAPI capabilities.

When you complete this chapter, you'll have a good understanding of the Microsoft MAPI API calls. You will also be
able to design and build applications that can use the MAPI interface to provide mail services from within your Visual
Basic programs.

The Simple MAPI API Calls

The Simple MAPI (SMAPI) calls allow you to add MAPI services to virtually any Windows program. While these calls
offer only the smallest set of MAPI services, they are still quite useful. In the next few sections, you'll learn about the
three user-defined structures needed to provide MAPI via the API calls, and you'll experiment with each of the API calls
themselves.

Note
All the SMAPI services for Visual Basic and VBA applications are
provided through the dynamic link library (DLL) called VBAMAP32.DLL
(or VBAMAPI.DLL for 16-bit plat-forms). If you do not already have this
DLL on your system, you can find it on the CD-ROM that ships with this
book.

Before you begin this part of the chapter, start Visual Basic 4.0 and begin a new project. Locate the VBAMAP32.BAS
module in the Chap06 folder and add that module to your project. This has all the API calls and structures defined
along with several constants and a helper error function.

Note
Throughout this chapter, you'll use the 32-bit version of the SMAPI set. A
16-bit version of the SMAPI calls can also be used. You'll find the 16-bit
version of the API calls in the VBAMAP16.BAS module on the CD-ROM.

The User-Defined Types

Simple MAPI defines three structures for passing information

● MAPIMessage-Contains all the information about a message packet, including originator, subject, text,
recipients, and attachments.
● MAPIRecip-Contains all the information about a message recipient, including name, address type, full address,
and unique entry ID.
● MAPIFile-Contains all the information about an attached file, including display name, operating system name,
and position in the message packet.

MAPIMessage

The MAPIMessage structure is used to hold all the vital information about a message packet. You will use this
structure to pass message data from your programs to the DLL and back. Table 6.1 shows the structure of the
MAPIMessage along with short descriptions for each element.

Table 6.1. The MAPIMessage structure.


Field Type Description
Reserved Long Reserved for future use. This field must be 0.
The subject text, limited to 256 characters or fewer.
(Messages saved using MAPISaveMail are not limited
Subject String
to 256 characters.) An empty string indicates no subject
text.
A string containing text in the message. An empty string
indicates no text. For inbound messages, each paragraph
is terminated with a carriage return-line feed pair
NoteText String (0x0d0a). For outbound messages, paragraphs can be
delimited with a carriage return, a line feed, or a carriage
return-line feed pair (0x0d, 0x0a, or 0x0d0a,
respectively).
A message type string used by applications other than
MessageType String interpersonal electronic mail. An empty string indicates
an interpersonal message (IPM) type.
A string indicating the date a message is received. The
DateReceived String format is YYYY/MM/DD HH:MM; hours are measured
on a 24-hour clock.
A string indicating the conversation thread ID to which
ConversationID String
this message belongs.
A bitmask of flags. Unused flags are reserved. Unused
flags must be 0 for outbound messages and are ignored
for inbound messages. The following flags are defined:
Flags Long
MAPI_RECEIPT_REQUESTED
MAPI_SENT
MAPI_UNREAD
A count of the recipient descriptor types pointed to by
RecipCount Long Recips. A value of 0 indicates that no recipients are
included.
A count of the file attachment descriptor types pointed to
FileCount Long by Files. A value of 0 indicates that no file attachments
are included.

Listing 6.1 shows how the MAPIMessage structure is built using Visual Basic 4.0.

Note
The user-defined types and API calls are all contained in the VBAMAP32.
BAS and VBAMAPI16.BAS modules on the CD-ROM. You do not have to
type these structures and API calls into your projects.

Listing 6.1. The MAPIMessage user-defined type.

'***************************************************
' MAPI Message holds information about a message
'***************************************************

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

MAPIRecip

The MAPIRecip structure holds all the important data related to a message recipient. Table 6.2 describes the structure,
and Listing 6.2 shows how it looks in VBA code.

Table 6.2. The MAPIRecip structure.


Field Type Description
Reserved Long Reserved for future use. This field must be 0.
Classifies the recipient of the message. (Different classes can be
RecipClass Long used to sort messages by the recipient class.) It can also contain
information about the originator of an inbound message.
Name String The name of the recipient that is displayed by Mail.
Provider-specific message delivery data. This can be used by
Address String the message system to identify recipients who are not in an
address list (one-off addresses).
EIDSize Long The size (in bytes) of the opaque binary data in EntryID.
A binary string used by Mail to efficiently specify the recipient.
Unlike the Address field, this data is opaque and is not
EntryID String
printable. Mail returns valid EntryIDs for recipients or
originators included in the address list.

Listing 6.2. The MAPIRecip user-defined type.

'************************************************
' MAPIRecip holds information about a message
' originator or recipient
'************************************************

Type MapiRecip
Reserved As Long
RecipClass As Long
Name As String
Address As String
EIDSize As Long
EntryID As String
End Type

MAPIFile
The last structure used by SMAPI is the MAPIFile structure. This user-defined type holds all the information about a
message attachment. Table 6.3 describes the structure, and Listing 6.3 shows the UDT definition.

Table 6.3. The MAPIFile structure.


Field Type Description
Reserved Long Reserved for future use. This field must be 0.
A bitmask of flags. Unused flags are reserved and must be
0. The following flags are defined:
MAPI_OLE
Flags Long MAPI_OLE_STATIC
MAPI_OLE is set if the attachment is an OLE object. If
MAPI_OLE_STATIC is also set, the attachment is a static
OLE object rather than an embedded OLE object.
An integer describing where the attachment should be
placed in the message body. Attachments replace the
character found at a certain position in the message body;
in other words, attachments replace the MapiMessage
Position Long
type field NoteText[Position]. Applications may
not place two attachments in the same location within a
message, and attachments may not be placed beyond the
end of the message body.
Field Type Description
The full pathname of the attached file. The file should be
PathName String
closed before this call is made.
The filename seen by the recipient. This name may differ
from the filename in PathName if temporary files are
being used. If FileName is empty, the filename from
FileName String
PathName is used. If the attachment is an OLE object,
FileName contains the class name of the object; for
example, "Microsoft Excel Worksheet."
A reserved descriptor that is used to indicate to the
recipient the type of the attached file. An empty string
FileType Long
indicates an unknown or operating system-determined file
type. Use 0 for this parameter at all times.

Listing 6.3. The MAPIFile user-defined type.

'******************************************************
' MapiFile holds information about file attachments
'******************************************************

Type MapiFile
Reserved As Long
Flags As Long
Position As Long
PathName As String
FileName As String
FileType As Long
End Type
These are the only three structures needed to establish MAPI services with the VBAMAPI DLLs. The next section
describes each of the API calls and constants and shows you examples of how to use them.

The API Functions

There are eleven SMAPI API calls. This set of calls provides access to the core MAPI services including

● Logging on and logging off MAPI sessions


● Gaining access to the MAPI address book
● Reading, sending, saving, and deleting MAPI Messages
● Performing address validations and lookups
● Handling binary message attachments

The next several sections describe each of the API calls and provide Visual Basic 4.0 examples of how to use them.

If you haven't already done so, start Visual Basic 4.0 and load the VBAMAP32.BAS module into your project. Listing
6.4 shows the complete set of API calls for SMAPI. You do not have to type this information into your project. You can
find this module in the Chap06 directory that was created when you installed the source code from the CD-ROM.

Listing 6.4. The Simple MAPI API call declarations.

'***************************
' FUncTION Declarations
'***************************

Declare Function MAPILogon Lib "VBAMAP32.DLL" Alias


"BMAPILogon" (ByVal UIParam&, ÂByVal User$, ByVal Password$, ByVal
Flags&, ByVal Reserved&, Session&) As Long

Declare Function MAPILogoff Lib "VBAMAP32.DLL" Alias


"BMAPILogoff" (ByVal Session&, ÂByVal UIParam&, ByVal Flags&, ByVal
Reserved&) As Long

Declare Function MAPIDetails Lib "VBAMAP32.DLL" Alias


"BMAPIDetails" (ByVal ÂSession&, ByVal UIParam&, Recipient As
MapiRecip, ByVal Flags&, ByVal Reserved&) ÂAs Long

Declare Function MAPIResolveName Lib "VBAMAP32.DLL" Alias


"BMAPIResolveName" (ByVal ÂSession&, ByVal UIParam&, ByVal UserName
$, ByVal Flags&, ByVal Reserved&, ÂRecipient As MapiRecip) As Long

Declare Function MAPISendDocuments Lib "VBAMAP32.DLL" Alias


"BMAPISendDocuments" Â(ByVal UIParam&, ByVal DelimStr$, ByVal
FilePaths$, ByVal FileNames$, ByVal ÂReserved&) As Long

Declare Function MAPIFindNext Lib "VBAMAP32.DLL" Alias


"BMAPIFindNext" (ByVal ÂSession&, ByVal UIParam&, ByVal MsgType$,
ByVal SeedMsgID$, ByVal Flag&, ByVal ÂReserved&, MsgID$) As Long

Declare Function MAPIDeleteMail Lib "VBAMAP32.DLL" Alias


"BMAPIDeleteMail" (ByVal ÂSession&, ByVal UIParam&, ByVal MsgID$,
ByVal Flags&, ByVal Reserved&) As Long
Declare Function MAPIAddress Lib "VBAMAP32.DLL" Alias
"BMAPIAddress" (ByVal ÂSession&, ByVal UIParam&, ByVal Caption$,
ByVal EditFields&, ByVal Label$, ÂRecipCount&, Recipients() As
MapiRecip, ByVal Flags&, ByVal Reserved&) As Long

Declare Function MAPISaveMail Lib "VBAMAP32.DLL" Alias


"BMAPISaveMail" (ByVal ÂSession&, ByVal UIParam&, Message As
MapiMessage, Recipient As MapiRecip, File As ÂMapiFile, ByVal
Reserved&, MsgID$) As Long

Declare Function MAPISendMail Lib "VBAMAP32.DLL" Alias


"BMAPISendMail" (ByVal ÂSession&, ByVal UIParam&, Message As
MapiMessage, Recipient As MapiRecip, File As ÂMapiFile, ByVal
Flags&, ByVal Reserved&) As Long

Declare Function MAPIReadMail Lib "VBAMAP32.DLL" Alias


"BMAPIReadMail" (ByVal ÂSession&, ByVal UIParam&, ByVal MsgID$,
ByVal Flags&, ByVal Reserved&, Message As ÂMapiMessage, Originator
As MapiRecip, recips() As MapiRecip, files() As MapiFile) ÂAs Long

There are also a number of CONSTANT declarations needed to make the API calls easier to work with. Listing 6.5 shows
the error constants and flag declarations used for SMAPI.

Listing 6.5. The SMAPI constants.

'**************************
' CONSTANT Declarations
'**************************
'

Global Const SUccESS_SUccESS = 0


Global Const MAPI_USER_ABORT = 1
Global Const MAPI_E_FAILURE = 2
Global Const MAPI_E_LOGIN_FAILURE = 3
Global Const MAPI_E_DISK_FULL = 4
Global Const MAPI_E_INSUFFICIENT_MEMORY = 5
Global Const MAPI_E_BLK_TOO_SMALL = 6
Global Const MAPI_E_TOO_MANY_SESSIONS = 8
Global Const MAPI_E_TOO_MANY_FILES = 9
Global Const MAPI_E_TOO_MANY_RECIPIENTS = 10
Global Const MAPI_E_ATTAchMENT_NOT_FOUND = 11
Global Const MAPI_E_ATTAchMENT_OPEN_FAILURE = 12
Global Const MAPI_E_ATTAchMENT_WRITE_FAILURE = 13
Global Const MAPI_E_UNKNOWN_RECIPIENT = 14
Global Const MAPI_E_BAD_RECIPTYPE = 15
Global Const MAPI_E_NO_MESSAGES = 16
Global Const MAPI_E_INVALID_MESSAGE = 17
Global Const MAPI_E_TEXT_TOO_LARGE = 18
Global Const MAPI_E_INVALID_SESSION = 19
Global Const MAPI_E_TYPE_NOT_SUPPORTED = 20
Global Const MAPI_E_AMBIGUOUS_RECIPIENT = 21
Global Const MAPI_E_MESSAGE_IN_USE = 22
Global Const MAPI_E_NETWORK_FAILURE = 23
Global Const MAPI_E_INVALID_EDITFIELDS = 24
Global Const MAPI_E_INVALID_RECIPS = 25
Global Const MAPI_E_NOT_SUPPORTED = 26

Global Const MAPI_E_NO_LIBRARY = 999


Global Const MAPI_E_INVALID_PARAMETER = 998

Global Const MAPI_ORIG = 0


Global Const MAPI_TO = 1
Global Const MAPI_cc = 2
Global Const MAPI_Bcc = 3

Global Const MAPI_UNREAD = 1


Global Const MAPI_RECEIPT_REQUESTED = 2
Global Const MAPI_SENT = 4

Listing 6.5. continued


'***********************
' FLAG Declarations
'***********************

Global Const MAPI_LOGON_UI = &H1


Global Const MAPI_NEW_SESSION = &H2
Global Const MAPI_DIALOG = &H8
Global Const MAPI_UNREAD_ONLY = &H20
Global Const MAPI_ENVELOPE_ONLY = &H40
Global Const MAPI_PEEK = &H80
Global Const MAPI_GUARANTEE_FIFO = &H100
Global Const MAPI_BODY_AS_FILE = &H200
Global Const MAPI_AB_NOMODIFY = &H400
Global Const MAPI_SUPPRESS_ATTAch = &H800
Global Const MAPI_FORCE_DOWNLOAD = &H1000

Global Const MAPI_OLE = &H1


Global Const MAPI_OLE_STATIC = &H2

These are all the basic tools needed to begin to write MAPI applications. The next section reviews each API call in
greater depth, including at least one coding example for each call.

Warning
In order for the 32-bit API calls to work, you must have the VBAMAP32.
DLL in your WINDOWS\SYSTEM folder. If you are using Visual Basic 4.0
on a 16-bit platform, you can load the VBAMAPI.BAS module and make
sure that the VBAMAPI.DLL is in your WINDOWS\SYSTEM folder.

MAPIErr-An Added Helper Function

All SMAPI calls return a status code (either SUccESS or some error). You should always check this value before
continuing on with your program. In order to make it easier to work with the SMAPI calls, you can add a helper function
that returns meaningful error messages for the established MAPI errors. Add a BAS module to your Visual Basic project
called MAPIERR.BAS and enter the code in Listing 6.6.
Listing 6.6. Adding the MAPIErr function.

Public Function MapiErr(lError) As String


'
' return displayable string for error
'
Dim cRtn As String
'
Select Case lError
Case MAPI_USER_ABORT ' 1
cRtn = "MAPI User Cancel"
Case MAPI_E_FAILURE ' 2
cRtn = "MAPI Failure"
Case MAPI_E_LOGIN_FAILURE ' 3
cRtn = "MAPI Login failure"
Case MAPI_E_DISK_FULL ' 4
cRtn = "MAPI Disk full"
Case MAPI_E_INSUFFICIENT_MEMORY ' 5
cRtn = "MAPI Insufficient memory"
Case MAPI_E_BLK_TOO_SMALL ' 6
cRtn = "MAPI Block too small"
Case MAPI_E_TOO_MANY_SESSIONS ' 8
cRtn = "MAPI Too many sessions"
Case MAPI_E_TOO_MANY_FILES ' 9
cRtn = "MAPI too many files"
Case MAPI_E_TOO_MANY_RECIPIENTS ' 10
cRtn = "MAPI Too many attachments"
Case MAPI_E_ATTAchMENT_NOT_FOUND ' 11
cRtn = "MAPI Attachment not found"
Case MAPI_E_ATTAchMENT_OPEN_FAILURE ' 12
cRtn = "MAPI Attachment open failure"
Case MAPI_E_ATTAchMENT_WRITE_FAILURE ' 13
cRtn = "MAPI Attachment Write Failure"
Case MAPI_E_UNKNOWN_RECIPIENT ' 14
cRtn = "MAPI Unknown recipient"
Case MAPI_E_BAD_RECIPTYPE ' 15
cRtn = "MAPI Bad recipient type"
Case MAPI_E_NO_MESSAGES ' 16
cRtn = "MAPI No messages"
Case MAPI_E_INVALID_MESSAGE ' 17
cRtn = "MAPI Invalid message"
Case MAPI_E_TEXT_TOO_LARGE ' 18
cRtn = "MAPI Text too large"
Case MAPI_E_INVALID_SESSION ' 19
cRtn = "MAPI Invalid session"
Case MAPI_E_TYPE_NOT_SUPPORTED ' 20
cRtn = "MAPI Type not supported"
Case MAPI_E_AMBIGUOUS_RECIPIENT ' 21
cRtn = "MAPI Ambiguous recipient"
Case MAPI_E_MESSAGE_IN_USE ' 22
cRtn = "MAPI Message in use"
Case MAPI_E_NETWORK_FAILURE ' 23
cRtn = "MAPI Network failure"
Case MAPI_E_INVALID_EDITFIELDS ' 24
cRtn = "MAPI Invalid edit fields"
Case MAPI_E_INVALID_RECIPS ' 25
cRtn = "MAPI Invalid Recipients"
Case MAPI_E_NOT_SUPPORTED ' 26
cRtn = "MAPI Not supported"
Case MAPI_E_NO_LIBRARY ' 999
cRtn = "MAPI No Library"
Case MAPI_E_INVALID_PARAMETER ' 998
cRtn = "MAPI Invalid parameter"
End Select
'
MapiErr = cRtn & " [" & CStr(lError) & "]"
'
End Function

Now you're ready to start coding your SMAPI examples.

MAPILogon and MAPILogOff

The MAPILogon and MAPILogOff functions are used to start and stop MAPI sessions.

Note
It is always a good idea to log off a session when you no longer need MAPI
services. Leaving an unused session open can slow your program and, if it's
left open after you exit, can lead to unexpected problems.

Table 6.4 shows the MAPILogon parameters along with their type and description.

Table 6.4. The MAPILogon parameters.

Parameter Type Description


The parent window handle for the dialog box. A value of 0
UIParam Long
specifies that any dialog box displayed is application modal.
A user account name. An empty string indicates that a sign-in
User String dialog box with an empty name field should be generated (if
the appropriate flag is set).
The user's MAPI password. An empty string indicates that a
sign-in dialog box with an empty password field should be
Password String
generated (if the appropriate flag is set) or that MAPI does
not expect a password.
A bitmask of flags. The following flags are defined:
MAPI_LOGON_UI=&H1
Flags Long
MAPI_NEW_SESSION=&H2
MAPI_FORCE-DOWNLOAD=&H1000
Reserved Long Reserved for future use. This parameter must be 0.
A unique session handle whose value is set by MAPI when
Session Long the MAPILogon call is successful. The session handle can
then be used in subsequent MAPI calls.

Table 6.5 shows the MAPILogOff parameters along with their type and description.

Table 6.5. The MAPILogOff parameters.

Parameter Type Description


A unique MAPI-assigned session handle whose value represents an
Session Long
already existing MAPI session.
The parent window handle for the dialog box. A value of 0
UIParam Long
specifies that any dialog box displayed is application modal.
Flags Long Reserved for future use. This parameter must be 0.
Reserved Long Reserved for future use. This parameter must be 0.

Add a new command button to your Visual Basic form. Set its Caption property to LogOn. Use Edit|Copy, Edit|
Paste to add a second command button as part of a control array. Set the second command button's caption to
LogOff. Then add the code in Listing 6.7 to the Command1_Click event.

Listing 6.7. Adding code to the Command1_Click event.

Private Sub Command1_Click(Index As Integer)


'
' handle button press
'
Select Case Index
Case 0 ' log on session
SMAPIStart
Case 1 ' log off session
SMAPIEnd
End Select
'
End Sub

Next add the following form-level declarations to the general declaration area of your form. You'll use these throughout
the project.

Option Explicit
'
Dim lReturn As Long ' return flag
Dim lSession As Long ' session handle
Dim udtMessage As MapiMessage ' message object
Dim udtRecip As MapiRecip ' recipient object
Dim udtRecips() As MapiRecip ' recipient collection
Dim udtFile As MapiFile ' attachment object
Dim udtFiles() As MapiFile ' attachment collection

Now add a new subroutine called SMAPIStart to the project, and enter the code shown in Listing 6.8.

Listing 6.8. Adding the SMAPIStart routine.

Public Sub SMAPIStart()


'
' start an SMAPI session
'
lReturn = MAPILogon(Me.hWnd, "", "", MAPI_LOGON_UI, 0, lSession)
If lReturn <> SUccESS_SUccESS Then
MAPIErr (lReturn)
End If
'
End Sub

Finally, add the SMAPIEnd routine, and enter the code shown in Listing 6.9.

Listing 6.9. Adding the SMAPIEnd routine.

Public Sub SMAPIEnd()


'
' end an SMAPI session
'
lReturn = MAPILogoff(lSession, Me.hWnd, 0, 0)
If lReturn <> SUccESS_SUccESS Then
MAPIErr (lReturn)
Else
MsgBox "MAPI Session Closed", vbInformation, "SMAPI LogOff"
End If
'
End Sub

Now save the form as SMAPI.FRM and the project as SMAPI.VBP. When you run the project, click the LogOn button
to bring up the logon dialog box (see Figure 6.1).

Figure 6.1 : The logon dialog box.

MAPIAddress

The MAPIAddress call produces the address book dialog box and allows users to add, edit, and delete records from the
address book. There are several flags you can use to control the address book dialog box behavior. Table 6.6 shows the
MAPIAddress call parameters and their type and description.

Table 6.6. The MAPIAddress parameters.


Parameter Type Description
A unique MAPI-assigned session handle whose value
represents a session with the messaging subsystem. The
session handle is returned by MAPILogon and
Session Long
invalidated by MAPILogoff. If the value is 0, MAPI
initiates a session from a system default session (if one
exists) or presents a sign-in dialog box.
The parent window handle for the dialog box. A value
UIParam Long of 0 specifies that any dialog box displayed is
application modal.
The caption of the address list dialog box. If this
Caption String parameter is an empty string, the default value "Address
Book" is used.
The number of edit controls that should be present in
the address list. The values 0 to 4 are valid. The edit
values are defined as:
ReadOnly(0)
EditFields String To(1)
cc(2)
Bcc(3)
If EditFields is 1 and more than one kind of entry
exists in Recips, Label is ignored.
A string used as an edit control label in the address list
Label String dialog box. This argument is ignored and should be an
empty string except when EditFields is 1.
The number of entries in Recipients. If
RecipCount Long
RecipCount is 0, Recipients is ignored.
The initial array of recipient entries to be used to
populate edit controls in the address list dialog box.
Recipients() MAPIRecip This array is re-dimensioned as necessary to
accommodate the entries made by the user in the
address list dialog box.
A bitmask of flags. Unspecified flags should always be
passed as 0. Undocumented flags are reserved. The
Flags Long following flags are defined:
MAPI_LOGON_UI=&H1
MAPI_NEW_SESSION=&H2
Reserved Long Reserved for future use. This parameter must be 0.

Now add a new button to the control array and set its caption to AddressBook. Then modify the Command1_Click
event to match the code in Listing 6.10.

Listing 6.10. Modifying the Command1_Click event.

Private Sub Command1_Click(Index As Integer)


'
' handle button press
'
Select Case Index
Case 0 ' log on session
SMAPIStart
Case 1 ' log off session
SMAPIEnd
Case 2 ' addressbook
AddressBook
End Select
'
End Sub

Next add the AddressBook subroutine and enter the code shown in Listing 6.11.

Listing 6.11. Adding the AddressBook routine.

Public Sub AddressBook()


'
' call up the address book
'
On Error Resume Next
'
lReturn = MAPIAddress(lSession, Me.hWnd, "SMAPI Address Book",
3, "", 0, ÂudtRecips(), 0, 0)
If lReturn <> SUccESS_SUccESS Then
MsgBox MAPIErr(lReturn)
End If
'
MsgBox "Selected Recipients: " & CStr(UBound(udtRecips) + 1),
vbInformation, Â"AddressBook"
'
End Sub

The AddressBook routine calls up the MAPI address book, passing a new dialog title, and enabling all possible
recipient class buttons (TO:, cc:, and Bcc:). The AddressBook function returns a collection of recipients.

Save and run the project. After clicking the AddressBook, you should see something like the screen in Figure 6.2.

Figure 6.2 : Viewing the address book.

MAPIResolveName

The MAPIResolveName function is used to look up and/or validate a recipient object. You can use this to retrieve the
complete e-mail address of a recipient and to present a dialog box to the user to resolve any unknown or ambiguous
names. The MAPIResolveName parameters are described in Table 6.7.
Table 6.7. The MAPIResolveName parameters.

Parameter Type Description


A unique, MAPI-assigned session handle whose value
represents a session with the messaging subsystem. The
session handle is returned by MAPILogon and invalidated
Session Long
by MAPILogoff. If the value is 0, MAPI initiates a session
from a system default session (if one exists) or by presenting
a sign-in dialog box.
The parent window handle for the dialog box. A value of 0
UIParam Long
specifies that any dialog box displayed is application modal.
UserName String A string containing the name to be resolved.
A bitmask of flags. The following flags are defined:
MAPI_LOGON_UI=&H1 (display logon dialog)
Flags Long MAPI_NEW_SESSION=&H2 (start new session)
MAPI_DIALOG=&H8 (show resolve dialog)
MAPI_AB_NOMODIFY=&H400 (no modify address book)
Reserved Long Reserved for future use. This parameter must be 0.
A recipient type set by MAPIResolveName if the
resolution results in a single match. The type contains the
Recipient MapiRecip recipient information of the resolved name. The descriptor
can then be used in calls to MAPISendMail,
MAPISaveMail, and MAPIAddress.

To test MAPIResolve, add a new button to the control array and set its caption to ResolveName. Then modify the
Command1_Click event to match the one in Listing 6.12.

Listing 6.12. Modifying the Command1_Click event.

Private Sub Command1_Click(Index As Integer)


'
' handle button press
'
Select Case Index
Case 0 ' log on session
SMAPIStart
Case 1 ' log off session
SMAPIEnd
Case 2 ' addressbook
AddressBook
Case 3 ' resolve name
ResolveName
End Select
'
End Sub

Now add the new ResolveName subroutine and enter the code in Listing 6.13.
Listing 6.13. Adding the ResolveName routine.

Public Sub ResolveName()


'
' test resolvename service
'
lReturn = MAPIResolveName(lSession, Me.hWnd, "MCA", MAPI_DIALOG,
0, udtRecip)
If lReturn <> SUccESS_SUccESS Then
MsgBox MAPIErr(lReturn)
End If
'
End Sub

If you run this routine and supply an ambiguous name, MAPI returns a dialog box asking you to resolve the differences
(see Figure 6.3).

Figure 6.3 : Viewing the ResolveName dialog box.

You can avoid viewing the dialog box (and just trap any error) by setting the Flag value to 0.

MAPIDetails

The MAPIDetails function returns a special dialog box that allows users to inspect and edit information about a single
recipient. You can use this in your programs to give users direct access to an address entry edit form. Table 6.8 shows
the MAPIDetails parameter list.

Table 6.8. The MAPIDetails parameters.


Parameter Type Description
A unique session handle whose value represents a
session with the messaging subsystem. Session handles
are returned by MAPILogon and invalidated by
Session Long
MAPILogoff. If the value is 0, MAPI sets up a
session from a system default session (if one exists) or
by presenting a sign-in dialog box.
The parent window handle for the dialog box. A value
UIParam Long of 0 specifies that any dialog box displayed is
application modal.
A recipient descriptor containing the entry whose
details are to be displayed. All fields of the
Recipient MAPIRecip MapiRecip type except EIDSize and EntryID are
ignored. If the field EIDSize is zero,
MAPI_E_AMBIG_RECIP is returned.
A bitmask of flags. Unspecified flags should always be
passed as 0. Undocumented flags are reserved. The
following flags are defined:
Flags Long MAPI_LOGON_UI=&H1 (force log on)
MAPI_NEW_SESSION=&H2 (force new session)
MAPI_AB_NOMODIFY=&H400 (no addrbook
changes)
Reserved Long Reserved for future use. This parameter must be 0.

Add a new button to the control array and set its caption to MAPIDetails. Then modify the Command1_Click
event to match the code in Listing 6.14.

Listing 6.14. Modifying the Command1_Click event.

Private Sub Command1_Click(Index As Integer)


'
' handle button press
'
Select Case Index
Case 0 ' log on session
SMAPIStart
Case 1 ' log off session
SMAPIEnd
Case 2 ' addressbook
AddressBook
Case 3 ' resolve name
ResolveName
Case 4 ' addr details
AddrDetails
End Select
'
End Sub

Now add the AddrDetails subroutine and fill it in with the code from Listing 6.15.

Listing 6.15. Adding the AddrDetails routine.

Public Sub AddrDetails()


'
' show the details of a valid address
'
lReturn = MAPIResolveName(lSession, Me.hWnd, "MCA/Win", 0, 0,
udtRecip)
If lReturn <> SUccESS_SUccESS Then
MsgBox MAPIErr(lReturn)
Exit Sub
End If
'
lReturn = MAPIDetails(lSession, Me.hWnd, udtRecip,
MAPI_LOGON_UI, 0)
If lReturn <> SUccESS_SUccESS Then
MsgBox MAPIErr(lReturn)
End If
'
End Sub

When you save and run the project, you will see an address entry dialog box appear when you press the AddrDetails
button (see Figure 6.4).

Figure 6.4 : Viewing the Address Book Details dialog box.

MAPISendDocuments

The MAPISendDocuments function is unique. You do not need to log in to MAPI before you call this function. As an
option, you can fill simple test strings with information for attachments and pass them in the call, too. When the call is
used, it brings up a full-featured compose dialog box that you can use to create and send an e-mail message. Table 6.9
shows the parameters for MAPISendDocuments.

Table 6.9. The MAPISendDocuments parameters.


Parameter Type Description
The parent window handle for the dialog box. A value of 0
UIParam Long
specifies that the Send Note dialog box is application modal.
A string containing the character used to delimit the names in the
DelimChar String FilePaths and FileNames parameters. This character
should not be used in filenames on your operating system.
A string containing the list of full paths (including drive letters)
for the attached files. The list is formed by concatenating
FilePaths String correctly formed file paths separated by the character specified in
the DelimChar parameter. The files specified in this parameter
are added to the message as file attachments.
A string containing the list of the original filenames (in 8.3
format) as they should be displayed in the message. When
FileNames String multiple names are specified, the list is formed by concatenating
the filenames separated by the character specified in the
DelimChar parameter.
Reserved Long Reserved for future use. This parameter must be 0.

Add another button to the control array and set its caption to SendDocs. Make sure your Command1_Click event
matches the one in Listing 6.16.

Listing 6.16. Modifying the Command1_Click event.

Private Sub Command1_Click(Index As Integer)


'
' handle button press
'
Select Case Index
Case 0 ' log on session
SMAPIStart
Case 1 ' log off session
SMAPIEnd
Case 2 ' addressbook
AddressBook
Case 3 ' resolve name
ResolveName
Case 4 ' addr details
AddrDetails
Case 5 ' send documents
SendDocs
End Select
'
End Sub

Now add the SendDocs subroutine and enter the code in Listing 6.17.

Listing 6.17. Adding the SendDocs routine.

Public Sub SendDocs()


'
' start senddocuments
'
Dim cFiles As String
Dim cNames As String
Dim cDelim As String
'
cDelim = ";"
cFiles = "C:\WINDOWS\WIN.INI;C:\CONFIG.SYS"
cNames = "WIN.INI;CONFIG.SYS"
'
lReturn = MAPISendDocuments(Me.hWnd, cDelim, cFiles, cNames, 0)
If lReturn <> SUccESS_SUccESS Then
MsgBox MAPIErr(lReturn)
End If
'
End Sub

Note
The code in Listing 6.17 refers to documents in the C:\WINDOWS
directory. If your Windows directory is not found at C:\WINDOWS, make
the needed changes to the code example.

Save and run the project. Remember that you do not have to press LogOn before you press the SendDocs button. The
MAPISendDocuments API will log you in automatically. Your screen should look similar to the one in Figure 6.5.

Figure 6.5 : The results of the SendDocs routine.

MAPISendMail

The MAPISendMail function is similar to MAPISendDocuments. The difference is that MAPISendMail uses the
MAPIRecip and MAPIFile structures to pass data to MAPI. The MAPISendMail function also allows you to
compose, address, and send a message without the use of any dialog boxes. Table 6.10 shows the MAPISendMail
parameters.

Table 6.10. The MAPISendMail parameters.


Parameter Type Description
An opaque session handle whose value represents a
session with the messaging subsystem. If the value
is 0, Mail sets up a session either from a system
Session Long
default session (if one exists) or by presenting a
sign-in dialog box. In all cases, Mail returns to its
state before the call.
The parent window handle for the dialog box. A
UIParam Long value of 0 specifies that any dialog box displayed is
application modal.
Message MapiMessage
An empty string indicates no subject text. Some
implementations may truncate subject lines that are
Subject
too long or that contain carriage returns, line feeds,
or other control characters.
An empty string indicates no text. Each paragraph
should be terminated with either a carriage return
Note Text (0x0d), a line feed (0x0a), or a carriage return-line
feed pair (0x0d0a). The implementation wraps lines
as appropriate.
A pointer to a string that is the message type. This
field is for use by applications other than
interpersonal mail (electronic forms, game message
Message Type
transmittal, and so on). For an interpersonal mail
message (IPM), specify an empty string for this
field.
The first element of an array of recipients. When
Recips MapiRecips
RecipCount is zero, this parameter is ignored.
The first element of an array of attachment files
written when the message is read. The number of
attachments per message may be limited in some
Files MapiFiles
systems. If the limit is exceeded, the error
MAPI_E_TOO_MANY_FILES is returned. When
FileCount is 0, this parameter is ignored.
A bitmask of flags. The following flags are defined:
MAPI_LOGON_UI=&H1 (force logon)
Flags Long
MAPI_NEW_SESSION=&H2 (use a new session)
MAPI_DIALOG=&H8 (show send dialog box)
Reserved Long Reserved for future use. This parameter must be 0.

Add another button to the control array and set its caption to SendDialog. Update the Command1_Click event to
match the code in Listing 6.18.

Listing 6.18. Updated Command1_Click event.

Private Sub Command1_Click(Index As Integer)


'
' handle button press
'
Select Case Index
Case 0 ' log on session
SMAPIStart
Case 1 ' log off session
SMAPIEnd
Case 2 ' addressbook
AddressBook
Case 3 ' resolve name
ResolveName
Case 4 ' addr details
AddrDetails
Case 5 ' send documents
SendDocs
Case 6 ' sendmail w/ dialog
SendDialog
End Select
'
End Sub

Now add the SendDialog subroutine and enter the code from Listing 6.19.

Listing 6.19. Adding the SendDialog routine.

Public Sub SendDialog()


'
' use sendmail w/ dialog
'
' first build message
udtMessage.Subject = "My Subject"
udtMessage.NoteText = "This was written using VB Code"
'
' next get valid recipient
lReturn = MAPIResolveName(lSession, Me.hWnd, "MCA/Win", 0, 0,
udtRecip)
If lReturn <> SUccESS_SUccESS Then
MsgBox MAPIErr(lReturn)
Exit Sub
End If
'
' now add recipient to the message
udtMessage.RecipCount = 1
udtMessage.FilesCount = 0
'
' now call sendmail w/ dialog
lReturn = MAPISendMail(lSession, Me.hWnd, udtMessage, udtRecip,
udtFile, ÂMAPI_DIALOG, 0)
If lReturn <> SUccESS_SUccESS Then
MsgBox MAPIErr(lReturn)
End If
'
End Sub

Save and run the project and press SendMail. You should see the Send Note dialog box on your screen, already filled
out and ready to go (see Figure 6.6).

Figure 6.6 : The completed Send Note dialog box.

You can use MAPISendMail to send a message without invoking the Send Note dialog box. To do this, modify the
MAPISendMail line by removing the MAPI_DIALOG constant and replacing it with 0.

MAPIFindNext and MAPIReadMail

The MAPIFindNext and MAPIReadMail functions are used to read messages that have been placed in the user's
InBox. The MAPIFindNext function is used to point to the next (or first) unread message in the InBox. The
MAPIReadMail function takes information received during the MAPIFindNext operation and retrieves the message
for viewing or other processing. Table 6.11 contains a list of MAPIFindNext parameters.

Table 6.11. The MAPIFindNext parameters.


Parameter Type Description
A unique session handle whose value represents
a session with the messaging subsystem.
Session handles are returned by MAPILogon
Session Long
and invalidated by MAPILogoff. If the value
is 0, MAPIFindNext returns
MAPI_E_INVALID_SESSION.
The parent window handle for the dialog box. A
UIParam Long value of 0 specifies that any dialog box
displayed is application modal.
A pointer to a string that is the message type. To
MessageType String specify an interpersonal mail message, use an
empty string, "".
A string that is the message identifier seed for
the request. If the identifier is an empty string,
SeedMessageID String
the first message matching the type specified in
the MessageType parameter is returned.
A bitmask of flags. Unspecified flags should
always be passed as 0. Undocumented flags are
reserved. The following flags are defined:
Flags Long MAPI_UNREAD_ONLY=&H20 (new messages
only)
MAPI_GUARANTEE_FIFO=&H100 (get oldest
first)
Reserved for future use. This parameter must be
Reserved long
0.
A unique variable-length string that is the
MessageID String
message identifier.

The MAPIReadMail function reads a message from the MAPI InBox and places it into the MAPIMessage structure.
You use MAPIReadMail to retrieve messages from the Inbox for review and subsequent action. Table 6.12 shows the
MAPIReadMail parameters.

Table 6.12. The MAPIReadMail parameters.


Parameter Type Description
A unique session handle whose value represents a
session with the messaging subsystem. If the value
Session Long
is 0, MAPIReadMail returns
MAPI_E_INVALID_SESSION.
The parent window handle for the dialog box. A
UIParam Long value of 0 specifies that any dialog box displayed
is application modal.
A variable-length string that is the message
identifier of the message to be read. Message IDs
MessageID String
can be obtained from the MAPIFindNext and
MAPISaveMail functions.
A bitmask of flags. Undocumented flags are
reserved. The following flags are defined:
MAPI_ENVELOPE_ONLY=&H40 (only get
headers)
Flags Long MAPI_SUPPRESS_ATTAch=&H800 (no
attachments)
MAPI_BODY_AS_FILE=&H200 (save body as
an attachment)
MAPI_PEEK=&H80 (don't mark message as read).
Reserved Long Reserved for future use. This parameter must be 0.
A type set by MAPIReadMail to a message
Message MAPIMessage
containing the message contents.
Originator MAPIRecip The originator of the message.
An array of recipients. This array is redimensioned
Recips ( ) MAPIRecip as necessary to accommodate the number of
recipients chosen by the user.
An array of attachment files written when the
message is read. When MAPIReadMail is called,
all message attachments are written to temporary
files. It is the caller's responsibility to delete these
files when no longer needed. When
Files ( ) MAPIFiles MAPI_ENVELOPE_ ONLY or
MAPI_SUPPRESS_ATTAch is set, no temporary
files are written and no temporary names are filled
into the file attachment descriptors. This array is re-
dimensioned as necessary to accommodate the
number of files attached by the user.

Now add another button to the control array and set its caption to ReadMail. Update the Command1_click event to
match Listing 6.20.

Listing 6.20. Updated Command1_Click event.

Private Sub Command1_Click(Index As Integer)


'
' handle button press
'
Select Case Index
Case 0 ' log on session
SMAPIStart
Case 1 ' log off session
SMAPIEnd
Case 2 ' addressbook
AddressBook
Case 3 ' resolve name
ResolveName
Case 4 ' addr details
AddrDetails
Case 5 ' send documents
SendDocs
Case 6 ' sendmail w/ dialog
SendDialog
Case 7 ' read mail
ReadMail
End Select
'
End Sub

Now add the ReadMail subroutine and enter the code from Listing 6.21.
Listing 6.21. Adding the ReadMail routine.

Public Sub ReadMail()


'
' read the first message in the Inbox
'
Dim cMsgID As String
Dim udtOrig As MapiRecip
Dim cMsg As String
Dim cMsgType As String
'
cMsgID = Space(256)
cMsgType = ""
'
lReturn = MAPIFindNext(lSession, Me.hWnd, cMsgType, cMsgID,
ÂMAPI_GUARANTEE_FIFO, 0, cMsgID)
If lReturn <> SUccESS_SUccESS Then
MsgBox MAPIErr(lReturn)
Exit Sub
End If
'
lReturn = MAPIReadMail(lSession, Me.hWnd, cMsgID, MAPI_PEEK, 0,
udtMessage, ÂudtOrig, udtRecips, udtFiles)
If lReturn <> SUccESS_SUccESS Then
MsgBox MAPIErr(lReturn)
Exit Sub
End If
'
cMsg = cMsg & "Message.Subject: " & udtMessage.Subject & Chr(13)
cMsg = cMsg & "Originator.Name: " & udtOrig.Name & Chr(13)
MsgBox cMsg, vbInformation, "ReadMail"
'
End Sub

Save and run the project. After you press the ReadMail button, you'll see a message box that shows the subject and
sender name (see Figure 6.7).

Figure 6.7 : The results of MAPIReadMail.

Even though MAPI services provide a built-in compose form (see SendMail), there is no built-in read form. You must
provide that through Visual Basic code.

Creating Mail-Aware Applications

The most basic form of MAPI applications is the mail-aware application. This is a program that offers mail services as
an added feature. A good example of this is the send option in Word, Excel, Access, and the other Microsoft Office
programs.

Making your programs mail-aware is about the same as making them aware of a printer. Usually, you can add a send
option to the main menu and treat mail output the same way you treat printer output. It is possible that you may have to
create an interim ASCII text file that you can then import into the message text using the clipboard or a few lines of
Visual Basic code. All in all, it's quite easy.

In this section, you'll develop a send feature for an Excel spreadsheet and then modify a Visual Basic project to add
MAPI-aware features to its menu.

Creating QIKMAIL.XLS with Excel

One of the quickest ways to add MAPI services to existing applications is through the use of the
MAPISendDocuments API call. This API requires no user-defined types and does not even require that you perform a
MAPI logon before attempting the send operation. All you need to do is add a MAPISendDocuments API declaration
and write a short routine to handle the selection of files for the send.

All the Microsoft Office applications allow you to build this kind of MAPI service into your spreadsheets, input forms,
and other projects. As an illustration, let's build a quick Excel spreadsheet that allows users to select from a friendly list
of accounting reports and then route those reports to someone else in the company.

Note
This example uses Excel 95 and requires the VBAMAP32.DLL be present in
the WINDOWS\SYSTEM folder. If you are running a 16-bit version of
Excel, you need to have the VBAMAPI.DLL installed in your WINDOWS
\SYSTEM folder, and you need to change the code referenced DLL to
match your version.

First, bring up Excel and start a new worksheet. Insert a code module (Insert | Macro | Module) and enter the
code shown in Listing 6.22.

Listing 6.22. Building a MAPI-aware Excel worksheet.

'
' declare API call for MAPI service
Declare Function MAPISendDocuments Lib "VBAMAP32.DLL" Alias
"BMAPISendDocuments"
(ByVal UIParam&, ByVal DelimStr$, ByVal FilePaths$, ByVal FileNames
$, ByVal Reserved&) As Long

'
' send file in active cell
'
Sub MAPIAware()
'
' send the selected file as attachments
'
Dim x As Long ' for return
Dim cFile As String
Dim cName As String
'
If ActiveCell = "" Then
MsgBox "Select a Report to Send"
Exit Sub
End If
'
cName = ActiveCell & ";"
cFile = ActiveCell.Offset(0, 1) & ";"
'
x = MAPISendDocuments(0, ";", cFile, cName, 0)
'
If x <> 0 Then
MsgBox "SendDocuments Error [" & Str(x) & "]"
End If
'
End Sub

Now select a new worksheet page and, using Figure 6.8 as a guide, enter the columns of information shown in Table
6.13.

Figure 6.8 : Laying out the QIKSEND.XLS worksheet.

Table 6.13. Column data for QIKSEND.XLS.


Column A Column B
Reports On File Hidden Column
Last Month's Sales SLS9602.XLS
3rd Qtr Budget BUDQ3.XLS
Monthly Cost Report COST9603.XLS
Department Fund Balances FUNDBAL.XLS

Add a button to the worksheet (selected from the Forms Toolbar) and connect the button to the MAPIAware routine you
built earlier. Set its caption to Send Report. Now hide column B so that users cannot see the exact operating system
filenames. To do this, click the column header, click the right mouse button, and select Hide. Finally, add a title to the
worksheet and save it as QIKSEND.XLS.

Now press Send Report. You'll be asked to log into the MAPI service; then MAPI will collect the file you selected
and present you with the Send Note dialog box with the attachment already in place (see Figure 6.9).

Figure 6.9 : Sending attachments from Excel.

That's all there is to it! You can create much more sophisticated forms and options, though-including building the entire
message, attaching the selected file, even routing directly to the person who is authorized to see the report. And all this
can be done without ever asking the user to do more than select the report and click Send!

Next you'll add MAPI services to a Visual Basic project.

Adding MAPI Services to Existing Visual Basic 4.0 Programs

For this example, you'll borrow the code from a sample program that ships with Visual Basic 4.0 Standard Edition: the
MDI sample application. This can be found in the SAMPLES\MDI folder within the main Visual Basic folder. If you do
not have this application or if you want to leave your copy alone, you can find another copy of it on the CD-ROM
shipped with this book.
This MDI application is a simple project that creates a multidocument editor that can save documents as ASCII files. To
make this system mail-aware will require a few lines of code behind a Send... menu item in one form.

Note
This project uses the MAPI controls that ship with Visual Basic 4.0. You'll
cover the MAPI controls in detail in the next chapter. For now, just
remember that the MAPI controls offer the same level of access to MAPI
services that the SMAPI API offers.

Load the MDI project and open the NOTEPAD form. First, add the MAPI Session and MAPI Message controls to
the bottom of the form. Next, add a separator line and a Send... menu item just after the Save As... menu item
(see Figure 6.10).

Figure 6.10. : Modifying the NOTEPAD form.

Finally, add the code in Listing 6.23 to the mnuFileSend_Click event. This is all the code you need to make this
application mail-aware.

Listing 6.23. MAPI Send routine for NOTEPAD.

Private Sub mnuFileSend_Click()


'
' log onto mail server
' start mail compose session
' log off mail server
'
On Error GoTo mnuFileSendErr
'
'
MAPISession1.DownloadMail = False ' dont check box
MAPISession1.SignOn ' start mail session
MAPIMessages1.SessionID = MAPISession1.SessionID
'
' handle sending msg
MAPIMessages1.Compose ' clear buffer
MAPIMessages1.MsgNoteText=Me.Text1 ' move text to message
MAPIMessages1.MsgSubject = Me.Caption ' get subject
MAPIMessages1.Send True ' show server dialog
'
MAPISession1.SignOff ' end mail session
'
Exit Sub
'
mnuFileSendErr:
MsgBox Error$, vbCritical, "Mail Send Error " + Str(Err)
On Error Resume Next
MAPISession1.SignOff
'
End Sub
Notice that the DownloadMail property is set to skip over checking the Inbox. The Inbox isn't important here; you
just want to send this document to someone via the mail server. The message text and message subject are set using data
from the input form.

Now save and run the project. Begin to edit a new document (see Figure 6.11).

Figure 6.11 : Using the MDI application.

When you are done editing the text, select the Send... menu item to send the document out. You'll see the default
compose form appear with the text and subject already supplied (see Figure 6.12).

Figure 6.12 : The MDI text ready to address and send.

There is a way to "bury" the mail features even deeper into this application. You really only need a way to tack on an
address to this document. Listing 6.24 shows a modified routine that calls only the address dialog box and then sends the
document out.

Note
In Listing 6.24, it is possible for the user to call the address book and exit it
without selecting a valid address. This will be reported as an error when the
Send method is invoked. To prevent the error, you could check the
RecipAddress property before invoking the Send method.

Listing 6.24. Sending NOTEPAD using Address Book.

Private Sub mnuFileSend_Click()


'
' log onto mail server
' start mail compose session
' log off mail server
'
On Error GoTo mnuFileSendErr
'
'
MAPISession1.DownloadMail = False ' dont check box
MAPISession1.SignOn ' start mail session
MAPIMessages1.SessionID = MAPISession1.SessionID
'
' handle sending msg
MAPIMessages1.Compose ' clear buffer
MAPIMessages1.MsgNoteText = Me.Text1 ' get text
MAPIMessages1.MsgSubject = Me.Caption ' get subject
MAPIMessages1.Show ' get address
MAPIMessages1.Send ' send without dialog
'
MAPISession1.SignOff ' end mail session
'
Exit Sub
'
mnuFileSendErr:
MsgBox Error$, vbCritical, "Mail Send Error " + Str(Err)
On Error Resume Next
MAPISession1.SignOff
'
End Sub

Save and run this project. When you select the Send... menu option, you now will see only the address dialog box
before the program sends your document out to the server.

As you can see, it's not at all difficult to add mail features to your existing applications. This technique of adding a send
option to the menu will work with just about any Windows application.

Summary

In this chapter, you learned how to make your programs MAPI-aware using the Simple MAPI API call set. This API set
provides very easy, very quick access to the most-needed MAPI services.

You learned that there are three user-defined types required to provide full SMAPI services:

● MAPIMessage-Contains all the information about a message packet, including originator, subject, text,
recipients, and attachments.
● MAPIRecip-Contains all the information about a message recipient, including name, address type, full address,
and unique entry ID.
● MAPIFile-Contains all the information about an attached file, including display name, operating system name,
and position in the message packet.

You also learned that there are eleven API calls in the SMAPI set. This set of calls provides access to the core MAPI
services, including

● Logging on and logging off MAPI sessions


● Gaining access to the MAPI address book
● Reading, sending, saving, and deleting MAPI messages
● Performing address validations and lookups
● Handling binary message attachments

You also discovered that the MAPISendDocuments API call is the only MAPI call that requires no use of user-
defined types to pass data via MAPI. This API call is very useful for adding quick MAPI support to existing
applications.

In the second half of the chapter, you used SMAPI to add send features to an Excel worksheet (using the
MAPISendDocuments API). You also modified an existing Visual Basic 4.0 project by adding a Send... menu
option to the form.

In the next chapter, you'll get an in-depth look at the Visual Basic MAPI controls, and in the process you'll build a fully
functional e-mail client application that you can use to read and write all your MAPI messages.
Chapter 5

Using the Microsoft Exchange Forms Designer

CONTENTS

● Introduction
● What Is the Microsoft Exchange Forms Designer?
❍ EFD Design Wizards

❍ The QuickHelp Feature

❍ Extending EFD with Visual Basic

● Using the Microsoft Exchange Forms Designer to Create a Custom Form


❍ Using the Microsoft Exchange Forms Designer Wizard

❍ Modifying the Job Request Form

❍ Completing the Job Request Form Fields

❍ Setting Form and Window Properties of the Job Request Form

❍ Adding Online Help to the Job Request Form

❍ Installing the Job Request Form

❍ Testing the Job Request Form

❍ More Forms Designing Techniques

● Designing Microsoft Exchange Folders


❍ Creating and Managing Folder Views

❍ Installing Forms in Folders

● Summary

Introduction

One of the quickest ways to develop MAPI applications is to use the Microsoft Exchange Forms Designer kit. This tool
ships with the Microsoft Exchange Server and includes a GUI form designer tool, sample templates, design wizards, and
an installation wizard. The Microsoft Exchange Forms Designer (called the EFD) generates Visual Basic 4.0 code. Once
the forms are generated, you can also use Visual Basic 4.0 to modify and enhance the forms.

Note
For those who do not own a copy of Visual Basic 4.0, the Microsoft
Exchange Forms Designer includes a version of the Visual Basic 4.0 16-bit
compiler.

To get the most out of this chapter, you should have access to a copy of the Microsoft Exchange Forms Designer on your
machine. You do not have to be linked to a Microsoft Exchange Server to complete the project in this chapter. If you do
not have a copy of the Microsoft Exchange Forms Designer, you can still get a lot out of this chapter. The concepts and
techniques discussed here apply to any project that uses Microsoft Exchange as a message platform. The last section of
the chapter focuses on folder views. You do not need the Microsoft Exchange Forms Designer to complete the exercises
in that section of the chapter.

You can use the EFD to develop two different types of forms:

● Send forms-These are forms used to send information from one location to the next. This is, in effect, a
formatted e-mail message.
● Post forms-These are forms used to place information into a particular folder. This is an application designed to
control the content of bulletin board messages to be viewed by several people.

You can also use the EFD to design folder views. Folder views are rules that control just how a folder appears to the
users. By setting values such as Sort Order, Message Grouping, and Message Filtering, you can present
folder contents in ways that reflect users' needs and highlight the most important aspects of the message collection.

When you complete this chapter, you'll know how to design, code, test, and install customized forms and folders using
the Microsoft Exchange Forms Designer. You'll learn how to use the EFD to create a Send form and a Post form. You'll
also create several new folders with custom views. Finally, you'll learn how to link customer forms to folders.

What Is the Microsoft Exchange Forms Designer?

The Microsoft Exchange Forms Designer is a development tool that is a part of Microsoft Exchange Server. The EFD is
a complete design tool for the creation and management of customized electronic message applications. You can design
forms that perform various tasks, including forms that

● Originate new messages


● Respond to existing messages
● Are read-only
● Are pre-addressed to one or more users
● Are posted for public viewing
● Allow for anonymous postings (no Sender address)

The Microsoft Exchange Forms Designer uses the Visual Basic development environment. If you are familiar with
Visual Basic or Microsoft Access, you'll have no trouble learning to use the Microsoft Exchange Forms Designer. Even
if you have not had a lot of experience with Visual Basic or Access, you'll find the EFD environment easy to work with.

EFD Design Wizards

Most of the form design work involves drag-and-drop operations to add fields to a form. When you use the EFD wizards,
toolbars, and menus, most of the basic message fields (To, Cc, Subject, and so on) are automatically added to your
forms. You can add custom controls such as labels, text boxes, list and combo boxes, check boxes and radio buttons, and
even tabs, frames, and picture boxes. One of the controls available with the Microsoft Exchange Forms Designer is a 16-
bit version of the rich-text control. This allows users to select fonts, type sizes, and colors within an editable text box.

The QuickHelp Feature

Another very handy feature of the Microsoft Exchange Forms Designer is the ability to add field-level and form-level
help to the project without having to create a WinHelp file. The EFD's QuickHelp allows you to enter help information
for each control on the form and for the form itself. You can create a message that appears on the status bar at the bottom
of the form. You can also create a message window that acts as context-sensitive help whenever the user presses the f1
key. And if you are really serious, the EFD allows you to enter help context IDs that link to standard WinHelp files.

Extending EFD with Visual Basic


It is also possible to use the Microsoft Exchange Forms Designer to generate an initial form and then use Visual Basic to
modify the form, for example, by

● Adding new controls


● Modifying the form layout or backgrounds
● Linking the form to existing databases
● Adding calculation fields to the form
● Adding the capability to launch executable programs from the form
● Linking the form to an existing WinHelp file

Although it is possible to use Visual Basic alone to design and implement Microsoft Exchange forms, the EFD provides
several advantages over "pure" Visual Basic. With the Microsoft Exchange Forms Designer, you get a tool that handles
most of the drudgery of linking message fields to form controls. The EFD helps you establish a consistent look and feel
for all your forms. The EFD also walks you through the installation process, which involves creating a custom message
type, registering that message type with Microsoft Exchange, and creating a configuration file to link the form to
Microsoft Exchange.

Using the Microsoft Exchange Forms Designer to Create a Custom Form

In this section, you'll use the Microsoft Exchange Forms Designer to create a job request form to initiate requests to have
maintenance jobs completed in a workplace. This will be a single-window Send form (addressed to a user). After you
build the form, you'll install it into your personal forms library for use at any time.

Using the Microsoft Exchange Forms Designer Wizard

The easiest way to start developing forms with the Microsoft Exchange Forms Designer is to use the Forms Designer
wizard. The wizard will take you through the initial steps in creating an electronic form. Once you answer all the
wizard's questions, you'll see the EFD build a basic form for your use. You can then use the EFD to modify the project
before saving and installing the new application.

If you haven't already done so, start up the Microsoft Exchange Forms Designer. You can do this from the Microsoft
Exchange program group. To do this, press the Start button on the Windows 95 task bar. Then select Programs |
Microsoft Exchange | Microsoft Exchange Forms Designer. You can also start the EFD directly
from Microsoft Exchange. To do this, start Microsoft Exchange and log in to your e-mail system. Then select Tools
| Application Design | Forms Designer... (see Figure 5.1).

Figure 5.1 : Selecting the EFD from the Windows Messaging client.

Tip
Starting the EFD from the Microsoft Exchange menu takes more memory.
On some systems, it may seem a bit slower than starting EFD from the
program menu. However, when you're developing an EFD form, it's really
handy to have Microsoft Exchange up and running at the same time. That
way you can easily switch between design and test mode while you debug
your EFD application.

Creating Your Form

The first screen you'll see is the Forms Designer Wizard. It asks whether you want to begin a new project using the
wizard, load a template form, or open an existing form (see Figure 5.2).

Figure 5.2 : Starting the Microsoft Exchange Forms Designer Wizard.

For now, select the Form Template Wizard option and press Next to continue.

Where Will Your Information Go?

The wizard asks whether you are designing a Send form or a Post form (see Figure 5.3).

Figure 5.3 : Creating a Send form or a Post form.

Send forms are used to send messages directly to one or more users. Send forms have a field on the form for the "To"
and "Cc" fields of a message. Post messages are sent to a folder, not a person, and therefore do not have a "To" or a "Cc"
field on them. For our example job request form, you want to use a Send form. This will make sure that the form is sent
directly to a person. Select Send and press Next.

How Will Your Form Be Used?

The wizard next asks whether you are creating a form to send information to someone or a form to respond to an existing
EFD form (see Figure 5.4).

Figure 5.4 : Creating a Send-or Response-type form.

The first option allows you to create an "initiating" form. The second option allows you to create a response form. For
now, select the Send option and press the Next button.

Warning
Don't confuse the Send option on this screen with the previous screen
where you were asked if you wanted to create a Send or a Post form. This
screen is really asking you to describe the action of your form-send or
respond. The previous screen asked about the destination of your form-a
person or a group.

One Window or Two?

The wizard now asks whether you want your form to have one or two windows (see
Figure 5.5).

Figure 5.5 : Specify a one-window or two-window form.

You use single-window forms when you want to allow the reader to be able to edit the same fields filled out by the
sender. A good example would be a form that you send to people with information for their editing and final approval.

You use the two-window form when you do not want to allow readers to alter the data on the form that they read. When
you select a two-window option, the EFD creates a compose form and a read form. The compose form appears when a
user first creates a form to send to someone. When the recipient opens the two-window form, only the read form appears.
This form has the same controls as the compose form, but the fields are all read-only.
For this example, select the one-window option and then press Next.

Setting the Form Name and Description

Finally, the Forms Designer Wizard asks you to supply the name and general description of your form (see Figure 5.6).

Figure 5.6 : Setting the form name and description.

The name you enter appears in list boxes (and in some cases on menus) within the Windows Messaging client. The
description you enter appears in information dialog boxes that users can view when selecting Microsoft Exchange forms
from their client interface. For now, enter the information contained in Table 5.1.

Table 5.1. Job request form name and description.


Property Setting
Name Job Request Form
Description Use this form to initiate a job request to the maintenance department.

After you supply the form name and description, press Next. The wizard displays a final screen telling you that you
have completed the wizard steps (see Figure 5.7).

Figure 5.7 : The final screen of the Forms Designer Wizard.

At this point you could press the Back button to return to previous screens and make any changes needed. When you are
sure that all screens have been completed properly, select Finish to close the wizard.

Your form should look like the one in Figure 5.8.

Figure 5.8 : The results of the Forms Designer Wizard.

You now have a basic electronic form ready for final modification and use. You can see that the top part of the form has
been filled in with the Date and From fields. These will be filled in automatically when you first execute the completed
form. You'll also see the To, Cc, and Subject fields. These fields will be filled in by the person executing the form.

The rest of the form has been left blank. It will contain application-specific controls and information. In the next few
sections, you'll add labels, input boxes, list controls, and a picture control to the form.

Before going further, save this project as JOBREQ.EFP.

Modifying the Job Request Form

Now that you have the basic electronic form built, it's time to add the fields needed to collect and display specific data.
To do this, you add controls for new fields and set the field properties. After adding all the needed fields, you set a few
form-level properties, add some help, and you're ready to install and test your form.

Adding a Field to the Job Request Form

It's very easy to add fields to the form. All you do is click once on the toolbox object you want to use, then move your
mouse to the desired location on the form, and click once again to drop the object onto the form.
Warning
If you're used to Visual Basic, you'll discover that the method for dragging
and dropping form objects is slightly different here. You do not paint the
objects onto the EFD form as you do in Visual Basic. Here you just click,
point, and click.

As a test, select the Entry Field object (called a text box in Visual Basic) and drop it onto the body of the form.

Note
You'll notice that you cannot use the EFD to place standard form objects in
the header area of the form. You can, however, place one of the MAPI
fields (From, Date, To, Cc, Bcc, and Subject) on the header.

Notice that the control is placed on the form along with an associated label control (see
Figure 5.9).

Figure 5.9 : Adding on Entry Field object to the form.

You can click on the label in design mode to edit the contents. You can also use the anchors on the object to move or
resize it as needed. It is also possible to "unlink" the caption and input control by selecting the large square on the upper
left of the input control and moving it independently of the caption.

Tip
Deleting the caption from the input form will also delete the input control
itself. You can use the General tab of the Field Properties dialog box
(double-click the control) to remove the caption. Locate the Position
drop-down list and set its value to None.

Now that you've added a field to the form, you need to adjust several of the field's properties. In the next section, you'll
learn how to do this using the Field Properties dialog box.

Using the Field Properties Dialog Box

The Field Properties dialog box gives you access to several properties of the field object (see Figure 5.10).

Figure 5.10 : Viewing the Field Properties dialog box.

This dialog has three tabs:

● General-Use this tab to set the name, caption, and position of the control on the form. You can also use the tab
to establish the control's locked, hidden, and required status and to enter the field-level help.
● Format-Use this tab to set the font, size, color, alignment, and other formatting properties of the control. Note
that you must use this same tab to set properties for both input control and the caption.
● Initial Value-Use this tab to enter the default value for this control. The contents of this tab depend on the type
of control. List and combo boxes allow you to enter multiple items, text boxes allow you to enter straight text,
picture boxes allow you to load a graphic image from the disk, and so on.

Use Table 5.2 as a guide in setting the field properties of the text box field on your job request form.

Table 5.2. Setting field properties of the text box control.


Tab Property Setting
General Reference Name ContactPhone
Column Name Contact Phone
Field Caption Contact Phone:
Required (Checked)
Format Maximum Characters (Checked) 80
Initial Value Initial Text XXX-XXXX

After setting the properties on the three tabs, select Close to save the information.

Completing the Job Request Form Fields

You need to add several fields to the job request form before it is complete. Now that you have an idea of how to add
fields and set their properties, use the information in Table 5.3 and Figure 5.11 to add, size, and locate the remaining
fields on the form.

Figure 5.11 : Laying out the Job request form.

Tip
The area of the EFD form where you place your controls is scrollable. It is
very easy to lose one or more fields due to unexpected scrolling when you
place a control on the form. To make it easy to see where things are on the
form, you need to turn on the scroll bars. Select View | Show Scroll
Bars from the main menu. If you do not want users to see these at runtime,
turn them off before you install the form.

Note
Be sure to use a complete path/directory name for the Picture Field control
in Table 5.3. You can find the Chap05 directory under the main directory
created when you installed the source code from the CD-ROM that ships
with the book.

After adding all the fields and setting their properties, save the project (JOBREQ.EFP) before continuing with the next
step.

Setting Form and Window Properties of the Job Request Form

There are several form- and window-level properties that you can set for Microsoft Exchange forms. These settings
affect the way your form looks and behaves once it's up and running under Microsoft Exchange.

First, select View | Window Properties from the main menu. Then set the properties using the information in
Table 5.4.

Table 5.3. Control properties for the job request form.


Control Property Tab Property Setting
Entry Field General Reference Name ContactPhone
Column Name Contact Phone
Field Caption Contact Phone:
Required (Checked)
Left 1290
Top 90
Width 2400
Height 300
Maximum
Format (Checked) 80
Characters
Initial
Initial Text XXX-XXXX
Value
ComboBox Field General Reference Name JobType
Column Name Job Type
Field Caption Job Type:
Required (Checked)
Left 1290
Top 545
Width 2400
Height 300
Format Style DropDown List
Initial
List Values Cleaning
Value
Construction
General
Plumbing
Frame Control General Reference Name Priority
Column Name Priority
Field Caption Priority:
Required (Checked)
Left 120
Top 1155
Width 1050
Height 1545
OptionButton Field General Reference Name Priority_1
Field Caption High
Left 120
Top 240
Width 900
Height 300
Initial
Unselected (checked)
Value
OptionButton Field General Reference Name Priority_2
Field Caption Medium
Left 120
Top 560
Width 900
Height 300
Initial
Selected (checked)
Value
OptionButton Field General Reference Name Priority_3
Field Caption Low
Left 120
Top 880
Width 900
Height 300
Initial
Unselected (checked)
Value
OptionButton Field General Reference Name Department
Column Name Department
Field Caption Department:
Caption Position Top
Left 1350
Top 1260
Width 2415
Height 1570
Required (checked)
Initial
List Values Administration
Value
Customer Service
Production
Repairs
Sales
Check Box Field General Reference Name Affects Production
Column Name Affects Production
Left 3945
Top 120
Width 1770
Height 300
Caption: Affects Production?
Initial
Unchecked (checked)
Value
Entry Field General Reference Name Description
Column Name Description
Left 3945
Top 900
Width 2265
Height 1785
Caption Description:
Caption Position Top
Required (checked)
Allow Multiple
Format (checked)
Lines
Vertical Scroll
(checked)
Bars
Initial Describe the nature of the
Initial Text
Values problem
Picture Field General Reference Name Logo
Omit this field (checked)
Left 6360
Top 60
Width 1620
Height 2640
Format Stretch Picture (checked)
Initial
Picture chap05\efd\clipbord.wmf
Value

Table 5.4. Setting the window properties of the job request form.
Dialog Tab Property Setting
General Window Name JobRequestWindow
Window Caption Job Request Form
Fields in Tab Order MAPI_To
MAPI_Subject
ContactPhone
JobType
Priority_1
Priority_2
Priority_3
Department
AffectsProduction
Description
Format Maximize Button (off)
Minimize Button (off)
ToolBar (off)
Formatting Toolbar (off)
Status Bar (on)
Window Sizing Options Fixed Size

Next you need to set the form-level properties. Select View | Form Properties from the main menu. Refer to
Table 5.5 for the proper settings.

Table 5.5. Setting the form properties of the job request form.
Tab Property Setting
General Form Display Name Job Request Form
Version 1
Number 1
Item Type IPM.JobRequest

You will note that the first time you bring up a new form, the Item Type property is set to a long string of letters and
numbers. This is a GUID (guaranteed unique ID). Microsoft Exchange uses this ID value internally to identify the form.
The value you enter here does not have to be this cryptic. It is a good idea to enter a value that will mean something to
you and others in your organization. It is, however, important that you keep this name unique.

Save this project again (JOBREQ.EFP) before you go on to your last development step-adding help.

Adding Online Help to the Job Request Form

It is very easy to add online help to your electronic forms. The Microsoft Exchange Forms Designer has a built-in
QuickHelp feature that lets you build tooltips and pop-up help boxes at the field, window, and form levels. You can even
add notes to the design-time version of the form for tracking development issues.

Adding Designer Notes

First, let's add a few notes to the design-time form. Select Help | Designer Notes... from the main menu to
bring up the Designer Notes screen (see Figure 5.12).

Figure 5.12 : Viewing the Designer Notes screen.

Enter a short comment about the form, the date, and the author. The information you enter here is stored with the project
and will be available each time you load the project into the Microsoft Exchange Forms Designer. Notice that this is a
rich-text box. You can set the font type, size, and color at any time.

Adding Form-Level Help


You can also have a help pop-up at the form level. To do this, bring up the Form Properties page (select View |
Form Properties | General Tab) and press the Form Help... button. You'll see a dialog box like the one
in Figure 5.13.

Figure 5.13 : Adding form-level help.

Notice that you can select No Help, QuickHelp, or enter a context ID for a standard WinHelp file. For now, enter a
short comment into the QuickHelp pop-up box and press Close to save the form. You can also set the Windows
Caption by moving the cursor up into the title bar of the sample help window and typing a caption.

Adding Window-Level Help

You can enter help at the window level, too. This is most useful when you have a project with multiple windows. For
now, select the Window Properties dialog box (View | Window Properties | General) and press the
Window Help... button. Your screen will look like the one in Figure 5.14.

Figure 5.14 : Viewing the window-level help dialog box.

Notice that you have an additional control on this dialog box. If you have multiple windows in your project, you can use
the drop-down list control to select each window and enter unique help information.

Adding Field-Level Help

Finally, you can also enter help information at the field level. Double-click a field object or select View | Field
Properties | General Tab to bring up the Field Properties dialog box. Then press the Field Help... button
to view the help dialog box (see Figure 5.15).

Figure 5.15 : Viewing the Field Help for Users dialog box.

Notice that there are now two controls at the top of the help dialog box. The drop-down list can be used to select the field
for which you want to create a help topic. The Status Bar control lets you enter a short help line that will appear at the
bottom of the form as you select each field. Of course, the QuickHelp box contains the help information that will appear
in a pop-up box if you press f1 at run-time while a field is in focus.

Enter QuickHelp information for several fields and then save the project. Save your project as JOBREQ.EFP before
continuing with the last step-installing and testing your new form.

Installing the Job Request Form

After you have completed the development phase of your Microsoft Exchange form, you need to run the Install
routine from the Microsoft Exchange Forms Designer. This routine

● Saves the project


● Generates Visual Basic 4.0 code from the project
● Loads Visual Basic 4.0 (16-bit version) and compiles the form
● Prompts you for a Microsoft Exchange forms library in which to install the new form
● Prompts you for a final set of Form Properties to help Microsoft Exchange categorize your form
● Registers the form with Microsoft Exchange and closes Microsoft Exchange
● Returns you to the Microsoft Exchange Forms Designer

This entire process may take awhile, depending on the size of your project and your hardware configuration. If you do
not have the project loaded now, open the Microsoft Exchange Forms Designer and load the JOBREQ.EFP project.

Select File | Install... from the main menu. You'll see a small dialog box telling you that the Microsoft
Exchange Forms Designer is generating Visual Basic code. Then you'll see Visual Basic 4.0 load and compile the
project.

After Visual Basic finishes, you'll see a dialog box that asks you where you want to install the form (see Figure 5.16).

Figure 5.16 : Selecting a forms library for installation.

Select Personal Forms Library for now. This will install the form on your workstation. Once you have tested it
thoroughly, you can re-install the form on a network location to allow others to use the form.

After selecting a forms library, you'll be asked to fill in a few more questions about the form (see Figure 5.17). The
information in these fields is used by Microsoft Exchange to categorize your form. Forms are sorted and grouped to
make them easier to locate and use.

Figure 5.17 : Supplying the form properties for Microsoft Exchange.

For now, enter your initials for the first category and Help for the second category. Enter your initials again as the
contact person. Notice that several of the fields you set in design mode appear here, too.

Your new Microsoft Exchange form is now installed. Exit the Microsoft Exchange Forms Designer and switch to your
Windows Messaging client so that you can start testing your new form.

Testing the Job Request Form

Once you have installed the form, you can switch to Microsoft Exchange and run it. In the previous step, you installed
the form in your personal forms library. In order to start an instance of the form, you need to launch your Windows
Messaging client and select Compose | New Forms... from the main menu. You'll see a dialog box that lists all
the forms you have in your personal library (see Figure 5.18).

Figure 5.18 : Viewing your personal forms library.

Select the job request form from the list to launch an instance of the form. You'll see the form appear with several fields
already filled in with suggested entries (see Figure 5.19).

Figure 5.19 : Viewing the job request form at run-time.

Complete the form and send it to yourself. Then check your inbox for the arrival of the message.

Tip
If your server is slow in returning your form to you, select Tools |
Deliver Now Using... | Microsoft Exchange or Tools |
Deliver Now Using... | Microsoft Mail if you are running a
standalone version of Microsoft Exchange for Windows 95.

When you open the message, you'll see that it appears in the same electronic form that it was sent in. If you select
Compose | Reply from the main menu of the form, you'll see your form automatically convert the data on the
application into a text message (see Figure 5.20).

Figure 5.20 : The reply gets converted into a text message.

You can now fill out a response to the request and return it to the person who sent you the note (in this case, yourself!).

More Forms Designing Techniques

You can use the Microsoft Exchange Forms Designer to create a response form that reads information from the job
request form and includes that data automatically on the response form. You can also create Post forms that are not
addressed to users, but to folders. These Post forms help you control discussion groups and other public data sharing in
your organization.

The Microsoft Exchange Forms Designer ships with several example projects that illustrate using multiwindow forms
and using several forms together to create a set of send/respond forms. Check out the Templates folder and the Samples
folder for more examples of Microsoft Exchange forms development.

In the next section, you'll learn how to create folder views and then how to install an electronic form in a folder.

Designing Microsoft Exchange Folders

Another very easy and powerful way to create custom MAPI interfaces is to use the Windows Messaging client's
capabilities to create and control folder views. Folder views are an excellent way to set up customized views of the
message base. You can create folder views that show and group messages according to their subject. You can also create
views that show only selected items in the folder based on subject, sender, or several other criteria.

In effect, you can use folder views to narrow the scope of your incoming messages. This is especially handy in large
organizations where you get a lot of information and must focus on the most important messages first.

In this section, you'll learn how to create a new discussion folder and establish its view properties. You'll then write
several messages to test the folder view. Finally, you'll install a form in the folder. This way every time someone wants
to post a message to the folder, he or she can use the custom form.

Note
You need to have access to the Windows Messaging client that ships with
the Microsoft Exchange Server. That version has the capability to create
folder views. You do not, however, have to be connected to the Microsoft
Exchange Server to create folders and views. This example uses personal
folders and views.

Creating and Managing Folder Views

Creating folder views is the easiest way to build custom discussion applications using Microsoft Exchange. You can
create a specialized view, test it on your personal system, and then publish it for others to use. You can even use the
Microsoft Exchange Forms Designer to create custom posting forms for use in the discussion forum. These forms can be
installed in the folder itself and will be available to anyone who enters the forum.

There are just a few steps to creating a custom folder and view:
● Select a message store and add a new folder.
● Create a folder view by setting the Sort, Group, and Filter options.
● Test the view by sending/posting messages to the folder.
● Install a new or existing form in the folder.
● Test the form by using it to send messages.
● Share the folder and place the form in the folder library.

Creating a New Folder

The first step is to create a new folder. If you haven't done so yet, start the Windows Messaging client and select your
personal message store. Point to the top level of the message store and add a new folder called "MAPI Discussion
Group" (see Figure 5.21).

Figure 5.21 : Adding the Mapi Discussion Group Folder.

Once the folder is created, it is a good idea to set its Description property. This description will help everyone know
what kind of information is supposed to be in the folder. It is also a good idea to add the name of the person who created
the folder and the date it was first created. To set the Description property of a new folder, select the folder, and
then select File | Properties from the main menu. Enter a general description of the folder along with a creation
date and the author's initials (see Figure 5.22).

Figure 5.22 : Setting the folder's Description property.

Once you fill in the description, press the Apply button or the OK button to update the folder properties.

Setting the Folder View

The folder view controls just which messages are seen by the user, along with the order in which they are seen and what
message columns appear in the summary listing. There are four main steps to setting a folder view:

● Select the columns to be displayed in the listing window.


● Select the grouping rules.
● Select the sorting rules.
● Set the filtering rules.

Once you have set all the view properties, you can test the folder view by posting messages to the folder.

First, highlight the MAPI Discussion Group folder again and select File | Properties to bring up the Folder
Properties page. This time select the View tab. Be sure the Folder Views radio button is selected and then press the
New button to create a new view.

The first step is to name the folder view. Enter "Group By Conversation Topic."

Tip
It is a good habit to name views based on grouping and sorting criteria. That
way, as you build up a library of folder views, it is easy to remember how
the view affects the message displays.
The next step is to select the columns to be displayed in the list window. Press the Columns button to bring up the
Columns dialog box (see Figure 5.23).

Figure 5.23 : Selecting the columns for the list window.

Locate and select the Conversation Topic column from the list box on the left. Add this column to the very top of the list
box on the right. Now delete the From column from the list box on the right. You can save this selection by pressing OK.

Tip
You may have noticed that there is a small input box at the lower right-hand
side of the dialog box. This input box allows you to set the display width of
each column in the list. The Conversation Topic column defaults to one
character. You do not need to change this. When messages are grouped
together, their topic will appear as a header within the listing. Adding the
conversation topic to the listing would only clutter the display.

Next you need to set the grouping value. Press the Group By button to bring up the Group By dialog box (see Figure
5.24).

Figure 5.24 : Setting the grouping criteria.

In the topmost combo box, select Conversation Topic and select the Ascending radio button. Notice that you
can set the sort order by activating the combo box at the bottom of the Group By dialog box. It should be set to
Received, Ascending. If it is not, set it now, and then press OK to update the grouping and sorting properties.

Now select the Filter button from the Views dialog box. This calls up the first of two filter dialogs boxes (see Figure
5.25).

Figure 5.25 : The first Filter dialog box.

Through this dialog box, you can set criteria for limiting the display of messages:

● From-Use this to limit the view to only those messages that are from a specified user or distribution list
(defined in the MAPI Address Book). Notice that you can list more than one address on this line.
● Sent To-Use this to limit the view to only those messages that you sent to a specified user or distribution list.
You can include more than one address on this line.
● Sent directly to me-Check this box if you want to see only messages addressed directly to you.
● Copied (Cc) to me-Check this box if you want to see only messages that have you on the Cc: line.
● Subject-When you enter text here, the Windows Messaging client displays only messages that have that text in
the Subject line. You cannot use wildcards.
● Message body-When you enter text here, the Windows Messaging client displays only messages that have that
text somewhere in the message body.

You can also set additional filter criteria by pressing the Advanced button on the Filter dialog box (see Figure 5.26).

Figure 5.26 : Viewing the Advanced Filter dialog box.

A second form appears through which you can set filters based on file size, date ranges, read/unread flags, and level of
importance. You can also set filters based on forms ("show me only Job Request Forms") or document statistics ("show
me only Word documents").
For now, select Cancel from the Advanced Filter dialog box and select Clear All at the main Filter dialog box to
turn off all filtering.

Warning
It is important to remember that setting message filters affects only the
display of message folders, not their content. If you have a folder that filters
all but a few messages, you should keep in mind that there may actually be
hundreds of messages in the folder, it's just that you can see only a few.
Message filtering will not remove messages from a folder; it just hides
them.

Under the Views tab of the Folder Properties dialog box, press Apply to update the properties and then select OK to
exit the dialog box. You have created a custom view for your folder. Now it's time to test the view.

Testing the Folder View

To test the new folder view you just created, you need to add a few messages. For now, you can add these messages and
replies yourself. Once you are sure the view is working properly, you can place this view in a new or existing public
folder and share it with other users.

Since this view was built as a discussion forum, you'll use a Post form instead of the standard Send form. To create a
new post in the MAPI Discussion Group, highlight the folder and select Compose | New Post in this
Folder... from the main menu. This will bring up the default posting form (see Figure 5.27).

Figure 5.27 : Using the default posting form.

After filling out the form and posting it, check the folder to see how the view works. You'll see that a conversation topic
has been started and that your first message appears underneath the topic. You can click on the topic to expand the
message listing and then select the message to read it. When you create a reply to the message, it is added to the folder,
under the same topic. Figure 5.28 shows you how an extended set of messages appears in a discussion folder.

Figure 5.28 : Viewing multiple messages in a discussion folder.

Note
Although the folder we created is a discussion folder, you are not restricted
to using Post messages while you are in the folder. If you wish, you can use
Send forms to reply directly to a user's inbox. This is a way to start private
conversations with people you first meet in a public discussion group.

Although there's a lot more to creating and managing folders and views, you should now have a pretty good idea of the
possibilities. If you are interested in learning more about Microsoft Exchange folders and views, check out the
documentation that ships with the Microsoft Exchange Server.

Installing Forms in Folders

The final step in this chapter is to install a custom form in a folder. You can install forms in personal forms libraries or
folder forms libraries. The advantage of installing forms in the personal forms library is that it is available to the users no
matter what other folder they are looking in. All they need to do is select Compose | New Forms... to locate the
form installed on the local workstation.

The advantage of installing forms in a folder form library is that each time the user enters the folder, that custom form
appears on the Compose menu. This makes it easier to find and more likely that it will be used.

For this example, you'll install the job request form in the MAPI Discussion Group folder.

Note
It actually doesn't matter what folder you use for this exercise.

First, start up the Windows Messaging client and select the target folder (the MAPI Discussion Group folder). Then
select File | Properties and select the Forms tab. This brings up the Forms page. Press the Manage button to
display the Forms Manager dialog box, and press the Set button above the list box on the left to bring up the Set
Library To dialog box (see Figure 5.29).

Figure 5.29 : Setting the forms library.

You've seen this dialog box before. It's the same one you used when you used the Microsoft Exchange Forms Designer
to install the job request form. Select the Personal Forms Library radio button; then press OK to return to the
Forms Manager.

You'll now see one or more topics and forms in the list box on the left side of the form (see Figure 5.30).

Figure 5.30 : Selecting a form to install.

Locate and select the job request form in the list box on the left. Then press Copy to copy the form to the list box on the
right. You have just associated the job request form with the MAPI Discussion Group folder. Now it's time to install the
form in the folder's library.

When you press Install..., Microsoft Exchange asks you for the location of the configuration file (.CFG) for the
form. This is stored in the subfolder in which you created the job request form. Locate the folder where you saved the
JOBREQ.EFP file. You'll see a subfolder called JOBREQ.VB. This is the folder that has the Visual Basic source code
and the JOBREQ.CFG file (see Figure 5.31).

Figure 5.31 : Locating and selecting the JOBREQ.CFG. file.

After locating and selecting the JOBREQ.CFG file, click OK to load the configuration file. Microsoft Exchange will then
show you the Form Properties dialog box from the Microsoft Exchange Forms Designer. Select a category and a
subcategory, and enter your initials as the contact name for the form (see Figure 5.32).

Figure 5.32 : Filling out the Form Properties dialog box.

Now you can press Close on the Forms Manager dialog box and press OK on the Form Properties tab page. You have
just installed your form into the MAPI Discussion Group folder.

Select Compose from the main menu, and you'll see an item at the bottom of the menu list called "New Job Request
Form" (see Figure 5.33).
Figure 5.33 : Selecting the new job request form the menu.

You can start from here and run the job request form just like any other form. If you move to another folder, however,
you will not see the form on the menu. It appears on a folder's menu only if it has been installed in that folder's form
library.

Summary

In this chapter, you learned how to use the Microsoft Exchange Forms Designer kit that ships with Microsoft Exchange
Server. You learned how to design, code, test, and install custom message forms for use at individual workstations or
over a large network.

The Microsoft Exchange Forms Designer discussion in this chapter covered

● Using the Microsoft Exchange Forms Designer


● Creating custom forms
● Testing and compiling custom forms
● Installing custom forms

You also learned how to set up Microsoft Exchange folders for use with custom forms,
including

● Creating new folders


● Setting folder views including sorting, filtering, and grouping messages
● Installing custom forms in folders

In the next chapter, you'll learn how to use the Messaging API to create MAPI-aware windows applications that can read
and write MAPI messages from outside Windows Messaging clients.
Chapter 9

Creating a MAPI Mailing List Manager with the OLE Messaging Library

CONTENTS

● Introduction
❍ Laying Out the MLM Form

❍ Coding the Support Routines

❍ Coding the Edit Routines

❍ Coding the MAPIStart and MAPIEnd routines

❍ Coding the SendMail Routines

❍ Coding the Inbox Routines

❍ Adding New Subscribers

❍ Dropping Subscribers

❍ Listing Archives

❍ Sending Requested Archives

● Running the MLM Application


❍ Testing the MLM Application

● Summary

Introduction

After reviewing the OLE Messaging Library objects in Chapter 8, "The OLE Messaging Library," you're now ready to
build a MAPI application for Win95 and Visual Basic 4.0 that uses these objects.

The Mailing List Manager application lets users define and manage automated mailing lists from the client desktop.
Messages can be distributed within a single server or across the Internet (depending on the available transports at the
desktop). All access to MAPI services will be performed through the OLE Message objects.

The key features of the Mailing List Manager (MLM) are:

● Automatically scans incoming mail for MLM messages.


● Automatically adds and drops subscribers from the group.
● Automatically distributes messages based on current date.
● Allows subscribers to request copies of archived messages.
● Allows subscribers to request a default FAQ file.
● Most features are modifiable using control files.

The MLM application allows individuals to create a set of text files to be distributed to a
controlled list of users at specified times. This project has only one simple form and several support routines. All
application rules are stored in a set of ASCII control files similar to INI/registry settings. These control files can be
changed by the list manager to determine how the mailing list operates and what features are available to subscribers.
Once you complete this application, you'll be able to establish and manage one or more one-way mailing lists from your
own desktop. These mailing lists can be limited to your current attached server or cross over any transport out onto the
Internet (depending on the transports installed on your desktop).

Laying Out the MLM Form

The MLM application has only one form. Since the primary purpose of the application is to manage automated lists,
there is very little needed in the way of a GUI interface. MLM has a set of command buttons to initiate specific tasks and
a single scrollable text box to show progress as the application processes incoming and outgoing mail.

Start a new Visual Basic project and lay out the MLM form. Refer to Table 9.1 and Figure 9.1 for details on the size and
position of the controls on the form.

Figure 9.1 : Laying out the MLM form.

Table 9.1. MLM form controls.


Control Property Setting
Form Name frmMLM
Height 5325
Left 1140
Top 1230
Width 7380
Text Box Name Text1
Font 8pt MS Sans Serif, Bold
Height 4635
Left 120
MultiLine -1 ' True
Scrollbars 2 ' Vertical
Top 120
Width 5115
Command Button Name Command1
Caption Edit &Controls
Font 8pt MS Sans Serif, Bold
Height 450
Index 0
Left 5400
Top 120
Width 1800
Command Button Name Command1
Caption Edit &Subs
Font 8pt MS Sans Serif, Bold
Height 450
Index 1
Left 5400
Top 720
Width 1800
Command Button Name Command1
Caption Edit &Sked
Font 8pt MS Sans Serif, Bold
Height 450
Index 2
Left 5400
Top 1320
Width 1800
Command Button Name Command1
Caption Edit &Archive
Font 8pt MS Sans Serif, Bold
Height 450
Index 3
Left 5400
Top 1920
Width 1800
Command Button Name Command1
Caption Read &Inbox
Font 8pt MS Sans Serif, Bold
Height 450
Index 4
Left 5400
Top 2520
Width 1800
Command Button Name Command1
Caption Send &Mail
Font 8pt MS Sans Serif, Bold
Height 450
Index 5
Left 5400
Top 3120
Width 1800
Command Button Name Command1
Caption &Read && Send
Font 8pt MS Sans Serif, Bold
Height 450
Index 6
Left 5400
Top 3720
Width 1800
Command Button Name Command1
Caption E&xit
Font 8pt MS Sans Serif, Bold
Height 450
Index 7
Left 5400
Top 4320
Width 1800

Note that the layout table calls for a control array of command buttons. Add a single button to the form, set its properties
(including the Index property), and then use the Edit | Copy, Edit | Paste menu options to make the additional
copies of the button. You can then edit the Caption properties as needed.

After you lay out the form, you need to add a handful of variables to the general declaration area, a few routines to
handle the standard form events, and one routine to respond to the command-button actions. Listing 9.1 shows the code
that declares the form-level variables for this project. Add this code to the general declaration area of the form.

Listing 9.1. Adding the form-level variables.

Option Explicit

Dim objMAPISession As Object ' session object


Dim cMLMFile As String ' control file
Dim cCtlName() As String ' control names
Dim cCtlValue() As String ' control values
Dim bErr As Boolean ' error flag
Dim nWidth As Integer ' form width
Dim nHeight As Integer ' form height
Dim EOL As String ' end of line

Next, add the code in Listing 9.2 to the Form_Load event. This code centers the form and then stores its current width
and height. This information will be used to prevent users from resizing the form at run-time.

Listing 9.2. Adding code to the Form_Load event.

Private Sub Form_Load()


'
Me.Top = (Screen.Height - Me.Height) / 2
Me.Left = (Screen.Width - Me.Width) / 2
'
nWidth = Me.Width
nHeight = Me.Height
EOL = (Chr(13) & Chr(10))
Text1 = ""
Me.Caption = "Mailing List Manager [" & cMLMFile & "]"
'
' check for passed parm
'
If Len(Command$) <> 0 Then
cMLMFile = Command$
Else
cMLMFile = "mlm.txt"
End If
'
End Sub

You'll also notice that the Form_Load event checks for a parameter passed on the command line at startup. This will be
used to determine what set of control files will be used for each run of the MLM application (you'll see more about this
later).

Next, add the code in Listing 9.3 to the Form_Resize event. This code uses the values established in the Form_Load
event to keep forcing the form back to its original size whenever a user tries to adjust the form size. Note, however, that
this routine will allow users to minimize the form.

Listing 9.3. Adding code to the Form_Resize event.

Private Sub Form_Resize()


'
If Me.WindowState <> vbMinimized Then
Me.Width = nWidth
Me.Height = nHeight
End If
'
End Sub

You also need to add code behind the command-button control array. Listing 9.4 contains the code that should be placed
in the Command1_Click event. This routine just calls a set of custom subroutines that you'll add a bit later in the
chapter.

Listing 9.4. Adding the code in the Command1_Click event.

Private Sub Command1_Click(Index As Integer)


'
' handle user clicks
'
Select Case Index
Case 0 ' edit controls
ControlsEdit
Case 1 ' edit subs
SubEdit
Case 2 ' edit schedule
SkedEdit
Case 3 ' edit archive
ArchEdit
Case 4 ' read inbox
ReadInbox
Case 5 ' send mail
SendMail
Case 6 ' read & send
ReadInbox
SendMail
Case 7 ' exit
Unload Me
End Select
'
End Sub

One more line of code is needed to complete this section. The text box control should be a read-only form object. By
adding the following line of code to the Text1_KeyPress event, you can trick Visual Basic into ignoring any
keyboard input performed within the text box control.

Private Sub Text1_KeyPress(KeyAscii As Integer)


KeyAscii = 0
End Sub

That's the code needed to support form events and controls. Save this form as MLM.FRM and save the project as MLM.
VBP. In the next section you'll add a series of simple support routines to the project.

Coding the Support Routines

Now you'll add a few support routines that are called frequently from other, high-level routines in the project. You'll add
all these routines to the general declaration section of the form.

First, add a new subroutine called Status, and add the code shown in Listing 9.5.

Listing 9.5. Adding the Status routine to the project.

Public Sub Status(cInfo As String)


'
' send info to status line
'
If cInfo = "" Then
Text1 = ""
Else
Text1 = Text1 & cInfo & Chr(13) & Chr(10)
End If
'
End Sub

The code in the Status routine places a new line in the text box. This will be used to pass progress information to the
text box control as the MLM is processing subscriber lists and the Microsoft Exchange inbox.

The MLM project gets its primary instructions from a set of ASCII text control files. The next routine you'll build in this
section is the one that reads the master control file. Add a new subroutine called ControlsLoad to the project, and
enter the code shown in Listing 9.6.

Listing 9.6. Adding the ControlsLoad routine.

Public Sub ControlsLoad()


'
' load control values into variables
'
On Error GoTo ControlsLoadErr
'
Status ""
Status "Loading Control Values..."
'
Dim nFile As Integer
Dim nCount As Integer
Dim cLine As String
Dim nPos As Integer
'
bErr = False
nCount = 0
nFile = FreeFile
'
Open cMLMFile For Input As nFile
While Not EOF(nFile)
Line Input #nFile, cLine
If Left(cLine, 1) <> ";" Then
nPos = InStr(cLine, "=")
If nPos <> 0 Then
nCount = nCount + 1
ReDim Preserve cCtlName(nCount)
ReDim Preserve cCtlValue(nCount)
cCtlName(nCount) = Left(cLine, nPos - 1)
cCtlValue(nCount) = Mid(cLine, nPos + 1, 255)
End If
End If
Wend
Close #nFile
Exit Sub
'
ControlsLoadErr:
MsgBox Error$, vbCritical, "ControlsLoad Error [" & CStr(Err) &
"]"
bErr = True
'
End Sub

Notice that the ControlsLoad routine reads each line of the ASCII text file, and if it is not a comment line (that is, it
starts with a ";"), it parses the line into a control name array and a control value array. You'll use these values throughout
your project.

Now that the control values are stored in a local array, you need a routine to retrieve a particular control value. Add a
new function (not a subroutine) to the project called ControlSetting, and add the code shown in Listing 9.7.

Listing 9.7. Adding the ControlSetting function.

Public Function ControlSetting(cName As String) As String


'
' look up control setting
'
Dim cReturn As String
Dim nCount As Integer
Dim x As Integer
'
nCount = UBound(cCtlName)
cName = UCase(cName)
'
For x = 1 To nCount
If cName = UCase(cCtlName(x)) Then
cReturn = cCtlValue(x)
Exit For
End If
Next x
'
ControlSetting = cReturn
'
End Function

The ControlSetting function accepts a single parameter (the name of the control value you are requesting) and
returns a single value (the value of the control setting you named). This routine accomplishes its task by simply reading
through the array of control names until the name is found.

That's all for the general support routines. Save this form and project again before continuing.

Coding the Edit Routines

This next set of routines allows users to edit the various control files required to manage the project. You'll use a call to
the NOTEPAD.EXE applet to edit the control files. This is much easier than spending the time to write your own text file
editor. Also, the first time you call these routines you'll be prompted to create the new files.
Add a new subroutine called ControlsEdit to the form, and enter the code shown in Listing 9.8.

Listing 9.8. Adding the ControlsEdit routine.

Public Sub ControlsEdit()


'
Dim rtn As Long
Dim cEditor
'
ControlsLoad
If bErr <> True Then
cEditor = ControlSetting("Editor")
'
Status "Opening Control File [" & cMLMFile & "]..."
rtn = Shell(cEditor & " " & cMLMFile, 1)
Status "Closing Control File..."
End If
'
End Sub

This routine first attempts to load the master control values, then launches the default editor to allow users to modify
those values. You can also see the use of the Status routine to update the form's text box. Go back to the
Command1_Click routine (see Listing 9.4) and remove the comment from in front of the ControlsLoad command.
Then save this project.

Before you can run this routine, you need to create the default control file. Start NOTEPAD.EXE and enter the
information shown in Listing 9.9. Once you complete the entry, save the file in the same folder as the MLM project and
call it MLM.TXT.

Tip
If you get errors attempting to launch the editor from these routines, you can
include the drive and path qualifiers in the Editor control value.

Listing 9.9. Creating the default MLM.TXT control file.

; ===================================================
; Mailing List Control values for MLM
; ===================================================
;
; read by MLM.EXE
;
; ===================================================
;
MAPIUserName=MCA
MAPIPassword=
SearchKey=MLM
ListName=MLM Mailing List
NewSub=SUB
NewSubMsg=MLMHello.txt
UnSubMsg=MLMBye.txt
UnSub=UNSUB
GetArchive=GET
ListArchive=LIST
ArchiveFile=MLMArch.txt
ListSchedule=MLMSked.txt
ListSubs=MLMSubs.txt
Editor=notepad.exe

Tip
If you don't want to spend time entering this control file information, you
can find it in the MLM folder that was created when you installed the
source code from the CD-ROM.

There are several entries in this control file. For now, make sure that the control names and values are entered correctly.
You'll learn more about how each one works as you go along. Once you get the hang of the control file, you can modify
it to suit your own mailing-list needs.

Now add a new subroutine, called SubEdit, to allow the editing of the subscriber list. Enter the code in Listing 9.10
into the routine.

Listing 9.10. Adding the SubEdit routine.

Public Sub SubEdit()


'
Dim lReturn As Long
Dim cEditor As String
Dim cFile As String
'
ControlsLoad
If bErr <> True Then
cFile = ControlSetting("ListSubs")
cEditor = ControlSetting("Editor")
Status "Loading Subscriber List [" & cFile & "]..."
lReturn = Shell(cEditor & " " & cFile, 1)
Status "Closing Subscriber List..."
End If
'
End Sub

Normally you will not need to pre-build the subscriber file. It will be created as you add new subscribers to your mailing
list via e-mail requests. However, for testing purposes, open up Notepad and enter the values shown in Listing 9.11.
When you are done, save the file as MLMSUBS.TXT in the same folder as the Visual Basic project.
Listing 9.11. Creating the test MLMSUBS.TXT file.

; ====================================================
; Mailing List Subscriber File
; ====================================================
;
; Read by MLM.EXE
;
; format:name^address^transport
;
; where:name = display name
; address = e-mail address
; transport = MAPI transport
;
; example:Mike Amundsen^[email protected]^SMTP
;
; ====================================================
;
Michael C. Amundsen^[email protected]^SMTP
Mike Amundsen^102461,1267^COMPUSERVE

The addresses in the file may not be valid e-mail addresses on your system, but they illustrate the format of the file. Each
address entry has three parts:

● Name-This is the display name that appears in address books.


● Address-This is the full e-mail address of the recipient.
● Type-This is the transport type code.

As users request to be on your mailing list, their mailing information is added to this file. Later in the chapter, you'll add
yourself to this list by sending yourself an e-mail request.

The next control file needed for the MLM application is the schedule file. This control file contains information on the
display name, complete filename, and scheduled delivery date of messages to be sent by MLM. Create a new routine
called SkedEdit, and add the code in Listing 9.12.

Listing 9.12. Adding the SkedEdit routine.

Public Sub SkedEdit()


'
Dim rtn As Long
Dim cFile As String
Dim cEditor As String
'
ControlsLoad
If bErr <> True Then
cFile = ControlSetting("ListSchedule")
cEditor = ControlSetting("Editor")
Status "Opening Schedule [" & cFile & "]..."
rtn = Shell(cEditor & " " & cFile, 1)
Status "Closing Schdule..."
End If
'
End Sub

You'll need to create a default schedule file for this project. Listing 9.13 shows the schedule file format. Use NOTEPAD.
EXE to build this file and save it in the project directory as MLMSKED.TXT.

Listing 9.13. Building the MLMSKED.TXT file.

; ==================================================
; Mailing List Schedule file
; ==================================================
;
; read by MLM.EXE
;
; format: YYMMDD,uafn,title
;
; where: YYMMDD = Year, Month, Day
; uafn = unambiguous file name
; title = descriptive title
;
; example: 960225,MLMFAQ.txt,MLM FAQ Document
;
; ==================================================

960225,mlmhello.txt,Hello and Welcome to MLM!


960226,mlmbye.txt,Goodbye - We'll Miss You!

You can see from the sample file that there are three control values for each entry:

● YYMMDD-The year, month, and day that the message should be sent.
● UAFN-An unambiguous filename. The contents of this text file will be placed in the body of the message to be
sent.
● Title-This is the title of the message. This value will be placed on the subject line of the message that is sent.

As you build your mailing list message base, you can add lines to this control file.

The last edit routine to add to the project is the one used to edit the archive list. Add a new subroutine called ArchEdit
to the project, and enter the code shown in Listing 9.14.

Listing 9.14. Adding the ArchEdit routine.


Public Sub ArchEdit()
'
Dim rtn As Long
Dim cEditor As String
Dim cArchFile As String
'
ControlsLoad
If bErr <> True Then
cEditor = ControlSetting("Editor")
cArchFile = ControlSetting("ArchiveFile")
'
Status "Opening Archive File [" & cArchFile & "]..."
rtn = Shell(cEditor & " " & cArchFile, 1)
Status "Closing Archive File..."
End If
'
End Sub

Again, you'll need to create an initial archive listing file before you first run your project. Use NOTEPAD.EXE to build a
file called MLMARch.TXT and enter the data shown in Listing 9.15. Save this file in the project directory.

Listing 9.15. Creating the MLMARch.TXT file.

; ==================================================
; Mailing List Archive File
; ==================================================
;
; read by MLM.EXE
;
; format: YYMMDD,uafn,title
;
; where: YYMMDD = Year, Month, Day
; uafn = unambiguous file name
; title = descriptive name
;
; example: 960225,MLMFAQ.txt,MLM FAQ Document
;
; ==================================================

960225,mlmhello.txt,Hello and Welcome to MLM!


960226,mlmbye.txt,Goodbye - We'll Miss You!

This file format is identical to the one used in the MLMSKED.TXT file. The contents of this file can be requested by
subscribers when they want to retrieve an old message in the database. By passing a GET YYMMDD line in the message
subject, subscribers can get a copy of the archive file sent to them automatically.

This is the last of the edit routines for the project. Be sure to save this project before you continue.
Coding the MAPIStart and MAPIEnd routines

Before you can start processing messages, you need to build the routines that will start and end your MAPI sessions. Add
a new subroutine called MAPIStart to the project, and enter the code that appears in Listing 9.16.

Listing 9.16. Adding the MAPIStart routine.

Public Sub MAPIStart()


'
On Error GoTo MAPIStartErr
'
Dim cProfile As String
Dim cPassword As String
Status "Starting MAPI Session..."
'
Set objMAPISession = CreateObject("MAPI.Session")
cProfile = ControlSetting("MAPIUserName")
cPassword = ControlSetting("MAPIPassword")
objMAPISession.Logon profilename:=cProfile, profilePassword:
=cPassword
Exit Sub
'
MAPIStartErr:
MsgBox "Unable to Start a MAPI Session", vbCritical, "MAPIStart
Error"
bErr = True
'
End Sub

Note the use of the OLE Messaging Library as the means of access into the MAPI system. Now add the MAPIEnd
subroutine to your project and enter the code from Listing 9.17.

Listing 9.17. Adding the MAPIEnd routine.

Public Sub MAPIEnd()


On Error Resume Next
Status "Closing MAPI Session..."
objMAPISession.Logoff
End Sub

These two routines are the start and end of the ReadMail and SendMail routines you'll add in the next two sections.

Coding the SendMail Routines


The next set of routines will check the schedule control file for the message of the day and automatically format and
send messages to all subscribers on the mailing list. This is handled with three routines. The first is the high-level routine
that is called from the command button routine. The other two routines handle the details of reading the schedule file,
reading the subscriber file, and composing and sending the messages.

Create a new routine called SendMail and add the code shown in Listing 9.18.

Listing 9.18. Adding the SendMail routine.

Public Sub SendMail()


'
' read mail
'
Status ""
ControlsLoad
MAPIStart
ProcessSubList
MAPIEnd
Status "Outbound processing complete."
MsgBox "Outbound processing complete", vbInformation, "SendMail"
'
End Sub

The SendMail routine first clears the status box and loads the master control file. Then the MAPIStart routine is
called. Once the MAPI session is established, the routine calls ProcessSubList to handle all processing of the
subscriber list. After the list is processed, the MAPIEnd routine is called and the status box is updated along with
message to the user announcing the completion of the processing.

Next add the ProcessSubList subroutine, and enter the code shown in Listing 9.19.

Listing 9.19. Adding the ProcessSubList routine.

Public Sub ProcessSubList()


'
' read sublist to send messages
'
Dim cErr As String
'
If bErr <> 0 Then
Exit Sub
Else
bErr = False
End If
'
Dim cSubList As String
Dim nSubList As Integer
Dim cListSked As String
Dim nListSked As Integer
Dim cSkedFile As String
Dim cFileDate As String
Dim cFileName As String
Dim cFileTitle As String
Dim cLine As String
Dim nPos1 As Integer
Dim nPos2 As Integer
'
cSubList = ControlSetting("ListSubs")
cListSked = ControlSetting("ListSchedule")
cSkedFile = Format(Now, "YYMMDD")
'
Status "Opening Schedule File [" & cListSked & "]..."
nListSked = FreeFile
Open cListSked For Input As nListSked
'
On Error Resume Next
Do While Not EOF(nListSked)
Line Input #nListSked, cLine
nPos1 = InStr(cLine, ",")
If nPos1 <> 0 Then
cFileDate = Left(cLine, nPos1 - 1)
End If
nPos2 = InStr(nPos1 + 1, cLine, ",")
If nPos2 <> 0 Then
cFileName = Mid(cLine, nPos1 + 1, nPos2 - (nPos1 + 1))
End If
If nPos2 + 1 < Len(cLine) Then
cFileTitle = Mid(cLine, nPos2 + 1, 255)
Else
cFileTitle = cFileName
End If
If cFileDate = cSkedFile Then
Exit Do
End If
Loop
Close nListSked
'
Status "Opening Subscriber List [" & cSubList & "]..."
nSubList = FreeFile
Listing 9.19. continued
Open cSubList For Input As nSubList
'
Do While Not EOF(nSubList)
Line Input #nSubList, cLine
If Left(cLine, 1) <> ";" Then
ProcessSubListMsg cLine, cFileName, cFileTitle
End If
Loop
End Sub

The main job of the ProcessSubList routine is to open the schedule file, and see if there is a message to send for
today's date. If one is found, the routine opens the subscriber control file and calls the ProcessSubListMsg routine
to compose and send the message.

Finally, add the ProcessSubListMsg routine and enter the code that appears in Listing 9.20.

Listing 9.20. Adding the ProcessSubListMsg routine.

Public Sub ProcessSubListMsg(cListAddr As String, cFile As String,


cTitle As ➂String)
'
' send out message
'
Dim nFile As Integer
Dim cLine As String
Dim cMsgBody As String
Dim cType As String
Dim cAddr As String
Dim cName As String
Dim objMsg As Object
Dim objRecip As Object
Dim nPos1 As Integer
Dim nPos2 As Integer
'
' parse address line
nPos1 = InStr(cListAddr, "^")
If nPos1 <> 0 Then
cName = Left(cListAddr, nPos1 - 1)
Else
Exit Sub
End If
'
nPos2 = InStr(nPos1 + 1, cListAddr, "^")
If nPos2 <> 0 Then
cAddr = Mid(cListAddr, nPos1 + 1, nPos2 - (nPos1 + 1))
Else
Exit Sub
End If
'
cType = Mid(cListAddr, nPos2 + 1, 255)
'
' now create message
Status "Sending Msg to " & cName & "..."
'
' get message text
nFile = FreeFile
'
Open cFile For Input As nFile
While Not EOF(nFile)
Line Input #nFile, cLine
cMsgBody = cMsgBody & EOL & cLine
Wend
Close #nFile
'
' now build a new message
Set objMsg = objMAPISession.Outbox.Messages.Add
objMsg.subject = ControlSetting("ListName") & " [" & cTitle & "]"
objMsg.Text = cMsgBody
' create the recipient
Set objRecip = objMsg.Recipients.Add
If cType = "MS" Then
objRecip.Name = cName ' handle local users
objRecip.Type = mapiTo
objRecip.Resolve
Else
objRecip.Name = cType & ":" & cAddr
objRecip.address = cType & ":" & cAddr
objRecip.Type = mapiTo
End If
' send the message
objMsg.Update
objMsg.Send showDialog:=False
'
End Sub

The most important part of the ProcessSubListMsg routine is the last section of code that composes and addresses
the message. There are two main processes in this part of the routine. The first process is the creation of a new
Message object:

' now build a new message


Set objMsg = objMAPISession.Outbox.Messages.Add
objMsg.subject = ControlSetting("ListName") & " [" & cTitle & "]"
objMsg.Text = cMsgBody

The second process is the creation of a new Recipient object and the addressing of the message:

' create the recipient


Set objRecip = objMsg.Recipients.Add
If cType = "MS" Then
objRecip.Name = cName ' handle local users
objRecip.Type = mapiTo
objRecip.Resolve
Else
objRecip.Name = cType & ":" & cAddr
objRecip.address = cType & ":" & cAddr
objRecip.Type = mapiTo
End If

Notice that addressing is handled a bit differently for MS-type messages. Messages with the address type of MS are
addresses within the Microsoft addressing scheme-they're local addresses. To handle these items, you only need to load
the Name property, set the recipient type (To:), and then call the MAPI Resolve method to force MAPI to look up the
name in the address book(s). When the name is found, MAPI loads the Address property with the complete transport
and e-mail address for routing. This is how most MAPI messages are usually sent.

However, for messages of type other than MS, it is likely that they are not in the locally available address books. These
messages can still be sent if you load the Address property of the message with both the transport type and the user's e-
mail address. This is the way to handle processing for messages that were sent to you from someone who is not in your
address book. This is known as one-off addressing. One-off addressing ignores the Name property and uses the
Address property to route the message.
That is all the code you need to send out daily messages to your subscriber list. The next set of routines will allow your
application to scan incoming messages for mailing list-related items and process them as requested.

Tip
It is a good idea to save your project as you go along. The next set of
routines are a bit longer and you may want to take a break before continuing.

Coding the Inbox Routines

The next set of routines handles the process of scanning the subject line of incoming messages for mailing-list
commands. These commands are then processed and subscribers are added or dropped from the list and archive items are
sent to subscribers as requested.

The Inbox processing can recognize four different commands on the subject line. These commands are:

● SUB-When this command appears in the subject line of a message, the sender's name and address are added to
the subscriber control file.
● UNSUB-When this command appears in the subject line of a message, the sender's name and address are
removed from the subscriber control file.
● LIST-When this command appears in the subject line of a message, MLM will compose and send a message
that contains a list of all the archived messages that the subscriber can request.
● GET YYMMDD-When this command appears in the subject line of a message, MLM will retrieve the archived
message (indicated by the YYMMDD portion of the command) and send it to the subscriber.

Tip
The exact word used for each of these four commands is determined by
settings in the master control file. See Listing 9.9 for an example of the
master control file. If you want to change the values for these commands,
you can do so in the control file. This is especially useful if you plan to
manage more than one list from the same e-mail address. Adding prefixes to
the commands will help MLM distinguish which command should be
respected and which commands are for some other mailing list.

The first three routines for handling the inbox are rather simple. The first routine clears the progress box, loads the
controls, calls the ProcessInbox routine, and performs cleanup functions upon return. Add the ReadInbox
subroutine and add the code shown in Listing 9.21.

Listing 9.21. Adding the ReadInbox routine.

Public Sub ReadInbox()


'
' read mail
'
Status ""
ControlsLoad
MAPIStart
ProcessInbox
MAPIEnd
Status "Inbox processing complete."
MsgBox "Inbox processing complete", vbInformation, "ReadInbox"
'
End Sub

Next, add a new subroutine called ProcessInbox, and add the code that appears in Listing 9.22.

Listing 9.22. Adding the ProcessInbox routine.

Public Sub ProcessInbox()


'
' read inbox for MLM messages
'
Dim cErr As String
'
If bErr <> 0 Then
Exit Sub
Else
bErr = False
End If
'
Dim objFolder As Object
Dim objMsgColl As Object
Dim objMessage As Object
Dim cSubject As String
'
Listing 9.22. continued
Status "Opening Inbox..."
Set objFolder = objMAPISession.Inbox
If objFolder Is Nothing Then
MsgBox "Unable to Open Inbox", vbCritical, "ProcessInbox
Error"
bErr = True
Exit Sub
End If
'
Status "Collecting Messages..."
Set objMsgColl = objFolder.Messages
If objMsgColl Is Nothing Then
MsgBox "Unable to access Folder's Messages", vbCritical,
"ProcessInbox ➂Error"
bErr = True
Exit Sub
End If
'
Status "Scanning Messages for [" & ControlSetting("SearchKey") &
"]..."
Set objMessage = objMsgColl.GetFirst
Do Until objMessage Is Nothing
cSubject = objMessage.subject
If InStr(cSubject, ControlSetting("SearchKey")) Then
ProcessInboxMsg objMessage
End If
Set objMessage = objMsgColl.GetNext
Loop
'
End Sub

This routine performs three main tasks. The first is to open the messages stored in the Inbox folder. Every message
store has an Inbox folder. All new messages are sent to the Inbox folder upon receipt.

Status "Opening Inbox..."


Set objFolder = objMAPISession.Inbox
If objFolder Is Nothing Then
MsgBox "Unable to Open Inbox", vbCritical, "ProcessInbox
Error"
bErr = True
Exit Sub
End If

The second step is to create a collection of all the messages in the Inbox folder. You access messages as a collection of
objects in the folder.

Status "Collecting Messages..."


Set objMsgColl = objFolder.Messages
If objMsgColl Is Nothing Then
MsgBox "Unable to access Folder's Messages", vbCritical,
"ProcessInbox ➂Error"
bErr = True
Exit Sub
End If

The third process in this routine is to inspect each message in the collection to see if its subject line contains the search
key word from the master control file. If found, the message is passed to the ProcessInboxMsg routine for further
handling.

Status "Scanning Messages for [" & ControlSetting("SearchKey") &


"]..."
Set objMessage = objMsgColl.GetFirst
Do Until objMessage Is Nothing
cSubject = objMessage.subject
If InStr(cSubject, ControlSetting("SearchKey")) Then
ProcessInboxMsg objMessage
End If
Set objMessage = objMsgColl.GetNext
Loop

Now add the ProcessInboxMsg subroutine. This routine checks the content of the message for the occurrence of
MLM command words (SUB, UNSUB, LIST, GET). If one is found, the appropriate routine is called to handle the
request. Enter the code shown in Listing 9.23.
Listing 9.23. Adding the ProcessInboxMsg routine.

Public Sub ProcessInboxMsg(objMsg As Object)


'
' check out message subject
'
Dim cSubject As String
'
cSubject = UCase(objMsg.subject)
'
If InStr(cSubject, ControlSetting("NewSub")) Then
ProcessInboxMsgNewSub objMsg
End If
'
If InStr(cSubject, ControlSetting("UnSub")) Then
ProcessInboxMsgUnSub objMsg
End If
'
If InStr(cSubject, ControlSetting("GetArchive")) Then
ProcessInboxMsgArcGet objMsg
End If
'
If InStr(cSubject, ControlSetting("ListArchive")) Then
ProcessInboxMsgArcList objMsg
End If
'
End Sub

In the next few sections you'll add supporting code to handle all the MLM subject-line commands. It's a good idea to
save the project at this point before you continue.

Adding New Subscribers

When a person sends you an e-mail message with the words MLM SUB in the subject line, the MLM application adds
that person's e-mail address to the subscriber list. The next set of routines handles all the processing needed to complete
that task. This version of the program will also automatically send the new subscriber a greeting message (one that you
designate in the control file).

First, add a new subroutine called ProcessInboxMsgNewSub to the project, and enter the code shown in Listing
9.24.

Listing 9.24. Adding the ProcessInboxMsgNewSub routine.

Public Sub ProcessInboxMsgNewSub(objMsg As Object)


'
' add a new sub
'
Dim objAddrEntry As Object
Dim cName As String
Dim cAddress As String
Dim cType As String
'
On Error Resume Next
Set objAddrEntry = objMsg.Sender
If SubFind(objAddrEntry) = False Then
SubWrite objAddrEntry
SubGreet objAddrEntry
End If
'
End Sub

This routine first checks to see if the name already exists in the subscriber control file. If not, it is added and the new
subscriber is sent a friendly greeting message.

Create a new function called SubFind, and enter the code from Listing 9.25.

Listing 9.25. Adding the SubFind function.

Public Function SubFind(objAddr As Object) As Boolean


'
' see if sub is in list
'
Dim cSubList As String
Dim bReturn As Boolean
Dim nFile As Integer
Dim cRdLine As String
Dim cSrchLine As String
'
cSubList = ControlSetting("ListSubs")
cSrchLine = objAddr.Name & "^" & objAddr.address & "^" & objAddr.
Type
nFile = FreeFile
bReturn = False
'
Open cSubList For Input As nFile
While Not EOF(nFile)
Line Input #nFile, cRdLine
If cRdLine = cSrchLine Then
bReturn = True
End If
Wend
Close #nFile
SubFind = bReturn
'
End Function

The SubFind routine accepts one parameter (the name to look up), and returns True if the name is found and False
if the name is not in the subscriber list.

Next, add the SubWrite subroutine to the project. The code for this routine is in Listing 9.26.

Listing 9.26. Adding the SubWrite routine.

Public Sub SubWrite(objAddr As Object)


'
' write new address to subscriber list
'
Dim cSubList As String
Dim nFile As Integer
'
cSubList = ControlSetting("ListSubs")
Status "Adding New Sub..." & objAddr.Name
'
nFile = FreeFile
Open cSubList For append As nFile
Print #nFile, objAddr.Name; "^"; objAddr.address; "^"; objAddr.
Type
Close nFile
'
End Sub

Notice that the SubWrite routine copies the Name, Address, and Type properties from the AddressEntry object
into the subscriber control file. Each value is separated by a caret (^). This separator character was chosen somewhat
arbitrarily. You can change it if you wish.

Warning
If you change the separator value, be sure you don't use a character that could be part of a
valid e-mail address. E-mail addresses today can have a comma (,), colon (:), semicolon (;),
slashes (/ or \), and other characters. You'll need to be careful when you choose your separator
character!

Next add the SubGreet routine. This composes and sends a friendly greeting message to all new subscribers. Add the
code from Listing 9.27 to your project.

Listing 9.27. Adding the SubGreet routine.

Public Sub SubGreet(objAddr As Object)


'
' send new sub a welcome greeting
'
Dim cSubGreet As String
Dim nFile As Integer
Dim cLine As String
Dim cMsgBody As String
Dim objMsg As Object
Dim objRecip As Object
'
Status "Sending Greet Msg to " & objAddr.Name & "..."
'
' get greeting message text
cSubGreet = ControlSetting("NewSubMsg")
nFile = FreeFile
'
Open cSubGreet For Input As nFile
While Not EOF(nFile)
Line Input #nFile, cLine
cMsgBody = cMsgBody & EOL & cLine
Wend
Close #nFile
'
' now build a new message
Set objMsg = objMAPISession.Outbox.Messages.Add
objMsg.subject = "Welcome to the " & ControlSetting("ListName")
objMsg.Text = cMsgBody
' create the recipient
Set objRecip = objMsg.Recipients.Add
If objAddr.Type = "MS" Then
objRecip.Name = objAddr.Name ' handle local users
objRecip.Type = mapiTo
objRecip.Resolve
Else
objRecip.Name = objAddr.Type & ":" & objAddr.Address
objRecip.address = objAddr.Type & ":" & objAddr.Address
objRecip.Type = mapiTo
End If
' send the message and log off
objMsg.Update
objMsg.Send showDialog:=False
'
End Sub

The SubGreet routine looks similar to the routine used to send daily messages to subscribers. The message sent as the
greeting pointed to the NewSubMsg parameter in the master control file.

Save your work before adding the code to drop subscribers from the list.

Dropping Subscribers

The routines needed to drop subscribers from the mailing list are very similar to the code needed to add them. You'll
create a routine to respond to the request and two supporting routines-one to delete the name from the list, and one to
send a goodbye message to the requester.

First add the ProcessInboxMsgUnSub subroutine to the project and enter the code from Listing 9.28.
Listing 9.28. Adding the ProcessInboxMsgUnSub routine.

Public Sub ProcessInboxMsgUnSub(objMsg As Object)


'
' drop an existing sub
'
Dim objAddrEntry As Object
Dim cName As String
Dim cAddress As String
Dim cType As String
'
On Error Resume Next
Set objAddrEntry = objMsg.Sender
If SubFind(objAddrEntry) = True Then
SubDelete objAddrEntry
SubBye objAddrEntry
End If
'
End Sub

This routine checks to make sure the name is in the subscriber list. If it is, then the name is dropped and a goodbye
message is sent. Add the SubDelete routine to the project by copying the code from Listing 9.29.

Listing 9.29. Adding the SubDelete routine.

Public Sub SubDelete(objAddr As Object)


'
' delete an address from the subscriber list
'
Dim cSubList As String
Dim cSubTemp As String
Dim nList As Integer
Dim nTemp As Integer
Dim cRdLine As String
Dim cSrchLine As String
'
cSubList = ControlSetting("ListSubs")
Status "Dropping a Sub..." & objAddr.Name
cSrchLine = objAddr.Name & "^" & objAddr.address & "^" & objAddr.
Type
cSubTemp = "tmp001.txt"
'
Listing 9.29. continued
nList = FreeFile
Open cSubList For Input As nList
nTemp = FreeFile
Open cSubTemp For Output As nTemp
'
While Not EOF(nList)
Line Input #nList, cRdLine
If cRdLine <> cSrchLine Then
Print #nTemp, cRdLine
End If
Wend
'
Close #nList
Close #nTemp
Kill cSubList
Name cSubTemp As cSubList
'
End Sub

This routine accomplishes the delete process by copying all the valid names to a temporary file, and then erasing the old
file and renaming the temporary file as the new master subscriber list. While this may seem a bit convoluted, it is the
quickest and simplest way to handle deletes in a sequential ASCII text file.

Note
In a more sophisticated project, you could build the subscriber list in a
database and use database INSERT and DELETE operations to manage the
list.

Next add the SubBye routine. This sends a goodbye message to the subscriber that was just dropped from the list. The
greeting message is kept in the text file pointed to by the value of the UnSubMsg control parameter in the master control
file.

Add the code shown in Listing 9.30.

Listing 9.30. Adding the SubBye routine.

Public Sub SubBye(objAddr As Object)


'
' send old sub a goodbye msg
'
Dim cSubBye As String
Dim nFile As Integer
Dim cLine As String
Dim cMsgBody As String
Dim objMsg As Object
Dim objRecip As Object
'
Status "Sending Bye Msg to " & objAddr.Name & "..."
'
' get bye message text
cSubBye = ControlSetting("UnSubMsg")
nFile = FreeFile
'
Open cSubBye For Input As nFile
While Not EOF(nFile)
Line Input #nFile, cLine
cMsgBody = cMsgBody & EOL & cLine
Wend
Close #nFile
'
' now build a new message
Set objMsg = objMAPISession.Outbox.Messages.Add
objMsg.subject = "So long from the " & ControlSetting("ListName")
objMsg.Text = cMsgBody
' create the recipient
Set objRecip = objMsg.Recipients.Add
If objAddr.Type = "MS" Then
objRecip.Name = objAddr.Name ' handle local users
objRecip.Type = mapiTo
objRecip.Resolve
Else
objRecip.Name = objAddr.Type & ":" & objAddr.Address
objRecip.address = objAddr.Type & ":" & objAddr.Address
objRecip.Type = mapiTo
End If
' send the message and log off
objMsg.Update
objMsg.Send showDialog:=False
'
End Sub

The next code routines will handle subscriber requests for the list of retrievable archive
messages.

Listing Archives

One of the added features of MLM is to allow subscribers to send requests for copies of old, archived messages. Users
can also request a list of messages that are in the archive. You need two routines to handle the LIST command-the main
caller, and the one to actually assemble and send the list.

Add the new subroutine ProcessInboxMsgArcList to the project and enter the code shown in Listing 9.31.

Listing 9.31. Adding the ProcessInboxMsgArcList routine.

Public Sub ProcessInboxMsgArcList(objMsg As Object)


'
' get list of archives and
' send to requestor
'
Listing 9.31. continued
On Error Resume Next
'
Dim objAddrEntry As Object
'
Set objAddrEntry = objMsg.Sender
If SubFind(objAddrEntry) = True Then
WriteArcList objAddrEntry
End If
'
End Sub

Now create the WriteArcList subroutine and add the code shown in Listing 9.32.

Listing 9.32. Adding the WriteArcList routine.

Public Sub WriteArcList(objAddr As Object)


'
' make list of archives
' build message and send
'
Dim objMsg As Object
Dim objRecip As Object
Dim cArcFile As String
Dim nArcFile As Integer
Dim cLine As String
Dim cFileDate As String
Dim cFileName As String
Dim cFileTitle As String
Dim cMsgBody As String
Dim nPos1 As Integer
Dim nPos2 As Integer
'
Status "Sending Archive List to " & objAddr.Name & "..."
'
cMsgBody = "Archive List for " & ControlSetting("ListName") &
EOL & EOL
cMsgBody = cMsgBody & "All records are in the following format:"
& EOL
cMsgBody = cMsgBody & "Date(YYMMDD),FileName,Title" & EOL & EOL
'
cArcFile = ControlSetting("ArchiveFile")
nArcFile = FreeFile
Open cArcFile For Input As nArcFile
Do While Not EOF(nArcFile)
Line Input #1, cLine
If Left(cLine, 1) <> ";" Then
cMsgBody = cMsgBody & cLine & EOL
End If
Loop
Close #nArcFile
'
' now add message to outbox
Set objMsg = objMAPISession.Outbox.Messages.Add
objMsg.subject = "Archive List from " & ControlSetting
("ListName")
objMsg.Text = cMsgBody
' create the recipient
Set objRecip = objMsg.Recipients.Add
If objAddr.Type = "MS" Then
objRecip.Name = objAddr.Name ' handle local users
objRecip.Type = mapiTo
objRecip.Resolve
Else
objRecip.Name = objAddr.Type & ":" & objAddr.Address
objRecip.address = objAddr.Type & ":" & objAddr.Address
objRecip.Type = mapiTo
End If
' send the message
objMsg.Update
objMsg.Send showDialog:=False
'
End Sub

The WriteArcList routine reads the MLMARch.TXT control file and creates a message body that has a brief set of
instructions and lists all available archived messages. Once this is done, the message is addressed and sent.

Save the project before you go on to the last coding section.

Sending Requested Archives

Once subscribers have received a list of available archives, they can send a MLM GET YYMMDD command on the
subject line of a message to ask for a specific message to be sent to them. You need three routines to handle this
processing:

● ProcessInboxMsgArcGet to make the initial call for getting the archive;


● FindArc for locating the requested archive message;
● WriteArcGet for retrieving and sending the archive message.

First add the ProcessInboxMsgArcGet subroutine to your project and enter the code in Listing 9.33.

Listing 9.33. Adding the ProcessInboxMsgArcGet routine.

Public Sub ProcessInboxMsgArcGet(objMsg As Object)


'
' get single archive and
' send to requestor
'
On Error Resume Next
'
Dim objAddrEntry As Object
'
Set objAddrEntry = objMsg.Sender
If SubFind(objAddrEntry) = True Then
WriteArcGet objMsg.subject, objAddrEntry
End If
'
End Sub
Next, add the FindArc function to the project and enter the code shown in Listing 9.34.

Listing 9.34. Adding the FindArc function.

Public Function FindArc(cFile As String) As String


'
' search for requested file in archive list
'
Dim cFileDate As String
Dim cFileName As String
Dim cFileTitle As String
Dim cArchFile As String
Dim nArchFile As Integer
Dim cLine As String
Dim nPos1 As Integer
Dim nPos2 As Integer
Dim cReturn As String
'
cReturn = ""
cArchFile = ControlSetting("ArchiveFile")
nArchFile = FreeFile
Open cArchFile For Input As nArchFile
Do Until EOF(nArchFile)
Line Input #nArchFile, cLine
If Left(cLine, 1) <> ";" Then
nPos1 = InStr(cLine, ",")
If nPos1 <> 0 Then
cFileDate = Left(cLine, nPos1 - 1)
End If
nPos2 = InStr(nPos1 + 1, cLine, ",")
If nPos2 <> 0 Then
cFileName = Mid(cLine, nPos1 + 1, nPos2 - (nPos1 +
1))
End If
If nPos2 < Len(cLine) Then
cFileTitle = Mid(cLine, nPos2 + 1, 255)
Else
cFileTitle = cFileName
End If
End If
'
' now compare!
If UCase(cFileDate) = UCase(cFile) Then
cReturn = cFileName
Exit Do
End If
Loop
Close #nArchFile
'
FindArc = cReturn
'
End Function
FindArc accepts one parameter (the archive file search number-YYMMDD) and returns the actual operating system
filename of the archived message. If no message is found, the return value is a zero-length string.

Finally, add the WriteArcGet routine to the project and enter the code shown in Listing 9.35.

Listing 9.35. Adding the WriteArcGet routine.

Public Sub WriteArcGet(cSubject As String, objAddr As Object)


'
' get single archive
' build message and send
'
Dim objMsg As Object
Dim objRecip As Object
Dim cArcFile As String
Dim nArcFile As Integer
Dim cLine As String
Dim cFileDate As String
Dim cFileName As String
Dim cFileTitle As String
Dim cMsgBody As String
Dim nPos1 As Integer
Dim nPos2 As Integer
Dim cArchive As String
'
'get archive name
nPos1 = InStr(UCase(cSubject), "GET")
If nPos1 <> 0 Then
nPos2 = InStr(nPos1 + 1, cSubject, " ") ' look for next space
If nPos2 <> 0 Then
cArchive = Mid(cSubject, nPos2 + 1, 255)
Else
cArchive = ""
End If
End If
'
cArcFile = FindArc(cArchive)
If Len(cArcFile) <> 0 Then
cMsgBody = "Archive File [" & cArchive & "]" & EOL & EOL
' read archive file
nArcFile = FreeFile
Open cArcFile For Input As nArcFile
Do While Not EOF(nArcFile)
Line Input #1, cLine
If Left(cLine, 1) <> ";" Then
cMsgBody = cMsgBody & cLine & EOL
End If
Loop
Close #nArcFile
Else
cMsgBody = "MLM Archive Error" & EOL & EOL
cMsgBody = cMsgBody & "*** Unable to locate Archive [" &
cArchive & "]." & ➂EOL
End If
'
' now add message to outbox
Status "Sending Archive " & cArchive & "to " & objAddr.Name &
"..."
Set objMsg = objMAPISession.Outbox.Messages.Add
objMsg.subject = "Archive " & cArchive & "from " & ControlSetting
("ListName")
Listing 9.35. continued
objMsg.Text = cMsgBody
' create the recipient
Set objRecip = objMsg.Recipients.Add
If objAddr.Type = "MS" Then
objRecip.Name = objAddr.Name ' handle local users
objRecip.Type = mapiTo
objRecip.Resolve
Else
objRecip.Name = objAddr.Type & ":" & objAddr.Address
objRecip.address = objAddr.Type & ":" & objAddr.Address
objRecip.Type = mapiTo
End If
' send the message
objMsg.Update
objMsg.Send showDialog:=False
'
End Sub

The WriteArcGet routine picks the archive name out of the subject line and, if it is found, reads the archived
message, composes a new message, and sends it to the requestor.

That is all the code for this project. The next step is to test the various MLM functions. Be sure to save the project before
you begin testing. Once testing is complete, you can make an executable version of the project for installation on any
workstation that has the MAPI OLE Messaging Library installed.

Running the MLM Application

In a production setting, you can set up an e-mail account that is dedicated to processing MLM requests. All e-mail
messages regarding that list can be addressed to this dedicated account. You (or someone else) can run the MLM once a
day and it can automatically log onto the dedicated account and perform the list processing. Also, since MLM accepts a
command-line parameter, you can build control files for several different mailing lists and run them all from the same
workstation. You just need to keep in mind that in order to process the messages, you need to start the MLM application
and run it at least once a day.

Tip
If you have the Microsoft Plus! pack installed, you can use the System
Agent to schedule MLM to run at off-peak times. If you decide to use the
System Agent, you need to add additional code to the project to allow you
to select the Read & Send button automatically. Just add an additional
parameter to the command line that will execute the Command1_Click
event for Read & Send.

For testing purposes, set the MLM control file to log onto your own e-mail account. You can then send messages to
yourself and test the features of MLM to make sure they are working properly.

Before you start testing you need to make sure you have valid ASCII files for the new subscribe greeting (MLMHELLO.
TXT) and the unsubscribe departing message (MLMBYE.TXT). Refer to Listings 9.36 and 9.37 for examples of each of
these files.

Tip
You can also find examples in the source code directory Chap08\MLM
created when you installed the CD-ROM.

Listing 9.36. Default MLMHELLO.TXT message.

You are now added to the MLM Mailing List!

Welcome!

MCA

Listing 9.37. Default MLMBYE.TXT message.

Sorry you're leaving us!

BYE - MCA

Use NOTEPAD.EXE to create these two files and save them in the project directory.

You may also need to modify the MLMSKED.TXT file to match today's date. Change the first entry from 960225 to make
sure that any registered subscriber gets a message when you run the SendMail routine.

Testing the MLM Application

Now you're ready to test MLM!


First, add your name to the mailing list. To do this, start up your MAPI client (you could use the one you built in Chapter
7,"Creating a Simple MAPI Client with the MAPI Controls") and send a message with the words MLM SUB in the
subject line. It does not matter what you put in the message body; it will be ignored by MLM (see Figure 9.2).

Figure 9.2 : Sending a request to join the MLM mailing list.

Once the request is sent, load MLM and select the ReadMail button. This will scan all the messages in your inbox, and
(if all goes right!) find and process your request to be added to the list. Figure 9.3 shows how the MLM screen looks as it
is processing.

Figure 9.3 : Running the MLM ReadMail process.

After MLM is done, start up your MAPI client again. You should see a greeting message in your inbox confirming that
you have been added to the MLM mailing list (see Figure 9.4).

Figure 9.4 : Confirmation greeting message from MLM.

Next, try sending a message that requests a list of available archives. Use your MAPI client to compose a message with
MLM LIST in the subject line. After sending the message, run the MLM ReadMail option again, and then check your
inbox with the MAPI client. You should see a message similar to the one in Figure 9.5.

Figure 9.5 : Receiving a list of available archives from MLM.

Once you get the list, you can send MLM a command to retrieve a specific message in the archive. Use your MAPI
client to send a message that asks for one of the messages. After sending the message, run MLM ReadMail, and then
recheck your MAPI client for the results. You should get a message like the one in Figure 9.6.

Figure 9.6 : Receiving a requested archive message from MLM.

Tip
You have probably noticed that messages that have already been read and
processed are being processed again each time you run MLM. You can
prevent this from happening by deleting the messages from the inbox by
hand, or by adding code to the project to delete each message after you are
finished processing it.

You can test the SendMail option by simply starting MLM and pressing the SendMail button. This will search for a
message file tagged with today's date and send it to all subscribers in the list. To test this, modify one of the entries in the
MLMSKED.TXT file so that it has the current date as its key number. Figure 9.7 shows what the MLM progress screen
looks like as it is working.

Figure 9.7 : Running the SendMail option of MLM.

Finally, you can test the unsubscribe feature of MLM by sending a message with MLM UNSUB in the subject. When
MLM receives this message, it will drop your name from the subscriber list and send you a departing message.

Summary
In this chapter you built a mailing list manager using Microsoft's OLE Messaging Library. This application lets you
define a mailing list, allow others to become list subscribers, publish messages automatically (based on date), and allow
others to query and retrieve old messages from the list archives. This program can accept members from within a single
network, or from around the world through Internet (or other) transports.

In the next chapter you'll use OLE to build a MAPI application that allows distant users to query databases and other
collections of information by way of e-mail.
Chapter 10

Building a MAPI-Enabled Fourm Tool

CONTENTS

● Discussion Groups versus E-Mail


❍ Folders as Destinations

❍ Using the ConversationTopic and ConversationIndex Properties

❍ Update versus Send

● The Discuss Project


● The MAPIPost Code Library
● The Discuss and Msgs Forms
❍ Laying Out the Discuss Form

❍ Coding the Discuss Form

❍ Laying Out and Coding the Msgs Form

● Building the Other Forms


❍ The Note Form

❍ The Options Form

❍ The About Dialog Box

● Testing the Discuss Forum Tool


● Summary

In this chapter you'll learn how to use MAPI services to create a threaded discussion client. Threaded discussions clients
are used quite frequently in groupware settings and on the Internet. The biggest difference between a discussion tool and
an e-mail client is that the e-mail client is designed primarily for conducting one-to-one conversations. The discussion
tool, however, is designed to support multiple parties, all participating in the same discussion. Even though MAPI
services were originally deployed as only an e-mail solution, Microsoft has added several features to MAPI that now
make it an excellent platform for building discussion applications.

Before getting into the details of building the tool, you'll learn a bit more about the differences between e-mail and
discussion communications. You'll also learn how to take advantage of little-known MAPI properties and new features
of the OLE Messaging library that make it easy to put together a standalone discussion tool that can be deployed from
within a Local Area Network, over a Wide Area Network, or even across the Internet. You'll learn

● The difference between the MAPI Send and Update methods.


● How to use the ConversationTopic and ConversationIndex properties to create threaded
discussions in MAPI messages.
● How to create messages that are posted to a MAPI folder instead of sent to a single individual.

As an added bonus, after you've completed the programming example in this chapter, you'll have a set of library
functions that allow you to easily add threaded discussion capabilities to new or existing Visual Basic applications.

Discussion Groups versus E-Mail


By now, most of us have used electronic discussion forums at least once in our lives. And there are many who use them
every single day. The advantage of a discussion forum over direct person-to-person e-mail is that you can communicate
with a large number of people without generating a large number of messages. In fact, reduced traffic is one of the
biggest reasons to consider employing discussion groups as part of your electronic message system.

There are a number of other advantages to using discussion groups instead of e-mail. Often, it is easier to find answers to
technical problems if you can go to a place where all the "techies" hang out. Discussion forums can work the same way.
Instead of trying to e-mail several people in attempts to solve your problem, you can often find a discussion forum where
you can send a single request that will reach hundreds (possibly thousands) of people who may be able to help you. So
discussion forums can increase your ability to get your problems solved sooner.

Another good way to highlight the differences between e-mail and discussion groups is by comparing several face-to-
face meetings (e-mail messages) with a single staff meeting where everyone shows up at the same place at the same time
(the discussion forum). Using forum communications can reduce the amount of time you need to spend communicating.
You can say it once and reach lots of people instead of having to repeat your message in lots of single e-mail messages.

Companies use forums as a way to communicate information of general interest as well as a way to take advantage of
specialized expertise within an organization. Where corporate e-mail can lead to isolated individuals communicating in a
vacuum, discussion groups can foster increased interaction with others and a sense of belonging to a special group-even
if one or more of the team members is half-way around the world.

Now that you have a general idea of what discussion groups are and how you can use them in your organization, you're
ready to review the three key concepts that make discussion systems different from e-mail systems.

Folders as Destinations

First, and most important, users participating in an online discussion forum do not send messages to individuals. Instead
they send their messaging to a single location where all messages are stored. In the MAPI model, the storage location is
called a folder. When you create MAPI-based forum tools, you'll select a folder (or folders) to hold all the
correspondence from others participating in the forum.

Note
There are times when forum participants will initiate private e-mail
correspondence in order to cover a topic in more depth or to delve deeper
into an aspect of the discussion that would not be interesting to most of the
other members.

Then, after each message has been composed, you just post the message to the discussion folder for others to see. They,
if appropriate, respond to your message with their own comments. The key point to remember is that messages are
addressed to locations, not people.

Using the ConversationTopic and ConversationIndex Properties

Another key aspect of discussion groups is the ability to track topics in a thread that leads from the beginning of the
discussion to the end. As each participant replies to an existing message, a relationship is established between the source
message and the reply. Of course, it is perfectly correct (and quite common) to generate a "reply to a reply." This can
continue on indefinitely. And in the process an intricate web of ideas and comments all collect in the storage folder that
houses the discussion group.
Some discussion tools simply list the messages in the chronological order in which they were received. These are
referred to as non-threaded or flat-table discussion tools (see Figure 10.1).

Figure 10.1 : Viewing messages in flat format, chronological order.

Non-threaded tools (often called readers) can be handy, but most people prefer the discussion tools that provide threaded
discussion tracking. With threaded tools, each conversation that is initiated is a branch. Each branch can have its own
subsequent conversation thread. And those threads can be combined with others, too. Viewing discussion messages as
related threads is really handy (see Figure 10.2).

Figure 10.2 : Viewing messages in threaded order.

MAPI uses two special properties of the Message object to keep track of each branch and sub-branch of a threaded
discussion. These two properties are the ConversationTopic and the ConversationIndex properties. The
ConversationTopic is usually a readable string. This is much like the subject line of a mail message. Conversation
topics for a discussion forum for a typical computer firm might be

● General questions
● Sales
● Technical support
● Press releases

Note
Some forums have fixed topics and only allow users to select from a
predetermined list when posting messages. Other forums allow users to
enter free-form text into the topic field.

MAPI uses the ConversationIndex property to keep track of conversation threads within the topic group. This is
usually done using some type of numbering scheme. Microsoft recommends that programmers use the same method that
is employed by Microsoft Exchange when populating the ConversationIndex field. Microsoft Exchange uses the
CoCreateGuid API call to generate a unique number to place in the ConversationIndex field. New messages
that respond to an existing message should inherit the ConversationIndex of the previous message and append
their own index value to the right. This way, each new branch of the conversation has a longer ConversationIndex.
You'll get a better look at this technique when you build the sample Visual Basic project later in this chapter.

Note
The CoCreateGuid API generates a unique value based on the system
date (among other things). You can use any method you wish to keep track
of conversation threads, but it is highly recommended that you use the
method described here. It is quick, effective, and requires very few
computing resources.

Update versus Send

The last major concept to deal with when building discussion tools is the use of the MAPI Update method instead of
the Send method. In all the examples covered in the book so far, when you complete a new message and want to place it
into the MAPI message pool, you use the Send method (or some type of Send API call) to release the message to
MAPI for delivery. This works when you have a person to whom you are addressing the message. However, because
discussion messages are not addressed to persons but to folders instead, you cannot "send" new messages. Instead you
use the Update method to update the contents of a MAPI folder. In fact, attempting to use the Send method to deliver
forum messages will result in a MAPI error.

Tip
The key idea here is to think of updating the contents of a MAPI folder as
opposed to sending a message to a person.

The actual process of placing a message in a MAPI folder includes adding a blank message to the target folder, setting
that message's subject and text body (just like any mail message) and then setting several other message properties, too.
Each of the properties shown in Table 10.1 must be set properly before you invoke the Update method to place a
message in a MAPI folder.

Note
You'll get to see the details of creating a forum message when you build the
forum tool later in this chapter. The main thing to keep in mind now is that
you do not use the Send method to place messages in folders.

So there are the three key points to remember about the differences between e-mail and discussion messages:

● Discussion messages are addressed to folders, not people.


● MAPI uses the ConversationTopic and ConversationIndex properties to keep track of discussion
threads.
● You use the MAPI Update method to deliver discussion messages. You use the MAPI Send method to
deliver mail messages.

Now it's time to use Visual Basic to build the example forum tool.

The Discuss Project

For the rest of the chapter, you'll build a complete discussion tool that can be used to track ongoing messages in a target
folder. You'll be able to use the program to read messages, generate replies to existing messages, and start new
discussion threads. You'll also be able to select the target folder for the discussion. This way, you can use the same tool
to monitor more than one discussion.

Note
The forum tool described in this chapter is a very basic project. Not much
time will be spent on the user interface. This is done to keep focus on the
general issues of creating a discussion tool for MAPI systems. If you plan to
put a version of this project into production, you'll want to add some bells
and whistles to make this a more friendly product.

You'll use the OLE Messaging library (OML) to access MAPI services for this project. The OML gives you all the
features you need to be able to manipulate message and folder properties within the MAPI file system. Although you can
use the MAPI.OCX to read most of the discussion-related properties, you cannot use the OCX to place messages in a
target MAPI folder. Also, you cannot use the simple MAPI API declarations to gain access to target folders or to
manipulate message properties.

Tip
If you are planning to build any software that manipulates folders, you'll
need to use the OLE Messaging library. No other Microsoft programming
interface (besides the C MAPI interface) allows you to gain access to the
folders collection.

There is one main code module and several forms in the Discuss project you'll build here.

● MAPIPost-This code module holds all the routines for posting new threads, replying to threads, and collecting
information on the available information stores, folders, and messages available to the workstation. You can use
the code here in other Visual Basic discussion projects.
● modDiscuss-This code module contains a few constants and global variables used for the Discuss project.
● mdiDiscuss-This is the main form. This acts as a wrapper for the note form and the message list form.
● frmNote-This form is used to read, compose, and reply to existing forum messages.
● frmMsgs-This form is used to display a list of discussion messages within the target folder. The messages are
shown in the threaded discussion form.
● frmOptions-This form allows users to change the target folder for the discussion, set the default MAPI logon
profile, and control whether the message listing is shown in threaded or flat-table format.
● frmAbout-This is the standard About box for the project.

Note
Before you start coding the Discuss project, make sure you have added the
OLE Messaging type library. Do this by selecting Tools | References
from the main menu of Visual Basic. You will need to install the OLE
Messaging library from the MSDN CD-ROM before you can locate it using
the "Tools | References" menu option.

The MAPIPost Code Library

The MAPIPost Code Library contains several very important routines. These routines will be used throughout the
project to gain access to folder collections and message collections within the folders. This also holds routines for
posting new messages and generating replies to existing messages. There are also several routines for performing folder
and message searches. You'll be able to use the routines in this module in other MAPI-related projects.

First, start a new Visual Basic project and add a BAS module (Insert | Module). Set its Name property to
MAPIPost and save the file as MAPIPOST.BAS. Now add the code shown in Listing 10.1 to the declaration section of
the form.

Listing 10.1. Adding the declarations for the module.

Option Explicit

'
' OLE message objects
Public objSession As Object
Public objMsgColl As Object
Public objMsg As Object
Public objRecipColl As Object
Public objRecip As Object
Public objAttachColl As Object
Public objAttach As Object
Public objAddrEntry As Object
Public objUserEntry As Object
Public objFolderColl As Object
Public objFolder As Object
Public objInfoStoreColl As Object
Public objInfoStore As Object
Public gnIndentlevel as Integer
Public cStoreID As String

'
' UDT for store/folder pairs
Type FolderType
Name As String
FolderID As String
StoreID As String
End Type
'
Public FolderRec() As FolderType ' members
Public iFldrCnt As Integer ' pointer

'
' UDT for message/conversation pairs
Type MsgType
Subject As String
Topic As String
ConvIndex As String
MsgID As String
Date As Date
End Type
'
Public MsgRec() As MsgType ' members
Public iMsgCnt As Integer ' pointer

'
' type for creating Exchange-compliant timestamp
Type GUID
guid1 As Long
guid2 As Long
Listing 10.1. continued
guid3 As Long
guid4 As Long
End Type
'
Declare Function CoCreateGuid Lib "OLE32.DLL" (pGuid As GUID) As Long
Public Const S_OK = 0

You'll notice the usual OLE Messaging library object declarations along with two user-defined types that will be used to
keep track of message and folder collections. This will come in quite handy as you'll see later on. There is also the
CoCreateGuid API call. You'll use this to generate unique ConversationIndex values for your message threads.
Next you need to add code that starts and ends MAPI services. Listing 10.2 shows the OLEMAPIStart and
OLEMAPIEnd routines. Add them to your project.

Listing 10.2. Adding the OLEMAPIStart and OLEMAPIEnd routines.

Public Sub OLEMAPIStart(cUserProfile As String)


'
' start an OLE MAPI session
'
On Error Resume Next
Set objSession = CreateObject("MAPI.Session")
objSession.Logon profilename:=cUserProfile
'
If Err <> 0 Then
MsgBox "Unable to Start MAPI Services!", vbCritical,
"OLEMAPIStart"
End
End If
'
End Sub

Public Sub OLEMAPIEnd()


'
On Error Resume Next
objSession.Logoff ' end mapi session
'
End Sub

The next routines are used to build a collection of all the folders in all the message stores available to the workstation.
You'll remember that MAPI 1.0 allows more than one message store for each workstation. Typically, users will have a
personal message store, a server-based collection of messages, and possibly a message store related to an outside
messaging service (such as Sprint, CompuServe, and so on). Each one of these stores has its own set of folders, too. The
routine in Listing 10.3 shows you how you can enumerate all the folders in all the message stores and build a local user-
defined type that you can use to locate and manipulate MAPI folders. Add the code shown in Listing 10.3 to your
project.

Listing 10.3. Adding the CollectFolders routine.

Public Sub CollectFolders(Optional ctrl As Variant)


'
' build folder tree
'
Dim objRootFolder As Object
Dim lobjFolders As Object
Dim nIter As Integer
Dim nStoreCount as Integer
Dim bLoadCtrl As Boolean
'
' loading optional display?
If IsMissing(ctrl) Or IsNull(ctrl) Then
ctrl = Null
bLoadCtrl = False
Else
bLoadCtrl = True
End If
'
gnIndentlevel = 0
cStoreID = ""
'
' get collection of information stores
Set objInfoStoreColl = objSession.InfoStores
nStoreCount = objInfoStoreColl.Count
'
' walk through all stores to get folders
For nIter = 1 To nStoreCount
iFldrCnt = iFldrCnt + 1
ReDim Preserve FolderRec(iFldrCnt)
FolderRec(iFldrCnt).StoreID = objInfoStoreColl.Item(nIter).ID
FolderRec(iFldrCnt).FolderID = "" ' no folder for a store!
FolderRec(iFldrCnt).Name = objInfoStoreColl.Item(nIter).Name
cStoreID = FolderRec(iFldrCnt).StoreID ' hold this for the
other folders
'
' add to display control
If bLoadCtrl = True Then
ctrl.AddItem FolderRec(iFldrCnt).Name ' add to display
End If
'
' point to top of store and start loading
Set objRootFolder = objInfoStoreColl.Item(nIter).RootFolder
Set objFolderColl = objRootFolder.Folders
Set lobjFolders = objFolderColl
LoadFolder lobjFolders, ctrl
gnIndentlevel = 0
Next nIter
'
End Sub

You'll see that the routine in Listing 10.3 walks through all the attached message stores and calls the LoadFolders
routine to actually collect all the folders in a message store. This routine also allows you to pass an option list control
(list box, combo box, or outline control). The routine will use this control to build an onscreen pick list of the available
folders.

Now add the LoadFolders routine from Listing 10.4 to your project.

Listing 10.4. Adding the LoadFolders routine.

Sub LoadFolder(aFolders As Object, Optional ctrl As Variant)


'
' look for folders in the collection
'
Dim mobjFolder As Object
Dim mFolderCol As Object
Dim bLoadCtrl As Boolean
'
gnIndentlevel = gnIndentlevel + 1
'
If IsMissing(ctrl) Or IsNull(ctrl) Then
bLoadCtrl = False
Else
bLoadCtrl = True
End If
'
Set mobjFolder = aFolders.GetFirst
While Not mobjFolder Is Nothing
iFldrCnt = iFldrCnt + 1
ReDim Preserve FolderRec(iFldrCnt)
'
FolderRec(iFldrCnt).StoreID = cStoreID
FolderRec(iFldrCnt).FolderID = mobjFolder.ID
FolderRec(iFldrCnt).Name = mobjFolder.Name
'
' optionally load screen display
If bLoadCtrl = True Then
ctrl.AddItem Space(gnIndentlevel * 5) & FolderRec
(iFldrCnt).Name
End If
'
' look for nested folders
Set mFolderCol = mobjFolder.Folders
LoadFolder mFolderCol, ctrl
'
' done with nested folders
gnIndentlevel = gnIndentlevel - 1
Set mobjFolder = aFolders.GetNext
Wend
'
End Sub

Notice that this routine is called recursively in order to collect all the folders that might be found within a folder. Since
MAPI places no restrictions on how many levels of folders may be defined, you need to use a recursive routine to locate
all the available folders.

Once you have built the folder collection, you'll need a method for pulling information out of the collection. Listing 10.5
shows a function that will take the friendly name of a folder and return the unique folder ID and store ID. Add this to
your project.

Listing 10.5. Adding the GetFolderRec function.


Public Function GetFolderRec(cFolderName As String) As FolderType
'
' take name, return structure
'
Dim x As Integer
Dim y As Integer
'
' start w/ a blank return
GetFolderRec.StoreID = ""
GetFolderRec.FolderID = ""
GetFolderRec.Name = ""
'
y = UBound(FolderRec) ' get total recs in array
'
' walk through array
For x = 0 To y - 1
If Trim(UCase(FolderRec(x).Name)) = Trim(UCase(cFolderName))
Then
GetFolderRec = FolderRec(x)
Exit Function
End If
Next x
'
End Function

Warning
This routine will return the folder record of the first folder with the name
you request. Because MAPI allows users to define two folders with the
same name, this routine may not always return the results expected. This
works fine for most projects, but you should keep it in mind when
developing MAPI search tools.

You also need some tools for collecting and accessing all the discussion messages in a folder. Listing 10.6 shows the
routine that you can use to collect all messages into a local user-defined type. Add the routine to your project.

Listing 10.6. Adding the OLEMAPIGetMsgs routine.

Public Sub OLEMAPIGetMsgs(cFolderName As String, Optional ctrl As


Variant)
'
' get all the discussion messages
'
Dim uFolder As FolderType
ReDim MsgRec(0)
Dim bLoadCtrl As Boolean
'
Set objFolder = Nothing
Set objMsgColl = Nothing
Set objMsg = Nothing
'
iMsgCnt = 0
uFolder = GetFolderRec(cFolderName)
'
' check for optional control
If IsMissing(ctrl) Or IsNull(ctrl) Then
ctrl = Null
bLoadCtrl = False
Else
bLoadCtrl = True
ctrl.Clear
End If
'
' now walk through folder to find discussion msgs
If uFolder.Name = "" Then
MsgBox "Unable to locate folder!", vbExclamation, uFolder.
Name
Exit Sub
End If
'
' open store, folder
Set objFolder = objSession.GetFolder(uFolder.FolderID, uFolder.
StoreID)
If objFolder Is Nothing Then
MsgBox "Unable to open folder!", vbExclamation, uFolder.Name
Exit Sub
End If
'
' get message collection
Set objMsgColl = objFolder.Messages
If objMsgColl Is Nothing Then
MsgBox "No messages in folder", vbExclamation, uFolder.Name
Exit Sub
End If
'
' ok, get first message
On Error Resume Next
Set objMsg = objMsgColl.GetFirst
'
' now walk through folder to get all discussion msgs
Do Until objMsg Is Nothing
If objMsg.Type = "IPM.Discuss" Then
iMsgCnt = iMsgCnt + 1
ReDim Preserve MsgRec(iMsgCnt)
MsgRec(iMsgCnt).Subject = objMsg.Subject
MsgRec(iMsgCnt).Topic = objMsg.ConversationTopic
MsgRec(iMsgCnt).ConvIndex = objMsg.ConversationIndex
MsgRec(iMsgCnt).MsgID = objMsg.ID
MsgRec(iMsgCnt).Date = objMsg.TimeReceived
'
' optionally load control
If bLoadCtrl = True Then
ctrl.AddItem objMsg.ConversationIndex
End If
End If
Set objMsg = objMsgColl.GetNext
Loop
'
Beep ' tell 'em you're done!
'
End Sub
You'll notice that this routine allows you to place the messages in a list control for sorting and display. You may also
have noticed that the field placed in the controls is the ConversationIndex property. Sorting the messages on the
ConversationIndex property will automatically give you the threaded list you need. You can then take the sorted
list from the list control and use that to generate an onscreen display of the subject or other properties of the message-all
in threaded order!

You'll need three different routines to access messages from the user-defined array. First, you need a routine that allows
you to pass in a pointer to the sorted list and that returns the ConversationIndex of a message. Listing 10.7 shows
how this is done.

Listing 10.7. Adding the MsgIndex function.

Public Function MsgIndex(ctrl As Control, iPtr As Integer) As String


'
' accept pointer to sorted list control
' and the sorted list control
' return msg convIndex property
'
MsgIndex = ctrl.List(iPtr)
'
End Function

Next you need a routine that takes the conversation index and returns the complete internal message structure. Listing
10.8 shows you how to do this step.

Listing 10.8. Adding the MsgPtr function.

Public Function MsgPtr(cIndex As String) As MsgType


'
' accepts conversation index, returns msg type
'
Dim x As Integer
Dim y As Integer
'
y = UBound(MsgRec)
'
For x = 1 To y
If MsgRec(x).ConvIndex = cIndex Then
MsgPtr = MsgRec(x)
Exit Function
End If
Next x
'
End Function
Finally, you can also use a function that returns the Message user-defined type based on the direct pointer. Add the code
from listing 10.9 to your project.

Listing 10.9. Adding the GetMsgRec function.

Public Function GetMsgRec(iPointer As Integer) As MsgType


'
' accept pointer, return strucutre
'

'
' start w blank records
GetMsgRec.MsgID = ""
GetMsgRec.ConvIndex = ""
GetMsgRec.Subject = ""
GetMsgRec.Topic = ""

'
' now try to find it
If iPointer < 0 Or iPointer > UBound(MsgRec) Then
MsgBox "Invalid Message pointer!", vbExclamation, "GetMsgRec"
Exit Function
Else
GetMsgRec = MsgRec(iPointer + 1)
End If
'
End Function

While you're coding the message routines, add the FillOutline subroutine shown in Listing 10.10. This routine
loads an outline control from the sorted list. The outline can then be displayed to the user.

Listing 10.10. Adding the FillOutline routine.

Public Sub FillOutline(ctrlIn As Control, ctrlOut As Control)


'
' accept a sorted list box as input
' copy the recs to an outline w/ indents
'
Dim x As Integer
Dim uMsg As MsgType
'
ctrlOut.Clear ' throw all the old stuff out
'
' load the outline in sorted order
For x = 0 To ctrlIn.ListCount - 1
uMsg = MsgPtr(ctrlIn.List(x))
ctrlOut.AddItem uMsg.Subject & " (" & Format(uMsg.Date,
"general date") & ➂")"
If bThreaded = True Then
ctrlOut.Indent(x) = Len(ctrlIn.List(x)) / 16
End If
Next x
'
' expand all nodes for viewing
For x = 0 To ctrlOut.ListCount - 1
If ctrlOut.HasSubItems(x) = True Then
ctrlOut.Expand(x) = True
End If
Next x
'
End Sub

The FillOutline routine also makes sure threaded messages are indented properly and expands the entire message
tree for users to see the various branches.

One more handy routine is the MakeTimeStamp function. This will be used to generate the ConversationIndex
values. Add the code from Listing 10.11 to your project.

Listing 10.11. Adding the MakeTimeStamp routine.

Public Function MakeTimeStamp() As String


'
' create Exchange-type time stamp
'
Dim lResult As Long
Dim lGuid As GUID
'
On Error GoTo LocalErr
'
lResult = CoCreateGuid(lGuid)
If lResult = S_OK Then
MakeTimeStamp = Hex$(lGuid.guid1) & Hex$(lGuid.guid2)
Else
MakeTimeStamp = "00000000" ' zeroes
End If
Exit Function

LocalErr:
MsgBox "Error " & Str(Err) & ": " & Error$(Err)
MakeTimeStamp = "00000000"
Exit Function
'
End Function
Only two routines remain: OLEMAPIPostMsg and OLEMAPIReplyMsg. The OLEMAPIPostMsg routine builds and
posts a new message thread to the target folder. Add the code from Listing 10.12 to your project.

Listing 10.12. Adding the OLEMAPIPostMsg routine.

Public Sub OLEMAPIPostMsg(cFolderName As String, cTopic As String,


cSubject As String, cBody As String)
'
' post a message to the folder (starts a new thread)
'
' --------------
' Inputs:
' cFolderName - name of target folder
' cTopic - general discussion topic
' cSubject - user's subject line
' cBody - user's body text
' --------------
'
Dim uFolder As FolderType
'
' get folder structure
uFolder = GetFolderRec(cFolderName) ' get the structure
If uFolder.FolderID = "" Then
MsgBox "Unable to Locate Folder!", vbExclamation, cFolderName
Exit Sub
End If
'
' open folder, store
Set objFolder = objSession.GetFolder(uFolder.FolderID, uFolder.
StoreID)
If objFolder Is Nothing Then
MsgBox "Unable to open folder!", vbExclamation, uFolder.Name
Exit Sub
End If
'
' create new message
Set objMsg = objFolder.Messages.Add
If objMsg Is Nothing Then
MsgBox "Unable to create new message in folder!",
vbExclamation, ➂uFolder.Name
Exit Sub
End If
'
' fix up subject/topic
If cTopic = "" And cSubject = "" Then
cTopic = "Thread #" & Format(Now(), "YYMMDDHHmm")
End If
'
If cTopic = "" And cSubject <> "" Then
cTopic = cSubject
End If
'
If cSubject = "" And cTopic <> "" Then
cSubject = cTopic
End If
'
' now compose the message
With objMsg
'
' set user-supplied info
.Subject = cSubject
.Text = cBody
'
' set stock properties
.Type = "IPM.Discuss"
.ConversationTopic = cTopic
.ConversationIndex = MakeTimeStamp
.TimeSent = Now()
.TimeReceived = .TimeSent
.Submitted = True
.Unread = True
.Sent = True
'
.Update ' force msg into the folder
End With
'
objFolder.Update ' update the folder object
'
End Sub

There are quite a few things going on in this routine. First, it locates the folder and message store where the message will
be posted. Then a new message object is created, populated with the appropriate values, and posted (using the Update
method) to the target folder.

The OLEMAPIReplyMsg is quite similar, but this method carries information forward from the source message to
make sure that the conversation thread is maintained. Add the code from Listing 10.13 to your project.

Listing 10.13. Adding the OLEMAPIReplyMsg routine.

Public Sub OLEMAPIReply(FolderName As String, iCount As Integer,


cSubject As ➂String, cBody As String)
'
' post a message reply (continues existing thread)
'
' -----------------
' Inputs:
' FolderName - string name of target folder
' iCount - index into msg array (points to source msg)
' cSubject - user's subject line (if null uses "RE:"
& source subject)
' cBody - user's msg body
' -----------------
'
Dim cIndex As String
Dim uMsg As MsgType
Dim uFolder As FolderType
Dim objSourceMsg As Object
'
' check msg pointer
If iCount < 0 Then
MsgBox "No Message Selected", vbExclamation, "OLEMAPIReply"
Exit Sub
End If
'
' get folder for posting
uFolder = GetFolderRec(FolderName)
If uFolder.FolderID = "" Then
MsgBox "Unable to locate folder!", vbExclamation, FolderName
Exit Sub
End If
'
' get source message
cIndex = MsgIndex(frmMsgs.list1, iCount)
uMsg = MsgPtr(cIndex)
Set objSourceMsg = objSession.GetMessage(uMsg.MsgID, uFolder.
StoreID)
If objSourceMsg Is Nothing Then
MsgBox "Unable to Load selected Message!", vbExclamation,
uMsg.Subject
Exit Sub
End If
'
' open target store, folder
Set objFolder = objSession.GetFolder(uFolder.FolderID, uFolder.
StoreID)
If objFolder Is Nothing Then
MsgBox "Unable to open target folder", vbExclamation,
uFolder.Name
Exit Sub
End If
'
' create a new blank message object
Set objMsg = objFolder.Messages.Add
If objMsg Is Nothing Then
MsgBox "Unable to add message to folder", vbExclamation,
objFolder.Name
Exit Sub
End If
'
' fix up target subject, if needed
If cSubject = "" Then
cSubject = "RE: " & objSourceMsg.Subject
End If
'
' now compose reply msg
With objMsg
'
' user properties
.Subject = cSubject
.Text = cBody
'
' stock properties
.Type = "IPM.Discuss"
.ConversationTopic = objSourceMsg.ConversationTopic
.ConversationIndex = objSourceMsg.ConversationIndex &
MakeTimeStamp
.TimeSent = Now()
.TimeReceived = .TimeSent
.Submitted = True
.Unread = True
.Sent = True
'
.Update ' force msg into folder
End With
'
objFolder.Update ' update folder object
'
End Sub

That's all there is to this module. Save the module (MAPIPOST.BAS) and the project (DISCUSS.VBP) before moving
on to the next section.

The Discuss and Msgs Forms

The two main forms for the Discuss project are the MDI Discuss form and the Msgs form. The MDI form presents a
button array and hosts all the other forms. The Msgs form is used to display the threaded discussion list.

You'll also need to add a few values to a short BAS module. These are project-level values that are used throughout the
project. Add a BAS module, set its Name property to ModDiscuss and save it as MODDISCUSS.BAS. Now add the
code shown in Listing 10.14 to the general declaration section of the form.

Listing 10.14. Adding project-level declarations.

Option Explicit

'
' constants
Public Const dscRead = 0
Public Const dscNewPost = 1
Public Const dscReply = 2

'
' variables
Public cGroup As String
Public cProfile As String
Public bThreaded As Boolean

That's all you need to add to this form. Save it (MODDISCUSS.BAS) and close it now.

Laying Out the Discuss Form


The Discuss form is the MDI form that controls the entire project. Refer to Figure 10.3 and Table 10.2 when laying out
the Discuss form.

Figure 10.3 : Laying out the Discuss form.

Table 10.2. Controls for the Discuss form.


Control Property Setting
VB.MDIForm Name mdiDiscuss
BackColor &H8000000C&
Caption "Discuss"
ClientHeight 5685
ClientLeft 735
ClientTop 1710
ClientWidth 9210
Height 6090
Left 675
LinkTopic "MDIForm1"
Top 1365
Width 9330
WindowState 2 'Maximized
VB.PictureBox Name Picture2
Align 2 'Align Bottom
Height 345
Left 0
ScaleHeight 285
ScaleWidth 9150
TabIndex 8
Top 5340
Width 9210
VB.Label Name lblStatus
Caption "Label1"
Height 315
Left 60
TabIndex 9
Top 0
Width 9135
VB.PictureBox Name Picture1
Align 1 'Align Top
Height 495
Left 0
ScaleHeight 435
ScaleWidth 9150
TabIndex 0
Top 0
Width 9210
VB.CommandButton Name cmdMain
Caption "&Close"
Font
name="MS Sans Serif"
charset=0
weight=700
size=8.25
underline=0 'False
italic=0 'False
strikethrough=0 'False
Height 300
Index 6
Left 7680
TabIndex 7
Top 60
Width 1200
VB.CommandButton Name cmdMain
Caption "&About"
Font
name= "MS Sans Serif"
charset=0
weight=700
size=8.25
underline=0 'False
italic=0 'False
strikethrough=0 'False
Height 300
Index 5
Left 6420
TabIndex 6
Top 60
Width 1200
VB.CommandButton Name cmdMain
Caption "&Options"
Font
name="MS Sans Serif"
charset=0
weight=700
size=8.25
underline=0 'False
italic=0 'False
strikethrough=0 'False
Height 300
Index 4
Left 5160
TabIndex 5
Top 60
Width 1200
VB.CommandButton Name cmdMain
Caption "&Load Msgs"
Font
name="MS Sans Serif"
charset=0
weight=700
size=8.25
underline=0 'False
italic=0 'False
strikethrough=0 'False
Height 300
Index 3
Left 3840
TabIndex 4
Top 60
Width 1200
VB.CommandButton Name cmdMain
Caption "&Reply"
Font
name="MS Sans Serif"
charset=0
weight=700
size=8.25
underline=0 'False
italic=0 'False
strikethrough=0 'False
Height 300
Index 2
Left 2580
TabIndex 3
Top 60
Width 1200
VB.CommandButton Name cmdMain
Caption "&New Post"
Font
name="MS Sans Serif"
charset=0
weight=700
size=8.25
underline=0 'False
italic=0 'False
strikethrough=0 'False
Height 300
Index 1
Left 1320
TabIndex 2
Top 60
Width 1200
VB.CommandButton Name cmdMain
Caption "R&ead"
Font
name="MS Sans Serif"
charset=0
weight=700
size=8.25
underline=0 'False
italic=0 'False
strikethrough=0 'False
Height 300
Index 0
Left 60
TabIndex 1
Top 60
Width 1200

After you complete the form layout, save the form as MDIDISCUSS.FRM before you begin coding.

Coding the Discuss Form


Most of the code in the MDI Discuss form is needed to react to user buttons or basic form events. There are also two
custom routines used for reading existing messages and creating new ones.

Listing 10.15 shows the MDIForm_Load event. Add this to your project.

Listing 10.15. Coding the MDIForm_Load event.

Private Sub MDIForm_Load()


'
' startup
'
cGroup = GetSetting(App.EXEName, "Options", "Group", "CDG")
cProfile = GetSetting(App.EXEName, "Options", "Profile", "")
bThreaded = GetSetting(App.EXEName, "Options", "Threaded", True)

'
lblStatus = "Logging into Discussion Group [" & cGroup & "]..."
'
OLEMAPIStart cProfile
CollectFolders
OLEMAPIGetMsgs cGroup, frmMsgs.list1
FillOutline frmMsgs.list1, frmMsgs.Outline1
frmMsgs.Show
lblStatus = ""
'
End Sub

Listing 10.16 shows the code for the Activate and Resize events of the form. Add these two routines to your
project.

Listing 10.16. Adding the MDIForm_Activate and MDIForm_Resize events.

Private Sub MDIForm_Activate()


'
Me.Caption = "Discuss [" & cGroup & "]"
'
End Sub

Private Sub MDIForm_Resize()


'
' adjust buttons on form
'
Dim iFactor As Integer
Dim iPosition As Integer
Dim x As Integer
'
iFactor = Me.Width / 7 ' # of buttons
If iFactor < 1200 Then iFactor = 1200
'
For x = 0 To 6
cmdMain(x).Width = iFactor * 0.9
cmdMain(x).Left = iFactor * x + 60
Next x
'
End Sub

Finally, add the code from Listing 10.17 to the MDIForm_Unload event.

Listing 10.17. Adding the MDIForm_Unload event code.

Private Sub MDIForm_Unload(Cancel As Integer)


'
' cleanup before leaving
'

'
' write to registry
SaveSetting App.EXEName, "Options", "Profile", cProfile
SaveSetting App.EXEName, "Options", "Group", cGroup
SaveSetting App.EXEName, "Options", "Threaded", bThreaded

'
'drop all loaded forms
'
Dim x As Integer
Dim y As Integer
'
x = Forms.Count - 1
For y = x To 0 Step -1
Unload Forms(y)
Next
End
'
End Sub

Next, you need to add code to the cmdMain_Click event to handle user selections from the button array. Listing
10.18 shows the code to handle this.

Listing 10.18. Coding the cmdMain_Click event.

Private Sub cmdMain_Click(Index As Integer)


'
' handle main user selections
'
Dim uFolder As FolderType
Dim uMsgRec As MsgType
Dim objMsg As Object
'
Select Case Index
Case 0 ' read
LoadMsgRec dscRead
Case 1 ' new post
NewMsgRec
Case 2 ' reply
LoadMsgRec dscReply
Case 3 ' load msgs
OLEMAPIGetMsgs cGroup, frmMsgs.list1
FillOutline frmMsgs.list1, frmMsgs.Outline1
Case 4 ' options
frmOptions.Show
Case 5 ' about
frmAbout.Show
Case 6 ' close
Unload Me
End Select
'
End Sub

You can see that the cmdMain routine calls two other routines: LoadMsgRec and NewMsgRec. You need to add these
routines to your project. First, add the NewMsgRec subroutine to the form. Add the code from Listing 10.19.

Listing 10.19. Adding the NerwMsgRec routine.

Public Sub NewMsgRec()


'
' start a new posting
'
Load frmNote
frmNote.txtSubject = "Msg #" & Format(Now(), "YYMMDDHHmm")
frmNote.txtTopic = frmNote.txtSubject
frmNote.txtBody = ""
frmNote.lblMode = dscNewPost
frmNote.Show
'
End Sub

The code in Listing 10.19 initializes values on the note form and then calls the form for the user.

The code for the LoadMsgRec routine is a bit more complicated. This routine must first locate the selected message,
load it into the form, and then call the note form. Add the code in Listing 10.20 to your form.
Listing 10.20. Adding the LoadMsgRec routine.

Public Sub LoadMsgRec(iMode As Integer)


'
' read a message record
'
Dim uFolder As FolderType
Dim uMsgRec As MsgType
Dim cIndex As String
'
' first get folder type
uFolder = GetFolderRec(cGroup)
If uFolder.FolderID = "" Then
MsgBox "Unable to locate Folder", vbExclamation,
"ReadMsgRec"
Exit Sub
End If
'
' get message type
cIndex = MsgIndex(frmMsgs.list1, frmMsgs.Outline1.ListIndex)
uMsgRec = MsgPtr(cIndex)
If uMsgRec.MsgID = "" Then
MsgBox "Unable to locate Message", vbExclamation,
"ReadMsgRec"
Exit Sub
End If
'
' now load real msg from MAPI
Set objMsg = objSession.GetMessage(uMsgRec.MsgID, uFolder.
StoreID)
If objMsg Is Nothing Then
MsgBox "Unable to Load Message.", vbExclamation,
"ReadMsgRec"
Exit Sub
End If
'
' must be good, stuff the form
Load frmNote
'
frmNote.txtTopic = objMsg.ConversationTopic
If iMode = dscReply Then
frmNote.txtSubject = "RE: " & objMsg.Subject
Else
frmNote.txtSubject = objMsg.Subject
End If
frmNote.txtBody = objMsg.Text
'
' mark it for read/reply and show it
frmNote.lblMode = iMode
frmNote.lblMsgNbr = frmMsgs.Outline1.ListIndex
frmNote.Show
'
End Sub
That's all for the MDI Discuss form. Save it as MDIDISCUSS.FRM, and save the project as DISCUSS.VBP before you
continue.

Laying Out and Coding the Msgs Form

The Msgs form is used to show the threaded discussion list. Refer to Figure 10.4 and Table 10.3 for details on laying out
the form.

Figure 10.4 : Laying out the Msgs form.

Table 10.3. Controls for the Msgs form.


Control Property Setting
VB.Form Name frmMsgs
Caption "Form1"
ClientHeight 2955
ClientLeft 1140
ClientTop 1515
ClientWidth 5175
Height 3360
Left 1080
LinkTopic "Form1"
MDIChild -1 'True
ScaleHeight 2955
ScaleWidth 5175
Top 1170
Width 5295
VB.ListBox Name List1
Height 840
Left 3060
Sorted -1 'True
TabIndex 1
Top 1200
Visible 0 'False
Width 1095
MSOutl.Outline Name Outline1
Height 2715
Left 120
TabIndex 0
Top 60
Width 4935
Font
Name="MS Sans Serif"
Size=8.25
Charset=0
Weight=700
Underline=0 'False
Italic=0 'False
Strikethrough=0 'False

Note
You'll notice that the form contains both an outline control and a list box
control. The list box control is not visible at run time. It is used to
automatically sort the messages by conversation index.

There is very little code to the form. Listing 10.21 shows all the code you need to add to the Msgs form.

Listing 10.21. Coding the Msgs form.

Private Sub Form_Load()


'
Me.Left = 0
Me.Top = 0
'
Me.Caption = "Discussion Message List"
'
End Sub

Private Sub Form_Resize()


'
' expand outline control to fill form
'
If Me.WindowState <> vbMinimized Then
Outline1.Left = Me.ScaleLeft
Outline1.Top = Me.ScaleTop
Outline1.Width = Me.ScaleWidth
Outline1.Height = Me.ScaleHeight
End If
End Sub

Private Sub Outline1_DblClick()


'
mdiDiscuss.LoadMsgRec dscRead
'
End Sub

Save this form as DSCMSGS.FRM and update the project before going to the next step.
Building the Other Forms

There are three other forms you need to add to the project. The Note form will be used to read and reply to messages; the
Options form allows the user to set and store some program options; and the About dialog box contains typical program
information.

The Note Form

Add a new form to the project and set its Name property to frmNote. Refer to Table 10.4 and Figure 10.5 in laying out
the note form.

Figure 10.5 : Laying out the Note form.

Table 10.4. Controls for the Note form.


Control Property Setting
VB.Form Name frmNote
Caption "Form2"
ClientHeight 4380
ClientLeft 1080
ClientTop 1995
ClientWidth 6255
Height 4785
Left 1020
LinkTopic "Form2"
MDIChild -1 'True
ScaleHeight 4380
ScaleWidth 6255
Top 1650
Width 6375
VB.CommandButton Name cmdBtn
Caption "Cancel"
Font
name="MS Sans Serif"
charset=0
weight=700
size=8.25
underline=0 'False
italic=0 'False
strikethrough=0 'False
Height 300
Index 1
Left 3600
TabIndex 8
Top 3840
Width 1200
VB.CommandButton Name cmdBtn
Caption "OK"
Font
name="MS Sans Serif"
charset=0
weight=700
size=8.25
underline=0 'False
italic=0 'False
strikethrough=0 'False
Height 300
Index 0
Left 4920
TabIndex 5
Top 3840
Width 1200
VB.TextBox Name txtTopic
Font
name="MS Sans Serif"
charset=0
weight=700
size=8.25
underline=0 'False
italic=0 'False
strikethrough=0 'False
Height 300
Left 1320
TabIndex 2
Text "Text3"
Top 120
Width 4800
VB.TextBox Name txtSubject
Font
name="MS Sans Serif"
charset=0
weight=700
size=8.25
underline=0 'False
italic=0 'False
strikethrough=0 'False
Height 300
Left 1320
TabIndex 1
Text "Text2"
Top 480
Width 4800
VB.TextBox Name txtBody
Font
name="MS Sans Serif"
charset=0
weight=700
size=8.25
underline=0 'False
italic=0 'False
strikethrough=0 'False
Height 2835
Left 120
MultiLine -1 'True
ScrollBars 2 'Vertical
TabIndex 0
Text "dscNote.frx":0000
Top 840
Width 6000
VB.Label Name lblMsgNbr
Caption "Label6"
Height 255
Left 420
TabIndex 7
Top 4020
Visible 0 'False
Width 1275
VB.Label Name lblMode
Caption "Label5"
Height 255
Left 1200
TabIndex 6
Top 3840
Visible 0 'False
Width 2055
VB.Label Name Label4
Caption "Subject:"
Font
name="MS Sans Serif"
charset=0
weight=700
size=8.25
underline=0 'False
italic=0 'False
strikethrough=0 'False
Height 300
Left 120
TabIndex 4
Top 480
Width 1200
VB.Label Name Label3
Caption "Topic:"
Font
name="MS Sans Serif"
charset=0
weight=700
size=8.25
underline=0 'False
italic=0 'False
VB.Form Name frmNote
strikethrough=0 'False
Height 300
Left 120
TabIndex 3
Top 120
Width 1200

After laying out the form, save it (DSCNOTE.FRM) before you add the code.

Along with the typical form-related events, you need to add code to handle user button selections and a custom routine to
establish the mode of the form (read, reply, or new message).

First, Listing 10.22 shows all the code for the Form_Load, Form_Activate, and Form_Resize events. Add this
to your project.
Listing 10.22. Adding the Form_Load, Form_Activate, and Form_Resize code.

Private Sub Form_Load()


'
' init on first entry
'
Label1 = "" ' from
Text1 = "" ' topic
Text2 = "" ' subject
Text3 = "" ' body
'
End Sub

Private Sub Form_Activate()


'
StatusUpdate
'
End Sub

Private Sub Form_Resize()


'
' adjust controls for form
'
If Me.WindowState <> vbMinimized Then
txtBody.Left = Me.ScaleLeft
txtBody.Top = 900
txtBody.Width = Me.ScaleWidth
txtBody.Height = Me.ScaleHeight - (1320)
'
txtTopic.Width = Me.ScaleWidth - 1320
txtSubject.Width = Me.ScaleWidth - 1320
'
cmdBtn(0).Left = Me.ScaleWidth - 1440
cmdBtn(1).Left = Me.ScaleWidth - 2880
cmdBtn(1).Top = Me.ScaleHeight - 420
cmdBtn(0).Top = Me.ScaleHeight - 420
End If
End Sub

Worth mentioning here is the Form_Resize code. This code will adjust controls on the form to fill out as much (or as
little) screen area as is allowed.

Another routine that deals with form controls is the StatusUpdate routine (Listing 10.23). This routine toggles the
various controls based on the mode of the form (read, reply, or new message). Add the code from Listing 10.23 to your
form.

Listing 10.23. Adding the StatusUpdate routine.


Public Sub StatusUpdate()
'
' check form status
'
Select Case lblMode
Case Is = dscRead
txtTopic.Enabled = False
txtSubject.Enabled = False
txtBody.Enabled = False
cmdBtn(0).Caption = "Reply"
cmdBtn(0).SetFocus
Me.Caption = "Discussion Read Form [" & txtSubject & "]"
Case Is = dscReply
txtTopic.Enabled = False
txtSubject.Enabled = True
txtBody.Enabled = True
cmdBtn(0).Caption = "Post"
txtSubject.SetFocus
Me.Caption = "Discussion Reply Form [" & txtSubject &
"]"
Case Is = dscNewPost
txtTopic.Enabled = True
txtSubject.Enabled = True
txtBody.Enabled = True
cmdBtn(0).Caption = "Post"
txtTopic.SetFocus
Me.Caption = "Discussion Post New Topic Form"
End Select
'
End Sub

The last routine you need to add to the form is the code for the cmdBtn_Click event. This also uses the mode of the
form to determine just what the program will do when the user presses the OK button. Add the code from Listing 10.24
to your form.

Listing 10.24. Adding the cmdBtn_Click event code.

Private Sub cmdBtn_Click(Index As Integer)


'
' handle use selections
'
Select Case Index
Case 0 ' new post
Select Case lblMode
Case Is = dscNewPost
OLEMAPIPostMsg cGroup, txtTopic, txtSubject,
txtBody
MsgBox "Message has been posted!",
vbInformation, "Discuss OLE"
OLEMAPIGetMsgs cGroup, frmMsgs.list1
FillOutline frmMsgs.list1, frmMsgs.Outline1
Unload Me
Case Is = dscReply
OLEMAPIReply cGroup, Val(lblMsgNbr), txtSubject,
txtBody
MsgBox "Reply has been sent!", vbInformation
OLEMAPIGetMsgs cGroup, frmMsgs.list1
FillOutline frmMsgs.list1, frmMsgs.Outline1
Unload Me
Case Is = dscRead
lblMode = dscReply
txtSubject = "RE: " & txtSubject
txtBody = String(45, 45) & Chr(13) & Chr(10) &
txtBody & Chr(13) & Chr(10) & String(45, 45) & Chr(13) & Chr(10)
Form_Activate
End Select
Case 1 ' close
Unload Me
End Select
'
End Sub

After you complete the code, save the form (DSCNOTE.FRM) and project (DISCUSS.VBP) before you go to the next
section.

The Options Form

The Options form allows the user to select the folder for the discussion group, enter a MAPI profile, and turn the
message display from flat to threaded. Refer to Figure 10.6 and Table 10.5 for laying out the Options form.

Figure 10.6 : Laying out the Options form.

Table 10.5. Controls for the Options form.


Control Property Setting
VB.Form Name frmOptions
BorderStyle 3 'Fixed Dialog
Caption "Form1"
ClientHeight 3270
ClientLeft 2370
ClientTop 2670
ClientWidth 5250
Height 3675
Left 2310
LinkTopic "Form1"
MaxButton 0 'False
MDIChild -1 'True
MinButton 0 'False
ScaleHeight 3270
ScaleWidth 5250
ShowInTaskbar 0 'False
Top 2325
Width 5370
VB.CheckBox Name chkThreaded
Caption "View Message Threads"
Font
name="MS Sans Serif"
charset=0
weight=700
size=8.25
underline=0 'False
italic=0 'False
strikethrough=0 'False
Height 435
Left 2700
TabIndex 7
Top 900
Width 2355
VB.CommandButton Name cmdBtn
Caption "OK"
Font
name="MS Sans Serif"
charset=0
weight=700
size=8.25
underline=0 'False
italic=0 'False
strikethrough=0 'False
Height 300
Index 1
Left 3840
TabIndex 5
Top 2340
Width 1200
VB.CommandButton Name cmdBtn
Caption "Cancel"
Font
name="MS Sans Serif"
charset=0
weight=700
size=8.25
underline=0 'False
italic=0 'False
strikethrough=0 'False
Height 300
Index 0
Left 3840
TabIndex 4
Top 2700
Width 1200
VB.TextBox Name txtProfile
Font
name="MS Sans Serif"
charset=0
weight=700
size=8.25
underline=0 'False
italic=0 'False
strikethrough=0 'False
Height 300
Left 2700
TabIndex 3
Top 480
Width 2400
VB.ListBox Name lstGroup
Font
name="MS Sans Serif"
charset=0
weight=700
size=8.25
underline=0 'False
italic=0 'False
strikethrough=0 'False
Height 2205
Left 120
TabIndex 1
Top 840
Width 2400
VB.Label Name lblSelected
BorderStyle 1 'Fixed Single
Font
name="MS Sans Serif"
charset=0
weight=700
size=8.25
underline=0 'False
italic=0 'False
strikethrough=0 'False
Height 300
Left 120
TabIndex 6
Top 480
Width 2400
VB.Label Name lblProfile
BorderStyle 1 'Fixed Single
Caption "Enter Login Profile:"
Font
name="MS Sans Serif"
charset=0
weight=700
size=8.25
underline=0 'False
italic=0 'False
strikethrough=0 'False
Height 300
Left 2700
TabIndex 2
Top 120
Width 2400
VB.Label Name lblGroup
BorderStyle 1 'Fixed Single
Caption "Select Discussion Group:"
Font
name="MS Sans Serif"
charset=0
weight=700
size=8.25
underline=0 'False
italic=0 'False
strikethrough=0 'False
Height 300
Left 120
TabIndex 0
Top 120
Width 2400

It's a good idea to save this form (DSCOPTIONS.FRM) before you start coding.

There is not much code for the Options form. Listing 10.25 shows the code for the Form_Load event.

Listing 10.25. Coding the Form_Load event.

Private Sub Form_Load()


'
' set up form
'
Me.Caption = "Discuss Options"
txtProfile = cProfile
lblSelected = cGroup
chkThreaded.Value = IIf(bThreaded = 1, 1, 0)

CollectFolders lstGroup
'
Me.Left = (Screen.Width - Me.Width) / 2
Me.Top = (Screen.Height - Me.Height) / 2
'
End Sub

The next code to add is for the cmdBtn_Click event. This routine will save the user's choices in the system registry
for later recall. Add the code from Listing 10.26 to your form.

Listing 10.26. Adding the cmdBtn_Click code.

Private Sub cmdBtn_Click(Index As Integer)


'
' handle user selection
'
Select Case Index
Case 0 ' cancel
' no action
Case 1 ' OK
'
MousePointer = vbHourglass
'
' save to vars
cGroup = Trim(lblSelected)
cProfile = Trim(txtProfile)
bThreaded = IIf(chkThreaded = 1, True, False)

'
' save to registry
SaveSetting App.EXEName, "Options", "Profile", Trim
(cProfile)
SaveSetting App.EXEName, "Options", "Group", Trim
(cGroup)
SaveSetting App.EXEName, "Options", "Threaded", IIf
(chkThreaded.Value = ➂1, 1, 0)
'
' update list & main form
OLEMAPIGetMsgs cGroup, frmMsgs.list1
FillOutline frmMsgs.list1, frmMsgs.Outline1
mdiDiscuss.Caption = "Discuss [" & cGroup & "]"
'
MousePointer = vbNormal
'
End Select
'
Unload Me
'
End Sub

Finally, you need to add one line to the lstGroup_DblClick event:

Private Sub lstGroup_DblClick()

'

lblSelected = Trim(lstGroup.List(lstGroup.ListIndex))

'

End Sub

Now save the form (DSCOPTIONS.FRM) and update the project. Only one more form to go!

The About Dialog Box

The About dialog box contains basic information about the project. Refer to Figure 10.7 and Table 10.6 for laying out
the form.

Figure 10.7 : Laying out the About dialog box.

Table 10.6. Controls for the About dialog box.


Control Property Setting
VB.Form Name frmAbout
BorderStyle 1 'Fixed Single
Caption "Form1"
ClientHeight 1785
ClientLeft 2820
ClientTop 3165
ClientWidth 4335
Height 2190
Left 2760
LinkTopic "Form1"
MaxButton 0 'False
MDIChild -1 'True
MinButton 0 'False
ScaleHeight 1785
ScaleWidth 4335
Top 2820
Width 4455
VB.CommandButton Name cmdOK
Caption "OK"
Font
name="MS Sans Serif"
charset=0
weight=700
size=8.25
underline=0 'False
italic=0 'False
strikethrough=0 'False
Height 300
Left 3000
TabIndex 0
Top 1380
Width 1200
VB.Image Name Image1
Height 1215
Left 120
Picture "dscAbout.frx":0000
Stretch -1 'True
Top 60
Width 1275
VB.Label Name Label1
Caption "Label1"
Font
name="MS Sans Serif"
charset=0
weight=700
size=8.25
underline=0 'False
italic=0 'False
strikethrough=0 'False
Height 1095
Left 1560
TabIndex 1
Top 120
Width 2655

You only need to add code to the Form_Load event and the cmdOK_click event. Listing 10.27 shows all the code
for the About form.

Listing 10.27. Coding the Form_Load and cmdOK_Click events.

Private Sub cmdOK_Click()


Unload Me
End Sub

Private Sub Form_Load()


'
' set up about box
'
Me.Caption = "About " & App.ProductName
'
Image1.Stretch = True
'
Label1.Caption = App.FileDescription & Chr(13) & Chr(13)
Label1 = Label1 & App.LegalCopyright & Chr(13) & Chr(13)
'
Me.Left = (Screen.Width - Me.Width) / 2
Me.Top = (Screen.Height - Me.Height) / 2
'
End Sub

That's the last of the code for the Discuss project. Save this form (DSCABOUT.FRM) and update the project before you
begin testing.

Testing the Discuss Forum Tool


Once you've coded and compiled the project, you'll be able to load it onto any workstation that has MAPI services
installed and available.

Note
The discussion tool will not work in a Remote Mail setup. You need to have
direct access to at least one message store.

When you first start Discuss, you'll be asked to log into MAPI. One of the first things you should do is open the Options
page and select a target folder and set up your default user profile (see Figure 10.8).

Figure 10.8 : Setting Discuss options.

You can select any folder as the discussion target. However, since the idea is to carry on discussions with other people,
you'll typically select a public folder as the discussion target.

Note
If you are working on a standalone machine, you can select any personal
folder for this demonstration. Just remember that no one but you can see the
results!

The program will list only messages that have their Type property set to IPM.Discuss. This means the first time you
select a target folder, you won't see any messages. So the next thing you need to do is add a message!

Press New Post to add a new thread to the folder (see Figure 10.9).

Figure 10.9 : Adding a new thread to the folder.

You can also highlight any message in the discussion and double-click it to read it. When you press the Reply button at
the bottom of a read form, it automatically turns into a reply form. Even the subject and message body are updated to
show that this is a reply (see Figure 10.10).

Figure 10.10 : Replying to a message.

Summary

In this chapter you learned how to use the OLE Messaging library to create online, threaded discussion tools using MAPI
services. You learned that there are three ways in which discussion messages differ from mail messages:

● Discussion messages are addressed to folders, not people.


● MAPI uses the ConversationTopic and ConversationIndex properties to keep track of discussion
threads.
● You use the MAPI Update method to deliver discussion messages. You use the MAPI Send method to
deliver mail messages.

You also learned how to build routines that collect all the message stores available to a workstation, all the folders in
each of those stores, and all the messages within a folder. You now know how to compose a message for Update
(rather than Send) and how to use the CoCreateGuid API to generate unique conversation index values used in
threaded discussion tools.

Best of all, most of the heart of this example program can be used to add discussion capabilities to other Visual Basic
projects.
Chapter 11

Creating a MAPI Email Agent

CONTENTS

● Designing the Email Agent


❍ Features of the MAPI Email Agent

❍ Storing the Rules in a Control File

● Coding the MAPI Email Agent Forms


❍ The Main Form

❍ The Add Rule Form

❍ The Setup Form

● Coding the Support Routines


❍ The Initialization Routines

❍ The List-Handling Routines

❍ The Message Processing Routines

● Installing and Testing the MAPI Email Agent


❍ Setting Up Your Email Folders

❍ Building MAPI Email Agent Actions, Tests, and Rules

❍ Running the MAPI Email Agent

● Summary

In this chapter you'll learn how to use the OLE Messaging library to create a stand-alone e-mail agent. This agent can
scan your incoming mail and, based on rules you establish, automatically handle messages for you. All actions are based
on rules you establish in a control file.

Features of the MAPI Email Agent are:

● Timed scanning of your inbox.


● Notification of message arrivals based on level of importance.
● Automatic routing of incoming messages to other e-mail users.
● Automatic replies to messages based on subject or sender.
● Automatic sorting of unread messages into separate folders based on subject, sender, or level of importance.

The first part of the chapter will discuss the concept of e-mail agents and cover the overall design of the program. The
next sections will detail the coding of the forms and support routines needed to complete the project. In the final section,
you'll install and test the MAPI Email Agent program.

Note
The Microsoft Exchange Server clients allow users to establish and code
their own mail agent (called the Inbox Assistant). If you have the Microsoft
Exchange Server client, now is a good time to review the Inbox
Assistant to get an idea of its features. The programming project
covered in this chapter works independently of the Inbox Assistant
and does not require that users have Microsoft Exchange Server installed on
their system.

When you complete the programming project in this chapter, you'll have a fully functional MAPI Email Agent that can
be installed on any workstation that has access to a MAPI-compliant e-mail system. Also, the techniques used to build
this project can be incorporated into other Windows programming projects to add message processing capabilities.

Designing the Email Agent

Before starting to code the project, it's a good idea to discuss the general features and functions of an e-mail agent. Once
you have a good idea of what e-mail agents can do, then you can lay out the basic design features of the MAPI Email
Agent programming project covered in this chapter.

Basically, an e-mail agent is a program that "acts for you." It is a program that reviews your messages and, based on
information you have supplied, processes the messages for you. Typically, e-mail agents process messages in the user's
inbox. Users can set up rules that tell the agent how to handle each new message. These rules can tell the e-mail agent to
check various parts of the incoming message and then take a specific action.

For example, the agent can be instructed to look for all messages that are received from your boss and then place those
messages in a special folder called "Urgent." Or the agent could be told that any message with the word "SALES" in the
subject line should be immediately forwarded to another user's inbox and then erased from your inbox without any
comment. You might also tell the agent to automatically reply to all senders that you are out on vacation and will return
next week.

Features of the MAPI Email Agent

For this chapter, you'll use Visual Basic 4.0 and the OLE Messaging library to build a stand-alone MAPI Email Agent
that has the following features:

● Timed scanning of your inbox-Users can start the program and allow it to run unattended. It scans the inbox for
new messages every N minutes. Users can also set configuration values that start the program as an icon on the
task bar or as a dialog box.
● Message notification-Users can establish a rule that causes a dialog box to pop up each time a specific message
is received. This notification can be based on sender ID, subject content, or level of importance.
● Automatic forwarding-Users can create rules that automatically forward messages to other addresses. Users can
also determine whether the original should be kept or discarded.
● Automatic replies-Users can create rules that generate automated replies to senders based on sender ID, subject
content, or level of importance.
● Automatic message copying or moving-Users can create rules that copy or move incoming messages to other
folders in the user's message store. This feature can be used to sort incoming message by sender ID, subject
content, or level of importance.

Note
The features described here are just the start of what can be accomplished
with an e-mail agent. The number of options has been limited to keep this
chapter focused on design and coding issues instead of content. As you
build your own agent, you can add many other capabilities.

To accomplish this, the MAPI Email Agent will keep track of rules created by the user. These rules will have three parts:
tests, comparisons, and actions. The test portion of the rule performs a simple scan of the designated portion of the
message, searching for requested content. The MAPI Email Agent described in this chapter is capable of inspecting three
message parts:

● SUBJECT-Checks the Subject property of the message


● SENDER-Checks the Sender.Name property of the message
● PRIORITY-Checks the Importance property of the message

For example, the test SUBJECT MAPI tells the agent to check the message subject for the word "MAPI." The phrase
SENDER Boss tells the agent to check for messages sent to the user from the e-mail ID "boss."

All tests must use a logical condition as part of the processing. The MAPI Email Agent uses comparisons to do this. The
program can check for the following four logical conditions:

● EQ-Equals (SENDER Sharon EQ)


● GT-Greater Than (PRIORITY 0 GT)
● LT-Less Than (PRIORITY 1 LT)
● CI-Is Contained In (SUBJECT VB CI)

You'll notice that the last value is able to check the selected message part for the occurrence of a word or phrase. Note
that all the comparisons are case-insensitive. It is important to note that the LT and GT can be used with character data,
too.

The last of the three portions of a rule is the action. This is the part of the rule that tells the agent what action to take if
the test criteria have been met. The MAPI Email Agent can perform the following actions on a message:

● MOVE-Move the message to another folder (MOVE Urgent)


● COPY-Copy the message to another folder (COPY Archive)
● FORWARD-Forward the message to another user (FORWARD [email protected])
● REPLY-Send reply text to the user (REPLY reply.txt)

The agent allows users to determine whether the forwarded and reply messages are retained or removed once the
forward/reply is generated.

Storing the Rules in a Control File

The MAPI Email Agent allows users to build tests and actions, and then use them to create rules. All this information is
stored in a text file similar to an INI file. This file also contains general control information, such as the scan interval,
whether the agent should create a log file, the default log on profiles, and so on. Listing 11.1 shows a sample rule file.

Listing 11.1. Sample rule file for the MAPI Email Agent.
; ********************************************************
; MAPI Email Agent Control File
; ********************************************************
;
[General]
Editor=notepad.exe
ScanInterval=2
LogFile=mea.log
LogFlag=1
RuleCount=3
ActionCount=4
TestCount=4
Profile=MCA
DeleteForwardFlag=0
NotifyDialog=1
DeleteReplyFlag=0
MinimzeOnStart=0
AutoStart=0
LastUpdated=04/29/96 9:27:30 PM

[Actions]