[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 964
  • Last Modified:

read and open events firing strangely in Outlook 2003 VBA


I'm trying to program Outlook 2003 so that whenever I open an e-mail message from my inbox by double-clicking on it, an event would fire that would save the message out to a database. I tried using read and open as the events as shown below:  however, neither of them are firing. The only time I got them to fire is when I double-click on the first message in my inbox (which as you can see it is what I initialize my variable to). And even when I double-click on the first message in my inbox, I get different behavior from the read event and the open event.  In fact the read event only gets fired the first time I open that message while the open event fires over and over again on that message. Unfortunately, whenever I open up any other message in the inbox none of those events fire. What am I doing wrong?


Public WithEvents myItem As Outlook.MailItem

Sub Initialize_handler()
    Set myItem = Application.ActiveExplorer.CurrentFolder.Items(1)
    'myItem.Display
End Sub

Private Sub Application_Startup()
    Initialize_handler
End Sub

Private Sub myItem_Open(Cancel As Boolean)
    MsgBox "open"
End Sub

Public Sub myItem_Read()
    MsgBox "read"
End Sub


Thanks,  Eyal
0
eyalyaari
Asked:
eyalyaari
  • 8
  • 7
1 Solution
 
David LeeCommented:
Hi, Eyal.

The reason this isn't working is because the sample does not include any code to set myItem to the new items.  When Application_Startup fires it is setting myItem to the first item in your mailbox.  myItem never changes after that, so the only item that Read and Open will work for is that item.  Make sense?

The way to trap these events for all items is to include code that detects when a new item is about to open and set myItem to it.  The code in the snippet demonstrates this.  Each time you open an item a new Inspector object is created.  This triggers the NewInspector event.  If you're trapping events for the inspectors collection, then any code in the NewInspector sub runs.  In this case it includes code to test the item you are opening to see if it's a message.  If it is, then the code sets the olkItem, what you were calling myItem, object to that item.  Now the Read and Open events will fire for each message you open, not just the first one in the mailbox.

One note.  If you open an item, then open a second item without closing the first item, olkItem will contain the second item.  The reference to the first is lost.  So long as you are only interested in the Read and Open events that's fine.  But, if you ever wanted to trap the other events associated with the item, then there's be a problem.  If you need to trap events for multiple items at the same time, then you have to write a wrapper class.
Private WithEvents olkInspectors As Outlook.Inspectors
Private WithEvents olkItem As Outlook.MailItem
 
Private Sub Application_Quit()
    Set olkInspectors = Nothing
    Set olkItem = Nothing
End Sub
 
Private Sub Application_Startup()
    Set olkInspectors = Outlook.Application.Inspectors
End Sub
 
Private Sub olkInspectors_NewInspector(ByVal Inspector As Inspector)
    If Inspector.CurrentItem.Class = olMail Then
        Set olkItem = Inspector.CurrentItem
    End If
End Sub
 
Private Sub olkItem_Close(Cancel As Boolean)
    MsgBox "Close"
    Set olkItem = Nothing
End Sub
 
Private Sub olkItem_Open(Cancel As Boolean)
    MsgBox "Open"
End Sub
 
Private Sub olkItem_Read()
    MsgBox "Read"
End Sub

Open in new window

0
 
eyalyaariAuthor Commented:
hi again,

okay I have a few questions about that:

1. From what I understood about the withevents statement, it kind of opens up a listening channel for all events that occur for that variable's object type. So in my case that would be Outlook.mailitem for the variable myItem.  and so I would expect that my program would receive all events related to that object type (not to the instance, myItem) and that my code would only get triggered on events that I coded for (read and/or open).  Therefore, when I double-click on an inbox message, I expected that that event (which I believe is an open event) would get trapped by my code since I was " listening " to all events related to Outlook.mailitem.  obviously I was wrong since my code doesn't work! But when I look at your code I see a difference in the object types that we are using (along with a different triggering event) but I don't see any conceptual difference. It seems that you replaced the mail item type with the inspectors type and just as double-clicking a message triggers a new inspector so should it also trigger an open event, no?

2. Looking at your code, was there any need for olkItem at all?  Wouldn't the code  have worked the same way with just the inspectors?

3. should the variable olkItem also have been in the application_startup?

4. When I ran your code in my Outlook, the open event would work each time while the read event would only occasionally fire maybe 20 or 30% of the time. And it didn't matter whether I was double-clicking on a message that was already previously read or a brand-new message.

5. Since I don't want the code to run for mailitems that are being sent (I am only interested in inbox messages) yet the open event gets fired when composing an e-mail to be sent, how do I check that the current item is an inbox message only?

Thanks, Eyal
0
 
David LeeCommented:
1.  "all events that occur for that variable's object type"
Not for the object type, for that one object.  For example

    Dim WithEvents Item As Outlook.MailItem

does not monitor events for every mailitem.  It only monitors them for that one mailitem.

2.  No.  Inspectors have a different set of events.

3.  No.  

4.  Hmmm.  Read should fire every time.  It doesn't make any difference whether the item has been read or not.  Read isn't testing to see if it's been read, in this context it's read as in getting the data from Outlook.  I tested the code before posting and the Read fired every time.  

5.  You can test where the item is by checking the Parent.  Replace the olkItem_Open sub with the version below.
Private Sub olkItem_Open(Cancel As Boolean)
    MsgBox "Open - Item is in the folder named '" & olkItem.Parent & "'"
End Sub

Open in new window

0
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 
eyalyaariAuthor Commented:
I see.

for what I need to do, which is to write out to a database all incoming e-mails that get opened, is the following code sufficient?

Private WithEvents olkInspectors As Outlook.Inspectors
 
Private Sub Application_Quit()
    Set olkInspectors = Nothing
End Sub
 
Private Sub Application_Startup()
    Set olkInspectors = Outlook.Application.Inspectors
End Sub
 
Private Sub olkInspectors_NewInspector(ByVal Inspector As Inspector)
    If Inspector.CurrentItem.Class = olMail And Inspector.CurrentItem.Sent = "True" Then
        'write to db
    End If
    MsgBox Inspector.CurrentItem.Sent
End Sub


also, what is the difference between Outlook.application. Inspectors and Outlook. Inspectors? Which do I use when?

0
 
David LeeCommented:
The code will record all messages you open that have been sent, including those in Sent Items.  And it'll do it every time they're opened, not just once.  If you want to record every opening, then you're in business.  Otherwise, you'll need to devise a means of marking them as having been processed so they don't get recorded in the database multiple times.

The difference between "Outlook.Inspectors" and "Outlook.Application.Inspectors" is that the former is used with a variable declaration to create an item of type "Outlook.Inspectors", while the latter is used to get the Inspectors collection which is a property of the Application object which in turn is a property of Outlook.  The latter can be abbreviated to "Application.Inspectors" if you prefer.
0
 
eyalyaariAuthor Commented:

okay, does this then take care of both of those issues?  I chose voting response field since I have no idea what the hell that is.  Also the sent field apparently is always false when sending out an e-mail.

Private WithEvents olkInspectors As Outlook.Inspectors
 
Private Sub Application_Quit()
    Set olkInspectors = Nothing
End Sub
 
Private Sub Application_Startup()
    Set olkInspectors = Outlook.Application.Inspectors
End Sub
 
Private Sub olkInspectors_NewInspector(ByVal Inspector As Inspector)
    If Inspector.CurrentItem.Class = olMail And _
    Inspector.CurrentItem.Sent = "True" And _
    Inspector.CurrentItem.VotingResponse <> "1" Then
        'write to db
        Inspector.CurrentItem.VotingResponse = "1"
        Inspector.CurrentItem.Save
    End If
End Sub

0
 
David LeeCommented:
That should do it.  VotingResponse is the field that would contain a user's vote submitted via voting buttons.  So long as you aren't using voting buttons or at least none that would return a 1, then you should be okay.
0
 
eyalyaariAuthor Commented:

okay this is finally coming to an end. Can you take a look at the code and tell me if it looks reasonable? I added in the code to also process the sent items. And  I'm missing the code for the one line that has a comment mark.  It is supposed to save out the item to the folder specified in the votingresponse property

Thx, Eyal
Private WithEvents olkInspectors As Outlook.Inspectors
Private WithEvents olkFolder As Outlook.Items
 
Private Sub Application_Quit()
    Set olkInspectors = Nothing
    Set olkFolder = Nothing
End Sub
 
Private Sub Application_Startup()
    Set olkInspectors = Outlook.Application.Inspectors
    Set olkFolder = Application.Session.GetDefaultFolder(olFolderSentMail).Items
End Sub
 
Private Sub olkFolder_ItemAdd(ByVal Item As Object)
    If Len(Item.VotingResponse) > 0 Then
        'save a copy of mailitem in folder path:  Item.VotingResponse
    End If
        Open "C:\fmtemp11.txt" For Append As 5
        Write #5, Item.EntryID
        Close #5
End Sub
 
Private Sub olkInspectors_NewInspector(ByVal Inspector As Inspector)
    If Inspector.CurrentItem.Class = olMail And _
    Inspector.CurrentItem.Sent = "True" And _
    Inspector.CurrentItem.VotingResponse <> "1" Then
        Open "C:\fmtemp10.txt" For Append As 4
        Write #4, Inspector.CurrentItem.EntryID
        Close #4
        Inspector.CurrentItem.VotingResponse = "1"
        Inspector.CurrentItem.Save
    End If
End Sub
 
Private Sub Application_ItemSend(ByVal Item As Object, Cancel As Boolean)
    If Item.Class = olMail Then
        If TypeName(Item.SaveSentMessageFolder) <> "Nothing" Then
            Item.VotingResponse = Item.SaveSentMessageFolder.FolderPath
            Set Item.SaveSentMessageFolder = Nothing
            Item.Save
            MsgBox ("8")
        End If
    End If
End Sub

Open in new window

0
 
David LeeCommented:
Eyal,

The code looks ok to me.  

Line 16 - This will save the item to the file system in .msg format.  You can adjust the path, filename, and file type.

    olkItem.SaveAs "C:\SomeFolder\Somefilename.msg", olMSG


Line 39 - I'm not sure this is going to work.  SaveSentMessageFolder normally has to be set before the item is sent.  At this point the item is in the process of being sent, so I don't know the value can be changed.  You can try it and see.
0
 
eyalyaariAuthor Commented:
hi,

After playing around with the code I finally got it to work, but it only works the first time. After the first time, the GetObject part of the code below returns an error, 429 that the ActiveX component can't create the object or return a reference to it.  Any ideas? Maybe something about the way I'm initializing or terminating the variables?

-- Eyal
Private WithEvents olkInspectors As Outlook.Inspectors
Private WithEvents olkFolder As Outlook.Items
'Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
 
Private Sub Application_Quit()
    Set olkInspectors = Nothing
    Set olkFolder = Nothing
End Sub
 
Private Sub Application_Startup()
    Set olkInspectors = Outlook.Application.Inspectors
    Set olkFolder = Application.Session.GetDefaultFolder(olFolderSentMail).Items
End Sub
 
Private Sub olkInspectors_NewInspector(ByVal Inspector As Inspector)
    Dim FMApp As FMPRO70Lib.Application
    Dim FMDocs As FMPRO70Lib.Documents
    Dim myOpenFile As Object
    If Inspector.CurrentItem.Class = olMail And _
    Inspector.CurrentItem.Sent = "True" And _
    Inspector.CurrentItem.VotingResponse <> "1" Then
        Open "C:\fmtemp10.txt" For Append As 4
        Write #4, Inspector.CurrentItem.EntryID, CStr(Inspector.CurrentItem.ReceivedTime)
        Close #4
        'On Error GoTo exitsub
        Set FMApp = GetObject(, "FMPRO.Application")
        FMApp.Visible = True
        Set FMDocs = FMApp.Documents
        Set myOpenFile = FMDocs.Item("Email.fp7")
        myOpenFile.DoFMScript ("Import_Outlook")
        While (FMApp.ScriptStatus() > 0)
        'nothing
        Wend
exitsub:
        Inspector.CurrentItem.VotingResponse = "1"
        Inspector.CurrentItem.Save
        Set FMApp = Nothing
        Set FMDocs = Nothing
        Set myOpenFile = Nothing
    End If
End Sub

Open in new window

0
 
eyalyaariAuthor Commented:

sorry, never mind, I figured it out.

BTW, how do I write a loop that if a user highlights multiple messages from his inbox for example, that I can then loop over the selection and execute a macro on each message.  This won't be event driven.  If you want me to put it into a separate thread let me know, otherwise this will end this thread.

Thx, Eyal
0
 
David LeeCommented:
No problem.

To process all selected items takes a loop like one of these.
'This loop will work so long as you are not deleting any of the items'
For Each Item in Application.ActiveExplorer.Selection
    'Your code goes here'
Next
 
'If deleting items, then use this loop instead'
For X = Application.ActiveExplorer.Selection.Count To 1 Step -1
    'Your code goes here'
Next

Open in new window

0
 
David LeeCommented:
Oops, the second loop should be this instead
'If deleting items, then use this loop instead'
For X = Application.ActiveExplorer.Selection.Count To 1 Step -1
    Set Item = Application.ActiveExplorer.Selection.Item(X)
    'Your code goes here'
Next

Open in new window

0
 
eyalyaariAuthor Commented:

Thanks again, .... till the next time  :-)

Eyal
0
 
David LeeCommented:
You're welcome, Eyal.
0

Featured Post

Nothing ever in the clear!

This technical paper will help you implement VMware’s VM encryption as well as implement Veeam encryption which together will achieve the nothing ever in the clear goal. If a bad guy steals VMs, backups or traffic they get nothing.

  • 8
  • 7
Tackle projects and never again get stuck behind a technical roadblock.
Join Now