Chapter 30

Creating TAPI-Enabled Applications


CONTENTS


In this chapter, you'll put together all the things you learned about TAPI in a single application. The TAPIFONE application is a complete single-line telephone device that runs on a Windows workstation. With a voice-data modem and sound card and this program up and running on your machine, you can completely eliminate the telephone handset from your desk.

In this program you'll add code for handling both inbound and outbound calls. You'll also give users access to the various TAPI dialog boxes and will maintain a set of configuration values for TAPIFONE in the Windows registry. You'll be able to write (and store) call notes on each outbound call made through TAPIFONE.

You'll use Data Access objects to maintain a simple phone book database and you'll also be able to keep track of outbound calls in a call log. This log can be exported to a comma-separated value (CSV) text file that can be loaded into Excel or Word.

When you complete this project you should have a full understanding of TAPI services and how you can use them to build full-featured telephone devices in Visual Basic.

Note
The best hardware configuration for this application is a voice-data modem and a sound card with external speakers and a microphone. You can also use this application with a standard modem and an attached telephone handset, but you'll only be able to make outbound calls.

Designing the TAPIFONE Application

The TAPIFONE application is designed to replace the telephone handset that appears on your desktop. As long as your Windows workstation has a voice-data modem or telephony card and an external microphone and speakers, you can use the TAPIFONE for all your in- and outbound voice call processing.

The TAPIFONE project has three forms and three code modules. The forms are:

The three code modules in the project are:

The TAPILine and TAPICall modules need not be keyed in since they are included on the CD-ROM that comes with this book. These are stock structures, constants, and API declares for the TAPILINE control covered earlier in this book (see Chapter 26, "TAPI Tools-Using the TAPILINE Control"). The libTAPI module will be built for this chapter.

Tip
If you do not want to enter all the code, you can locate and load the TAPIFONE project on the CD-ROM that ships with this book.

Along with the standard dial keypad, the TAPIFONE application has a Phone Book page that lists the person's name and phone number along with the last day and time that person was called using TAPIFONE. Users can select a name from this list and press the Dial button directly from the phonebook. As each call is placed, users will be asked to add call notes. These call notes are written to a log that can be viewed online or exported to a comma-delimited text file for further manipulation.

Users can set several TAPIFONE parameters to control the behavior of the application. These control values are stored in the Windows registry and recalled each time the program is run. Values are stored in the HKEY_CURRENT_USER\Software\Visual Basic and VBA Program Settings\TAPIFONE branch of the registry tree. Key values stored there are:

The libTAPI Module

The first step in building the TAPIFONE project is to start Visual Basic and create a new project. Load the TAPILINE.BAS and TAPICALL.BAS modules from a previous project (See Chapters 26 and 28 for more on these modules) by selecting File | Add File from the main menu. Once you've added these two BAS modules, save the project as TAPIFONE.VBP.

Next you need to add a new module to the project (Insert | Module). Set its Name property to LibTAPIWrapper and save it as LIBTAPI.BAS.

First, add some declaration code to the module. You need to declare a handful of public variables to handle flags and global values for the project. You'll also add two private (module-level) variables used to handle TAPI device information. Finally, you'll add a user-defined type to make it easy to place outbound calls. Open the declaration section of the form and enter the code shown in Listing 30.1.


Listing 30.1. Adding the declaration code to the LIBTAPI module.
'
Private udtLineDevCaps() As LINEDEVCAPS
Private cLineDevCapsExtra() As String * 2048
'
Public gDialString As String ' phone number to dial
Public gPlaceCall As Boolean ' ok to place call
Public gName As String ' name to call
Public gLineDev As Integer ' selected line device
'
Public gMinimize As Integer ' minimize at start
Public gMonitor As Integer ' monitor at startup
Public gOutLog As Integer ' log all outbound calls
Public gOrigNumber As String ' for calledID
Public gStartPage As Integer ' for default start page
'
Type DialParams
    DeviceNumber As Integer
    DialableString As String
    Privilege As Long
    MediaMode As Long
End Type

Next, add the routine to handle the initialization of TAPI services for the application. This routine will actually perform three things:

Add a new function called initTAPI to your project and enter the code shown in Listing 30.2.


Listing 30.2. Adding the initTAPI function.

Public Function InitTAPI(ctrl As Control, cAppName As String) As Long
    '
    ' perform initial startup of TAPI services
    '
    Dim lRtn As Long
    Dim iNumDev As Long
    Dim iLoop As Integer
    '
    TAPIClearHandles ctrl ' keep it clean!
    '
    ' start it up
    lRtn = ctrl.LineInitialize(cAppName)
    If lRtn < 0 Then
        GoTo TAPIErr
    End If
    '
    ' confirm devices
    iNumDev = ctrl.NumDevices
    For iLoop = 0 To iNumDev - 1
        lRtn = ctrl.LineNegotiateAPIVersion(iLoop, 65536, 65540)
        If lRtn < 0 Then
            GoTo TAPIErr
        End If
    Next
    '
    ' fill line device capabilities structures
    ReDim Preserve udtLineDevCaps(iNumDev)
    ReDim Preserve cLineDevCapsExtra(iNumDev)
    '
    For iLoop = 0 To iNumDev - 1
        lRtn = ctrl.LineGetDevCaps(iLoop)
        If lRtn = 0 Then
            LineDevCapsFunc TAPI_READ, udtLineDevCaps(iLoop), ÂcLineDevCapsExtra(iLoop)
            cLineDevCapsExtra(iLoop) = Clean(cLineDevCapsExtra(iLoop))
        End If
    Next
    '
    GoTo TAPIExit
    '
TAPIErr:
    MsgBox "Unable to Start TAPI Services", vbCritical, "InitTAPI"
    End
    '
TAPIExit:
    InitTAPI = lRtn
    '
End Function

You'll notice that there are two parameters for the function. The first is the TAPILINE control used on the main form. The second is the application name when initializing TAPI services. You'll also notice that this routine loads the capabilities of each device and stores that information in a private array. This array can be accessed using another function to be defined later in this section.

Note
You might be thinking this kind of code should be placed in a Visual Basic class module. And you'd be right-except for one little thing. Since the TAPILINE control must be placed on a form, we cannot use a class module to handle properties and methods on the control. Forms and controls cannot be encapsulated in class modules.

Next, you need to add the routine used to read the device capabilities stored in the udtLineDevCaps array. Add a new function to the project called ReadLineDevCaps and add the code shown in Listing 30.3.


Listing 30.3. Adding the ReadLineDevCaps function.

Public Function ReadLineDevCaps(iDevNum As Integer, udtLineDev As LINEDEVCAPS, ÂcExtra As String) As Long
    '
    ' return line caps
    '
    On Error GoTo LocalErr
    '
    Dim lRtn As Long
    '
    lRtn = 0
    udtLineDev = udtLineDevCaps(iDevNum)
    cExtra = Trim(cLineDevCapsExtra(iDevNum))
    '
    Exit Function
    '
LocalErr:
    lRtn = Err
    '
End Function

Next add the routine that will be called to place an outbound call. Add a new function called TAPIDial and enter the code from Listing 30.4.


Listing 30.4. Adding the TAPIDial function.

Public Function TAPIDial(ctrl As Control, dpType As DialParams) As Long
    '
    ' dial a selected number
    '
    Dim lRtn As Long
    '
    lRtn = ctrl.LineOpen(dpType.DeviceNumber, 0, dpType.Privilege, ÂdpType.MediaMode)
    If lRtn < 0 Then
        TAPIDial = lRtn
        Exit Function
    End If
    '
    SetCallParams dpType.MediaMode ' load defaults
    '
    ' place the call
    lRtn = ctrl.LineMakeCall(dpType.DialableString, 1)
    '
    TAPIDial = lRtn
    '
End Function

You'll notice that this routine uses the DialParams user-defined type. It also calls another custom routine-SetCallParams. You'll define this next. Add a new subroutine called SetCallParams to the project and enter the code shown in Listing 30.5.


