Chapter 36

The Talk Mail Project


CONTENTS


The last integration project in the book combines MAPI services with audio recording. The Talk Mail project allows you to record messages and then send them to others via e-mail. It's a bit like an asynchronous telephone conversation.

You will need a sound card, a microphone, and speakers (or headphones) in order to run this project. You will be able to select one or more recipients, press the record button, record a message, and ship both the header data and the audio binary file to any other person in the MAPI directory.

Note
In this version of the Talk Mail project, all MAPI addresses are resolved against current names in your address book(s). You could add code to allow any user with a universal e-mail address (for example, yourname@internet.com) to receive the audio recordings. However, you'll need to ensure that the file is sent and received properly by Internet users before you add this feature.

You'll use the OLE Messaging library to implement the MAPI services. The audio recording will be handled using the Windows Media Control Interface (MCI). This project will make use of the new Windows 95 controls for Visual Basic, including the listview control, the toolbar control, and the statusbar control.

Warning
Because this project makes extensive use of the Windows 95 controls, you will not be able to build it on a 16-bit Windows platform. You'll need to use the 32-bit version of Visual Basic and run it on Windows 95 or Windows NT.

Design Considerations

One of the advantages of the OLE Messaging library over the MAPI OCX tool is the added access to MAPI objects and properties. In this project we'll define a new message type (IPM.Note.TalkMail) for all messages generated by our Talk Mail application. The real benefit in this is that we'll be able to tell our Talk Mail client to pay attention only to the messages generated by other Talk Mail clients. This way, when our new voice-mail client checks the MAPI inbox for new messages, only the Talk Mail messages will appear on the screen.

The voice recordings, in the form of WAV files, will be shipped as an attachment with each MAPI message. The Talk Mail client will automatically load the WAV file into the MCI form control, ready for playback (or further recording). The Talk Mail client never tells the user that there is an attachment to the message. In fact, as far as the user is concerned, there is no attachment!

Tip
All Talk Mail messages can be viewed from the standard Windows Messaging or Microsoft Mail MAPI clients. When you use the standard MAPI clients, you'll see the WAV file appear as an attachment to the text message.

It may seem that we should use the SAPI services for the "talking" portion of the application. However, because the primary focus of this application is voice, it's a better idea to use the pure voice recording instead of rendering text into speech. Also, by using the MCI tool that ships with Visual Basic Professional and Enterprise Editions, you can provide full record and playback services with very little coding. Therefore, this application will take advantage of users' tendency to prefer actual voices over computer-generated sound.

Project Forms and Resources

The project has four forms and one code module. The code module holds routines for handling several OLE operations along with global variable declarations and other support routines. The four forms for the project are

You'll need to make sure you have the following references and custom controls added to your project:

Tip
You load the object libraries using the Tools | References menu option. You load the OCX controls using the Tools | Custom Controls menu option.

Now that you have a good idea of how the project works, let's jump right into creating the BAS module that will hold the general support routines for the project.

Coding the LibTalkMail Module

The LibTalkMail module has seven subroutines and three functions. Most of these routines deal with MAPI service requests but others include error handling and simple helper functions to manage the Talk Mail messages. This module also has several public variables that are declared here and used throughout the program.

First, start a new Visual Basic project and make sure you have loaded the OLE Messaging library and the custom controls listed in the preceding section.

Next, add a module to the project (Insert | Module) and set its Name property to LibTalkMail. Now add the code in Listing 36.1 to the general declaration section of the module.


Listing 36.1. Public variables and objects for the Talk Mail project.
Option Explicit

'
' internal message pointer
Type MsgPtr
    ID As String
    Subject As String
End Type
'
' to track msgs
Public uMsgP() As MsgPtr
Public iMsgCount As Integer
Public cMsgID As String
'
' ole message libary objects
Public objSession As Object
Public objInBox As Object
Public objOutBox As Object
Public objMsgColl As Object
Public objAttach As Object
Public objAttachColl As Object
Public objRecipColl As Object
Public objRecip As Object
Public objMsg As Object
Public objAddrEntry As Object
'
' for MCI wav work
Public cWavFile As String
Public Const conInterval = 50
Public Const conIntervalPlus = 55

There are three sets of variables and objects in the code in Listing 36.1. First you see the creation of a user-defined type (UDT) that will hold the unique MAPI message ID and the MAPI subject. This UDT will be used to perform quick lookups of a selected record in the mail system. After defining the MsgPtr type, three variables are defined to hold information about the message pool. The next set of declarations defines the OLE message objects. These will be used (and re-used) throughout the program. Finally, the module contains a few variables for handling the MCI control.

Next you need to add the MAPI-related subroutines, the first of which is the MAPIStart routine. This routine is called to begin a MAPI session. Add a new subroutine called MAPIStart (select Insert | Procedure) and enter the code shown in Listing 36.2.


Listing 36.2. Adding the MAPIStart routine.
Public Sub MAPIStart()
    '
    ' log into mapi
    '
    On Error GoTo LocalErr
    '
    Set objSession = CreateObject("MAPI.Session")
    objSession.Logon
    '
    Set objInBox = objSession.Inbox
    Set objOutBox = objSession.Outbox
    Exit Sub
    '
LocalErr:
    MsgBox ErrMsg(Err), vbCritical, "MAPIStart"
    '
End Sub

The MAPIEnd routine is called when the user wants to end the MAPI session completely. Most of the code in the MAPIEnd routine is required to free precious workstation memory.

Tip
It is always a good idea to set Visual Basic objects equal to Nothing when you are through with them. This frees workstation memory and can improve overall workstation performance.

Add a new subroutine (MAPIEnd) and enter the code from Listing 36.3.


Listing 36.3. Adding the MAPIEnd routine.

Public Sub MAPIEnd()
    '
    On Error Resume Next
    '
    objSession.Logoff
    Set objSession = Nothing
    Set objInBox = Nothing
    Set objOutBox = Nothing
    Set objMsgColl = Nothing
    Set objRecip = Nothing
    Set objMsg = Nothing
    '
End Sub

Two short MAPI routines are MAPIAddrBook and MAPIDeleteMsg. These two routines give the user access to the MAPI address book and delete a selected message, respectively. Add these two subroutines to your project from Listings 36.4 and 36.5.


Listing 36.4. Adding the MAPIAddrBook routine.
Public Sub MAPIAddrBook()
    '
    On Error GoTo LocalErr
    '
    objSession.AddressBook
    Exit Sub
    '
LocalErr:
    MsgBox ErrMsg(Err), vbCritical, "MAPIAddrBook"
    '
End Sub

The MAPIDeleteMsg routine will remove a selected message object from the collection. Add the code from Listing 36.5 to your project.


Listing 36.5. Adding the MAPIDeleteMsg routine.
Public Sub MAPIDeleteMsg(cMsgID As String)
    '
    ' delete selected message
    '
    On Error GoTo LocalErr
    '
    Dim iAns As Integer
    '
    Set objMsg = objSession.GetMessage(cMsgID)
    '
    iAns = MsgBox(objMsg.Subject, vbExclamation + vbYesNo, "Delete Talk Mail ÂMessage")
    If iAns = vbYes Then
        objMsg.Delete
    End If
    '
    Exit Sub
    '
LocalErr:
    MsgBox ErrMsg(Err), vbCritical, "MAPIDeleteMsg"
    '
End Sub

