In this chapter you'll learn how to use the OLE Messaging library to create a stand-alone e-mail agent. This agent can scan your incoming mail and, based on rules you establish, automatically handle messages for you. All actions are based on rules you establish in a control file.
Features of the MAPI Email Agent are:
The first part of the chapter will discuss the concept of e-mail agents and cover the overall design of the program. The next sections will detail the coding of the forms and support routines needed to complete the project. In the final section, you'll install and test the MAPI Email Agent program.
Note |
The Microsoft Exchange Server clients allow users to establish and code their own mail agent (called the Inbox Assistant). If you have the Microsoft Exchange Server client, now is a good time to review the Inbox Assistant to get an idea of its features. The programming project covered in this chapter works independently of the Inbox Assistant and does not require that users have Microsoft Exchange Server installed on their system. |
When you complete the programming project in this chapter, you'll have a fully functional MAPI Email Agent that can be installed on any workstation that has access to a MAPI-compliant e-mail system. Also, the techniques used to build this project can be incorporated into other Windows programming projects to add message processing capabilities.
Before starting to code the project, it's a good idea to discuss the general features and functions of an e-mail agent. Once you have a good idea of what e-mail agents can do, then you can lay out the basic design features of the MAPI Email Agent programming project covered in this chapter.
Basically, an e-mail agent is a program that "acts for you." It is a program that reviews your messages and, based on information you have supplied, processes the messages for you. Typically, e-mail agents process messages in the user's inbox. Users can set up rules that tell the agent how to handle each new message. These rules can tell the e-mail agent to check various parts of the incoming message and then take a specific action.
For example, the agent can be instructed to look for all messages that are received from your boss and then place those messages in a special folder called "Urgent." Or the agent could be told that any message with the word "SALES" in the subject line should be immediately forwarded to another user's inbox and then erased from your inbox without any comment. You might also tell the agent to automatically reply to all senders that you are out on vacation and will return next week.
For this chapter, you'll use Visual Basic 4.0 and the OLE Messaging library to build a stand-alone MAPI Email Agent that has the following features:
Note |
The features described here are just the start of what can be accomplished with an e-mail agent. The number of options has been limited to keep this chapter focused on design and coding issues instead of content. As you build your own agent, you can add many other capabilities. |
To accomplish this, the MAPI Email Agent will keep track of rules created by the user. These rules will have three parts: tests, comparisons, and actions. The test portion of the rule performs a simple scan of the designated portion of the message, searching for requested content. The MAPI Email Agent described in this chapter is capable of inspecting three message parts:
For example, the test SUBJECT MAPI tells the agent to check the message subject for the word "MAPI." The phrase SENDER Boss tells the agent to check for messages sent to the user from the e-mail ID "boss."
All tests must use a logical condition as part of the processing. The MAPI Email Agent uses comparisons to do this. The program can check for the following four logical conditions:
You'll notice that the last value is able to check the selected message part for the occurrence of a word or phrase. Note that all the comparisons are case-insensitive. It is important to note that the LT and GT can be used with character data, too.
The last of the three portions of a rule is the action. This is the part of the rule that tells the agent what action to take if the test criteria have been met. The MAPI Email Agent can perform the following actions on a message:
The agent allows users to determine whether the forwarded and reply messages are retained or removed once the forward/reply is generated.
The MAPI Email Agent allows users to build tests and actions, and then use them to create rules. All this information is stored in a text file similar to an INI file. This file also contains general control information, such as the scan interval, whether the agent should create a log file, the default log on profiles, and so on. Listing 11.1 shows a sample rule file.
Listing 11.1. Sample rule file for the MAPI Email Agent.
; ********************************************************
; MAPI Email Agent Control File
; ********************************************************
;
[General]
Editor=notepad.exe
ScanInterval=2
LogFile=mea.log
LogFlag=1
RuleCount=3
ActionCount=4
TestCount=4
Profile=MCA
DeleteForwardFlag=0
NotifyDialog=1
DeleteReplyFlag=0
MinimzeOnStart=0
AutoStart=0
LastUpdated=04/29/96 9:27:30 PM
[Actions]
Action0=MOVE MAPI
Action1=MOVE Urgent
Action2=FORWARD mamund@iac.net
Action3=COPY SavedMail
[Tests]
Test0=SENDER MCA
Test1=SENDER Boss
Test2=SUBJECT SAPI
Test3=SUBJECT MAPI
[Rules]
RuleName0=Boss's Mail
RuleTest0=SENDER Boss
RuleAction0=Move Urgent
RuleCompare0=EQ
RuleName1=Send To ISP
RuleTest1=SENDER MCA
RuleAction1=FORWARD mamund@iac.net
RuleCompare1=EQ
RuleName2=MAPI Mail
RuleTest2=SUBJECT MAPI
RuleAction2=MOVE MAPI
RuleCompare2=CI
The next sections show you how to code the MAPI Email Agent forms and support routines that will create and process the rules described here.
You'll use Visual Basic 4.0 to create the three forms of the MAPI Email Agent. These forms will allow you to start and stop message processing; add or delete new tests, actions, and rules; and modify default configuration settings for the MAPI Email Agent.
The next three sections of this chapter outline the steps needed to layout and code these three forms. If you haven't done so yet, start Visual Basic 4.0 now and create a new project.
The Main form of the MAPI Email Agent shows the current list of rules, tests, and actions. You can also launch the message scan routine from this screen, access the setup dialog box, and inspect the MAPI Email Agent log file. Figure 11.1 shows an example of the Main form in run-time mode.
Figure 11.1 : The MAPI Email Agent Main form.
The Main form has a set of command button control arrays to handle the user selections. The first control array covers the top row of buttons. These buttons handle the main processing steps:
The second command button control array handles the adding and deleting of tests, actions, and rules. To keep things simple for this project, the system is capable only of adding or deleting rules. Existing rules cannot be edited and saved again. Also, only basic input editing is performed by this program. In a production environment, this program should be enhanced to add an improved user interface with additional input checking and recovery.
Table 11.1 contains a list of all the controls used on the MAPI
Email Agent main form along with their property settings. Use
this table along with Figure 11.1 to build the MAPI Email Agent
Main form.
Control | Property | Setting |
VB.Form | Name | frmMEA |
Caption | "MAPI Email Agent" | |
ClientHeight | 6585 | |
ClientLeft | 975 | |
ClientTop | 1575 | |
ClientWidth | 8175 | |
Height | 6990 | |
Left | 915 | |
LinkTopic | "Form1" | |
MaxButton | 0 'False | |
ScaleHeight | 6585 | |
ScaleWidth | 8175 | |
Top | 1230 | |
Width | 8295 | |
VB.CommandButton | Name | Command1 |
Caption | "E&xit Program" | |
Height | 495 | |
Index | 5 | |
Left | 6780 | |
TabIndex | 18 | |
Top | 120 | |
Width | 1200 | |
VB.CommandButton | Name | Command1 |
Caption | "Re&fresh" | |
Height | 495 | |
Index | 4 | |
Left | 5460 | |
TabIndex | 16 | |
Top | 120 | |
Width | 1200 | |
VB.CommandButton | Name | Command2 |
Caption | "Delete R&ule" | |
Height | 495 | |
Index | 5 | |
Left | 6900 | |
TabIndex | 15 | |
Top | 3000 | |
Width | 1100 | |
VB.CommandButton | Name | Command2 |
Caption | "New &Rule" | |
Height | 495 | |
Index | 4 | |
Left | 5700 | |
TabIndex | 14 | |
Top | 3000 | |
Width | 1100 | |
VB.CommandButton | Name | Command1 |
Caption | "View L&og" | |
Height | 495 | |
Index | 3 | |
Left | 4140 | |
TabIndex | 13 | |
Top | 120 | |
Width | 1200 | |
VB.CommandButton | Name | Command2 |
Caption | "De&lete Action" | |
Height | 495 | |
Index | 3 | |
Left | 6960 | |
TabIndex | 12 | |
Top | 5700 | |
Width | 1100 | |
VB.CommandButton | Name | Command2 |
Caption | "Ne&w Action" | |
Height | 495 | |
Index | 2 | |
Left | 5760 | |
TabIndex | 11 | |
Top | 5700 | |
Width | 1100 | |
VB.CommandButton | Name | Command1 |
Caption | "SetU&p" | |
Height | 495 | |
Index | 2 | |
Left | 2820 | |
TabIndex | 10 | |
Top | 120 | |
Width | 1200 | |
VB.CommandButton | Name | Command1 |
Caption | "E&nd Timer" | |
Height | 495 | |
Index | 1 | |
Left | 1500 | |
TabIndex | 9 | |
Top | 120 | |
Width | 1200 | |
VB.CommandButton | Name | Command2 |
Caption | "&Delete Test" | |
Height | 495 | |
Index | 1 | |
Left | 2820 | |
TabIndex | 8 | |
Top | 5700 | |
Width | 1100 | |
VB.CommandButton | Name | Command2 |
Caption | "&New Test" | |
Height | 495 | |
Index | 0 | |
Left | 1620 | |
TabIndex | 7 | |
Top | 5700 | |
Width | 1100 | |
VB.ListBox | Name | lstActions |
Font | ||
name="Courier" | ||
charset=0 | ||
weight=400 | ||
size=9.75 | ||
underline=0 'False | ||
italic=0 'False | ||
strikethrough=0 'False | ||
Height | 1815 | |
Left | 4200 | |
TabIndex | 3 | |
Top | 3720 | |
Width | 3855 | |
VB.ListBox | Name | lstTests |
Font | ||
name="Courier" | ||
charset=0 | ||
weight=400 | ||
size=9.75 | ||
underline=0 'False | ||
italic=0 'False | ||
strikethrough=0 'False | ||
Height | 1815 | |
Left | 120 | |
TabIndex | 2 | |
Top | 3720 | |
Width | 3795 | |
VB.ListBox | Name | lstRules |
Font | ||
name="Courier" | ||
charset=0 | ||
weight=400 | ||
size=9.75 | ||
underline=0 'False | ||
italic=0 'False | ||
strikethrough=0 'False | ||
Height | 1815 | |
Left | 120 | |
TabIndex | 1 | |
Top | 1020 | |
Width | 7875 | |
VB.CommandButton | Name | Command1 |
Caption | "&Start Timer" | |
Height | 495 | |
Index | 0 | |
Left | 180 | |
TabIndex | 0 | |
Top | 120 | |
Width | 1200 | |
VB.Timer | Name | Timer1 |
Left | 4860 | |
Top | 3000 | |
VB.Label | Name | lblStatus |
BorderStyle | 1 'Fixed Single | |
Caption | "Label5" | |
Height | 255 | |
Left | 0 | |
TabIndex | 17 | |
Top | 6300 | |
Width | 8115 | |
VB.Label | Name | Label3 |
AutoSize | -1 'True | |
Caption | "Actions:" | |
Font | ||
name="Courier" | ||
charset=0 | ||
weight=400 | ||
size=9.75 | ||
underline=0 'False | ||
italic=0 'False | ||
strikethrough=0 'False | ||
Height | 195 | |
Left | 4260 | |
TabIndex | 6 | |
Top | 3480 | |
Width | 960 | |
VB.Label | Name | Label2 |
AutoSize | -1 'True | |
Caption | "Tests:" | |
Font | ||
name="Courier" | ||
charset=0 | ||
weight=400 | ||
size=9.75 | ||
underline=0 'False | ||
italic=0 'False | ||
strikethrough=0 'False | ||
Height | 195 | |
Left | 180 | |
TabIndex | 5 | |
Top | 3420 | |
Width | 720 | |
VB.Label | Name | Label1 |
AutoSize | -1 'True | |
Caption | "Current Rules:" | |
Font | ||
name="Courier" | ||
charset=0 | ||
weight=400 | ||
size=9.75 | ||
underline=0 'False | ||
italic=0 'False | ||
strikethrough=0 'False | ||
Height | 195 | |
Left | 180 | |
TabIndex | 4 | |
Top | 780 | |
Width | 1680 |
Notice that the font size and type used for this form is slightly different. By using a fixed-width font for the text boxes, it is very easy to get consistent alignment. This is a quick way to present a grid-like look to your list boxes. After completing the form layout, save the form as MEA.FRM and save the project as MEA.VBP.
Next you need to add some code to the form. This code covers several key events for the form including the command button selections. The first thing to do is add two form-level variables (see Listing 11.2).
Listing 11.2. Adding the form-level variables to the Main form.
Option Explicit
Dim iLocalHeight As Integer
Dim iLocalWidth As Integer
These variables are used to retain the original size and shape of the form.
Next add code to the Form_Load event. This code executes some one-time initializations and prepares the form for the user (see Listing 11.3).
Listing 11.3. Adding the Form_Load code.
Private Sub Form_Load()
'
Left = (Screen.Width - Me.Width) / 2
Top = (Screen.Height - Me.Height) / 2
'
Timer1.Enabled = False ' start as OFF
Timer1.Interval = 60000 ' tick off the minutes
lCounter = 0 ' start at zero
'
lblstatus = ""
Me.Icon = LoadPicture(App.Path & "\" & "mail14.ico")
'
' if user set flag, minimize
If cMinOnStartValue = "1" Then
Me.WindowState = vbMinimized
End If
'
' if user set flag, go!
If cAutoStartValue = "1" Then
Timer1_Timer ' fire event
Timer1.Enabled = True
End If
'
' remember your size
iLocalHeight = Me.Height
iLocalWidth = Me.Width
'
End Sub
Note the code that initializes the form-level variables. These variables are used in the next routine: the Form_Resize event, which will prevent users from resizing the form. Add the code shown in Listing 11.4.
Listing 11.4. Adding code to the Form_Resize event.
Private Sub Form_Resize()
'
' don't go changin!
'
If WindowState = vbNormal Then
Me.Height = iLocalHeight
Me.Width = iLocalWidth
End If
'
End Sub
The next section of code goes in the Timer1_Timer event (see Listing 11.5).
Listing 11.5. Adding code to the Timer1_Timer event.
Private Sub Timer1_Timer()
'
' fire off message scan
'
lblstatus = "Minutes to next scan: " & CStr(lCounter)
lblstatus.Refresh
Me.Caption = "MAPI Email Agent [" & CStr(lCounter) & " min]"
DoEvents
'
If lCounter = 0 Then
Timer1.Enabled = False
StartProcess ' run scan loop
lCounter = Val(cScanIntervalValue)
Timer1.Enabled = True
Else
lCounter = lCounter - 1
End If
'
End Sub
This event fires off every minute (see Listing 11.3 where the interval was set to 60000). Each time the event fires, the lCounter variable is decremented. When it hits zero, the StartProcess routine is called. This is the routine that actually scans the messages. Notice also that the routine is designed to report timer progress on the form title bar. This same information appears as part of the task bar when the form is minimized.
Now it's time to add the code behind the first set of command buttons. This first set of buttons calls all the main operations of the program. Add the code in Listing 11.6 to the Command1_Click event.
Listing 11.6. Adding the code for the Command1_Click event.
Private Sub Command1_Click(Index As Integer)
'
' handle button selection
'
Dim x As Long
'
Select Case Index
Case 0 ' start scan countdown
LogWrite "Message Scan Started"
lCounter = 0 ' clear counter
Timer1_Timer ' fire it off right away
Timer1.Enabled = True ' start counting
Case 1 ' end scan countdown
Timer1.Enabled = False
LogWrite "Message Scan Stopped"
Case 2 ' view control file
Timer1.Enabled = False
frmMEASetUp.Show vbModal
InitStuff
Case 3 ' view log file
x = Shell(cEditorValue & " " & cLogFileValue, 1)
Case 4 ' refresh lists
Timer1.Enabled = False
InitStuff
Case 5 ' exit program
Unload Me
End Select
'
End Sub
Warning |
Most of the code in Listings 11.6 and 11.7 refer to routines and forms that have not been built yet. If you attempt to run the project before all your routines are complete, you'll get error messages from Visual Basic. If you want to test your code by running the project, you'll need to build subroutines to cover the ones that are missing or comment out the lines that call for other routines. |
The last code you need to add to the MAPI Email Agent main form is the code for the Command2_Click event. This code handles the calls for adding and deleting rules, actions, and tests. Add the code in Listing 11.7 to the Command2_Click event.
Listing 11.7. Adding code to the Command2_Click event.
Private Sub Command2_Click(Index As Integer)
'
' get edit selections
'
Dim cInput As String
Dim cCommand As String
'
Select Case Index
Case 0 ' new test
cInput = InputBox("Enter Test String [SENDER|SUBJECT|PRIORITY Value]:", "New Test")
If Trim(cInput) <> "" Then
cCommand = ParseWord(cInput)
If InStr(UCase(cTestCommands), UCase(cCommand)) = 0 Then
MsgBox "Invalid Test Command - use " & cTestCommands, vbCritical, "Invalid Command"
Else
MousePointer = vbHourglass
AddTest cInput
MousePointer = vbNormal
End If
End If
Case 1 ' delete test
If lsttests.ListIndex = -1 Then
MsgBox "Must select a Test item to delete", vbCritical, "Delete Test"
Else
MousePointer = vbHourglass
DeleteTest lsttests.ListIndex
MousePointer = vbNormal
End If
Case 2 ' new action
cInput = InputBox("Enter Test String [MsgPart Value]:", "New Test")
cCommand = ParseWord(cInput)
If InStr(UCase(cActionCommands), UCase(cCommand)) = 0 Then
MsgBox "Invalid Action Command - use " & cActionCommands, vbCritical, "Invalid Command"
Else
MousePointer = vbHourglass
AddAction cInput
MousePointer = vbNormal
End If
Case 3 ' delete action
If lstactions.ListIndex = -1 Then
MsgBox "Must select a Action item to delete", vbCritical, "Delete Test"
Else
MousePointer = vbHourglass
DeleteAction lstactions.ListIndex
MousePointer = vbNormal
End If
Case 4 ' new rule
AddRule ' call add routine
Case 5 ' delete rule
If lstrules.ListIndex = -1 Then
MsgBox "Must select a Rule to delete", vbCritical, "Delete Test"
Else
MousePointer = vbHourglass
DeleteRule lstrules.ListIndex
MousePointer = vbNormal
End If
End Select
'
End Sub
That's all the coding for the MAPI Email Agent main form. Save this form (MEA.FRM) and this project (MEA.VBP) before continuing.
The rule form is used to compose new rules for the MAPI Email Agent. This form is actually quite simple. It has three list boxes that allow the user to select a test, a compare value, and an action. By combining these three items, the user creates a valid MAPI e-mail agent rule. Once the rule is given a name, it can be saved. All saved rules are acted upon each time MAPI Email Agent scans the incoming messages.
Note |
This version of MAPI Email Agent does not let you turn rules on or off. If a rule is in the database, it will be processed each time the messages are scanned. The only way to tell the MAPI Email Agent to not process a rule is to permanently remove the rule from the database. Toggling rules on and off would make a nice enhancement for future versions of MAPI Email Agent. |
Refer to Figure 11.2 and Table 11.2 when laying out the new MAPI Email Agent Rule form.
Figure 11.2 : Laying out the MAPI Email Agent Rule form.
Control | Property | Setting |
VB.Form | Name | frmMEARule |
BorderStyle | 3 'Fixed Dialog | |
Caption | "Create Rule" | |
ClientHeight | 3990 | |
ClientLeft | 1140 | |
ClientTop | 1515 | |
ClientWidth | 6165 | |
Height | 4395 | |
Left | 1080 | |
LinkTopic | "Form1" | |
MaxButton | 0 'False | |
MinButton | 0 'False | |
ScaleHeight | 3990 | |
ScaleWidth | 6165 | |
ShowInTaskbar | 0 'False | |
Top | 1170 | |
Width | 6285 | |
VB.TextBox | Name | Text1 |
Height | 315 | |
Left | 1260 | |
TabIndex | 6 | |
Text | "Text1" | |
Top | 120 | |
Width | 4755 | |
VB.CommandButton | Name | Command1 |
Caption | "&OK" | |
Height | 495 | |
Index | 1 | |
Left | 4800 | |
TabIndex | 5 | |
Top | 3360 | |
Width | 1215 | |
VB.CommandButton | Name | Command1 |
Caption | "&Cancel" | |
Height | 495 | |
Index | 0 | |
Left | 3420 | |
TabIndex | 4 | |
Top | 3360 | |
Width | 1215 | |
VB.ListBox | Name | List3 |
Height | 1815 | |
Left | 2820 | |
TabIndex | 2 | |
Top | 1380 | |
Width | 495 | |
VB.ListBox | Name | List2 |
Height | 1815 | |
Left | 3420 | |
TabIndex | 1 | |
Top | 1380 | |
Width | 2595 | |
VB.ListBox | Name | List1 |
Height | 1815 | |
Left | 120 | |
TabIndex | 0 | |
Top | 1380 | |
Width | 2595 | |
VB.Label | Name | Label2 |
Caption | "Rule Name:" | |
Height | 315 | |
Left | 180 | |
TabIndex | 7 | |
Top | 120 | |
Width | 975 | |
VB.Label | Name | Label1 |
BorderStyle | 1 'Fixed Single | |
Caption | "Label1" | |
Height | 675 | |
Left | 120 | |
TabIndex | 3 | |
Top | 540 | |
Width | 5895 |
There are only a few events in the MAPI Email Agent Rule form that need coding. Listing 11.8 shows the code for the Form_Load event.
Listing 11.8. Adding the code for the MAPI Email Agent Rule form Form_Load event.
Private Sub Form_Load()
'
' fill local lists
'
FillList "tests", Me.List1
FillList "actions", Me.List2
FillList "compares", Me.List3
'
Label1 = "" ' clear rule display
Text1 = "" ' clear rule name
'
Me.Left = (Screen.Width - Me.Width) / 2
Me.Top = (Screen.Height - Me.Height) / 2
Me.Icon = LoadPicture(App.Path & "\" & "mail14.ico")
Me.Caption = "MAPI Email Agent - Create Rule"
'
End Sub
The Form_Load event first fills the local list boxes, then initializes the other local controls, centers the form, and sets the form's icon and caption.
The code in Listing 11.9 handles the user selections on the command button control array. Add this code to the Command1_Click event.
Listing 11.9. Adding code to the Command1_Click event.
Private Sub Command1_Click(Index As Integer)
'
' handle button clicks
'
Select Case Index
Case 0 ' cancel
Unload Me
Case 1 ' OK
If Trim(Text1) <> "" Then
If Trim(Label1) <> "" Then
MakeRule Text1.Text, Label1.Caption
Text1 = ""
Label1 = ""
Else
MsgBox "Must Construct Named Rule", vbCritical, "Rule Error"
End If
Else
MsgBox "Must Name the Rule", vbCritical, "Rule Error"
End If
End Select
End Sub
Finally, you need to add code to the DblClick events of the three list boxes. By double-clicking each box, the user can create a new rule to add to the database. Refer to Listing 11.10 for the code lines to add to each list box.
Listing 11.10. Adding code to the DblClick_event of each list box.
Private Sub List1_DblClick()
Label1 = List1.List(List1.ListIndex) & " | "
End Sub
Private Sub List2_DblClick()
Label1 = Label1 & List2.List(List2.ListIndex)
End Sub
Private Sub List3_DblClick()
Label1 = Label1 & List3.List(List3.ListIndex) & " | "
End Sub
That is all you need to do for the MAPI Email Agent Rule form. Save this form as MEARULE.FRM and save the project (MEA.VBP) before continuing.
The last form you need to add to the project is the MAPI Email Agent Setup form. This form allows users to modify the default configuration settings for the MAPI Email Agent. Add a new form to your project called MEASETUP.FRM. Refer to Figure 11.3 and Table 11.3 as you lay out the form.
Figure 11.3 : Laying out the MAPI Email Agent Setup form.
Control | Property | Setting |
VB.Form | Name | frmMEASetUp |
BorderStyle | 3 'Fixed Dialog | |
Caption | "Form2" | |
ClientHeight | 5940 | |
ClientLeft | 2175 | |
ClientTop | 1605 | |
ClientWidth | 4005 | |
Height | 6345 | |
Left | 2115 | |
LinkTopic | "Form2" | |
MaxButton | 0 'False | |
MinButton | 0 'False | |
ScaleHeight | 5940 | |
ScaleWidth | 4005 | |
ShowInTaskbar | 0 'False | |
Top | 1260 | |
Width | 4125 | |
VB.CommandButton | Name | Command1 |
Caption | "&OK" | |
Height | 495 | |
Index | 1 | |
Left | 2640 | |
TabIndex | 23 | |
Top | 5280 | |
Width | 1215 | |
VB.CommandButton | Name | Command1 |
Caption | "&Cancel" | |
Height | 495 | |
Index | 0 | |
Left | 1320 | |
TabIndex | 22 | |
Top | 5280 | |
Width | 1215 | |
VB.CheckBox | Name | Check6 |
Alignment | 1 'Right Justify | |
Caption | "Log Activity to File:" | |
Height | 495 | |
Left | 2040 | |
TabIndex | 13 | |
Top | 2940 | |
Width | 1800 | |
VB.CheckBox | Name | Check5 |
Alignment | 1 'Right Justify | |
Caption | "Use PopUp Dialog on Notify:" | |
Height | 495 | |
Left | 2040 | |
TabIndex | 12 | |
Top | 2400 | |
Width | 1800 | |
VB.CheckBox | Name | Check4 |
Alignment | 1 'Right Justify | |
Caption | "Start Scan at Load:" | |
Height | 495 | |
Left | 2040 | |
TabIndex | 11 | |
Top | 1860 | |
Width | 1800 | |
VB.CheckBox | Name | Check3 |
Alignment | 1 'Right Justify | |
Caption | "Minimize On Startup" | |
Height | 495 | |
Left | 120 | |
TabIndex | 10 | |
Top | 2940 | |
Width | 1800 | |
VB.CheckBox | Name | Check2 |
Alignment | 1 'Right Justify | |
Caption | "Delete Replied Messages:" | |
Height | 495 | |
Left | 120 | |
TabIndex | 9 | |
Top | 2400 | |
Width | 1800 | |
VB.CheckBox | Name | Check1 |
Alignment | 1 'Right Justify | |
Caption | "Delete Forwarded Messages:" | |
Height | 495 | |
Left | 120 | |
TabIndex | 8 | |
Top | 1860 | |
Width | 1800 | |
VB.TextBox | Name | Text4 |
Height | 315 | |
Left | 1440 | |
TabIndex | 7 | |
Text | "Text1" | |
Top | 1380 | |
Width | 2400 | |
VB.TextBox | Name | Text3 |
Height | 315 | |
Left | 1440 | |
TabIndex | 6 | |
Text | "Text1" | |
Top | 960 | |
Width | 2400 | |
VB.TextBox | Name | Text2 |
Height | 315 | |
Left | 1440 | |
TabIndex | 5 | |
Text | "Text1" | |
Top | 540 | |
Width | 2400 | |
VB.TextBox | Name | Text1 |
Height | 315 | |
Left | 1440 | |
TabIndex | 1 | |
Text | "Text1" | |
Top | 120 | |
Width | 2400 | |
VB.Label | Name | Label12 |
BorderStyle | 1 'Fixed Single | |
Height | 300 | |
Left | 1440 | |
TabIndex | 21 | |
Top | 3600 | |
Width | 1200 | |
VB.Label | Name | Label11 |
BorderStyle | 1 'Fixed Single | |
Height | 300 | |
Left | 1440 | |
TabIndex | 20 | |
Top | 4860 | |
Width | 2415 | |
VB.Label | Name | Label10 |
BorderStyle | 1 'Fixed Single | |
Height | 300 | |
Left | 1440 | |
TabIndex | 19 | |
Top | 4440 | |
Width | 1200 | |
VB.Label | Name | Label9 |
BorderStyle | 1 'Fixed Single | |
Height | 300 | |
Left | 1440 | |
TabIndex | 18 | |
Top | 4080 | |
Width | 1200 | |
VB.Label | Name | Label5 |
BorderStyle | 1 'Fixed Single | |
Caption | "Rule Count:" | |
Height | 300 | |
Left | 120 | |
TabIndex | 17 | |
Top | 3600 | |
Width | 1200 | |
VB.Label | Name | Label8 |
BorderStyle | 1 'Fixed Single | |
Caption | "Last Updated:" | |
Height | 300 | |
Left | 120 | |
TabIndex | 16 | |
Top | 4860 | |
Width | 1200 | |
VB.Label | Name | Label7 |
BorderStyle | 1 'Fixed Single | |
Caption | "Action Count:" | |
Height | 300 | |
Left | 120 | |
TabIndex | 15 | |
Top | 4440 | |
Width | 1200 | |
VB.Label | Name | Label6 |
BorderStyle | 1 'Fixed Single | |
Caption | "Test Count:" | |
Height | 300 | |
Left | 120 | |
TabIndex | 14 | |
Top | 4020 | |
Width | 1200 | |
VB.Label | Name | Label4 |
BorderStyle | 1 'Fixed Single | |
Caption | "Profile:" | |
Height | 300 | |
Left | 120 | |
TabIndex | 4 | |
Top | 1380 | |
Width | 1200 | |
VB.Label | Name | Label3 |
BorderStyle | 1 'Fixed Single | |
Caption | "Log File:" | |
Height | 300 | |
Left | 120 | |
TabIndex | 3 | |
Top | 960 | |
Width | 1200 | |
VB.Label | Name | Label2 |
AutoSize | -1 'True | |
BorderStyle | 1 'Fixed Single | |
Caption | "Scan Interval:" | |
Height | 300 | |
Left | 120 | |
TabIndex | 2 | |
Top | 540 | |
Width | 1200 | |
VB.Label | Name | Label1 |
AutoSize | -1 'True | |
BorderStyle | 1 'Fixed Single | |
Caption | "Editor:" | |
Height | 300 | |
Left | 120 | |
TabIndex | 0 | |
Top | 120 | |
Width | 1200 |
The MAPI Email Agent Setup form needs code for two events and two custom routines. The Custom routines are needed to load the forms controls with the configuration values for editing and then to save them after the values have been modified.
Add a new subroutine called SetupPageLoad to the MAPI Email Agent Setup form and enter the code shown in Listing 11.11.
Listing 11.11. Adding the SetupPageLoad routine.
Public Sub SetupPageLoad()
'
Text1 = cEditorValue
Text2 = cScanIntervalValue
Text3 = cLogFileValue
Text4 = cProfileValue
'
Label9 = cTestCountValue
Label10 = cActionCountValue
Label11 = cLastUpdateValue
Label12 = cRuleCountValue
'
Check1.Value = Val(cDelFwdFlagValue)
Check2.Value = Val(cDelReplyFlagValue)
Check3.Value = Val(cMinOnStartValue)
Check4.Value = Val(cAutoStartValue)
Check5.Value = Val(cNotifyDialogValue)
Check6.Value = Val(cLogFlagValue)
'
End Sub
Now add another new subroutine called SetupPageSave and enter the code shown in Listing 11.12.
Listing 11.12. Adding the SetupPageSave routine.
Public Sub SetupPageSave()
'
' save updated config data
' to vars for later
'
cEditorValue = Text1
cScanIntervalValue = Text2
cLogFileValue = Text3
cProfileValue = Text4
'
cDelFwdFlagValue = CStr(Check1)
cDelReplyFlagValue = CStr(Check2)
cMinOnStartValue = CStr(Check3)
cAutoStartValue = CStr(Check4)
cNotifyDialogValue = CStr(Check5)
cLogFlagValue = CStr(Check6)
'
' now save to file
SaveValues
'
End Sub
Only two events need coding-the Form_Load event and the Command1_Click event. Listing 11.13 shows the code for the Form_Load event.
Listing 11.13. Adding code to the MAPI Email Agent Setup Form_Load event.
Private Sub Form_Load()
'
' load controls
'
SetupPageLoad
'
Me.Left = (Screen.Width - Me.Width) / 2
Me.Top = (Screen.Height - Me.Height) / 2
Me.Icon = LoadPicture(App.Path & "\" & "mail14.ico")
Me.Caption = "MAPI Email Agent - Setup Page"
'
End Sub
Warning |
In the second-to-last line, this code refers to an icon in the local folder. The icon can be found on the CD-ROM that ships with this book, and is copied to your machine when you install the source code from the CD-ROM. If you get an error message on this line, you may need to locate the icon or use another icon for your project. |
The last event you need to code for the form is the Command1_Click event. This event handles the saving (or canceling) of a new rule. Add the code shown in Listing 11.14 to the Command1_Click event.
Listing 11.14. Adding code to the Command1_Click event.
Private Sub Command1_Click(Index As Integer)
'
' handle key clicks
'
Select Case Index
Case 0 ' cancel
' na
Case 1 ' ok
SetupPageSave
End Select
'
Unload Me
'
End Sub
That is the end of the coding for the MAPI Email Agent Setup form. Save this form as MEASETUP.FRM and save the project (MEA.VBP) before you continue to the next section.
In the next section, you'll add the support routines that are needed to make the MAPI Email Agent really work.
The real heart of the MAPI Email Agent program is the support routines. There are three main sets of routines in the program:
The next three sections of this chapter walk you through the process of building the support routines for the MAPI Email Agent program.
All the code for the support routines will be added to a BAS module. Before going on to the next sections, add a BAS module called LIBMEA.BAS to the MAPI Email Agent project.
The initialization routines declare the global variables and set them to their initial values. There are also routines to handle the reading and writing of configuration values and the storing and retrieving of the test, action, and rule records.
Note |
All of the control information is kept in a single ASCII text file (MEA.RUL) in the same folder as the program. This format was chosen for simplicity. In a production environment, you will want to consider a more sophisticated storage system including database formats. |
Since we will be using an ASCII control file similar to the 16-bit INI files, we need to declare two API calls to handle the reading and writing of those values. Listing 11.15 shows the two API calls needed for this project. Add this code to the general declarations section of the BAS module.
Listing 11.15. Adding the API calls.
Option Explicit
'
' APIs for read/write of shared INI settings
Declare Function GetPrivateProfileString Lib "kernel32" Alias "GetPrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Long, ByVal lpFileName As String) As Long
Declare Function WritePrivateProfileString Lib "kernel32" Alias "WritePrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpString As Any, ByVal lpFileName As String) As Long
The next code to add declares the global variables. Listing 11.16 shows all the declarations needed for the project. Add these values to the general declarations section of the module.
Listing 11.16. Declaring the global variables.
Global cRuleFile As String
Global cIsEqualTo As String
Global cIsContainedIn As String
Global cIsGreaterThan As String
Global cIsLessThan As String
Global cForwardMsg1 As String
Global cForwardMsg2 As String
Global lCounter As Long
'
' general section
Global cGeneralSection As String
Global cGeneralScanInterval As String
Global cScanIntervalValue As String
Global cGeneralAutoStart As String
Global cAutoStartValue As String
Global cGeneralEditor As String
Global cEditorValue As String
Global cGeneralLogFile As String
Global cLogFileValue As String
Global cGeneralLogFlag As String
Global cLogFlagValue As String
Global cGeneralRuleCount As String
Global cRuleCountValue As String
Global cGeneralTestCount As String
Global cTestCountValue As String
Global cGeneralActionCount As String
Global cActionCountValue As String
Global cGeneralProfile As String
Global cProfileValue As String
Global cGeneralDelFwdFlag As String
Global cDelFwdFlagValue As String
Global cGeneralDelReplyFlag As String
Global cDelReplyFlagValue As String
Global cGeneralNotifyDialog As String
Global cNotifyDialogValue As String
Global cGeneralMinOnStart As String
Global cMinOnStartValue As String
Global cGeneralLastUpdate As String
Global cLastUpdateValue As String
'
' test section
Global cTestSection As String
Global cTestPriority As String
Global cTestSubject As String
Global cTestSender As String
Global cTestCommands As String
'
' action section
Global cActionSection As String
Global cActionCopy As String ' COPY (folder)
Global cActionMove As String ' MOVE (folder)
Global cActionForward As String ' FORWARD (address)
Global cActionNotify As String ' NOTIFY
Global cActionReply As String ' REPLY (textfile)
Global cActionCommands As String
'
' rules section
Global cRulesSection As String
Global cRuleName() As String
Global cRuleTest() As String
Global cRuleAction() As String
Global cRuleCompare() As String
Global cTest() As String
Global cAction() As String
Global iTestCount As Integer
Global iActionCount As Integer
Global iRuleCount As Integer
'
' standard mapi objects
Global objSession As Object
Global objMsgColl As Object
Global objMessage As Object
Global objOriginator As Object
Global objRecipColl As Object
Global objRecipient As Object
Global objFolderColl As Object
Global objFolder As Object
Global objAddrEntry As Object
Global objInfoStoreColl As Object
Global objInfoStore As Object
Global objAttachColl As Object
Global objAttachments As Object
Next add the Main subroutine to the project. This Visual Basic project launches from a Main() subroutine instead of a form. The Main routine first calls a routine that performs the initialization routines, then calls the main form. Once the form is closed by the user, a short cleanup routine is called. Add the code in Listing 11.17 to the project.
Listing 11.17. Adding the Main routine.
Public Sub Main()
'
' main start and end
'
InitStuff
frmMEA.Show vbModal
CloseDown
'
End Sub
Now add the InitStuff subroutine to the project and enter the code in Listing 11.18.
Listing 11.18. Adding the InitStuff routine.
Public Sub InitStuff()
'
' it all starts here
'
ReDim cAction(0)
ReDim cTest(0)
ReDim cRuleName(0)
ReDim cRuleTest(0)
ReDim cRuleAction(0)
ReDim cRuleCompare(0)
'
LoadStrings
LoadValues
StartMAPI
LoadLists
'
End Sub
The routine in Listing 11.18 calls four other routines. The first one is LoadStrings. This routine initializes the local variables at startup. Add the LoadStrings subroutine and enter the code in Listing 11.19.
Listing 11.19. Adding the LoadStrings routine.
Public Sub LoadStrings()
'
' init all internals
'
cRuleFile = App.Path & "\" & App.EXEName & ".RUL"
cIsEqualTo = "EQ"
cIsContainedIn = "CI"
cIsGreaterThan = "GT"
cIsLessThan = "LT"
'
cGeneralSection = "General"
cGeneralEditor = "Editor"
cEditorValue = "notepad.exe"
cGeneralLogFile = "LogFile"
cLogFileValue = App.Path & "\" & App.EXEName & ".LOG"
cGeneralLogFlag = "LogFlag"
cLogFlagValue = "1"
cGeneralScanInterval = "ScanInterval"
cScanIntervalValue = "15"
cGeneralRuleCount = "RuleCount"
cRuleCountValue = "0"
cGeneralTestCount = "TestCount"
cTestCountValue = "0"
cGeneralActionCount = "ActionCount"
cActionCountValue = "0"
cGeneralProfile = "Profile"
cProfileValue = "MCA"
cGeneralDelFwdFlag = "DeleteForwardFlag"
cDelFwdFlagValue = "0"
cGeneralNotifyDialog = "NotifyDialog"
cNotifyDialogValue = "0"
cGeneralDelReplyFlag = "DeleteReplyFlag"
cDelReplyFlagValue = "0"
cGeneralMinOnStart = "MinimzeOnStart"
cMinOnStartValue = "0"
cGeneralAutoStart = "AutoStart"
cAutoStartValue = "0"
cGeneralLastUpdate = "LastUpdated"
cLastUpdateValue = Now()
'
cTestSection = "Tests"
cTestPriority = "Priority"
cTestSubject = "Subject"
cTestSender = "Sender"
cTestCommands = cTestPriority & " " & cTestSubject & " " & cTestSender
'
cActionSection = "Actions"
cActionCopy = "Copy"
cActionMove = "Move"
cActionForward = "Forward"
cActionReply = "Reply"
cActionNotify = "Notify"
cActionCommands = cActionCopy & " " & cActionMove & " " & cActionForward
cActionCommands = cActionCommands & " " & cActionReply & " " & cActionNotify
'
cRulesSection = "Rules"
'
End Sub
Tip |
Most of the strings in the LoadString routine are the names of control file keys ("Editor," "ScanInterval," "LogFile," and so on). By storing the names of the keys in this way, you can easily localize the project for other languages-because the MAPI Email Agent project checks only variable names, you can change the values in this section to match other languages without having to re-code most of the program. |
The next few routines all deal with data transfers to and from the ASCII control file. Listing 11.20 shows two new routines-LoadValues and SaveValues. Add these routines to your module and enter the code shown in Listing 11.20.
Listing 11.20. Adding the LoadValues and SaveValues routines.
Public Sub LoadValues()
'
' process rules file
'
INIGeneral "LOAD"
INIRules "LOAD"
INITests "LOAD"
INIActions "LOAD"
'
End Sub
Public Sub SaveValues()
'
' save ini values for next time
'
INIGeneral "SAVE"
INIRules "SAVE"
INITests "SAVE"
INIActions "SAVE"
'
End Sub
You can see that both routines call the same subroutines using different parameters for the save and load events. Now add the INIGeneral subroutine to your module and enter the code shown in Listing 11.21.
Listing 11.21. Adding the INIGeneral routine.
Public Sub INIGeneral(cAction As String)
'
' load general rule stuff
'
cLastUpdateValue = Format(Now(), "general date")
'
cEditorValue = INIRegSetting(cAction, cRuleFile, cGeneralSection, cGeneralEditor, cEditorValue)
cScanIntervalValue = INIRegSetting(cAction, cRuleFile, cGeneralSection, cGeneralScanInterval, cScanIntervalValue)
cLogFileValue = INIRegSetting(cAction, cRuleFile, cGeneralSection, cGeneralLogFile, cLogFileValue)
cLogFlagValue = INIRegSetting(cAction, cRuleFile, cGeneralSection, cGeneralLogFlag, cLogFlagValue)
cRuleCountValue = INIRegSetting(cAction, cRuleFile, cGeneralSection, cGeneralRuleCount, cRuleCountValue)
cActionCountValue = INIRegSetting(cAction, cRuleFile, cGeneralSection, cGeneralActionCount, cActionCountValue)
cTestCountValue = INIRegSetting(cAction, cRuleFile, cGeneralSection, cGeneralTestCount, cTestCountValue)
cProfileValue = INIRegSetting(cAction, cRuleFile, cGeneralSection, cGeneralProfile, cProfileValue)
cDelFwdFlagValue = INIRegSetting(cAction, cRuleFile, cGeneralSection, cGeneralDelFwdFlag, cDelFwdFlagValue)
cNotifyDialogValue = INIRegSetting(cAction, cRuleFile, cGeneralSection, cGeneralNotifyDialog, cNotifyDialogValue)
cDelReplyFlagValue = INIRegSetting(cAction, cRuleFile, cGeneralSection, cGeneralDelReplyFlag, cDelReplyFlagValue)
cMinOnStartValue = INIRegSetting(cAction, cRuleFile, cGeneralSection, cGeneralMinOnStart, cMinOnStartValue)
cAutoStartValue = INIRegSetting(cAction, cRuleFile, cGeneralSection, cGeneralAutoStart, cAutoStartValue)
cLastUpdateValue = INIRegSetting(cAction, cRuleFile, cGeneralSection, cGeneralLastUpdate, cLastUpdateValue)
'
End Sub
You'll notice that the INIRegSetting function called in each line of Listing 11.21 is very similar to the SaveSetting/GetSetting functions built into Visual Basic 4.0. However, unlike the built-in Visual Basic functions, this one routine can be used to both read and write values. Also, this custom version always writes to a disk file. The Visual Basic 4.0 SaveSetting function saves values to the Registry under Windows NT and Windows 95.
Add the INIRegSetting function to your project and enter the code shown in Listing 11.22.
Listing 11.22. Adding the INIRegSetting function.
Public Function INIRegSetting(cAction As String, cFile As String, cSection As String, cKey As String, cValue As String) As String
'
' handle read/write of local ini settings
' for public use
'
INIRegSetting = ""
'
If UCase(cAction) = "LOAD" Then
INIRegSetting = LocalGetSetting(cFile, cSection, cKey, cValue)
End If
'
If UCase(cAction) = "SAVE" Then
INIRegSetting = LocalSaveSetting(cFile, cSection, cKey, cValue)
End If
'
End Function
The INIRegSetting routine makes one last call down to a pair of custom routines. These custom routines (LocalGetSetting and LocalSaveSetting) are the wrapper functions for the API declarations you added at the start of this module. Add the two new functions (LocalGetSetting and LocalSaveSetting) and enter the code shown in Listing 11.23.
Listing 11.23. Adding the LocalGetSetting and LocalSaveSetting functions.
Public Function LocalGetSetting(cFile As String, cSection As String, cKey As String, cDefault As String) As String
'
' mimic GetSetting/SaveSetting for 32-bit text files
'
Dim lRtn As Long
Dim lSize As Long
Dim cTemp As String * 1024
'
lSize = Len(cTemp)
lRtn = GetPrivateProfileString(cSection, cKey, cDefault, cTemp, lSize, cFile)
If Trim(cTemp) = "" Then
cTemp = Trim(cDefault)
lRtn = WritePrivateProfileString(cSection, cKey, cTemp, cFile)
End If
'
LocalGetSetting = Left(cTemp, lRtn)
'
End Function
Public Function LocalSaveSetting(cFile As String, cSection As String, cKey As String, cValue As String)
'
' mimic INI/Registry stuff for public use
'
Dim lRtn As Long
'
lRtn = WritePrivateProfileString(cSection, cKey, cValue, cFile)
LocalSaveSetting = cValue ' return what was saved
'
End Function
Next you need to add the routines to load the rules, actions, and tests from the control file into memory variables. Listing 11.24 contains the code for the INIRules routine.
Listing 11.24. Adding the INIRules routine.
Public Sub INIRules(cAction As String)
'
' load all rules
'
Dim x As Integer
'
iRuleCount = Val(cRuleCountValue)
ReDim Preserve cRuleName(iRuleCount)
ReDim Preserve cRuleTest(iRuleCount)
ReDim Preserve cRuleAction(iRuleCount)
ReDim Preserve cRuleCompare(iRuleCount)
'
If iRuleCount > 0 Then
For x = 0 To iRuleCount - 1
cRuleName(x) = INIRegSetting(cAction, cRuleFile, cRulesSection, "RuleName" & CStr(x), cRuleName(x))
cRuleTest(x) = INIRegSetting(cAction, cRuleFile, cRulesSection, "RuleTest" & CStr(x), cRuleTest(x))
cRuleAction(x) = INIRegSetting(cAction, cRuleFile, cRulesSection, "RuleAction" & CStr(x), cRuleAction(x))
cRuleCompare(x) = INIRegSetting(cAction, cRuleFile, cRulesSection, "RuleCompare" & CStr(x), cRuleCompare(x))
Next x
End If
'
End Sub
Listing 11.25 shows the code for the INITests and INIActions routines. Add these to your project.
Listing 11.25. Adding the INITests and INIActions routines.
Public Sub INITests(cAction As String)
'
' load all tests
'
Dim x As Integer
'
iTestCount = Val(cTestCountValue)
ReDim Preserve cTest(iTestCount)
'
If iTestCount > 0 Then
For x = 0 To iTestCount - 1
cTest(x) = INIRegSetting(cAction, cRuleFile, cTestSection, "Test" & CStr(x), cTest(x))
Next x
End If
'
End Sub
Public Sub INIActions(cEvent As String)
'
' load all actions
'
Dim x As Integer
'
iActionCount = Val(cActionCountValue)
ReDim Preserve cAction(iActionCount)
'
If iActionCount > 0 Then
For x = 0 To iActionCount - 1
cAction(x) = INIRegSetting(cEvent, cRuleFile, cActionSection, "Action" & CStr(x), cAction(x))
Next x
End If
'
End Sub
The last routines needed for the initialization section are the ones that start and end your MAPI sessions (you remember MAPI, right?), and the routine that handles program exit cleanup.
First add the CloseDown cleanup routine to your project and enter the code shown in Listing 11.26.
Listing 11.26. Adding the CloseDown routine.
Public Sub CloseDown()
'
' close down system
'
SaveValues
MAPIEnd
'
End Sub
Now add the StartMAPI and MAPIEnd routines and enter the code from Listing 11.27.
Listing 11.27. Adding the StartMAPI and MAPIEnd routines.
Public Sub StartMAPI()
'
' log into mapi system
'
Set objSession = CreateObject("MAPI.Session")
objSession.Logon profilename:=cProfileValue, profilepassword:="", newsession:=True
'
End Sub
Public Sub MAPIEnd()
'
' log off mapi system
objSession.Logoff
Set objSession = Nothing
'
End Sub
That is the end of the support routines for initialization. Save this module (MEA.BAS) and project (MEA.VBP) before continuing to the next section.
The next set of routines handle the addition and deletion of records from the rules, tests, and actions lists. There are also two routines that handle the populating of the list controls on the MAPI Email Agent forms.
First, add a new subroutine called LoadLists to the module and enter the code shown in Listing 11.28. This routine simply calls the low-level function that actually fills the list box control.
Listing 11.28. Adding the LoadLists routine.
Public Sub LoadLists()
'
' load the main form lists
'
FillList "rules", frmMEA.lstrules
FillList "tests", frmMEA.lsttests
FillList "actions", frmMEA.lstactions
'
End Sub
Next, add the low-level routine that is called by LoadLists. Add the subroutine FillList to the project and enter the code shown in Listing 11.29.
Listing 11.29. Adding the FillList routine.
Public Sub FillList(cListName As String, lstControl As Control)
'
' fill form lists with data
'
Dim x As Integer
Dim ln As Integer
Dim cLine As String
'
Select Case UCase(cListName)
Case "RULES"
lstControl.Clear
For x = 0 To iRuleCount - 1
If Len(cRuleName(x)) > 18 Then
ln = 18
Else
ln = Len(cRuleName(x))
End If
cLine = Left(cRuleName(x), 18) & Space(20 - ln)
'
If Len(cRuleTest(x)) > 13 Then
ln = 13
Else
ln = Len(cRuleTest(x))
End If
'
cLine = cLine & Left(cRuleTest(x), 13) & Space(15 - ln)
cLine = cLine & cRuleCompare(x)
cLine = cLine & Space(2)
cLine = cLine & cRuleAction(x)
lstControl.AddItem cLine
Next x
Case "TESTS"
lstControl.Clear
For x = 0 To iTestCount - 1
lstControl.AddItem cTest(x)
Next x
Case "ACTIONS"
lstControl.Clear
For x = 0 To iActionCount - 1
lstControl.AddItem cAction(x)
Next x
Case "COMPARES"
lstControl.Clear
lstControl.AddItem cIsEqualTo
lstControl.AddItem cIsGreaterThan
lstControl.AddItem cIsLessThan
lstControl.AddItem cIsContainedIn
End Select
'
End Sub
Notice the use of column-like spacing for the rules list. By computing length and setting spacing evenly, you can give the effect of a grid format while still using a list box control.
Tip |
This technique works well only if you set your list box control font type to a fixed-width font such as Courier or System. |
The next two routines deal with the addition and deletion of tests. First, add the new routine AddTest to the project and enter the code shown in Listing 11.30.
Listing 11.30. Adding the AddTest routine.
Public Sub AddTest(cTestLine As String)
'
' add a new test to system
'
Dim x As Integer
Dim p As Integer
'
' look for empty spot
p = -1
For x = 0 To iTestCount - 1
If cTest(x) = "" Then
p = x
Exit For
End If
Next x
'
' no empty spot, make a new one
If p = -1 Then
iTestCount = iTestCount + 1
cTestCountValue = CStr(iTestCount)
ReDim Preserve cTest(iTestCount)
p = iTestCount - 1
End If
'
cTest(p) = cTestLine ' save item
'
' refresh lists
SaveValues
LoadValues
LoadLists
'
End Sub
This routine works by adding an item to the Test() array. First, the routine attempts to find an open slot in the existing list. If none is found, the routine will expand the list and add the new item at the bottom of the list. As a final step, AddTest saves the new data to the control file and then reloads the control data to refresh the local variables.
The DeleteTest function is quite simple. It just removes the selected item from the array by blanking it out. This is simple, but not the most desirable. When you run the program you'll see that each deletion leaves a hole in the list. As new items are added, these holes are filled. The holes do not adversely affect processing, but they are a bit unsightly in the list controls. In a production application, more time can be spent on the user interface. For now, just keep in mind you have a routine that works-you can add the bells and whistles later.
Add the DeleteTest subroutine to your project and enter the code in Listing 11.31.
Listing 11.31. Adding the DeleteTest routine.
Public Sub DeleteTest(itest As Integer)
'
' remove test from list
'
cTest(itest) = ""
'
SaveValues
LoadValues
LoadLists
'
End Sub
The AddAction and DeleteAction routines are almost identical to the AddTest and DeleteTest routines. Add these two new subroutines to your project and enter the code from Listings 11.32 and 11.33.
Listing 11.32. Adding the AddAction routine.
Public Sub AddAction(cInput As String)
'
' add a new test to system
'
Dim x As Integer
Dim p As Integer
'
' look for empty spot
p = -1
For x = 0 To iActionCount - 1
If cAction(x) = "" Then
p = x
Exit For
End If
Next x
'
' no empty spot, make a new one
If p = -1 Then
iActionCount = iActionCount + 1
cActionCountValue = CStr(iActionCount)
ReDim Preserve cAction(iActionCount)
p = iActionCount - 1
End If
'
cAction(p) = cInput ' save item
'
' refresh lists
SaveValues
LoadValues
LoadLists
'
End Sub
Listing 11.33. Adding the DeleteAction routine.
Public Sub DeleteAction(iAction As Integer)
'
' remove action from list
'
cAction(iAction) = ""
'
SaveValues
LoadValues
LoadLists
'
End Sub
The AddRule routine is a bit different. The AddRule routine calls the MAPI Email Agent Rule form to add new rules. This form then calls a function to actually add the new rule to the control file. So there are really three routines:
First, add the new AddRule subroutine and enter the code shown in Listing 11.34.
Listing 11.34. Adding the AddRule routine.
Public Sub AddRule()
'
' handle adding new rule
'
frmMEARule.Show vbModal
'
frmMEA.MousePointer = vbHourglass
'
SaveValues
LoadValues
LoadLists
'
frmMEA.MousePointer = vbNormal
'
End Sub
Now add the MakeRule subroutine to your project. This is the routine that actually saves the results of data entry on the MAPI Email Agent Rule form. Enter the code from Listing 11.35.
Listing 11.35. Adding the MakeRule routine.
Public Sub MakeRule(cName As String, cRule As String)
'
' store new rule
'
Dim x As Integer
Dim p As Integer
Dim cTestPart As String
Dim cComparePart As String
Dim cActionPart As String
Dim nPos1 As Integer
Dim nPos2 As Integer
'
' first get parts
'
nPos1 = 1
nPos2 = InStr(nPos1, cRule, " | ")
If nPos2 <> 0 Then
cTestPart = Trim(Mid(cRule, nPos1, nPos2 - nPos1))
End If
'
nPos1 = nPos2 + 3
nPos2 = InStr(nPos1, cRule, " | ")
If nPos2 <> 0 Then
cComparePart = Trim(Mid(cRule, nPos1, nPos2 - nPos1))
End If
'
nPos1 = nPos2 + 3
cActionPart = Trim(Mid(cRule, nPos1, 255))
'
'
' look for empty spot
p = -1
For x = 0 To iRuleCount - 1
If Trim(cRuleName(x)) = "" Then
p = x
Exit For
End If
Next x
'
' no empty spot, make a new one
If p = -1 Then
iRuleCount = iRuleCount + 1
cRuleCountValue = CStr(iRuleCount)
ReDim Preserve cRuleName(iRuleCount)
ReDim Preserve cRuleTest(iRuleCount)
ReDim Preserve cRuleCompare(iRuleCount)
ReDim Preserve cRuleAction(iRuleCount)
p = iRuleCount - 1
End If
'
' save it
cRuleName(p) = cName
cRuleTest(p) = cTestPart
cRuleCompare(p) = cComparePart
cRuleAction(p) = cActionPart
'
' refresh storage
SaveValues
LoadValues
'
End Sub
The last list-handling routine you need to add is the DeleteRule subroutine. After adding the routine, enter the code you see in Listing 11.36.
Listing 11.36. Adding the DeleteRule routine.
Public Sub DeleteRule(iAction As Integer)
'
' remove rule from list
'
cRuleName(iAction) = ""
cRuleTest(iAction) = ""
cRuleCompare(iAction) = ""
cRuleAction(iAction) = ""
'
SaveValues
LoadValues
LoadLists
'
End Sub
There is one more support routine needed before you are done with this section. You need a routine to write out log messages when requested. Add a new subroutine called LogWrite to your project and enter the code shown in Listing 11.37. This routine writes lines to a text file along with the date and time the line was written. This routine also sends the same message to the status line of the MAPI Email Agent main form.
Listing 11.37. Adding the LogWrite routine.
Public Sub LogWrite(cLine As String)
'
' write an entry in the log
'
Dim nFile As Integer
Dim cWriteLine As String
'
If cLogFlagValue <> "1" Then
Exit Sub ' write is OFF
End If
'
cWriteLine = Format(Now, "general date")
cWriteLine = cWriteLine & Space(3)
cWriteLine = cWriteLine & cLine
'
nFile = FreeFile
Open cLogFileValue For append As nFile
Print #nFile, cWriteLine
Close #nFile
'
frmMEA.lblstatus = cWriteLine
'
End Sub
You now have all the routines needed to add and delete items from the lists. Actually you have everything built except the message processing routines. This is a good time to test the forms and list-handling routines. First, you need to add a "stub" routine to your project. This routine does nothing on its own, but it allows you to test your forms without getting errors. Just create a new subroutine called StartProcess.
Public Sub StartProcess()
'
End Sub
Now save the module (MEA.BAS) and the project (MEA.VBP) before doing any test runs. After you are satisfied the routines are working properly, you can go on to the next section for the last bit of coding-the message processing routines.
This last set of routines is where the MAPI services are finally used. The goal of the message processing routines is to inspect each message in the user's inbox and check the messages against the rules that have been established for the MAPI Email Agent.
The top-level routines are StartProcess and ScanMsgs. The StartProcess routine is called by the Timer1_Timer event or by pressing the Start button on the main form. StartProcess checks to see if there are any messages in the user's inbox. If there are, then the ScanMsg routine is called to process each message.
If you already added the StartProcess routine, locate it now. Otherwise, add the new routine and enter the code shown in Listing 11.38.
Listing 11.38. Adding the StartProcess code.
Public Sub StartProcess()
'
' start main process loop
'
Set objMsgColl = objSession.Inbox.Messages
If objMsgColl Is Nothing Then
MsgBox "No Messages to scan"
Else
ScanMsgs
Set objMsgColl = Nothing
End If
'
End Sub
The StartProcess routine attempts to create a message collection object based on the Session inbox. If that is successful, the ScanMsgs routine can be called. Now add the ScanMsgs subroutine and enter the code in Listing 11.39.
Listing 11.39. Adding the ScanMsgs routine.
Public Sub ScanMsgs()
'
' check each message for hits
'
Dim x As Long
'
Set objMessage = objMsgColl.GetFirst
If objMessage Is Nothing Then
Exit Sub ' no messages!
End If
Do Until objMessage Is Nothing
CheckRule
Set objMessage = objMsgColl.GetNext
Loop
'
LogWrite "Msg Scan Completed"
'
End Sub
The ScanMsgs routine selects each message in the collection and submits it to the CheckRule routine for processing.
Now add the CheckRule subroutine and type in the code shown in Listing 11.40.
Listing 11.40. Adding the CheckRule routine.
Public Sub CheckRule()
'
' check for rule hit
'
Dim x As Integer
Dim cCmd As String
Dim bRtn As Boolean
Dim objAddrEntry As Object
'
On Error GoTo CheckRuleErr
'
For x = 0 To iRuleCount - 1
cCmd = ParseWord(cRuleTest(x))
'
Select Case UCase(cCmd)
Case UCase(cTestSender)
Set objAddrEntry = objMessage.Sender
If objAddrEntry Is Nothing Then
' na
Else
If CheckSender(x, objMessage.Sender.Name) = True Then
DoAction x
End If
End If
Case UCase(cTestSubject)
If CheckSubject(x, objMessage.subject) = True Then
DoAction x
End If
Case UCase(cTestPriority)
If CheckPriority(x, objMessage.importance) = True Then
DoAction x
End If
End Select
'
Next x
'
Exit Sub
'
CheckRuleErr:
MsgBox Error$, vbCritical, "CheckRuleErr [" & CStr(Err) & "]"
'
End Sub
Several things are going on in this routine. First, the first "word" on the line is removed using the ParseWord() function. This word is then used to determine the type of test to perform on the message. The appropriate check subroutine is called (CheckSender, CheckSubject, CheckPriority) and, if the return is positive, the DoAction routine is called to handle the action portion of the rule.
Now add the ParseWord function and enter the code shown in Listing 11.41.
Listing 11.41. Adding the ParseWord function.
Public Function ParseWord(cLine As String) As String
'
' pick a word off line
'
Dim nPos As Integer
'
nPos = InStr(cLine, " ")
If nPos <> 0 Then
ParseWord = Left(cLine, nPos - 1)
Else
ParseWord = ""
End If
'
End Function
The ParseWord() function accepts a string and returns the first full word found in the string. For example ParseWord("SENDER Smith") would return SENDER. This is used to pull the command portion of a test or action record.
The CheckRule routine you entered earlier (in Listing 11.40) uses ParseWord() to get the message portion command of a rule (SENDER, SUBJECT, PRIORITY). This value is then used to call the three message-part-specific check routines (CheckSender, CheckSubject, and CheckPriority). You need to add these routines next.
First add the CheckSender function, and enter the code shown in Listing 11.42.
Listing 11.42. Adding the CheckSender function.
Public Function CheckSender(nRule As Integer, cName As String)
'
' check name against sender test
'
Dim nPos As Integer
Dim cSender As String
'
nPos = InStr(cRuleTest(nRule), " ")
If nPos <> 0 Then
cSender = Trim(Mid(cRuleTest(nRule), nPos + 1, 255))
End If
'
cSender = Trim(UCase(cSender))
cName = Trim(UCase(cName))
'
Select Case UCase(cRuleCompare(nRule))
Case UCase(cIsEqualTo)
If cSender = cName Then
CheckSender = True
Else
CheckSender = False
End If
Case UCase(cIsGreaterThan)
If cSender > cName Then
CheckSender = True
Else
CheckSender = False
End If
Case UCase(cIsLessThan)
If cSender < cName Then
CheckSender = True
Else
CheckSender = False
End If
Case UCase(cIsContainedIn)
If InStr(cSender, cName) <> 0 Then
CheckSender = True
Else
CheckSender = False
End If
End Select
'
End Function
Note that this routine uses a SELECT CASE structure to handle the compare portion of the rule. After locating the correct compare operation, CheckSender tests the Sender portion against the Name in the rule and returns the result (TRUE or FALSE).
The CheckSubject and CheckPriority functions work the same way. The only difference is that the CheckPriority function does not test for CI ("is contained in"). Add the CheckSubject function and enter the code from Listing 11.43.
Listing 11.43. Adding the CheckSubject routine.
Public Function CheckSubject(nRule As Integer, cSubjMsg As String) As Boolean
'
' check subject against message test
'
Dim nPos As Integer
Dim cSubjRule As String
'
nPos = InStr(cRuleTest(nRule), " ")
If nPos <> 0 Then
cSubjRule = Trim(Mid(cRuleTest(nRule), nPos + 1, 255))
End If
'
cSubjRule = UCase(Trim(cSubjRule))
cSubjMsg = UCase(Trim(cSubjMsg))
'
Select Case UCase(cRuleCompare(nRule))
Case UCase(cIsEqualTo)
If cSubjRule = cSubjMsg Then
CheckSubject = True
Else
CheckSubject = False
End If
Case UCase(cIsLessThan)
If cSubjRule < cSubjMsg Then
CheckSubject = True
Else
CheckSubject = False
End If
Case UCase(cIsGreaterThan)
If cSubjRule > cSubjMsg Then
CheckSubject = True
Else
CheckSubject = False
End If
Case UCase(cIsContainedIn)
If InStr(cSubjRule, cSubjMsg) <> 0 Then
CheckSubject = True
Else
CheckSubject = False
End If
End Select
'
End Function
Finally, add the CheckPriority function and enter the code from Listing 11.44.
Listing 11.44. Adding the CheckPriority routine.
Public Function CheckPriority(nRule As Integer, nImpMsg) As Boolean
'
' check subject against message test
'
Dim nPos As Integer
Dim cImpRule As String
Dim nImpRule As Integer
'
nPos = InStr(cRuleTest(nRule), " ")
If nPos <> 0 Then
cImpRule = Trim(Mid(cRuleTest(nRule), nPos + 1, 255))
End If
'
nImpRule = Val(cImpRule)
'
Select Case UCase(cRuleCompare(nRule))
Case UCase(cIsEqualTo)
If nImpRule = nImpMsg Then
CheckPriority = True
Else
CheckPriority = False
End If
Case UCase(cIsLessThan)
If nImpRule < nImpMsg Then
CheckPriority = True
Else
CheckPriority = False
End If
Case UCase(cIsGreaterThan)
If nImpRule > nImpMsg Then
CheckPriority = True
Else
CheckPriority = False
End If
End Select
'
End Function
If the Checknnn routine returns TRUE, an action must take place. The DoAction routine is used to execute the appropriate e-mail action. DoAction accepts the index to the rule as its only parameter. Like the CheckRule routine, DoAction uses a SELECT CASE structure to act on each command word (NOTIFY, COPY, MOVE, FORWARD, and REPLY).
Add the DoAction subroutine to the project and enter the code shown in Listing 11.45.
Listing 11.45. Adding the DoAction routine.
Public Sub DoAction(nRule As Integer)
'
' handle valid action
' nRule points to rule in array
' use current objMessage
'
Dim cCmd As String ' action command
Dim cTarget As String ' action target
Dim nPos As Integer
'
' get command and target
cCmd = ParseWord(cRuleAction(nRule))
nPos = InStr(cRuleAction(nRule), " ")
If nPos <> 0 Then
cTarget = Trim(Mid(cRuleAction(nRule), nPos + 1, 255))
End If
'
' now execute command
Select Case UCase(cCmd)
Case UCase(cActionMove)
MsgMoveCopy "MOVE", cTarget, objMessage
Case UCase(cActionCopy)
MsgMoveCopy "COPY", cTarget, objMessage
Case UCase(cActionForward)
MsgFwdReply "FORWARD", cTarget, objMessage
Case UCase(cActionReply)
MsgFwdReply "REPLY", cTarget, objMessage
Case UCase(cActionNotify)
MsgNotify cTarget, objMessage
End Select
'
End Sub
There are only three routines used to act on all five commands. This is because the FORWARD and REPLY commands act on messages, and the COPY and MOVE commands act on folders. Only one routine is needed for each (with slight behavior changes within each routine).
The NOTIFY option is the easiest to handle. All that is needed is a pop-up dialog box when the message arrives. Add the MsgNotify routine and type in the code from Listing 11.46.
Listing 11.46. Adding the MsgNotify routine.
Public Sub MsgNotify(cNotify As String, objMsg As Object)
'
Dim cMsg As String
'
cMsg = "Message Notification for ["
cMsg = cMsg & objMsg.subject
cMsg = cMsg & "] from ["
cMsg = cMsg & objMsg.Sender.Name & "]"
'
' send out pop-up?
If cNotifyDialogValue = "1" Then
MsgBox cMsg, vbExclamation, "MAPI Email Agent Notification"
End If
LogWrite (cMsg)
'
End Sub
The routine needed for forwarding and replying to messages involves making a new message (with the contents of the original) and sending it to a new address. Since this is actually a messaging operation, you'll use the .Send function to force the message into the transport for delivery. Add the MsgFwdReply subroutine and enter the code in Listing 11.47.
Listing 11.47. Adding the MsgFwdReply routine.
Public Sub MsgFwdReply(cEvent As String, cDestAddr As String, objMsg As Object)
'
Dim cMsg As String
'
Dim objLocalMsgColl As Object
Dim objCopyMsg As Object
Dim cHeader As String
Dim cSubjPrefix As String
'
cTarget = UCase(cEvent)
'
cHeader = "<------ Message " & cEvent
cHeader = cHeader & " from ["
cHeader = cHeader & objSession.Name
cHeader = cHeader & "] by MAPI Email Agent ------>"
cHeader = cHeader & Chr(13) & Chr(10) & Chr(13) & Chr(10)
'
If cEvent = "REPLY" Then
cSubjPrefix = "RE: "
Else
cSubjPrefix = "FW: "
End If
'
Set objLocalMsgColl = objSession.Outbox.Messages
Set objCopyMsg = objLocalMsgColl.Add
With objCopyMsg
.subject = cSubjPrefix & objMsg.subject
.Text = cHeader & objMsg.Text
End With
'
' add recipient
Set objRecipient = objCopyMsg.Recipients.Add
objRecipient.Name = cDestAddr
objRecipient.Type = mapiTo
objCopyMsg.Recipients.Resolve showdialog:=False
objCopyMsg.Update
objCopyMsg.Send showdialog:=False
'
' delete old message?
If cDelFwdFlagValue = "1" And cEvent = "FORWARD" Then
objMessage.Delete
End If
If cDelReplyFlagValue = "1" And cEvent = "REPLY" Then
objMessage.Delete
End If
objSession.Outbox.Update
'
' send out status
cMsg = cEvent & " Message ["
cMsg = cMsg & objMsg.subject
cMsg = cMsg & "] to [" & cDestAddr & "]"
'
LogWrite (cMsg)
'
End Sub
There are a few things to keep in mind about this routine. First, a good REPLY routine should allow users to attach, or append, a text message to the original. Here, that code is left out for brevity. You'll also notice that only one recipient is added to the note. In some cases, it is possible that more than one person should receive the forwarded message. This can be handled by using distribution lists. Finally, there is no code here to handle any attachments to the original note. This should be added in a production environment.
The other action to be handled by the MAPI Email Agent is copying or moving messages to other folders. This is accomplished using the .Update method. Moving messages is similar to posting them. For this reason you do not want to attempt to "send" the message. Add the MsgMoveCopy subroutine and enter the code shown in Listing 11.48.
Listing 11.48. Adding the MsgMoveCopy routine.
Public Sub MsgMoveCopy(cEvent As String, cFolder As String, objMsg As Object)
'
Dim objLocalFolder As Object
Dim objLocalMsg As Object
Dim objLocalRecipient As Object
'
Dim cMsg As String
Dim cFldrID As String
Dim cRecipName As String
Dim cHeader As String
Dim i As Integer
'
' carry sender info with you
cHeader = "<------ " & UCase(cEvent) & " from ["
cHeader = cHeader & objMsg.Sender.Name
cHeader = cHeader & "] by MAPI Email Agent ------>"
cHeader = cHeader & Chr(13) & Chr(10) & Chr(13) & Chr(10)
'
' look for folder
cFldrID = FindFolder(cFolder)
If cFldrID = "" Then
Exit Sub
End If
'
' move to folder
Set objLocalFolder = objSession.GetFolder(cFldrID)
Set objLocalMsg = objLocalFolder.Messages.Add
'
' copy from objmsg to objlocalmsg
With objLocalMsg
.DeliveryReceipt = objMsg.DeliveryReceipt
.Encrypted = objMsg.Encrypted
.importance = objMsg.importance
.ReadReceipt = objMsg.ReadReceipt
.Sent = objMsg.Sent
.Signed = objMsg.Signed
.subject = objMsg.subject
.Submitted = objMsg.Submitted
.Text = cHeader & objMsg.Text
.TimeReceived = objMsg.TimeReceived
.TimeSent = objMsg.TimeSent
.Type = objMsg.Type
.Unread = objMsg.Unread
End With
'
' add recipients
For i = 1 To objMsg.Recipients.Count Step 1
cRecipName = objMsg.Recipients.Item(i).Name
If cRecipName <> "" Then
Set objLocalRecipient = objLocalMsg.Recipients.Add
objLocalRecipient.Name = cRecipName
End If
Next i
'
' now save the update
objLocalMsg.Update
'
' if a move, dump the original message
If UCase(cEvent) = "MOVE" Then
objMsg.Delete
End If
'
' send out notices
cMsg = UCase(cEvent) & " Message ["
cMsg = cMsg & objMsg.subject
cMsg = cMsg & "] to [" & cFolder & "]"
LogWrite (cMsg)
'
End Sub
Again, a few things worth pointing out here. First, moving or copying messages to other folders requires that you actually find the target folder first. This is not as simple as looking up a name in a list. MAPI message stores are recursively hierarchical. That means that folders can exist within folders. And MAPI does not publish a list of the folder tree-you must traverse it each time yourself. This means you need your own FindFolder routine (you'll add that in a minute).
Warning |
Since traversing the folder tree can be time-consuming, you might be tempted to build a tree at startup and use that throughout your program. Be careful! Since the Microsoft Exchange message stores can be shared among users, the tree can change rather rapidly. Not only could you encounter new folders or discover that old folders have been deleted, it is also possible that your target folder has been moved. It is a good idea to traverse the folder tree each time you attempt to find a folder. |
Notice that the process of moving a message really involves creating a copy in the new folder. The code here copies the most commonly used items, but does not copy any attachments. Keep in mind that some properties may not exist for some messages. You'll need error trapping to prevent program crashes.
The last routine you need for the MAPI Email Agent application is the FindFolder function. This routine accepts a folder name and returns its unique MAPI ID value. Add the FindFolder function and enter the code shown in Listing 11.49.
Listing 11.49. Adding the FindFolder function.
Public Function FindFolder(cFldrName As String) As String
'
' see if you can locate the requested folder
' if found, return the unqiue Folder ID
'
Dim cRtnID As String
Dim cTempID As String
Dim objInfoStore As Object
Dim objTempFldr As Object
Dim objTempColl As Object
Dim x As Integer
'
cRtnID = "" ' assume not found
'
' scan the folder collection
Set objTempColl = objSession.Inbox.Folders
Set objTempFldr = objTempColl.GetFirst
Do
cTempID = objTempFldr.ID
If UCase(objTempFldr.Name) = UCase(cFldrName) Then
cRtnID = objTempFldr.ID
Exit Do
End If
Set objTempFldr = objTempColl.GetNext
Loop While objTempFldr.Name <> ""
'
FindFolder = cRtnID ' return the result
'
End Function
You'll notice that the MAPI Email Agent inspects only the top-level folder collection of the Session.InBox. This is done to keep the code brief. If you wanted to search all the folders, you'd need to start at the Session.RootFolder and, after collecting the folders, inspect each of them for the existence of folders, and so on and so forth. For now, the MAPI Email Agent program only works with folders in the inbox. You can add code to expand the search capabilities of the Findfolder routine in future MAPI projects.
Save this module (MEA.BAS) and the project (MEA.VBP) before you start testing the MAPI Email Agent.
After you have completed the MAPI Email Agent project, you can install it on any workstation that has access to MAPI services. You do not have to have Microsoft Exchange or Microsoft Mail clients running in order to use the MAPI Email Agent.
In order to set up MAPI Email Agent to process your incoming mail you need to build test, action, and rule records. You also need to use your Microsoft Exchange or Microsoft Mail client to build any new target folders you want to include as parts of COPY or MOVE actions.
Once your folders are built and your rules are entered, you can launch the MAPI Email Agent at startup each day and let the program do its thing!
The next few sections talk you through a simple example of running the MAPI Email Agent on your workstation.
First, start up your MAPI e-mail client and make sure you have the following folders within your inbox:
After adding these folders to your inbox, it should look something like the one in Figure 11.4.
Figure 11.4 : Adding folders to your inbox.
You'll use these folders as destinations when you add rules to your MAPI Email Agent database.
Next you need to add test, action, and rule records to the MAPI
Email Agent database. Start the MAPI Email Agent program and enter
the tests and actions shown in Table 11.4.
Record Type | Contents |
Test | SENDER Boss |
SUBJECT MAPI | |
SUBJECT SALES | |
PRIORITY 0 | |
SENDER Assistant | |
Action | FORWARD Boss |
MOVE Urgent | |
COPY MAPI | |
REPLY Sales | |
NOTIFY Me |
Figure 11.5 shows how the MAPI Email Agent looks after the tests and actions have been entered.
Figure 11.5 : Adding the test and action records.
Now you're ready to create some rules for the MAPI Email Agent.
Enter the rules shown in Table 11.5.
Rule Name | Test | Compare | Action |
Bosses Mail | SENDER Boss | EQ | COPY Urgent |
Save MAPI | SUBJECT MAPI | CI | COPY MAPI |
Assistant Routing | SENDER Assistant | EQ | FORWARD Boss |
Wake Me Up | PRIORITY 0 | EQ | NOTIFY Me |
Sales Handler | SUBJECT SALES | CI | REPLY Sales |
Figure 11.6 shows what your screen should look like as you build your MAPI Email Agent rules.
Figure 11.6 : Building MAPI Agent rules.
You're now ready to test your MAPI Email Agent!
You can easily test the MAPI Email Agent by sending yourself some messages that meet the entered criteria. Send yourself a note that has MAPI in the subject or send a note that is marked high priority. Once the messages are sent, click the Start Timer button on the MAPI Email Agent to scan your inbox. You'll see messages along the status bar as the program scans; when the program finishes, check the log file by clicking the View Log button.
In this chapter you learned how to use the OLE Messaging library
to create a stand-alone
e-mail agent. This agent can scan your incoming mail and, based
on rules you establish, automatically handle messages for you.
All actions are based on rules you establish in a control file.
Features of the MAPI Email Agent include:
You also learned that the process of sending messages is handled differently than posting messages (.Send versus .Update), and you learned how to traverse the folder tree to locate a desired MAPI storage folder.