Listing 30.5. Adding the SetCallParams routine.

Public Sub SetCallParams(lMode As Long)
    '
    ' set up call params data
    '
    Dim cp As LINECALLPARAMS
    Dim cpx As String
    '
    ' defaults for voice calls
    cp.dwBearerMode = LINEBEARERMODE_VOICE
    cp.dwMinRate = 9.6
    cp.dwMaxRate = 28.8
    cp.dwMediaMode = lMode
    cp.dwCallParamFlags = 0
    cp.dwAddressMode = lMode
    cp.DialParams.dwDialPause = 0
    cp.DialParams.dwDialSpeed = 0
    cp.DialParams.dwDigitDuration = 0
    cp.DialParams.dwWaitForDialtone = 0
    cp.dwOrigAddressSize = Len(Trim(gOrigNumber))
    cp.dwOrigAddressOffset = 0
    cp.dwCalledPartySize = 0
    cp.dwCommentSize = 0
    cp.dwUserUserInfoSize = 0
    cp.dwHighLevelCompSize = 0
    cp.dwLowLevelCompSize = 0
    cp.dwDevSpecificSize = 0
    '
    cpx = Trim(gOrigNumber)
    cp.dwTotalSize = Len(cp) + Len(cpx)
    LineCallParamsFunc TAPI_WRITE, cp, cpx
    '
End Sub

This routine sets an important TAPI structure. This structure contains information about the type of call that will be requested. Notice that the gOrigNumber variable is used to fill in the originating address. This will tell those who have caller ID who you are when they receive your calls.

There is one more high-level routine used when placing a call. This is the routine that will fill the DialParams structure with the actual number to dial and the type of call to make. Add a new subroutine called TAPIPlaceCall to the project and enter the code from Listing 30.6.


Listing 30.6. Adding the TAPIPlaceCall routine.
Public Sub TAPIPlaceCall(ctrl As Control, cdialtarget As String)
    '
    Dim lRtn As Long
    Dim cMsg As String
    Dim dpValue As DialParams
    '
    dpValue.DeviceNumber = gLineDev
    dpValue.Privilege = LINECALLPRIVILEGE_NONE
    dpValue.MediaMode = LINEMEDIAMODE_INTERACTIVEVOICE
    dpValue.DialableString = cdialtarget ' number to call
    '
    lRtn = TAPIDial(ctrl, dpValue)
    If lRtn < 0 Then
        cMsg = "TAPIDial - " & LineErr(lRtn)
        GoTo LocalErr
    End If
    '
    Exit Sub
    '
LocalErr:
    If Err <> 0 Then
        lRtn = Err
        cMsg = Error$
    End If
    '
    MsgBox cMsg, vbCritical, "Dialing Err[" & Hex(lRtn) & "]"
    '
End Sub

Only three support routines remain to be added. All three deal with the closing down of TAPI services. One, TAPIHangUp is used to gracefully end an active call. The TAPIClearHandles routine cleans up control properties after a call is completed. Finally, the TAPIShutdown routine closes down all TAPI services for the application. Add the three routines shown in Listing 30.7 to your project.


Listing 30.7. Adding TAPIHangUp, TAPIClearHandles, and TAPIShutdown.

Public Sub TAPIHangUp(ctrl As Control)
    '
    ' hang up any current call
    '
    ctrl.LineDrop "", 0
    ctrl.LineDeallocateCall
    ctrl.LineClose
    '
End Sub

Public Sub TAPIClearHandles(ctrl As Control)
    '
    ' clean up handles to a call
    '
    ctrl.HandleToCall = 0
    ctrl.LineHandle = 0
    ctrl.AddressID = 0
    '
End Sub

Public Sub TAPIShutdown(ctrl As Control)
    '
    ' close down TAPI services
    '
    ctrl.LineShutdown
    '
End Sub

Those are all the routines for the LIBTAPI.BAS module. Be sure to save the module and the project (TAPIFOINE.VBP) before you continue.

frmTAPI-The Main Form

The main form for the TAPIFONE project is the frmTAPI form. This form uses a tabbed dialog box to present five different services from one form:

The form also contains a menu that allows users to view information about the application (Help | About), exit the program (File | Exit), and access TAPI configuration dialog boxes (Configure...).

Laying Out the frmTAPI Form

The next few sections show you how to lay out the frmTAPI form. Since a tabbed dialog box is used, you are actually building five different dialog boxes in one. Refer to the figures in each section while adding the controls shown in the accompanying tables. Be sure to add the tab control first and to draw all the other controls on the tab control workspace. This will ensure that the other controls are child controls of the tab control.

Before building the details of each tab, you need to add the tab control itself along with a few command buttons. Refer to Table 30.1 and Figure 30.1 when adding these controls.

Figure 30.1 : Adding the base controls to the frmTAPI form.

Table 30.1. Base controls for the frmTAPI form.
ControlProperty Settings
VB.Form Name frmTAPI
 Caption "Form1"
 Left 1635
 LinkTopic "Form1"
 MaxButton 0'False
 Top 1665
 Width 5685
VB.CommandButton Name cmdButtons
 Caption "E&xit"
 Height 300
 Index 3
 Left 4140
 TabIndex 39
 Top 3480
 Width 1200
VB.CommandButton Name cmdButtons
 Caption "&Start Monitor"
 Height 300
 Index 0
 Left 180
 TabIndex 33
 Top 3480
 Width 1200
VB.CommandButton Name cmdButtons
 Caption "&Dial"
 Height 300
 Index 1
 Left 1500
 TabIndex 32
 Top 3480
 Width 1200
VB.CommandButton Name cmdButtons
 Caption "&HangUp"
 Height 300
 Index 2
 Left 2820
 TabIndex 31
 Top 3480
 Width 1200
TabDlg.SSTab Name SSTab1
 Height 3255
 Left 120
 TabIndex 17
 Top 120
 Width 5295
 TabsPerRow 5
 Tabs 5
 Style 1
 TabCaption(0) "Dial Pad"
 TabCaption(1) "Phone Book"
 TabCaption(2) "Call Log"
 TabCaption(3) "Lines"
 TabCaption(4) "SetUp"
VB.Label Name lblStatus
 BorderStyle 1 'Fixed Single
 Caption "Label1"
 Height 255
 Left 120
 TabIndex 16
 Top 3900
 Width 5295

After you've added the base controls, save the form as FRMTAPI.FRM before continuing.

The Dial Pad Tab

The first page of the dialog box is the Dial Pad. This provides the user with a keypad similar to that found on desktop phones. The user can also type the phone number directly into the text box, perform a redial on the last number dialed, clear the text box, or use its contents as the start of a new address listing.

Refer to Figure 30.2 and Table 30.2 when laying out this page.

Figure 30.2 : Laying out the Dial Pad page

Warning
Be sure to place the Frame control on the dialog page first. Also, you'll want to add the small command buttons using the control array feature of Visual Basic 4.0.

Table 30.2. The Dial Pad page controls.
Control NameProperty Settings
VB.Frame Name FraDial
 Height 2475
 Left 120
 TabIndex 18
 Top 480
 Width 2055
VB.CommandButton Name cmdKey
 Caption "1"
 Font  
  name="MS Serif"
  charset=0
  weight=700
  size=13.5
 Height 450
 Index 0
 Left 180
 TabIndex 30
 Top 240
 Width 450
VB.CommandButton Name cmdKey
 Caption "2"
 Font  
  name="MS Serif"
  charset=0
  weight=700
  size=13.5
 Height 450
 Index 1
 Left 780
 TabIndex 29
 Top 240
Width 450
VB.CommandButton Name cmdKey
 Caption "3"
 Font  
  name="MS Serif"
  charset=0
  weight=700
  size=13.5
 Left 1380
 TabIndex 28
 Top 240
 Width 450
VB.CommandButton Name cmdKey
 Caption "4"
 Font  
  name="MS Serif"
  charset=0
  weight=700
  size=13.5
 Height 450
 Index 3
 Left 180
 TabIndex 27
 Top 780
 Width 450