Next you need to add two routines that call the tmRead and tmNew dialog boxes to read or create new messages. These two routines are almost identical. The only difference is the name of the form that is launched. Add the MAPINewMsg subroutine and enter the code form Listing 36.6.


Listing 36.6. Adding the MAPINewMsg routine.
Public Sub MAPINewMsg()
    '
    ' allow user to compose a message
    '
    frmTMView.CMDialog1.Filter = "WaveForm File (*.wav)|*.wav"
    frmTMNew.MMControl1.DeviceType = "WaveAudio"
    frmTMNew.Show
    frmTMView.Hide
    '
End Sub

Note that this routine (and the MAPIReadMsg routine) both just hide the main form rather than unloading it. This is necessary because the project has some values that must be shared between forms. Now add the MAPIReadMsg routine shown in Listing 36.7.


Listing 36.7. Adding the MAPIReadMsg routine.
Public Sub MAPIReadMsg(cMsgID As String)
    '
    ' allow user to view a message
    '
    frmTMView.CMDialog1.Filter = "WaveForm File (*.wav)|*.wav"
    frmTMRead.MMControl1.DeviceType = "WaveAudio"
    frmTMRead.Show
    frmTMView.Hide
    '
End Sub

The next routine is the largest one in the module. The MAPISendMsg routine is the one that builds the MAPI message object and makes sure it is sent off to the recipient(s). Add the MAPISendMsg subroutine to the project and enter the code from Listing 36.8.


Listing 36.8. Adding the MAPISendMsg routine.

Public Sub MAPISendMsg(objRecipList As Object, cSubject As String, cBody As String, ÂcWavFile As String)
    '
    ' construct a message and send it off
    '
    On Error GoTo LocalErr
    '
    Dim x As Integer
    '
    ' build new message
    Set objMsg = objOutBox.Messages.Add
    objMsg.Type = "IPM.Note.TalkMail"
    objMsg.Subject = cSubject
    objMsg.Text = " " & cBody
    '
    ' add wav file as attachment
    If Len(cWavFile) <> 0 Then
        Set objAttach = objMsg.Attachments.Add
        objAttach.Name = "Talk Mail.WAV" 'cWavFile
        objAttach.position = 0
        objAttach.Type = mapiFileData
        objAttach.Source = cWavFile
        objAttach.ReadFromFile cWavFile
    End If
    '
    ' load recipient(s)
    If objRecipList Is Nothing Then
        MsgBox "No Recipients!"
    Else
        For x = 1 To objRecipList.Count
            Set objRecip = objMsg.Recipients.Add
            objRecip.Name = objRecipList.Item(x).Name
        Next x
    End If
    '
    ' send it out quietly
    On Error Resume Next
    objMsg.Update ' update all the collections
    objMsg.Send showdialog:=False
    '
    ' let everyone know
    MsgBox "Message Sent", vbInformation, "Talk Mail"
    '
    Exit Sub
    '
LocalErr:
    MsgBox ErrMsg(Err), vbCritical, "MAPISendMsg"
    '
End Sub

There are four main tasks performed by this routine. First, the message body is assembled. Note the use of IPM.Note.TalkMail in the Type property. You'll use this value as a search criterion when you refresh the list of available Talk Mail messages.

Next, the recorded WAV audio file is added to the package as an attachment. There are a couple of important points to make here. First, it is important that you use ".WAV" as the suffix of the Name property of the attachment object. This will make sure you can click the object, and Windows will call up the audio player automatically. Also, when you are attaching data to the message (not just linking, but attaching), you need to invoke the ReadFromFile method to actually load the selected file into the message package. Unlike the OCX controls, OLE does not do this for you.

Third, you need to add the recipients from the collection returned by the address book into the collection for the message. This requires iterating through the source object and using the results to insert into the message's receipt object.

Lastly, the routine updates all its child objects and quietly sends the MAPI package to the MAPI spooler for delivery.

There are three general helper functions that need to be added to the LibTalkMail module. The first one is the FindSubject function. This is used to return a unique MAPI message ID using the UDT you built at the start of this section. Add the FindSubject function and enter the code shown in Listing 36.9.


Listing 36.9. Adding the FindSubject function.

Public Function FindSubject(cSubject As String) As String
    '
    ' takes subject line, returns MsgID
    '
    Dim x As Integer
    Dim cRtn As String
    '
    cRtn = "" ' start empty
    For x = 1 To iMsgCount
        If UCase(cSubject) = UCase(uMsgP(x).Subject) Then
            cRtn = uMsgP(x).ID
            Exit For
        End If
    Next x
    '
    FindSubject = cRtn
    '
End Function

Another very valuable helper function is the GetRecipients function. This routine accepts a collection of recipients and extracts the display name into a single string for producing an onscreen list of the message recipients. After adding the new GetRecipients function to the project, enter the code shown in Listing 36.10.


Listing 36.10. Adding the new GetRecipients function.

Public Function GetRecipients(objRecipColl As Object) As String
    '
    ' pull qualified recipients from object collection
    '
    Dim cRtn As String
    Dim x As Integer
    '
    cRtn = ""
    '
    If objRecipColl Is Nothing Then
        GoTo LeaveHere
    End If
    '
    For x = 1 To objRecipColl.Count
        Set objRecip = objRecipColl.Item(x)
        cRtn = cRtn & objRecip.Name & ";"
    Next x
    '
    If Len(cRtn) > 0 Then
        cRtn = Left(cRtn, Len(cRtn) - 1)
    End If
    '
LeaveHere:
    GetRecipients = cRtn
    '
End Function

The last helper function you need to add to the LibTalkMail module is the ErrMsg function. This routine uses the new Visual Basic 4.0 Err object to build an informative message concerning the name, type, and originator of the message. Add the code in Listing 36.11 to your project.


Listing 36.11. Add the ErrMsg function.
Public Function ErrMsg(ErrObj As Object) As String
    '
    ' return formatted message
    '
    Dim cMsg As String
    '
    cMsg = "ErrCode:" & Chr(9) & CStr(ErrObj.Number) & Chr(13)
    cMsg = cMsg & "ErrMsg:" & Chr(9) & ErrObj.Description & Chr(13)
    cMsg = cMsg & "Source:" & Chr(9) & ErrObj.Source
    '
    ErrMsg = cMsg
    '
End Function

That's all you need to add to the BAS module of the Talk Mail project. Be sure to save this file under the name LIBTMAIL.BAS and the project under the name TALKMAIL.VBP before continuing.

The tmView Form

The main form shows a list of all the Talk Mail messages in the user's inbox. It also has a set of toolbar buttons for performing the basic MAPI actions of logon, logoff, viewing the address book, creating a new Talk Mail message, reading an Existing Talk Mail message, and deleting a Talk Mail message. There are also buttons for adjusting the list view from large icon, small icon, list, and detail formats.

Laying Out tmView

The first step is to lay out the form's controls. Refer to Table 36.1 and Figure 36.1 for details on the layout of the tmView form.

Figure 36.1 : Laying out the tmView form.

Table 36.1. The tmView form controls.
ControlProperty Setting
VB.FormNamefrmTMView
 Caption"Form1"
 Height4410
 Left1185
 Top1665
 Width8715
MSComDlg.CommonDialogName CMDialog1
 Left6360
 Top2880
ComctlLib.StatusBarName StatusBar1
 Align2 'Align Bottom
 Height315
 Left0
 TabIndex2
 Top3405
 Width8595
 AlignSet-1 'True
 SimpleText""
