Distributing Application Updates
Transportation Issues
The first issue, particularly with the Mail side, was that we couldn't simply attach our file to the mail message and send it off to the user who then would need to save the attachment to disk and import it. That was just fraught with danger. We needed the whole distribution to be invisible to the user.
Simple MAPI has two types of messages. These are IPC's and IPM's. IPM is what a normal mail message is. It is displayed inside Exchange and can be manipulated by the user. An IPC or Inter-Personal Communication, as opposed to Message, does not get displayed as part of exchange and is only accessible via program control.
This enabled our application at the client's site to periodically check for the existance of any IPC messages in the system and then to process them.
The code below demonstrates how to create an IPC message, Attach files to it, create a recipient list and send the message.
' Create an e-mail with the recipient list in it.
MAPIMessages.Action = 6 ' Compose.
MAPIMessages.MsgSubject = "GUI Distribution Item" ' Inform user.
MAPIMessages.MsgIndex = -1
MAPIMessages.MsgType = "IPC.GUI.DistributionItem"
' Do not show in 'In Box'.
' Set on error condition. Only errors expected here are MAPI errors.
On Error Resume Next
' Take all the recipient names and add them to the message.
tmpID = ""
RecipientIndex = 0
' Loop through all the recipients.
For t = 1 To Len(RecipList)
If Mid$(RecipList, t, 1) = ";" Or t = Len(RecipList) Then
If t = Len(RecipList) Then
tmpID = tmpID & Mid$(RecipList, t, 1)
End If
' Create an index for this recipient.
MAPIMessages.RecipIndex = RecipientIndex
' Add the name to the recipient list.
MAPIMessages.RecipDisplayName = Trim$(tmpID)
' Resolve this recipients name.
MAPIMessages.Action = 13
' Check for a known error
If Err = 32014 Then 'Unknown MAPI recipient.
' Add the name to an array for later processing.
ReDim Preserve FailedRecipients(UBound(FailedRecipients) + 1)
FailedRecipients(UBound
(FailedRecipients)) = Trim$(tmpID)
' Delete the failed recipient.
MAPIMessages.Delete (1)
Else
' Set the type of recipient as 'To' rather than 'CC'.
MAPIMessages.RecipType = 1
' Increment index count.
RecipientIndex = RecipientIndex + 1
End If
tmpID = ""
Else
tmpID = tmpID & Mid$(RecipList, t, 1)
End If
Next t
On Error GoTo ResolveMailRecipientsErr
' Attach the Distribution file to the mail message.
MAPIMessages.AttachmentPosition = 0 ' First attachment.
MAPIMessages.AttachmentType = 0 ' Data attachment.
MAPIMessages.AttachmentPathName = App.Path & "\Distrib.MDB"
' Attachment.
' Process the failed recipients and/or send the message.
If UBound(FailedRecipients) > 0 Then
' Set up a message header.
Msg = "The following Mail Recipient(s) failed to
resolve;" & Chr$(10)& Chr$(13) & Chr$(10) & Chr$(13)
' Add all the failed recipients to the message.
For t = 1 To UBound(FailedRecipients)
Msg$ = Msg$ & Chr$(9) & t & ") " & FailedRecipients(t)
& Chr$(10) & Chr$(13)
Next t
' Add a footer to the message.
Msg$ = Msg$ & Chr$(10) & Chr$(13) & "Please check their properties
to resolve the problem."
' If we have recipients that resolved,
prompt to send without failures.
If MAPIMessages.RecipCount > 0 Then
er% = MsgBox(Msg$ & Chr$(10) & Chr$(13) & Chr$(10) & Chr$(13)
& "Send mail distribution anyway?",
vbExclamation + vbYesNo, "Mail Failure")
If er% = vbYes Then
' Send the mail message at this point.
MAPIMessages.Action = 3
Else
UserAction = MAPICancel
End If
End If
' Ask the user if they wish to go to the
Address book to resolve the failed recipients.
er% = MsgBox(Msg$ & Chr$(10) & Chr$(13) & Chr$(10)
& Chr$(13) & "View_ Address Book Now?",
vbQuestion + vbYesNo, "Resolve Failure")
If er% = vbYes Then
Call frmAddressBook.SetupBook("Distribution List")
frmAddressBook.txtName = FailedRecipients(1)
frmAddressBook.Show 1
End If
Else
MAPIMessages.Action = 3 ' No failures so send the message.
End If
Granted, the first you look at this code, it's a little confusing. Actually, its damn confusing if you've never done this stuff before.
Basically the line;
MAPIMessages.MsgType = "IPC.GUI.DistributionItem"
' Do not show in 'In Box'
actually creates the IPC message. Replacing the GUI and the DistributionItem allows you to customise the IPC so that you can determine at the other end, whether or not this message is one of yours. Don't forget that even Schedule + also uses IPC's - you don't want to go and start processing them. This also allows you to send several different types of communications for the same application.
Adding recipients requires you to supply the MAPI control with a semi-colon separated list of names. It's a real good idea to resolve each name in turn so that when an error occurs, you can process the bad recipient without having to unpick the recipient list again to see which one does not match.
The following lines;
MAPIMessages.AttachmentPosition = 0 ' First attachment.
MAPIMessages.AttachmentType = 0 ' Data attachment.
MAPIMessages.AttachmentPathName = App.Path & "\Distrib.MDB"
' Attachment.
attach the file we want to distribute. Each attachment has its own position in the message and is zero based. Were this an IPM rather than an IPC, the order in which the user sees the attachments in the message is the order of the AttachmentPositon property.
Once you have the message built, you can send it. Exchange will then send the message next time it's scheduled to. Be aware that you will not be able to see the message in the outbox either. Again this is due to the fact that this message is an IPC.
One tip I will share with you is that when you are testing all this, make sure that you first set the message as an IPM so that you can see if the attachment is being attached properly, that the recipients appear and that the subject text, if any, is also there. There is nothing more impossible than debugging something you can't even see. Add that to the fact that if you did create an IPC, with an attachment (or not), then that IPC will get sent and sit in someone's in-box forever until you write a small application listing all the IPC's and removing them. This could make for some very large Exchange files clogging up the hard drive for no real reason. Bitter experience? Can you tell?
So, now that the message has been sent, you will want to pick it up at the other end yes? Look at the code below :
' Dim a few variables.
Dim MessageID As String
' Find the first mail message.
er = MAPIFindNext(gSession, 0, "IPC.GUI.DistributionItem",
"",_ MAPI_UNREAD_ONLY, 0, MessageID)
' If Messages found then prompt user
If er = SUCCESS_SUCCESS Then
er = MsgBox("You have received new form versions in the mail. Upgrade
now?", vbYesNo + vbQuestion, "Upgrade Forms")
If er = vbYes Then Call ProcessMail
End If
Looking for the message is as simple as looking for the IPC you created. You can, if you want, also check IPM's in the same way. Once the message has been identified, you will need to open it up, extract the files, mark as read and then, if required, delete it.
Sub ProcessMail()
' Dim a few variables.
Dim MessageID As String
Dim Message As MapiMessage
Dim Originator As MapiRecip
Dim Recips() As MapiRecip
Dim Files() As MapiFile
Dim MailDB As Database
' Set a few entry level parameters.
FileCounter = 1
' Get the first message that is a distribution message.
er = MAPIFindNext(gSession, 0, "IPC.GUI.DistributionItem",
"",_ MAPI_UNREAD_ONLY, 0, MessageID)
' While there are still messages to process.
While er = SUCCESS_SUCCESS
' Read the mail item.
er = MAPIReadMail(gSession, 0, MessageID, MAPI_UNREAD_ONLY,
0,_ Message, Originator, Recips(), Files())
' Process the files attached.
For t = 0 To UBound(Files)
FileCopy Files(t).PathName, App.Path & "\Distrib." & FileCounter
Next t
' Delete the mail item.
er = MAPIDeleteMail(gSession, 0, MessageID, 0, 0)
' Locate the next message in the message queue.
er = MAPIFindNext(gSession, 0, "IPC.GUI.DistributionItem",
"",_ MAPI_UNREAD_ONLY, 0, MessageID)
' Increment the file counter.
FileCounter = FileCounter + 1
Wend
By default, when you open a mail message, be it an IPM or an IPC, mail will save the attachments to a temporary directory and then remove them when the message is closed. The file names and paths are returned in the Files() array. In our application, we copy these files to our own directory, remove the IPC and then process the files. You could process the files directly from the temp directory to save you time and space.I just inherently don't trust temp directories.
That's primarily that. Yes, there is a some more you'll need to know, such as creating a session and some basic error handling. However, most of that is pretty well documented and should not create a great deal of pain.
![]()