VB.CommandButton Name cmdKey
 Caption "5"
 Font  
  name="MS Serif"
  charset=0
  weight=700
  size=13.5
 Height 450
 Index 4
 Left 780
 TabIndex 26
 Top 780
 Width 450
VB.CommandButton Name cmdKey
 Caption "6"
 Font  
  name="MS Serif"
  charset=0
  weight=700
  size=13.5
 Height 450
 Index 5
 Left 1380
 TabIndex 25
 Top 780
Width 450
VB.CommandButton Name cmdKey
 Caption "7"
 Font  
  name="MS Serif"
  charset=0
  weight=700
  size=13.5
 Height 450
 Index 6
 Left 180
 TabIndex 24
 Top 1320
 Width 450
VB.CommandButton Name cmdKey
 Caption "8"
 Font  
  name="MS Serif"
  charset=0
  weight=700
  size=13.5
 Height 450
 Index 7
 Left 780
 TabIndex 23
 Top 1320
 Width 450
VB.CommandButton Name cmdKey
 Caption "9"
 Font  
  name="MS Serif"
  charset=0
  weight=700
  size=13.5
 Height 450
 Index 8
 Left 1380
 TabIndex 22
 Top 1320
Width 450
VB.CommandButton Name cmdKey
 Caption "*"
 Font  
  name="MS Serif"
  charset=0
  weight=700
  size=13.5
 Height 450
 Index 9
 Left 180
 TabIndex 21
 Top 1860
 Width 450
VB.CommandButton Name cmdKey
 Caption "0"
 Font  
  name="MS Serif"
  charset=0
  weight=700
  size=13.5
 Height 450
 Index 10
 Left 780
 TabIndex 20
 Top 1860
 Width 450
VB.CommandButton Name cmdKey
 Caption "#"
 Font  
  name="MS Serif"
  charset=0
  weight=700
  size=13.5
 Height 450
 Index 11
 Left 1380
 TabIndex 19
 Top 1860
 Width 450
VB.Label Name lblMonitor
 BackStyle 0 'Transparent
 Caption "Monitoring Inbound Calls"
 Font name="MS Sans Serif"
  charset=0
  weight=700
  size=8.25
 ForeColor &H000000FF& (Red)
 Height 735
 Left 2460
 TabIndex 47
 Top 420
 Visible 0 'False
 Width 975
TapilineLib.Tapiline Name Tapiline1
 Left 2520
 Top 1080
VB.Image Name Image1
 Height 915
 Left 3000
 Stretch -1 'True
 Top 480
 Width 2055

The Phone Book Tab

The next tab in the form is the Phone Book tab. This tab is a data entry form for the phone book. Users can also press the Dial button to place a call from this form. Use Figure 30.3 and Table 30.3 to place the controls on the form.

Figure 30.3 : Adding the Phone Book controls

Table 30.3. The Phone Book page controls.
Control NameProperty Settings
VB.Data Name Data2
 Caption "Data2"
 Connect "Access"
 DatabaseName ""
 Exclusive 0 'False
 Height 300
 Left -72720
 Options 0
 ReadOnly 0 'False
 RecordsetType 1 'Dynaset
 RecordSource ""
 Top 1080
 Visible 0 'False
 Width 1140
VB.Data Name Data3
 Caption "Data3"
 Connect "Access"
 DatabaseName ""
 Exclusive 0 'False
 Height 300
 Left -73980
 Options 0
 ReadOnly 0 'False
 RecordsetType 1 'Dynaset
 RecordSource ""
 Top 1800
 Visible 0 'False
 Width 2175
VB.TextBox Name txtName
 DataField "Name"
 DataSource "Data1"
 Height 300
 Left -73440
 TabIndex 13
 Text "Text2"
 Top 2100
 Width 3600
VB.CommandButton Name cmdDialPad
 Caption "&Add to Phone Book"
 Height 300
 Index 2
 Left 2520
 TabIndex 37
 Top 2640
 Width 2400
VB.CommandButton Name cmdDialPad
 Caption "&Clear Dial String"
 Height 300
 Index 1
 Left 2520
 TabIndex 36
 Top 2280
 Width 2400
VB.CommandButton Name cmdDialPad
 Caption "&ReDial Number"
 Height 300
 Index 0
 Left 2520
 TabIndex 35
 Top 1920
 Width 2400
VB.CommandButton Name cmdData
 Caption "R&estore"
 Height 300
 Index 3
 Left -74760
 TabIndex 8
 Top 1560
 Width 1200
VB.CommandButton Name cmdData
 Caption "&Update"
 Height 300
 Index 2
 Left -74760
 TabIndex 9
 Top 1200
 Width 1200
VB.TextBox Name txtDialString
 Alignment 1 'Right Justify
 Height 300
 Left 2520
 TabIndex 34
 Text "Text6"
 Top 1500
 Width 2400
VB.CommandButton Name cmdData
 Caption "&Remove"
 Height 300
 Index 1
 Left -74760
 TabIndex 10
 Top 840
 Width 1200
VB.CommandButton Name cmdData
 Caption "&Add"
 Height 300
 Index 0
 Left -74760
 TabIndex 11
 Top 480
 Width 1200
VB.Data Name Data1
 Caption "Data1"
 Connect "Access"
 DatabaseName ""
 Exclusive 0 'False
 Height 315
 Left -72600
 Options 0
 ReadOnly 0 'False
 RecordsetType 1 'Dynaset
 RecordSource ""
 Top 1980
 Visible 0 'False
 Width 1695
VB.TextBox Name txtLastCalled
 DataField "Last Called"
 DataSource "Data1"
 Height 300
 Left -73440
 TabIndex 15
 Text "Text2"
 Top 2820
 Width 3600
VB.TextBox Name txtPhoneNumber
 DataField "PhoneNumber"
 DataSource "Data1"
 Height 300
 Left -73440
 TabIndex 14
 Text "Text2"
 Top 2460
 Width 3600
VB.Image Name Image2
 Height 1395
 Left -71160
 Stretch -1 'True
 Top 540
 Width 1275
MSDBGrid.DBGrid Name DBGrid1
 Bindings "frmTAPI.frx":B60A
 Height 2295
 Left -74940
 OleObjectBlob "frmTAPI.frx":B618
 TabIndex 38
 Top 420
 Width 5115
MSDBCtls.DBList Name DBList1
 Bindings "frmTAPI.frx":BD20
 Height 1425
 Left -73440
 TabIndex 12
 Top 480
 Width 2175
 _Version 65536
 _ExtentX 3836
 _ExtentY 2514
 _StockProps 77
 ForeColor -2147483640
 BackColor -2147483643
 ListField "Name"
 BoundColumn "Name"
VB.Label Name Label3
 BorderStyle 1 'Fixed Single
 Caption "Last Called:"
 Height 300
 Left -74760
 TabIndex 5
 Top 2820
 Width 1200
VB.Label Name Label2
 BorderStyle 1 'Fixed Single
 Caption "Phone Number:"
 Height 300
 Left -74760
 TabIndex 6
 Top 2460
 Width 1200
VB.Label Name Label1
 BorderStyle 1 'Fixed Single
 Caption "Name:"
 Height 300
 Left -74760
 TabIndex 7
 Top 2100
 Width 1200

After you've built this page, be sure to save the project before continuing.

The Call Log Tab

The Call Log tab shows the user the list of all calls logged through the TAPIFONE application. Refer to Figure 30.4 and Table 30.4 when you build this page.

Figure 30.4 : Building the Call Log page

Table 30.4. The Call Log controls.
Control NameProperty Settings
VB.CommandButton Name cmdLog
 Caption "&Refesh"
 Default -1 'True
 Height 300
 Index 1
 Left -72300
 TabIndex 45
 Top 2820
 Width 1200
VB.CommandButton Name cmdLines
 Cancel -1 'True
 Caption "&Cancel"
 Height 300
 Index 1
 Left -72300
 TabIndex 43
 Top 2820
 Width 1200