ComctlLib.ImageListName imglstSmall
 Left3720
 Top1440
ComctlLib.ImageListName imglstBig
 Left3900
 Top660
ComctlLib.ImageListName imglstToolBar
 Left5040
 Top120
 ImageWidth16
 ImageHeight16
ComctlLib.ToolbarName tbrMain
 Align1 'Align Top
 Height390
 Left0
 TabIndex1
 Top0
 Width8595
 ImageList""
 AlignSet-1 'True
ComctlLib.ListViewName ListView1
 Height2295
 Left120
 TabIndex0
 Top480
 Width8355
 appearance1
 Arrange2
 Icons"ImageList3"
 SmallIcons"ImageList2"
 View2

The form doesn't look like much at first, but you'll add all the fancy stuff (bitmaps, icons, and so on) at runtime. Once you get the controls onto the form, you need to build the menu. Figure 36.2 and Table 36.2 show you the details of the menu layout.

Figure 36.2 : Laying out the tmView menu.

Tip
The menu layout makes extensive use of menu arrays. Be sure to pay close attention to the index number values when building the menu. You'll use these index values throughout the program.

Table 36.2. The tmView form menu.
Menu LevelProperties Settings
TopName mnuFile
 Caption "File"
Level 2Name mnuFileItem
 Caption "Log &In..."
 Index 0
Level 2Name mnuFileItem
 Caption "Log &Out"
 Index 1
Level 2Name mnuFileItem
 Caption "-"
 Index 2
Level 2Name mnuFileItem
 Caption "&Scan Inbox"
 Index 3
Level 2Name mnuFileItem
 Caption "&Address Book..."
 Index 4
Level 2Name mnuFileItem
 Caption "-"
 Index 5
Level 2Name mnuFileItem
 Caption "E&xit"
 Index 6
TopName mnuMsgs
 Caption "&Messages"
Level 2Name mnuMsgsItem
 Caption "&New..."
 Index 0
Level 2Name mnuMsgsItem
 Caption "&Read"
 Index 1
Level 2Name mnuMsgsItem
 Caption "&Delete"
 Index 2
TopName mnuView
 Caption "&View"
Level 2Name mnuViewItem
 Caption "Lar&ge Icons"
 Index 0
Level 2Name mnuViewItem
 Caption "&Small Icons"
 Index 1
Level 2Name mnuViewItem
 Caption "L&ist"
 Index 2
Level 2Name mnuViewItem
 Caption "&Details"
 Index 3
TopName mnuHelp
 Caption "&Help"
Level 2Name mnuHelpItem
 Caption "&About"
 Index 0

Once you have the form controls and menus in place, save the form as TMVIEW.FRM and save the project (TALKMAIL.VBP) before you go on to add the form code.

Coding tmView

Most of the code for the tmView form is needed to set up the toolbar, listview, and statusbar controls. There are also several code sections for handling the menu selections and the basic form events. There are a few short events for handling toolbar and list view clicks and there is one routine for requesting new messages from the MAPI service.

The code can be divided into the following related groups:

Coding the Form Events

The Form_Load event calls the routines to build the form controls and then set some properties of the form itself. Enter the code shown in Listing 36.12 into the Form_Load event.


Listing 36.12. Coding the Form_Load event.

Private Sub Form_Load()
    '
    BuildToolBar
    BuildListView
    BuildStatusBar
    '
    Me.Caption = "Talk Mail 95"
    Me.Left = (Screen.Width - Me.Width) / 2
    Me.Top = (Screen.Height - Me.Height) / 2
    Me.Icon = LoadPicture(App.Path & "\tmMsg32.ico")
    '
End Sub

Warning
The code example above uses the App.Path property to locate the icon file. This will not work properly if you locate your project in a root directory of a drive. It is recommended that you place all project files in a single directory. If you place your files in a root directory, you'll need to modify the code that uses the app.path object.

You also need to add code to the Form_Resize event. This code will resize the listview control to make sure it fills the form. Enter the code from Listing 36.13 into your project.


Listing 36.13. Adding the Form_Resize event code.

Private Sub Form_Resize()
    '
    ' handle form resizing
    '
    If Me.WindowState <> vbMinimized Then
        ListView1.Left = 0
        ListView1.Top = tbrMain.Height
        ListView1.Width = Me.ScaleWidth
        ListView1.Height = Me.ScaleHeight - tbrMain.Height - StatusBar1.Height
    End If
    '
End Sub

The Control Building Code

Three of the controls (listview, toolbar, and statusbar) require extensive setup. While this could be done at design time using the custom property boxes, it is easier to modify the properties if you build the controls at run-time.

Add a new subroutine called BuildStatusBar and enter the code shown in Listing 36.14.


Listing 36.14. Adding the BuildStatusBar routine.
Public Sub BuildStatusBar()
    '
    ' create details of status bar display
    '
    Dim I As Integer
    '
    For I = 1 To 4
        StatusBar1.Panels.Add   ' Add Panel objects.
        StatusBar1.Panels(I).AutoSize = sbrContents
    Next I

    With StatusBar1.Panels
        .Item(1).Style = sbrText
        .Item(2).Style = sbrCaps ' Caps lock
        .Item(3).Style = sbrIns ' insert
        .Item(4).Style = sbrDate ' date
        .Item(5).Style = sbrTime ' time
    End With
    '
    StatusBar1.Panels(1).Text = "Talk Mail" & Space(50)
    StatusBar1.Style = sbrNormal
    '
End Sub

The code in Listing 36.14 sets up a status bar that will show the status of the CAPS and INS buttons, the date and time, and reserve some space for a status message.

Next, add a new subroutine called BuildListView and enter the code shown in Listing 36.15.


Listing 36.15. Adding the BuildListView routine.
Public Sub BuildListView()
    '
    ' build list view
    '
    Dim clmX As ColumnHeader
    Dim ItmX As ListItem
    Dim ImgX As ListImage
    '
    ' set view properties
    ListView1.BorderStyle = ccFixedSingle
    ListView1.View = lvwReport
    ListView1.Sorted = True
    mnuViewItem(3).Checked = True ' show check on menu
    '
    ' set column headers for detail view
    Set clmX = ListView1.ColumnHeaders. _
        Add(, , "Subject", ListView1.Width * 0.25)
    Set clmX = ListView1.ColumnHeaders. _
        Add(, , "RecvDate", ListView1.Width * 0.25)
    Set clmX = ListView1.ColumnHeaders. _
        Add(, , "From", ListView1.Width * 0.25)
    '
    ' set large image list control
    imglstbig.ImageHeight = 32
    imglstbig.ImageWidth = 32
    Set ImgX = imglstbig.ListImages. _
        Add(, , LoadPicture(App.Path & "\tmMsg32.ico"))
    Set ImgX = imglstbig.ListImages. _
        Add(, , LoadPicture(App.Path & "\note32.ico"))
    '
    ' set small image control
    imglstsmall.ImageHeight = 16
    imglstsmall.ImageWidth = 16
    Set ImgX = imglstsmall.ListImages. _
        Add(, , LoadPicture(App.Path & "\tmMsg16.ico"))
    Set ImgX = imglstsmall.ListImages. _
        Add(, , LoadPicture(App.Path & "\note16.ico"))
    '
    ' link image and listview
    ListView1.Icons = imglstbig
    ListView1.SmallIcons = imglstsmall
    '
End Sub

The code in Listing 36.15 performs several tasks. First, columns are defined for the detail view. Next, two image controls are initialized for the large and small icon views. Finally, those two image controls are bound to the listview control so that the list view knows which icons to use when displaying the contents.

The last of the "build" routines is the BuildToolBar routine. This is the most involved of the three because it contains fourteen buttons (some of them separators) with all their icons, tooltips, and keys. Add the new subroutine (BuildToolBar) and enter the code shown in Listing 36.16.


Listing 36.16. Adding the BuildToolBar routine.

Public Sub BuildToolBar()
    '
    ' set up win95 tool bar
    '
    Dim ImgX As ListImage
    Dim BtnX As Button
    Dim tlbrPics(14) As String
    Dim tlbrKeys(14) As String
    Dim tlbrTips(14) As String
    Dim tlbrStyle(14) As Integer
    Dim x As Integer
    '
    tlbrPics(1) = "tmStart.ico"
    tlbrPics(2) = "tmEnd.ico"
    tlbrPics(3) = "tmstart.ico"
    tlbrPics(4) = "tmAdrBk.ico"
    tlbrPics(5) = "tmScan.ico"
    tlbrPics(6) = "tmstart.ico"
    tlbrPics(7) = "tmMsg16.ico"
    tlbrPics(8) = "tmRead.ico"
    tlbrPics(9) = "tmTrash.ico"
    tlbrPics(10) = "tmstart.ico"
    tlbrPics(11) = "vw-lrgic.bmp"
    tlbrPics(12) = "vw-smlic.bmp"
    tlbrPics(13) = "vw-list.bmp"
    tlbrPics(14) = "vw-dtls.bmp"
    '
    tlbrKeys(1) = "LOGIN"
    tlbrKeys(2) = "LOGOUT"
    tlbrKeys(3) = ""
    tlbrKeys(4) = "ADDRBOOK"
    tlbrKeys(5) = "SCANINBOX"
    tlbrKeys(6) = ""
    tlbrKeys(7) = "NEWMSG"
    tlbrKeys(8) = "READMSG"
    tlbrKeys(9) = "DELETE"
    tlbrKeys(10) = ""
    tlbrKeys(11) = "ICONS"
    tlbrKeys(12) = "SMALLICONS"
    tlbrKeys(13) = "LISTVIEW"
    tlbrKeys(14) = "DETAILS"
    '
    tlbrTips(1) = "Log In"
    tlbrTips(2) = "Log Out"
    tlbrTips(3) = ""
    tlbrTips(4) = "Address Book"
    tlbrTips(5) = "Scan InBox"
    tlbrTips(6) = ""
    tlbrTips(7) = "New Msg"
    tlbrTips(8) = "Read Msg"
    tlbrTips(9) = "Delete Msg"
    tlbrTips(10) = ""
    tlbrTips(11) = "Icons"
    tlbrTips(12) = "Small Icons"
    tlbrTips(13) = "List View"
    tlbrTips(14) = "Details"
    '
    imglstToolbar.ImageHeight = 16
    imglstToolbar.ImageWidth = 16
    '
    ' fill image list
    For x = 1 To 14
        Set ImgX = imglstToolbar.ListImages. _
          Add(, , LoadPicture(App.Path & "\" & tlbrPics(x)))
    Next x
    '
    ' fill toolbar
    tbrMain.ImageList = imglstToolbar
    For x = 1 To 14
        Set BtnX = tbrMain.Buttons.Add
        BtnX.Key = tlbrKeys(x)
        BtnX.ToolTipText = tlbrTips(x)
        If tlbrTips(x) <> "" Then
            BtnX.Style = tbrDefault
            BtnX.Image = x
        Else
            BtnX.Style = tbrSeparator
        End If
    Next x
End Sub

Note
This routine refers to several icon files. These icons can be found on the CD-ROM that ships with this book. They should have been copied to your local drive when you installed the CD-ROM. Make sure you copy them to your project directory before you run the project.

The Menu Support Routines

There are four main menu branches: File, Message, View, and Help. Each branch is built as a menu array. You need to add code to handle user selections for each menu branch.

First, add the code shown in Listing 36.17 to the mnuFileItem_Click event.


Listing 36.17. Adding the mnuFileItem_Click event code.

Private Sub mnuFileItem_Click(Index As Integer)
    '
    ' handle file menu stuff
    '
    Select Case Index
        Case 0 ' login
            MAPIStart
        Case 1 ' logout
            MAPIEnd
        Case 3 ' scan inbox
            MAPIScanInBox
        Case 4 ' addr book
            MAPIAddrBook
        Case 6 ' exit
            MAPIEnd
            Unload Me
    End Select
    '
End Sub

Next, add the code from Listing 36.18 to the mnuMsgItem_Click event.


Listing 36.18. Coding the mnuMsgItem_Click event.

Private Sub mnuMsgsItem_Click(Index As Integer)
    '
    ' handle msgs menu
    '
    Dim Itm As Object
    Dim x As Integer
    '
    Select Case Index
        Case 0 ' new
            MAPINewMsg
        Case 1 ' read
            If cMsgID <> "" Then
                MAPIReadMsg cMsgID
            End If
        Case 2 ' delete
            If cMsgID <> "" Then
                MAPIDeleteMsg cMsgID
                MAPIScanInBox ' update view
            End If
    End Select
    '
End Sub

The next code section deals with adjustments in the list view options. Add the code from Listing 36.19 to the mnuViewItem_Click event.


Listing 36.19. Coding the mnuViewItem_Click event.

Private Sub mnuViewItem_Click(Index As Integer)
    '
    ' handle views
    '
    Dim x As Integer
    '
    Select Case Index
        Case 0 ' large icon
            ListView1.View = lvwIcon
        Case 1 ' small icon
            ListView1.View = lvwSmallIcon
        Case 2 ' list view
            ListView1.View = lvwList
        Case 3 ' detail view
            ListView1.View = lvwReport
    End Select
    '
    For x = 0 To 3
        If x = Index Then
            mnuViewItem(x).Checked = True
        Else
            mnuViewItem(x).Checked = False
        End If
    Next x
    '
End Sub

The last menu code is for the mnuHelpItem_Click event. Enter the code from Listing 36.20 into your project.


Listing 36.20. Adding the mnuHelpItem_Click event code.

Private Sub mnuHelpItem_Click(Index As Integer)
    '
    ' handle help menu
    '
    Select Case Index
        Case 0 ' about
            frmAbout.Show vbModal
    End Select
    '
End Sub

The last control event code you need to add is the code that handles the toolbar selections. This code looks very similar to the menu event code. In this case, the user selection is determined by the value of the Button.Key property. Enter the code from Listing 36.21 into the tbrMain_ButtonClick event.


Listing 36.21. Coding the tbrMain_ButtonClick event.
Private Sub TbrMain_ButtonClick(ByVal Button As Button)
    '
    Select Case Button.Key
        Case "LOGIN"
            MAPIStart
            MAPIScanInBox
        Case "LOGOUT"
            MAPIEnd
            Unload Me
        Case "ADDRBOOK"
            MAPIAddrBook
        Case "NEWMSG"
            MAPINewMsg
        Case "READMSG"
            MAPIReadMsg cMsgID
        Case "DELETE"
            MAPIDeleteMsg cMsgID
            MAPIScanInBox
        Case "SCANINBOX"
            MAPIScanInBox
        Case "ICONS"
            mnuViewItem_Click 0
        Case "SMALLICONS"
            mnuViewItem_Click 1
        Case "LISTVIEW"
            mnuViewItem_Click 2
        Case "DETAILS"
            mnuViewItem_Click 3
    End Select
    '
End Sub

Coding the Control Events

The last set of code routines for the tmView form is the code that is executed for various control events. There are three events for the list control. These events adjust the sorting order, collect the subject of the selected item in the list, and launch the Talk Mail message reader. Add code for the three routines shown in Listing 36.22.


Listing 36.22. Coding the ListView events.
Private Sub ListView1_ColumnClick(ByVal ColumnHeader As ColumnHeader)
    ListView1.SortKey = ColumnHeader.Index - 1 ' change sort order
End Sub

Private Sub ListView1_DblClick()
    MAPIReadMsg cMsgID ' launch reader form
End Sub

Private Sub ListView1_ItemClick(ByVal Item As ListItem)
    cMsgID = FindSubject(Item.Text) ' get subject text
End Sub

There is only one more code routine for the tmView form-the MAPIScanInBox routine. This routine scans the MAPI inbox for any Talk Mail messages addressed to the user and fills the list view with the results. This routine also builds an array of message pointers that will be used to locate messages in the MAPI infostore whenever the user wishes to read a particular message. Add the MAPIScanInBox subroutine to the project and enter the code shown in Listing 36.23.


Listing 36.23. Adding the MAPIScanInBox code.

Private Sub MAPIScanInBox()
    '
    ' scan inbox for msgs
    '
    On Error Resume Next
    '
    Dim ItmX As ListItem
    '
    ' refresh collections
    Set objInBox = objSession.Inbox
    Set objMsgColl = objInBox.Messages
    '
    ListView1.ListItems.Clear
    iMsgCount = 0
    ReDim uMsgP(0)
    '
    ' only get talk mail records
    Set objMsg = objMsgColl.GetFirst("IPM.Note.TalkMail")
    Do Until objMsg Is Nothing
        Set ItmX = ListView1.ListItems.Add
        ItmX.Icon = 1
        ItmX.SmallIcon = 1
        If Trim(objMsg.Subject) = "" Then
            ItmX.Text = "<No Subject>"
        Else
            ItmX.Text = objMsg.Subject
        End If
        '
        ItmX.SubItems(1) = Format(objMsg.TimeReceived, "general date")
        '
        Set objAddrEntry = objMsg.Sender
        If objAddrEntry Is Nothing Then
            ItmX.SubItems(2) = "<No Sender>"
        Else
            ItmX.SubItems(2) = objAddrEntry.Name
        End If
        '
        ' store in pointer set
        iMsgCount = iMsgCount + 1
        ReDim Preserve uMsgP(iMsgCount)
        uMsgP(iMsgCount).ID = objMsg.ID
        uMsgP(iMsgCount).Subject = ItmX.Text
        '
        Set objMsg = objMsgColl.GetNext
    Loop
    '
    Set objMsg = Nothing
    Set objMsgColl = Nothing
    '
    StatusBar1.Panels(1).Text = CStr(iMsgCount) & " Talk Mail Message(s)" & ÂSpace(40)
    '
    Exit Sub
    '
LocalErr:
    MsgBox ErrMsg(Err), vbCritical, "MAPIScanInBox"
    '
End Sub

That's the end of the code routines for the tmView form. Be sure to save the form (TMVIEW.FRM) and the project (TALKMAIL.VBP) before you continue.

The tmNew and tmRead Forms

The tmNew and tmRead forms are where the real action happens. These forms present the user with some input controls for selecting recipients, setting a subject line, and entering a supporting text message. The center of the form is the MCI control. This can be used for playback and recording of the audio WAV file.

Laying Out tmNew

First, refer to Figure 36.3 and Table 36.3 for the layout of the tmNew form.

Figure 36.3 : Laying out the tmNew form.

Table 36.3. The tmNew form controls.
ControlProperty Setting
VB.FormNamefrmTMNew
 Caption"Talk Mail Compose Form"
 Height6030
 Left2415
 Top1455
 Width5250
VB.HScrollBarNameHScroll1
 Height300
 Left120
 Max100
 TabIndex12
 Top2220
 Width4860
VB.CommandButtonName cmdNewMsg
 Cancel-1 'True
 Caption"&Cancel"
 Height300
 Index1
 Left2460
 TabIndex5
 Top4920
 Width1200
VB.CommandButtonName cmdAddrBook
 Caption"Send To... "
 Height300
 Left120
 TabIndex0
 Top120
 Width1200
VB.CommandButtonName cmdNewMsg
 Caption"&Send"
 Height300
 Index0
 Left3780
 TabIndex4
 Top4920
 Width1200
VB.TextBoxNametxtSubject
 Height300
 Left1380
 TabIndex1
 Text"Text3"
 Top1080
 Width3600
VB.TextBoxNametxtBody
 Height1995
 Left120
 MultiLine-1 'True
 ScrollBars2 'Vertical
 TabIndex3
 Top2820
 Width4860
VB.LabelNameLabel3
 Alignment2 'Center
 Caption"Pause"
 Height300
 Left2520
 TabIndex16
 Top1860
 Width795
VB.LabelNamelblFFwd
 Alignment2 'Center
 Caption"FFwd"
 Height300
 Left960
 TabIndex15
 Top1860
 Width735
VB.LabelNameLabel1
 Height255
 Left120
 TabIndex14
 Top2520
 Width600
VB.LabelNameLabel2
 Height255
 Left4380
 TabIndex13
 Top2520
 Width600
VB.LabelNamelblRecord
 Alignment2 'Center
 Caption"Record"
 Height300
 Left4140
 TabIndex11
 Top1860
 Width855
VB.LabelNamelblStop
 Alignment2 'Center
 Caption"Stop"
 Height300
 Left3360
 TabIndex10
 Top1860
 Width735
VB.LabelNamelblPlay
 Alignment2 'Center
 Caption"Play"
 Height300
 Left1740
 TabIndex9
 Top1860
 Width735
VB.LabelNamelblRewind
 Alignment2 'Center
 Caption"Rewind"
 Height300
 Left120
 TabIndex8
 Top1860
 Width795
MCI.MMControlNameMMControl1
 Height315
 Left120
 TabIndex2
 Top1500
 Width4875
 BackVisible0 'False
 StepVisible0 'False
 EjectVisible0 'False
 BorderStyle1
VB.LabelNamelblRecips
 BorderStyle1 'Fixed Single
 Caption"Label3"
 Height900
 Left1380
 TabIndex7
 Top120
 Width3615
VB.LabelNamelblSubject
 BorderStyle1 'Fixed Single
 Caption"Subject:"
 Height300
 Left120
 TabIndex6
 Top1080
 Width1200

The tmNew form also has a short menu. Refer to Figure 36.4 and Table 36.4 for laying out the form menu.

Figure 36.4 : Building the tmNew form menu.

Table 36.4. The tmNew form menu.
LevelProperties Settings
TopName mnuFile
 Caption "File"
Level 2Name mnuFileItem
 Caption "Save Wave File..."
 Index 0
Level 2Name mnuFileItem
 Caption "&Send Message"
 Index 1
Level 2Name mnuFileItem
 Caption "-"
 Index 2
Level 2Name mnuFileItem
 Caption "&Cancel && Exit"
 Index 3

Now is a good time to save the new form (TMNEW.FRM) and the project (TALKMAIL.VBP) before you add the code to the form.

Coding tmNew

Most of the code for the tmNew form is needed to handle MCI control events. However, there are also menu and button events that need to be addressed.

First, add the following lines to the general declaration area:

Option Explicit
'
Dim CurrentValue As Double

This variable is used to keep track of the progress of the audio playback.

Next, add the Form_Load event code shown in Listing 36.24. This code sets some basic form properties, creates a temporary filename for the audio file, and calls the HandleOpen routine to establish the audio WAV file.


Listing 36.24. Coding the Form_Load event.
Private Sub Form_Load()
    '
    ' first startup of form
    '
    MMControl1.Wait = True
    '
    txtSubject = "Talk Mail Message #" & Format(Now(), "DDHHmmss")
    txtBody = "Message Recorded at " & Format(Now, "general date")
    lblrecips = ""
    '
    Me.Icon = LoadPicture(App.Path & "\tmMsg32.ico")
    Me.Caption = "Talk Mail Compose Form"
    Me.Left = (Screen.Width - Me.Width) / 2
    Me.Top = (Screen.Height - Me.Height) / 2
    '
    cWavFile = App.Path & "\" & Format(Now(), "DDHHmmss") & ".wav"
    FileCopy App.Path & "\xxx.wav", cWavFile
    HandleOpen cWavFile
    '
End Sub

Warning
The code in Listing 36.25 refers to a file called "xxx.wav." This is an empty WAV format audio file that is used as a "starter" file for each new message. This file can be found on the CD-ROM that ships with this book. If you cannot find the file, you can make a new one using the WAV audio applet that ships with Windows or the one that came with your sound card.

Listing 36.25 shows a short bit of code for the Form_Unload event. Add this to your project.


Listing 36.25. Coding the Form_Unload event.
Private Sub Form_Unload(Cancel As Integer)
    Me.Hide
    frmTMView.Show
End Sub

The next big chunk of code is for the HandleOpen subroutine. This routine initializes the MCI control and clears the scrollbar values. Add the code from Listing 36.26 to your form.


Listing 36.26. Adding the HandleOpen routine.

Private Sub HandleOpen(cWavFile As String)
    Dim msec As Double

    ' Set the number of milliseconds between successive
    ' StatusUpdate events.
    MMControl1.UpdateInterval = 0

    ' If the device is open, close it.
    If Not MMControl1.Mode = mciModeNotOpen Then
        MMControl1.Command = "Close"
    End If

    ' Open the device with the new filename.
    MMControl1.filename = cWavFile
    On Error GoTo MCI_ERROR
    MMControl1.Command = "Open"
    On Error GoTo 0
    '
    SetTiming
    '
    ' Set the scrollbar values.
    Hscroll1.value = 0
    CurrentValue = 0#
    '
    Exit Sub

MCI_ERROR:
    MsgBox ErrMsg(Err), vbCritical, "frmTMNew.HandleOpen"
    Resume MCI_EXIT

MCI_EXIT:
    Unload Me

End Sub

The HandleOpen routine calls another support routine-the SetTiming routine. This short routine establishes the total length of existing recordings and is used to set the start and stop limits of the scrollbar. Add the code from Listing 36.27 to your form.


Listing 36.27. Adding the SetTiming routine.
Public Sub SetTiming()
    '
    ' Set the timing labels on the form.
    Dim msec As Double
    '
    MMControl1.TimeFormat = mciFormatMilliseconds
    Label1.Caption = "0.0"
    msec = (CDbl(MMControl1.Length) / 1000)
    Label2.Caption = Format$(msec, "0.00")
    '
End Sub

Next you can add the code to handle the menu array. This code allows the user to save the recorded file to disk, send the message, and exit the form without sending the message. Add the code from Listing 36.28 to your form.


Listing 36.28. Coding the mnuFileItem_Click event.
Private Sub mnuFileItem_Click(Index As Integer)
    '
    ' handle user menu selections
    '
    Select Case Index
        Case 0 ' save as
            MMControl1.filename = cWavFile
            frmTMView.CMDialog1.ShowSave
            MMControl1.filename = frmTMView.CMDialog1.filename
            MMControl1.Command = "Save"
        Case 1 ' send
            cmdNewMsg_Click 0
        Case 2 ' na
        Case 3 ' close
            cmdNewMsg_Click 1
    End Select
End Sub

There are two command buttons on the form in a control array-Cancel and OK. The code in Listing 36.29 should be added to the cmdNewMsg_Click event.


Listing 36.29. Coding the cmdNewMsg_Click event.
Private Sub cmdNewMsg_Click(Index As Integer)
    '
    ' handle new msg buttons
    '
    Select Case Index
        Case 0 ' OK to send
            MMControl1.Command = "Save" ' save the wave file
            MMControl1.Command = "Close" ' close out and release
            MAPISendMsg objRecipColl, txtSubject, txtBody, cWavFile ' send message
        Case 1 ' cancel send
            ' na
    End Select
    '
    On Error Resume Next
    Kill cWavFile ' remove temp file
    Unload Me ' return to caller
    '
End Sub

Notice the call to the MAPISendMsg routine. You built this routine in the LibTalkMail module at the start of the chapter.

There is one other button on the form-the Send To... button. When the user presses this button, the MAPI address book appears. When the user is finished selecting addresses, a recipients collection is returned. This is the list of people who will receive the message.

Note
In this program users can send messages only if they use the To: option and not Cc: or Bcc: This was done to simplify the project. If you want, you can modify the project to address messages to courtesy copy and blind courtesy copy recipients.

Once the collection is returned, the GetRecipients function is used to pull the names out of the collection and insert them into the label control on the form. Add the code shown in Listing 36.30 to the cmdAddrBook_Click event.


Listing 36.30. Coding the cmdAddrBook_Click event.
Private Sub cmdAddrBook_Click()
    '
    ' get recipients from user
    '
    Dim x As Integer
    '
    Set objRecipColl = objSession.AddressBook(reciplists:=1, Title:="Select ÂTalkMail Recipient(s)")
    lblrecips = GetRecipients(objRecipColl)
    '
    '
End Sub

The last set of code routines for the tmNew form are for the MCI multimedia control. First, add the code in Listing 36.31 to the MMControl1_StatusUpdate event.


Listing 36.31. Coding the MMControl1_StatusUpdate event.

Private Sub MMControl1_StatusUpdate()
    Dim value As Integer

    ' If the device is not playing, reset to the beginning.
    If Not MMControl1.Mode = mciModePlay Then
        Hscroll1.value = Hscroll1.Max
        MMControl1.UpdateInterval = 0
        Exit Sub
    End If

    ' Determine how much of the file has played.  Set a
    ' value of the scrollbar between 0 and 100.
    CurrentValue = CurrentValue + conIntervalPlus
    value = CInt((CurrentValue / MMControl1.Length) * 100)

    If value > Hscroll1.Max Then
        value = 100
    End If

    Hscroll1.value = value
End Sub

This code is used to compute playback progress and update the scrollbar control on the form.

There are four other code events for the MCI control. These are executed whenever the user presses Pause, Play, Rewind, or Stop. Listing 36.32 shows the code for these four events.


Listing 36.32. Coding the MCI button events.
Private Sub MMControl1_PauseClick(Cancel As Integer)
    ' Set the number of milliseconds between successive
    ' StatusUpdate events.
    If MMControl1.UpdateInterval = 0 Then
        MMControl1.UpdateInterval = conInterval
    Else
        MMControl1.UpdateInterval = 0
    End If
End Sub

Private Sub MMControl1_PlayClick(Cancel As Integer)
    ' Set the number of milliseconds between successive
    ' StatusUpdate events.
    MMControl1.UpdateInterval = conInterval
End Sub

Private Sub MMControl1_PrevClick(Cancel As Integer)
    ' Set the number of milliseconds between successive
    ' StatusUpdate events.
    MMControl1.UpdateInterval = 0

    ' Reset the scrollbar values.
    Hscroll1.value = 0
    CurrentValue = 0#

    MMControl1.Command = "Prev"
End Sub

Private Sub MMControl1_StopClick(Cancel As Integer)
    '
    SetTiming ' compute any new value
    '
End Sub

That's the end of the code for the tmNew form. Save the form (TMNEW.FRM) and project (TALKMAIL.VBP) before coding the tmRead form.

Laying Out tmRead

The tmRead form is almost identical to the tmNew form. This form is used to read Talk Mail messages sent to the user. For this reason, most of the controls have been disabled and the form is basically in a "read-only" mode.

Normally, users would read a message and then generate a reply to the sender or forward the message to another party. For this example, no reply buttons have been placed on the form. This has been done to keep the project simple and to focus on the integration of MAPI and MCI services. You can add these options later if you wish.

Add a new form to the project and lay out the controls as shown in Figure 36.5 and in Table 36.5.

Figure 36.5 : Laying out the tmRead form.

Table 36.5. The tmRead controls.
ControlProperty Setting
VB.Form NamefrmTMRead
 Caption "Talk Mail Reader Form"
 Height 6030
 Left 2415
 Top 1455
 Width 5250
VB.HScrollBar NameHScroll1
 Height 300
 Left 120
 Max 100
 TabIndex 9
 Top 2220
 Width 4860
VB.CommandButton NamecmdNewMsg
 Caption "&Close"
 Height 300
 Index 0
 Left 3780
 TabIndex 2
 Top 4980
 Width 1200
VB.TextBox NametxtBody
 BackColor &H00C0C0C0&
 Height 1995
 Left 120
 MultiLine -1 'True
 ScrollBars 2 'Vertical
 TabIndex 1
 Top 2880
 Width 4860
VB.Label NamelblSubjLine
 BorderStyle 1 'Fixed Single
 Caption "Label4"
 Height 315
 Left 1380
 TabIndex 15
 Top 1080
 Width 3615
VB.Label NamelblSender
 BorderStyle 1 'Fixed Single
 Caption "Sent by:"
 Height 300
 Left 120
 TabIndex 14
 Top 120
 Width 1200
VB.Label NameLabel3
 Alignment 2 'Center
 Caption "Pause"
 Height 300
 Left 2520
 TabIndex 13
 Top 1860
 Width 795
VB.Label NamelblFFwd
 Alignment 2 'Center
 Caption "FFwd"
 Height 300
 Left 960
 TabIndex 12
 Top 1860
 Width 735
VB.Label NameLabel1
 Height 255
 Left 120
 TabIndex 11
 Top 2520
 Width 600
VB.Label NameLabel2
 Height 255
 Left 4380
 TabIndex 10
 Top 2520
 Width 600
VB.Label NamelblRecord
 Alignment 2 'Center
 Caption "Record"
 Height 300
 Left 4140
 TabIndex 8
 Top 1860
 Width 855
VB.Label NamelblStop
 Alignment 2 'Center
 Caption "Stop"
 Height 300
 Left 3360
 TabIndex 7
 Top 1860
 Width 735
VB.Label NamelblPlay
 Alignment 2 'Center
 Caption "Play"
 Height 300
 Left 1740
 TabIndex 6
 Top 1860
 Width 735
VB.Label NamelblRewind
 Alignment 2 'Center
 Caption "Rewind"
 Height 300
 Left 120
 TabIndex 5
 Top 1860
 Width 795
MCI.MMControl NameMMControl1
 Height 315
 Left 120
 TabIndex 0
 Top 1500
 Width 4875
 BackVisible 0 'False
 StepVisible 0 'False
 EjectVisible 0 'False
 BorderStyle 1
VB.Label NamelblRecips
 BorderStyle 1 'Fixed Single
 Caption "Label3"
 Height 900
 Left 1380
 TabIndex 4
 Top 120
 Width 3615
VB.Label NamelblSubject
 BorderStyle 1 'Fixed Single
 Caption "Subject:"
 Height 300
 Left 120
 TabIndex 3
 Top 1080
 Width 1200

After laying out the form controls, you can add the form menu. Refer to Figure 36.6 and Table 36.6 for details on the tmRead menu.

Figure 36.6 : Adding the tmRead menu.

Table 36.6. The tmRead menu levels and properties.
LevelProperties Settings
TopName mnuFile
 Caption "File"
Level 2Name mnuFileItem
 Caption "Save Wave File..."
 Index 0
Level 2Name mnuFileItem
 Caption "-"
 Index 2
Level 2Name mnuFileItem
 Caption "&Close"
 Index 3

After building the form, save it as TMREAD.FRM before you go on to add the code.

Coding tmRead

As mentioned earlier in the chapter, the tmNew and tmRead forms are very similar. In fact, the two largest sections of form code, the HandleOpen and the MMControl1... events, are the same in both projects. As long as you are careful, you can copy the code from the tmNew form onto the tmRead form. Do this by bringing up the tmNew form, highlighting all of the HandleOpen routine, select Edit | Copy, and then move to the tmRead form and use Edit | Paste to place the code in the general declarations section.

The following code sections from tmNew can be copied to tmRead using the same technique:

Once you've successfully copied these routines from tmNew to tmRead you need to add just a few more routines to the tmRead form.

First, add the following lines to the general declaration section of the form.

Option Explicit
'
Dim CurrentValue As Double

Next, add the Form_Load event code shown in Listing 36.33.


Listing 36.33. Adding the Form_Load event code.

Private Sub Form_Load()
    '
    ' first startup of form
    '
    On Error GoTo LocalErr
    '
    MMControl1.Wait = True
    '
    Dim cID As String
    Dim x As Integer
    '
    Set objMsg = objSession.GetMessage(cMsgID)
    '
    lblSubjLine = objMsg.Subject
    txtBody = objMsg.Text
    lblrecips = GetRecipients(objMsg.Recipients)
    '
    Me.Icon = LoadPicture(App.Path & "\tmMsg32.ico")
    Me.Caption = "Talk Mail Reader Form"
    Me.Left = (Screen.Width - Me.Width) / 2
    Me.Top = (Screen.Height - Me.Height) / 2
    '
    ' get attachment to read
    Set objAttachColl = objMsg.Attachments
    If objAttachColl Is Nothing Then Exit Sub
    For x = 1 To objAttachColl.Count
        Set objAttach = objAttachColl.Item(x)
        objAttach.WriteToFile "temp.wav"
        cWavFile = objAttach.Source
    Next x
    '
    ' go open it with the WAV device
    HandleOpen "temp.wav"
    '
    Exit Sub
    '
LocalErr:
    MsgBox ErrMsg(Err), vbCritical, "frmTMRead.Form_Load"
    '
End Sub

This code is slightly different from the code in the Form_Load event of tmNew. Here, you want to open the selected message object and extract the audio WAV attachment, save it to a temporary file, and then place that audio file into the MCI control for playback (using the OpenHandle routine).

Next, add the code for the Form_Unload event shown in Listing 36.34.


Listing 36.34. Coding the Form_Unload event.
Private Sub Form_Unload(Cancel As Integer)
    Me.Hide
    frmTMView.Show
End Sub
Because this form only has a Close button, you need to add one line of code to the cmdNewmsg_Click event:
Private Sub cmdNewMsg_Click(Index As Integer)
    Unload Me
End Sub

]Next, you need to add a line of code to the txtBody control to prevent users from typing the read-only form.

Private Sub txtBody_KeyPress(KeyAscii As Integer)
    KeyAscii = 0 ' ignore any keystrokes here
End Sub

Tip
You may notice that the txtBody control's background color was set to light gray to make it look like a label control instead of an input control. Under the Windows GUI standards, "actionable" controls have a white background and read-only controls have a light gray background.

The only form code left is the code that handles the user's menu selections. Add the code shown in Listing 36.35 to your form.


Listing 36.35. Coding the mnuFileItem_Click event.
Private Sub mnuFileItem_Click(Index As Integer)
    '
    ' handle user menu selections
    '
    Select Case Index
        Case 0 ' save as
            MMControl1.filename = cWavFile
            frmTMView.CMDialog1.ShowSave
            MMControl1.filename = frmTMView.CMDialog1.filename
            MMControl1.Command = "Save"
        Case 2 ' close
            cmdNewMsg_Click 1
    End Select
End Sub

That's the end of coding for the tmRead form. Save the form (TMREAD.FRM) and the project (TALKMAIL.VBP) before you add the final About box to the application.

The tmAbout Box

The tmABout box is a simple About dialog box for the project. Figure 36.7 and Table 36.7 show the layout information for the form.

Figure 36.7 : Laying out the tmAbout form.

Table 36.7. The tmAbout controls.
ControlProperty Setting
VB.Form NamefrmAbout
 BorderStyle 3 'Fixed Dialog
 Caption "About Talk Mail"
 Height 2415
 Left 2955
 MaxButton 0 'False
 MinButton 0 'False
 ShowInTaskbar 0 'False
 Top 2415
 Width 4230
VB.CommandButton NamecmdOK
 Caption "OK"
 Height 300
 Left 2760
 TabIndex 0
 Top 1560
 Width 1200
VB.Label NameLabel1
 Caption "Label1"
 Height 1155
 Left 1320
 TabIndex 1
 Top 180
 Width 2595
VB.Image NameImage1
 Height 1215
 Left 180
 Top 120
 Width 915

You need to add just a little bit of code to the Form_Load event to complete the tmAbout form. Listing 36.36 shows the code you should add to the form.


Listing 36.36. Adding code to the Form_Load event.
Private Sub Form_Load()
    '
    ' build simple about box
    '
    Me.Icon = LoadPicture(App.Path & "\tmMsg32.ico")
    Me.Caption = "About " & App.Title
    Me.Left = (Screen.Width - Me.Width) / 2
    Me.Top = (Screen.Height - Me.Height) / 2
    '
    Image1.Stretch = True
    Image1.Picture = LoadPicture(App.Path & "\tmMsg32.ico")
    '
    Label1 = App.Title & Chr(13) & Chr(13)
    Label1 = Label1 & App.LegalCopyright & Chr(13) & Chr(13)
    Label1 = Label1 & App.FileDescription
    '
End Sub

The last bit of code is a single line in the cmdOK_Click event:

Private Sub cmdOK_Click()
    Unload Me
End Sub

Now save the form (TMABOUT.FRM) and the project (TALKMAIL.VBP). You are now finished coding and ready to test the program.

Testing Talk Mail

First, you need to compile Talk Mail and save it to disk, but before you compile it, you need to add a bit of information to the App object properties. These properties will be displayed in the application's About box.

Select File | Make EXE | Options to bring up the dialog box for setting the App objects properties. Refer to Table 36.8 and Figure 36.8 for setting the properties.

Figure 36.8 : The APP object properties dialog box.

Table 36.8. Setting the App object properties.
PropertySetting
Title Talk Mail
Comments Demonstration of MAPI and MCI
Company MCA/SAMS
File Description E-mail Client for Voice Recording
Legal Copyright (c)1996 MCA/SAMS Publishing
Auto Increment Checked ON

Once you set these properties, press OK and then compile the project. When you are able to compile the project without errors, you're ready to start testing Talk Mail.

Tip
It is a good idea to compile the Talk Mail project before running it. The process of compiling will point out any coding errors and the resulting program will run faster than in design mode.

When you first start Talk Mail, you see the populated button bar and an empty listview control. Press the green stoplight button (or select File | Log In from the menu) to log into MAPI and collect any Talk Mail messages you have waiting in your inbox (see Figure 36.9).

Figure 36.9 : Logging into Talk Mail.

The first time you log into Talk Mail, you'll see no messages for you. You can fix this by using Talk Mail to send yourself the first message. To do this, press the microphone toolbar button or select Message | New from the menu. You'll see the Talk Mail compose form ready to record your message (see Figure 36.10).

Figure 36.10 : Adding a new Talk Mail message.

The form comes up with a default subject line and a notation in the text message area. You can press the record button to begin recording your message and press stop when you are finished. You can also press playback, pause, rewind, and fast-forward to review your message. You can even append additional information to the end of your message if you wish.

You must select at least one recipient before you can send your message. To address the Talk Mail message, press the Send To... button at the top left of the form. You'll see the MAPI address dialog box appear (see Figure 36.11).

Figure 36.11 : Addressing the Talk Mail message.

For your first message, address it to yourself. You can then "read" this message using Talk Mail. After addressing the message, press the Send button at the bottom of the form or select File | Send Message from the menu. You'll receive notification that the message was sent and then you will be returned to the list view form.

Now you can select File | Scan InBox from the menu or press the inbox button to re-read your inbox. Your message should now appear in the list view. You can listen to your message by selecting the item and pressing the read button (it's the ear!), or by selecting Message | Read from the menu. You'll see the read form and be able to use the MCI control to play back the recording (see Figure 36.12).

Figure 36.12 : Reading an incoming Talk Mail message.

To delete a Talk Mail message, simply highlight the message and press the trash can toolbar button or select Messages | Delete from the menu. You'll see a confirmation message before you actually delete the item.

You can also use the toolbar to adjust the list view the same way you can adjust the Windows 95 Explorer views. Figure 36.13 shows the Talk Mail client in large icon view while showing the About box.

Figure 36.13 : The Talk Mail About box.

That's the end of the tour!

Summary

In this chapter, you learned how to combine the MAPI and Media Control Interface (MCI) services to create a voice-mail client. In the process you learned about declaring your own MAPI message type and about how to use the Windows 95 common controls.

This project is handy, but not quite complete. If you wish to use this application as more than a demonstration, you'll need to add the ability for users to reply to and forward received messages. You may also want to consider deploying this as a Microsoft Exchange form instead of as a standalone MAPI client. That way, all Microsoft Exchange users could install the form and use it throughout your organization.