VB.CommandButton Name cmdLines
 Caption "&Apply"
 Height 300
 Index 0
 Left -70980
 TabIndex 42
 Top 2820
 Width 1200
VB.CommandButton Name cmdLog
 Caption "&Clear"
 Height 300
 Index 0
 Left -70980
 TabIndex 40
 Top 2820
 Width 1200
VB.Label Name Label4
 BackColor &H00C0C0C0&
 Caption "Originating Number"
 Height 315
 Left -74700
 TabIndex 46
 Top 2400
 Width 2175
VB.Label Name lblLines
 Caption "Select a Line Device:"
 Height 255
 Left -74760
 TabIndex 44
 Top 480
 Width 3555
VB.Image Name Image3
 Height 1815
 Left -71040
 Picture "frmTAPI.frx":52E4
 Stretch -1 'True
 Top 720
 Width 1155
VB.CommandButton Name cmdLog
 Caption "&Export"
 Height 300
 Index 2
 Left -73620
 TabIndex 48
 Top 2820
Width 1200

As a safety precaution, save this form before continuing.

The Line Tab

The Line tab allows user to select which line is active for the project. Refer to Figure 30.5 and Table 30.5 when building this page.

Figure 30.5 : Adding the line page

Table 30.5. The Line page controls.
Control NameProperty Settings
VB.ListBox NameList1
 Height 1815
 Left -74760
 TabIndex 41
 Top 720
 Width 3615
VB.CommandButton NamecmdLines
 Cancel -1 'True
 Caption "&Cancel"
 Height 300
 Index 1
 Left -72300
 TabIndex 43
 Top 2820
 Width 1200
VB.CommandButton NamecmdLines
 Caption "&Apply"
 Height 300
 Index 0
 Left -70980
 TabIndex 42
 Top 2820
 Width 1200
VB.Label NamelblLines
 Caption "Select a Line Device:"
 Height 255
 Left -74760
 TabIndex 44
 Top 480
 Width 3555
VB.Image NameImage3
 Height 1815
 Left -71040
 Stretch -1 'True
 Top 720
 Width 1155

Before building the last tab for this form, save the form and project.

The Setup Tab

The Setup tab contains a small group of controls that are used to update registry settings. Refer to Figure 30.6 and Table 30.6 to build this last page of the form.

Figure 30.6 : Building the Setup page

Table 30.6. The Setup page controls.
Control NameProperty Settings
VB.Image NameImage4
 Height 1875
 Left -71880
 Picture "frmTAPI.frx":0442
 Stretch -1 'True
 Top 420
 Width 1815
VB.CheckBox NamechkMinimize
 Alignment 1 'Right Justify
 Caption "Minimize On StartUp"
 Height 300
 Left -74700
 TabIndex 52
 Top 1440
 Width 2595
VB.Frame NamefraStartup
 Caption "Startup Page"
 Height 915
 Left -74700
 TabIndex 49
 Top 480
 Width 2595
VB.OptionButton NameoptStartup
 Caption "Phone Book"
 Height 255
 Index 1
 Left 180
 TabIndex 51
 Top 540
 Width 1275
VB.OptionButton NameoptStartup
 Caption "Dial Pad"
 Height 255
 Index 0
 Left 180
 TabIndex 50
 Top 240
 Width 1275
VB.CommandButton NamecmdStartUp
 Caption "&Cancel"
 Height 300
 Index 1
 Left -72300
 TabIndex 0
 Top 2820
 Width 1200
VB.CommandButton NamecmdStartUp
 Caption "&Apply"
 Height 300
 Index 0
 Left -70980
 TabIndex 1
 Top 2820
 Width 1200
VB.TextBox NametxtOrigNumber
 Height 300
 Left -72300
 TabIndex 4
 Text "Text1"
 Top 2400
 Width 2475
VB.CheckBox NamechkOutLog
 Alignment 1 'Right Justify
 Caption "Log all OutBound Calls"
 Height 300
 Left -74700
 TabIndex 3
 Top 2040
 Width 2595
VB.CheckBox NamechkMonitor
 Alignment 1 'Right Justify
 Caption "Monitor Calls At Start Up"
 Height 300
 Left -74700
 TabIndex 2
 Top 1740
 Width 2595

This is the last of the dialog pages for the main form. Save the form as FRMTAPI.FRM and the project as TAPIFONE.VBP.

The frmTAPI Menu

The main form also has a set of menu options. These options allow the user to call up TAPI-generated dialog boxes to control communication between TAPI and your programs. Refer to Figure 30.7 and Table 30.7 when building the menu.

Figure 30.7 : Building the FRMTAPI menu

Table 30.7. Adding menus to the FRMTAPI event.
MenuProperty Value
VB.Menu NamemnuFile
 Caption "&File"
VB.Menu NamemnuFileExit
 Caption "E&xit"
VB.Menu NamemnuConf
 Caption "Configure"
VB.Menu NamemnuConfItem
 Caption "&Service Provider..."
 Index 0
VB.Menu NamemnuConfItem
 Caption "&Line Device..."
 Index 1
VB.Menu NamemnuConfItem
 Caption "&Dialing Properties..."
 Index 2
VB.Menu NamemnuHelp
 Caption "&Help"
VB.Menu NamemnuHelpAbout
 Caption "About &TAPILine"
 Index 0
VB.Menu NamemnuHelpAbout
 Caption "About &Virtual Phone"
 Index 1

Coding the frmTAPI Form

There are several code routines that need to be added to the frmTAPI form. These routines fall into four main groups:

Coding the Form-Level Routines

The first form-level code is the variable declaration code. Add the code shown in Listing 30.8 to the declaration section of the form.


Listing 30.8. Adding the form-level declarations.
Option Explicit
'
Dim lAdd As Boolean ' phonebook adding flag
Dim lMonitor As Boolean ' call monitoring flag
Dim lDevErr As Boolean ' line device select flag
'
Dim iWidth As Integer ' remember my size
Dim iHeight As Integer ' remember
'
Dim uLineDevCaps As LINEDEVCAPS ' line devices
Dim cLineDevCapsExtra As String ' extra data strings

Next, add the code for the Form_Load event. This code calls several support routines you'll add later. Enter the code from Listing 30.9 into the Form_Load event of the form.


Listing 30.9. Adding the Form_Load event code.

Private Sub Form_Load()
    '
    On Error GoTo LocalErr
    '
    Dim lRtn As Long
    Dim cMsg As String
    '
    ' save for later
    iWidth = Me.Width
    iHeight = Me.Height
    '
    ' initialize TAPI services
    lRtn = InitTAPI(Me.Tapiline1, App.EXEName)
    If lRtn < 0 Then
        cMsg = "InitTAPI - " & LineErr(lRtn)
        GoTo LocalErr
    End If
    '
    LoadImages ' fill in image controls
    InitDB ' set up data connections
    LoadDevices ' get device names
    ReadStartup ' get registry settings
    '
    ' check startup page
    If gLineDev = -1 Then
        SSTab1.Tab = 3 ' gotta select a device first
    Else
        SSTab1.Tab = gStartPage ' user selection
    End If
    '
    ' check monitor at startup
    If gMonitor And gLineDev <> -1 Then
        lblMonitor.Visible = True
        lMonitor = False ' pretend it's off
        cmdButtons_Click 0 ' start monitoring
    End If
    '
    ' check for minimize on startup
    If gMinimize And gLineDev <> -1 Then
        Me.WindowState = vbMinimized
    End If
    '
    ' clean up controls
    txtDialString = ""
    lblstatus = ""
    '
    ' setup this form
    Me.Icon = LoadPicture(App.Path & "\phone04.ico")
    Me.Caption = "TAPILine Virtual Phone"
    Me.Left = (Screen.Width - Me.Width) / 2
    Me.Top = (Screen.Height - Me.Height) / 2
    '
    Exit Sub
    '
LocalErr:
    If Err <> 0 Then
        lRtn = Err
        cMsg = Error$
    End If
    '
    MsgBox cMsg, vbCritical, "Form_Load Err[" & Hex(lRtn) & "]"
    TAPIShutdown Me.Tapiline1
    MousePointer = vbNormal
    End ' that's it!
    '
End Sub

Listing 30.10 shows the code for the Form_Resize and Form_Unload events. Add this to your project.


Listing 30.10. Adding the Form_Resize and Form_Unload event code.
Private Sub Form_Resize()
    '
    ' prevent resizing
    '
    If Me.WindowState <> vbMinimized _
       And Me.WindowState <> vbMaximized Then
        Me.Width = iWidth
        Me.Height = iHeight
    End If
    '
End Sub

Private Sub Form_Unload(Cancel As Integer)
    '
    TAPIHangUp Me.Tapiline1 ' hangup any open line
    TAPIShutdown Me.Tapiline1 ' close out TAPI Services
    '
End Sub

Listing 30.11 shows the code that belongs in the SSTab1_Click event. This is fired each time the user clicks to change a tab. This code will continue to remind the user to select a line device, if needed.


Listing 30.11. Coding the SSTab1_Click event.

Private Sub SSTab1_Click(PreviousTab As Integer)
    '
    ' force user to select a device, if needed
    If lDevErr = True Then
        MsgBox "You must select a Line Device!", vbCritical, "No Line Device"
        SSTab1.Tab = 3
    End If
    '
End Sub

The last bit of form-level code is the code for the Tapiline1_TapiCallBack event. This code is the same code you used in the TAPIANS project of Chapter 28. Add the code shown in Listing 30.12 to the TAPICallBack event of the TAPILINE control.


Listing 30.12. Coding the TAPICallBack event.
Private Sub Tapiline1_TapiCallBack(ByVal hDevice As Long, ByVal dwMessage As Long, ÂByVal dwInstance As Long, ByVal dwParam1 As Long, ByVal dwParam2 As Long, ByVal ÂdwParam3 As Long)
    '
    ' handle TAPI messages
    '
    Dim cMsg As String
    '
    ' handle inbound call actions
    Select Case dwMessage
        Case LINE_CALLSTATE ' state of call on line changed
            Select Case dwParam1
                Case LINECALLSTATE_OFFERING ' get call handle
                    Tapiline1.HandleToCall = hDevice ' update handle to call
                    Beep ' tell user a call appeared
                    Me.WindowState = vbNormal ' pop up, if needed
                Case LINECALLSTATE_IDLE ' they hung up on me!
                    cmdButtons_Click 2 ' hangup process
                    TAPIClearHandles Me.Tapiline1
            End Select
        Case LINE_CLOSE ' line was closed
            cmdButtons_Click 2 ' force hangup
    End Select
    '
    ' report to status line
    lblstatus = TapiCallBackHandler(hDevice, dwMessage, dwInstance, dwParam1, ÂdwParam2, dwParam3)
    '
End Sub

That's it for the form-level code. Save the form and project before continuing.

Coding the Menu-Level Routines

There are three main menu branches. The first is the File menu. It has only one submenu-Exit. Listing 30.13 shows the code you need to add to the mnuFileExit_Click event.


Listing 30.13. Coding the mnuFileExit_Click event.
Private Sub mnuFileExit_Click()
    '
    Unload Me
    '
End Sub

The next branch in the menu tree is the Configure branch. This was built as a menu array with three elements. Listing 30.14 shows the code that should be added to the mnuConfItem_Click event.


Listing 30.14. Coding the mnuConfItem_Click event.
Private Sub mnuConfItem_Click(Index As Integer)
    '
    ' handle TAPI configuration dialogs
    '
    Select Case Index
        Case 0 ' service provider
            Tapiline1.PermanentProviderID = 1
            Tapiline1.LineConfigProvider Me.hWnd
        Case 1 ' line device
            Tapiline1.LineConfigDialog 5, Me.hWnd, "tapi/line"
        Case 2 ' dialing properties
            Tapiline1.LineTranslateDialog 5, Me.hWnd, ""
    End Select
    '
End Sub

The final menu routine is the one that handles the Help menu branch. This branch also contains a menu array for displaying the TAPILINE control About box and a custom About box for the application (you'll build it later). Add the code in Listing 30.15 to the mnuHelpAbout_Click event.


Listing 30.15. Coding the mnuHelpAbout_Click event.
Private Sub mnuHelpAbout_Click(Index As Integer)
    '
    ' handle about boxes
    '
    Select Case Index
        Case 0 ' tapiline
            Me.Tapiline1.AboutBox
        Case 1 ' virtual phone
            frmVPhone.Show vbModal
    End Select
    '
End Sub

That's the end of the menu-level routines. Save the form and project before continuing on to the page-level routines.

Coding the Page-Level Routines

There are ten page-level routines in the frmTAPI form. Most of these routines are for handling selections of button array commands. Listing 30.16 shows the code for the cmdButtons_Click event. This handles the button presses on the bottom of the form. Add this code to your project.


Listing 30.16. Coding the cmdButtons_Click event.

Private Sub cmdButtons_Click(Index As Integer)
    '
    ' handle user selections on main buttons
    '
    Select Case Index
        Case 0 ' start monitor
            If cmdButtons(0).Caption = "&Start Monitor" Then
                lMonitor = LineMonitor(Me.Tapiline1, True)
            Else
                lMonitor = LineMonitor(Me.Tapiline1, False)
            End If
            '
            If lMonitor = True Then
                cmdButtons(0).Caption = "&End Monitor"
                lblMonitor.Visible = True
            Else
                cmdButtons(0).Caption = "&Start Monitor"
                lblMonitor.Visible = False
            End If
        Case 1 ' Dial Number
            '
            ' check for call monitoring
            If lMonitor = True Then
                cmdButtons_Click 0 ' toggle off monitoring
            End If
            '
            Select Case SSTab1.Tab ' check selected tab
                Case 0 ' dialpad
                    gDialString = txtDialString
                    gName = "<Unknown>"
                    frmCall.Show vbModal
                    If gPlaceCall = True Then
                        TAPIPlaceCall frmTAPI.Tapiline1, gDialString
                    End If
                Case 1 ' phonebook
                    gDialString = txtPhoneNumber
                    gName = txtName
                    frmCall.Show vbModal
                    If gPlaceCall = True Then
                        TAPIPlaceCall frmTAPI.Tapiline1, gDialString
                    End If
                Case 2 ' call log
                    ' no action
                Case 3 ' properties
                    ' no action
            End Select
        Case 2 ' Hang Up
            '
            ' check call monitoring
            If lMonitor = True Then
                cmdButtons_Click 0 ' toggle monitoring off
            End If
            '
            TAPIHangUp Me.Tapiline1
            lblstatus = ""
        Case 3 ' exit
            Unload Me
    End Select
    '
End Sub

Next you need to add code to handle the command buttons on the Dial Pad itself. Enter the code from Listing 30.17 into the cmdDialPad_Click event.


Listing 30.17. Coding the cmdDialPad_Click event.

Private Sub cmdDialPad_Click(Index As Integer)
    '
    ' handle user selections on dial pad page
    '
    Select Case Index
        Case 0 ' redial last number
            cmdButtons_Click 1 ' press dial
        Case 1 ' clear dialstring
            txtDialString = ""
        Case 2 ' add a new record to the phone book
            cmdData_Click 0 ' add a new record
            txtPhoneNumber = txtDialString
            txtName.SetFocus
            SSTab1.Tab = 1 ' show page
    End Select
    '
End Sub

This same page has the array of command buttons that simulate the telephone handset keypad. Listing 30.18 shows the code that should be copied into the cmdKey_Click event.


Listing 30.18. Coding the cmdKey_Click event.
Private Sub cmdKey_Click(Index As Integer)
    '
    ' handle user pressing keypad
    '
    Dim cDigit As String
    '
    cDigit = Mid("123456789*0#", Index + 1, 1)
    txtDialString = txtDialString & cDigit
    '
End Sub

The Phone Book page has two form-level routines. Listing 30.19 shows the code for handling the button array on the phone book page. Add this to your project.


Listing 30.19. Coding the cmdData_Click event.

Private Sub cmdData_Click(Index As Integer)
    '
    ' handle data stuff
    '
    Dim x As Integer
    '
    Select Case Index
        Case 0 ' add new
            Data1.Recordset.AddNew
            txtName.SetFocus
            lAdd = True
        Case 1 ' delete
            Data1.Recordset.Delete
            lAdd = False
        Case 2 ' update
            If Trim(txtName) <> "" Then
                Data1.UpdateRecord
            End If
            lAdd = False
        Case 3 ' refresh
            If lAdd = False Then
                Data1.Recordset.UpdateControls
            Else
                Data1.Recordset.CancelUpdate
            End If
            lAdd = False
    End Select
    '
    ' update the list and data table
    If lAdd = False Then
        Data1.Refresh
        Data2.Refresh
        DBList1.Refresh
    End If
    '
    ' enable all buttons
    For x = 0 To 3
        cmdData(x).Enabled = True
    Next x
    '
    ' turn of add/remove if add was pressed
    If lAdd = True Then
        cmdData(0).Enabled = False
        cmdData(1).Enabled = False
    End If
    '
End Sub

Listing 30.20 shows the code you need to add to the DBList1_DblClick event.


Listing 30.20. Coding the DbList1_DblClick event.

Private Sub DBList1_DblClick()
    '
    ' update data1 with this record
    '
    Dim cName As String
    '
    cName = DBList1.BoundText
    Data1.Recordset.FindFirst "Name='" & cName & "'"
    txtName.SetFocus
    '
End Sub

The Call Log page has one event routine-the cmdLog_Click event. Add the code shown in Listing 30.21 to your project.


Listing 30.21. Coding the cmdLog_Click event.
Private Sub cmdLog_Click(Index As Integer)
    '
    ' clear all records from the log
    '
    Dim iAns As Integer ' get user's answer
    '
    Select Case Index
        Case 0 ' clear log
            ClearLog
        Case 1 ' refresh dynaset
            Data3.Refresh
        Case 2 ' export data to text file
            ExportLog
    End Select
    '
End Sub

The Line page has a single event, too. Add the code from Listing 30.22 to the cmdLines_Click event of the form.


Listing 30.22. Coding the cmdLines_Click event.

Private Sub cmdLines_Click(Index As Integer)
    '
    ' handle user selection of line device
    '
    Select Case Index
        Case 0 ' apply
            If List1.ListIndex = -1 Then
                MsgBox "You must select a line device!", vbCritical, "No Line Selected"
                lDevErr = True
            Else
                gLineDev = List1.ListIndex
                SaveSetting App.EXEName, "LineDevice", "Selected", CStr(List1.ListIndex)
                lDevErr = False
            End If
        Case 1 ' cancel
            If List1.ListIndex = -1 Then
                MsgBox "You must select a line device!", vbCritical, "No Line Selected"
                lDevErr = True
            End If
    End Select
    '
End Sub

The final page-level routine is the one that handles the Startup page. The code in Listing 30.23 shows what you should add to the cmdStartUp_Click event.


Listing 30.23. Coding the cmdStartUp_Click event.

Private Sub cmdStartUp_Click(Index As Integer)
    '
    ' review startup values for application
    '
    Select Case Index
        Case 0 ' apply
            gMinimize = chkminimize
            gMonitor = chkMonitor
            gOutLog = chkOutLog
            gOrigNumber = txtOrigNumber
            gStartPage = IIf(optStartup(0) = True, 0, 1)
            '
            SaveSetting App.EXEName, "Startup", "MinimizeOnStart", gMinimize
            SaveSetting App.EXEName, "Startup", "Monitor", gMonitor
            SaveSetting App.EXEName, "Startup", "OutLog", gOutLog
            SaveSetting App.EXEName, "Startup", "OrigNumber", gOrigNumber
            SaveSetting App.EXEName, "Startup", "StartPage", gStartPage
            '
        Case 1 ' Cancel
            ' na
    End Select
    '
End Sub

That is the end of the page-level routines for the frmTAPI form. Save the form and project before continuing.

Coding the Support Routines

There are eight support routines for the frmTAPI form. Half of these routines are called from the Form_Load event as initialization routines. The rest are called at various times throughout the project.

First, add a new subroutine called LoadImages to the form. Then add the code shown in Listing 30.24 to the project.


Listing 30.24. Adding the LoadImages routine.

Public Sub LoadImages()
    '
    ' fill in image controls
    ' on the pages
    '
    On Error GoTo LocalErr
    '
    ' page 0
    Image1.Picture = LoadPicture(App.Path & "\answmach.wmf")
    ' page 1
    Image2.Picture = LoadPicture(App.Path & "\rolodex.wmf")
    ' page 3
    Image3.Picture = LoadPicture(App.Path & "\clipbord.wmf")
    ' page 4
    Image4.Picture = LoadPicture(App.Path & "\phone.wmf")
    '
    Exit Sub
    '
LocalErr:
    MsgBox Error$, vbCritical, "LoadImages Err [" & CStr(Err) & "]"
    '
End Sub

Next, add the LoadDevices subroutine. This is the code that loads the device names into the list box on the Lines page. Enter the code from Listing 30.25.


Listing 30.25. Adding the LoadDevices routine.

Public Sub LoadDevices()
    '
    ' Load line devices into list box
    '
    Dim x As Integer
    Dim lRtn As Long
    '
    For x = 0 To Tapiline1.NumDevices - 1
        lRtn = ReadLineDevCaps(x, uLineDevCaps, cLineDevCapsExtra)
        If lRtn >= 0 Then
            List1.AddItem GetOffset(Len(uLineDevCaps) + 4, uLineDevCaps.dwLineNameOffset, uLineDevCaps.dwLineNameSize, cLineDevCapsExtra)
        Else
            List1.AddItem "<BAD DEVICE> #" & CStr(x)
        End If
    Next
    '
    gLineDev = CStr(GetSetting(App.EXEName, "LineDevice", "Selected", "-1"))
    List1.ListIndex = gLineDev
    SaveSetting App.EXEName, "LineDevice", "Selected", CStr(List1.ListIndex)
    '
End Sub

Next add a new subroutine called ReadStartUp to the project and enter the code from Listing 30.26. This code loads the registry values at program startup.


Listing 30.26. Adding the ReadStartUp routine.

Public Sub ReadStartup()
    '
    ' load startup values
    '
    gMinimize = GetSetting(App.EXEName, "Startup", "MinimizeOnStart", 0)
    gMonitor = GetSetting(App.EXEName, "Startup", "Monitor", 0)
    gOutLog = GetSetting(App.EXEName, "Startup", "OutLog", 1)
    gOrigNumber = GetSetting(App.EXEName, "Startup", "OrigNumber", "")
    gStartPage = GetSetting(App.EXEName, "Startup", "StartPage", 0)
    '
    chkminimize = gMinimize
    chkMonitor = gMonitor
    chkOutLog = gOutLog
    txtOrigNumber = gOrigNumber
    '
    If gStartPage = 0 Then
        optStartup(0) = True
    Else
        optStartup(1) = True
    End If
    '
End Sub

The next two routines deal with loading or, if needed, creating the TAPI.MDB database file. First, create a subroutine called InitDB and enter the code from Listing 30.27.


Listing 30.27. Adding the InitDB routine.

Public Sub InitDB()
    '
    ' establish initial data connections
    '
    On Error Resume Next ' i'll handle this
    '
    ' see if db exists, if not - make it
    Open App.Path & "\tapi.mdb" For Input As 1
    If Err = 53 Then
        MakeDB ' must create the database!
    Else
        Close #1 ' fine, just checking
    End If
    '
    On Error GoTo LocalErr ' ok, pass them on...
    '
    ' the phone book
    Data1.DatabaseName = App.Path & "\tapi.mdb"
    Data1.RecordSource = "SELECT * FROM TAPI"
    Data1.Refresh
    '
    ' the lookup list
    Data2.DatabaseName = App.Path & "\tapi.mdb"
    Data2.RecordSource = "SELECT Name from TAPI ORDER BY Name"
    Data2.Refresh
    '
    ' the call log
    Data3.DatabaseName = App.Path & "\tapi.mdb"
    Data3.RecordSource = "SELECT Format(CallDate,'general date') AS DateCalled, Name, NumberCalled, Comment FROM Log ORDER BY Name, CallDate"
    Data3.Refresh
    '
    Exit Sub
    '
LocalErr:
    MsgBox Error$, vbCritical, "InitDB Err [" & CStr(Err) & "]"
    End ' can't start without this!
    '
End Sub

The first thing the InitDB routine does is check for the existence of the TAPI.MDB data file. If it does not exist, the MakeDB routine is called to create the database. Add the MakeDB subroutine to the form and enter the code shown in Listing 30.28.


Listing 30.28. Adding the MakeDB routine.

Public Sub MakeDB()
    '
    ' create TAPIFONE database
    '
    Dim cMakeTAPI As String
    Dim cMakeLOG As String
    Dim cDBName As String
    Dim ws As Workspace
    Dim db As Database
    '
    ' set db name
    ' and SQL statements
    cDBName = App.Path & "\TAPI.MDB"
    cMakeTAPI = "CREATE TABLE TAPI (Name TEXT(30), PhoneNumber TEXT(20), [Last Called] DATE)"
    cMakeLOG = "CREATE TABLE LOG (Name TEXT(30), CallDate DATE, NumberCalled TEXT(20), Comment MEMO)"
    '
    ' create new database
    Set ws = Workspaces(0)
    Set db = ws.CreateDatabase(cDBName, dbLangGeneral)
    '
    ' execute SQL statements
    db.Execute cMakeTAPI, dbFailOnError
    db.Execute cMakeLOG, dbFailOnError
    '
    ' shut things down
    db.Close
    Set db = Nothing
    Set ws = Nothing
    '
    Exit Sub
    '
LocalErr:
    MsgBox Error$, vbCritical, "MakeDB Err [" & Str(Err) & "]"
    End ' gotta stop here!
    '
End Sub

The next two routines are called from the Call Log page of the form. Listing 30.29 shows the ClearLog routine. Add this to your project.


Listing 30.29. Adding the ClearLog routine.

Public Sub ClearLog()
    '
    ' clear all recs out of log
    '
    On Error GoTo LocalErr
    '
    Dim iAns As Integer
    '
    iAns = MsgBox("Ready to Erase All Log Entries", vbInformation + vbYesNo, "Clear Call Log")
    If iAns = vbYes Then
        MousePointer = vbHourglass ' tell user you're busy
        '
        Data3.Recordset.MoveFirst ' start at the top
        '
        Do Until Data3.Recordset.EOF ' do until empty
            Data3.Recordset.Delete ' remove a rec
            Data3.Recordset.MoveNext ' go to next one
        Loop ' back to top
        '
        MousePointer = vbNormal ' ok, tell user you're done
        '
        MsgBox "Call Log Cleared", vbInformation ' alter user
    End If
    Exit Sub
    '
LocalErr:
    MsgBox Error$, vbCritical, "ClearLog Err [" & CStr(Err) & "]"
    '
End Sub

Listing 30.30 shows the ExportLog routine. This is used to create a text file version of the call log. Add this code to your project.


Listing 30.30. Adding the ExportLog routine.


Public Sub ExportLog()
    '
    ' write call log to text file
    '
    Dim cFldDelim As String
    Dim cLine As String
    Dim cFileName As String
    Dim cQuote As String
    '
    cFldDelim = Chr(44)
    cQuote = Chr(34)
    cFileName = App.Path & "\" & App.EXEName & ".log"
    '
    ' create new file
    Open cFileName For Output As 1
    '
    ' write header line
    cLine = "DateCalled,Name,NumberCalled,Comment"
    Print #1, cLine
    '
    ' export records
    Data3.Recordset.MoveFirst
    Do Until Data3.Recordset.EOF
        With Data3.Recordset
            cLine = cQuote & Trim(.Fields("DateCalled")) & cQuote & cFldDelim
            cLine = cLine & cQuote & Trim(.Fields("Name")) & cQuote & cFldDelim
            cLine = cLine & cQuote & Trim(.Fields("NumberCalled")) & cQuote & ÂcFldDelim
            cLine = cLine & cQuote & Trim(.Fields("Comment")) & cQuote
            Print #1, cLine
            .MoveNext
        End With
    Loop
    Close #1
    '
    MsgBox "Call Log Exported to [" & cFileName & "]", vbInformation, "Log Export"
    '
End Sub

The last support routine for the frmTAPI form is the LineMonitor function. This routine toggles on or off the TAPI call monitoring service. When monitoring is on, TAPIFONE answers the incoming call. Add the code in Listing 30.31 to your form.


Listing 30.31. Adding the LineMonitor function.

Public Function LineMonitor(ctrl As Control, lMonitor As Boolean) As Boolean
    '
    ' start/stop line monitoring
    '
    Dim lRtn As Long
    '
    If lMonitor = True Then
        lRtn = ctrl.LineOpen(gLineDev, 0, LINECALLPRIVILEGE_OWNER + ÂLINECALLPRIVILEGE_MONITOR, LINEMEDIAMODE_INTERACTIVEVOICE)
        If lRtn < 0 Then
            MsgBox LineErr(lRtn), vbCritical, "Line Monitor Error"
            LineMonitor = False
        Else
            LineMonitor = True
        End If
    Else
        TAPIHangUp ctrl
        LineMonitor = False
    End If
    '
End Function

That is all the coding needed for the frmTAPI form. Save this form and update the project before you add the Call and About dialog boxes.

The Call and About Dialog Boxes

The Call and About dialog boxes are two small forms that are used to support actions from the frmTAPI form. The Call dialog box appears just before an outbound call is placed. This allows the user to confirm the call and add any call notes if desired.

The About dialog box shows the name of the program, the current version, and other important information about the TAPIFONE application.

Laying Out and Coding the Call Dialog Box

Add a new form to your project and build the Call dialog box shown in Figure 30.8. You can also refer to Table 30.8 when laying out the frmCall form.

Figure 30.8 : Laying out the frmCall form

Table 30.8. The frmCall form controls.
ControlProperty Settings
VB.Form NamefrmCall
 BorderStyle 3 'Fixed Dialog
 Caption "Ready to Place a Call"
 ClientHeight 3450
 ClientLeft 3090
 ClientTop 1770
 ClientWidth 3780
 Height 3855
 Icon "frmCall.frx":0000
 Left 3030
 LinkTopic "Form1"
 MaxButton 0 'False
 MinButton 0 'False
 ScaleHeight 3450
 ScaleWidth 3780
 ShowInTaskbar 0 'False
 Top 1425
 Width 3900
VB.TextBox NametxtNotes
 Height 1575
 Left 960
 MultiLine -1 'True
 ScrollBars 2 'Vertical
 TabIndex 5
 Text "frmCall.frx":0442
 Top 1320
 Width 2655
VB.Data NameData1
 Caption "Data1"
 Connect "Access"
 DatabaseName ""
 Exclusive 0 'False
 Height 300
 Left 180
 Options 0
 ReadOnly 0 'False
 RecordsetType 1 'Dynaset
RecordSource ""
 Top 3060
 Visible 0 'False
 Width 1140
VB.CommandButton NamecmdCall
 Caption "&Cancel"
 Height 300
 Index 1
Left 1080
 TabIndex 3
 Top 3000
 Width 1200
VB.CommandButton NamecmdCall
 Caption "&OK"
 Height 300
 Index 0
 Left 2400
 TabIndex 2
 Top 3000
 Width 1200
VB.Label NameLabel3
 Caption "Call Notes:"
 Height 195
 Left 1020
 TabIndex 6
 Top 1140
 Width 2595
VB.Label NamelblDialString
 Alignment 2 'Center
 Caption "xxx-xxx-xxxx"
 Font  
  name="MS Sans Serif"
  charset=0
  weight=700
  size=8.25
  underline=0 'False
  italic=0 'False
  strikethrough=0 'False
 Height 300
 Left 0
 TabIndex 4
 Top 780
 Width 3795
VB.Image NameImage1
 Height 1575
 Left -60
 Picture "frmCall.frx":0448
 Stretch -1 'True
 Top 1260
 Width 870
VB.Label NamelblName
 Alignment 2 'Center
 Caption "name"
 Font  
  name="MS Sans Serif"
  charset=0
  weight=700
  size=8.25
  underline=0 'False
  italic=0 'False
  strikethrough=0 'False
 Height 300
 Left 0
 TabIndex 1
 Top 420
 Width 3795
VB.Label NameLabel1
 Alignment 2 'Center
 Caption "Placing Call to:"
 Font  
  name="MS Sans Serif"
  charset=0
  weight=700
  size=8.25
  underline=0 'False
  italic=0 'False
  strikethrough=0 'False
 Height 300
 Left 0
 TabIndex 0
 Top 60
 Width 3795

After you finish laying out the frmCall form, you need to add three code routines. The first is the Form_Load event code. Listing 30.32 shows the code you need to add to the frmCall form.


Listing 30.32. Adding the Form_Load event code.

Private Sub Form_Load()
    '
    ' init stuff
    '
    lblDialString = Trim(gDialString)
    lblname = Trim(gName)
    txtNotes = ""
    Image1.Picture = LoadPicture(App.Path & "\payphone.wmf")
    '
    ' init form stuff
    Me.Caption = "Ready to Place a Call..."
    Me.Icon = LoadPicture(App.Path & "\phone04.ico")
    Me.Left = (Screen.Width - Me.Width) / 2
    Me.Top = (Screen.Height - Me.Height) / 2
    '
    ' handle db connections
    Data1.DatabaseName = App.Path & "\tapi.mdb"
    Data1.RecordSource = "Log"
    Data1.Refresh
    '
End Sub

Next you need to add the code shown in Listing 30.33 to the cmdCall_Click event of the form.


Listing 30.33. Coding the cmdCall_Click event.

Private Sub cmdCall_Click(Index As Integer)
    '
    ' handle user selection
    '
    Select Case Index
        Case 0 ' place call
            gPlaceCall = True ' ok to place call
            UpdateDB ' update tables
        Case 1 ' cancel call
            gPlaceCall = False ' no, skip it
    End Select
    '
    Unload Me
    '
End Sub

Finally, you need to add some code to support the cmdCall_Click event. Add a new subroutine called UpdateDB to the form and enter the code shown in Listing 30.34.


Listing 30.34. Adding the UpdateDB routine.

Public Sub UpdateDB()
    '
    ' update call log and master file
    '
    ' add call log record
    With frmCall.Data1.Recordset
        .AddNew
        .Fields("Name") = lblname
        .Fields("CallDate") = Now()
        .Fields("NumberCalled") = lblDialString
        .Fields("Comment") = txtNotes & " "
        .Update
    End With
    frmCall.Data1.Refresh
    '
    ' update master record
    frmTAPI.Data1.Recordset.FindFirst "Name='" & lblname & "'"
    If frmTAPI.Data1.Recordset.NoMatch = False Then
        With frmTAPI.Data1.Recordset
            .Edit
            .Fields("[Last Called]") = Now()
            .Update
        End With
    End If
    frmTAPI.Data1.Refresh
    '
End Sub

That is the end of the frmCall form. Save this form and update the project before you add the About dialog box.

Laying Out and Coding the About Dialog Box

The About dialog box is displayed when the user selects Help | About TAPIFONE. Add a new form to the project. Refer to Figure 30.9 and Table 30.9 when laying out the new form.

Figure 30.9 : Laying out the About dialog box

Table 30.9. The About dialog box controls.
ControlProperty Settings
VB.Form NamefrmVPhone
 Caption "About TAPILine Virtual Phone"
 ClientHeight 2670
 ClientLeft 2190
 ClientTop 2880
 ClientWidth 5955
 Height 3075
 Icon "frmVPhon.frx":0000
 Left 2130
 LinkTopic "Form1"
 ScaleHeight 2670
 ScaleWidth 5955
 Top 2535
 Width 6075
VB.CommandButton NameCommand1
 Caption "&OK"
 Height 300
 Left 4620
 TabIndex 2
 Top 2280
 Width 1200
VB.Label NameLabel2
 BorderStyle 1 'Fixed Single
 Caption "Label2"
 Font  
  name="MS LineDraw"
  charset=2
  weight=400
  size=8.25
  underline=0 'False
  italic=0 'False
  strikethrough=0 'False
 Height 1335
 Left 120
 TabIndex 1
 Top 780
 Width 5715
VB.Label NameLabel1
 Alignment 2 'Center
 BorderStyle 1 'Fixed Single
 Caption "TAPILine Virtual Phone"
 Font name="MS Sans Serif"
  charset=0
  weight=700
  size=18
  underline=0 'False
  italic=0 'False
  strikethrough=0 'False
 Height 555
 Left 120
 TabIndex 0
 Top 120
 Width 5715

There are only two code routines needed for this form. Listing 30.35 shows the Form_Load event code. Add this to the form.


Listing 30.35. Adding the Form_Load event code.

Private Sub Form_Load()
    '
    ' fill in controls
    '
    Dim cAboutData
    Dim cVersion
    '
    cAboutData = ""
    cVersion = CStr(App.Major) & "." & CStr(App.Minor) & "." & CStr(App.Revision)
    '
    cAboutData = cAboutData & "Version......." & cVersion & Chr(13)
    cAboutData = cAboutData & "Title........." & App.Title & Chr(13)
    cAboutData = cAboutData & "Description..." & App.FileDescription & Chr(13)
    cAboutData = cAboutData & "Copyright....." & App.LegalCopyright
    '
    Label2 = cAboutData
    '
    Me.Left = (Screen.Width - Me.Width) / 2
    Me.Top = (Screen.Height - Me.Height) / 2
    '
End Sub

Listing 30.36 shows the line of code to add the Command1_Click event. This exits the About dialog box.


Listing 30.36. Adding the code to exit the dialog box.
Private Sub Command1_Click()
    Unload Me
End Sub

That's the end of the coding for the TAPIFONE project. Now save the form and the project. You are ready to test the application.

Testing TAPIFONE

When you first run TAPIFONE you'll be prompted to select a line device. This device will be used for all inbound and outbound calls (see Figure 30.10).

Figure 30.10 : TAPIFONE requires you to select a line device

Next, it's a good idea to select the Setup tab to review your startup configuration (see Figure 30.11).

Figure 30.11 : Adjusting the startup configuration

You can place a call using the keypad on the Dial Pad page (see Figure 30.12) or by selecting an entry in the phone book.

Figure 30.12 : Using the Dial Pad to place a call

In either case, when you press the Dial button, the Call dialog box appears asking you to confirm your call and enter any call notes (see Figure 30.13).

Figure 30.13 : Using the phone book to place a call

Once your call is completed you can view the results on the log page and even export the log to a text file (see Figure 30.14).

Figure 30.14 : Exporting the log page to a text file

Summary

In this chapter you applied all the information you have learned in the TAPI section to create a complete phone in software. You can use this device on any workstation that has TAPI services installed and has at least a voice-data modem. If the workstation has a speaker phone, you do not even need a telephone handset on your desk.

This chapter showed you what you can build in Visual Basic using the TAPILINE control. In the next chapter, you'll learn about three other TAPI tools that you can use to create TAPI-enabled applications. Two of the products are TAPI development tools. The third product is Microsoft Phone-Microsoft's e-mail-aware, telephone answering machine that can read text messages to you over the telephone.