I have VBA code in an Outlook rule. I want when I get email with a specific subject, Outlook starts to run a script. However, when email is received, Outlook starts to run the code immediately picking up previous email, probably because the email was just received and not moved to a specific folder yet.
I tried
Application.Wait (Now + TimeValue("0:00:5"))
and
Outlook.Application.Wait (Now + TimeValue("0:00:5"))
just before grabbing the email with
Set oLookMailitem = Application.ActiveExplorer.CurrentFolder.Items("Apples Sales")
VBA shows an error
Object doesn't support this property or method
Here is the beginning of my code: The error occurs on Application.Wait (Now + TimeValue("0:00:5")).
Sub ExportOutlookTableToExcel()
Dim oLookInspector As Inspector
Dim oLookMailitem As MailItem
Dim oLookWordDoc As Word.Document
Dim oLookWordTbl As Word.Table
Dim xlApp As Excel.Application
Dim xlBook As Excel.Workbook
Dim xlWrkSheet As Excel.Worksheet
Dim Today As String
Today = Date
Application.Wait (Now + TimeValue("0:00:5"))
'Grab Email Item
Set oLookMailitem =Application.ActiveExplorer.CurrentFolder.Items("Apples Sales")
Set oLookInspector = oLookMailitem.GetInspector
Set oLookWordDoc = oLookInspector.WordEditor
Outlook has no Application.Wait
You can workaround this with a Do loop, a Timer and DoEvents:
Public Sub Sleep(ByVal SleepSeconds As Single)
Dim Tmr As Single
Tmr = Timer
Do While Tmr + SleepSeconds > Timer
DoEvents
Loop
End Sub
And call it like Sleep 5 instead of your Application.Wait.
Application.Wait is not a panacea for your goal!
Consider using Windows API functions to set up a timer and run your actions (processing items) when it fires. See Outlook VBA - Run a code every half an hour for more information.
Also to handle incoming emails immediately you need to handle the NewMailEx event which is fired when a new message arrives in the Inbox and before client rule processing occurs. You can use the Entry ID returned in the EntryIDCollection array to call the NameSpace.GetItemFromID method and process the item. Use this method with caution to minimize the impact on Outlook performance. However, depending on the setup on the client computer, after a new message arrives in the Inbox, processes like spam filtering and client rules that move the new message from the Inbox to another folder can occur asynchronously. You should not assume that after these events fire, you will always get a one-item increase in the number of items in the Inbox.
Related
When you preview a report, right-click offers the option, Send to... -> Mail Recipient.
I need to catch this event and execute code the reads the recipient e-mail address as typed into Outlook when it appears. This code should be in a module in the Access database.
If possible, I would also like to read the Subject.
It's hard to find answers on the Web, and despite some experience with Access and VBA, I don't know how to even start.
Edit: From Dmitry's answer I found this loop through Inspectors,
Private Sub Form_Timer()
Dim myInspectors As Outlook.Inspectors
Dim x As Integer
Dim iCount As Integer
Set myInspectors = Application.Inspectors
iCount = Application.Inspectors.Count
If iCount > 0 Then
For x = 1 To iCount
MsgBox myInspectors.Item(x).Caption
Next x
Else
MsgBox "No inspector windows are open."
End If
End Sub
but it gives this compile error:
Edit 2:
I have moved the code into a function, and when Outlook is running, I get no errors from this GetObject call. But with this function on a 2s timer, objApp.Inspectors.Count remains 0 while I compose an email and send it.
Public Function checkInspectors() As Boolean
Dim myInspectors As Outlook.Inspectors
Dim OutLookWasNotRunning As Boolean
Dim objApp As Object
Set objApp = GetObject(, "Outlook.Application")
If Err.Number <> 0 Then OutLookWasNotRunning = True
Err.Clear ' Clear Err object in case error occurred.
If Not OutLookWasNotRunning Then
Set myInspectors = objApp.Inspectors
Dim x As Integer
Dim iCount As Integer
iCount = objApp.Inspectors.Count
If iCount > 0 Then
For x = 1 To iCount
Debug.Print myInspectors.Item(x).Caption
Next x
Debug.Print "---"
Else
'MsgBox "No inspector windows are open."
End If
End If
End Function
Normally, Application.Inspectors.NewInspector event would fire, but Outlook disables that event for the messages opened through Simple MAPI. Your best bet is to scan the Application.Inspectors collection periodically (timer?) to check if there is a new inspector open.
Once you have an Inspector object, you can check the Inspector.CurrentItem.Recipients collection.
Well, one of the possible solutions is to develop an Outlook add-in or VBA macro which may track outgoing emails. The ItemSend event of the Application class which is fired whenever a Microsoft Outlook item is sent, either by the user through an Inspector (before the inspector is closed, but after the user clicks the Send button) or when the Send method for an Outlook item, such as MailItem, is used in a program.
When I call Inbox_ItemAdd to make a copy of a Mail Item and then move the copy to a different folder, I get an error. The operation completes though.
When I debug the code it steps through without an error. It only errors when I remove the breakpoints. Finally, if I comment out:
moveMail.Move DestinationFolder
or
copied = MoveToFolder(copyMail, FolderName)
It creates volumes of copies in the originating folder. Its earliest entry point is from the ItemAdd event, so I'm wondering if
Set copyMail = olItem.Copy
results in kicking that event off again.
Here's my CopyToFolder and MoveToFolder functions:
Function MoveToFolder(olItem As Outlook.MailItem, FolderName As String) As Boolean
Dim objNS As Outlook.NameSpace
Dim Inbox As Outlook.Folder
Dim DestinationFolder As Outlook.Folder
Dim moveMail As Outlook.MailItem
Set objNS = Application.GetNamespace("MAPI")
Set DestinationFolder = objNS.Folders("MyMailBox#mailboxes.com").Folders(FolderName)
Set moveMail = olItem
moveMail.Move DestinationFolder
Set moveMail = Nothing
Set DestinationFolder = Nothing
Set Inbox = Nothing
Set objNS = Nothing
End Function
Function CopyToFolder(olItem As Outlook.MailItem, FolderName As String) As Boolean
Dim copyMail As Outlook.MailItem
Dim copied As Boolean
Set copyMail = olItem.Copy
copied = MoveToFolder(copyMail, FolderName)
CopyToFolder = copied
Set copyMail = Nothing
End Function
And I might call the CopyToFolder function by:
copyResult = CopyToFolder(olItem, "External")
Here is what MSDN states for the NewMailEx event:
The NewMailEx event fires when a new message arrives in the Inbox and before client rule processing occurs. You can use the Entry ID returned in the EntryIDCollection array to call the NameSpace.GetItemFromID method and process the item. Use this method with caution to minimize the impact on Outlook performance. However, depending on the setup on the client computer, after a new message arrives in the Inbox, processes like spam filtering and client rules that move the new message from the Inbox to another folder can occur asynchronously. You should not assume that after these events fire, you will always get a one-item increase in the number of items in the Inbox.
Looks like you have got some rules set up in Outlook. And these rules can be run against the item after the vent handler or asynchronously, i.e. when you try to call the Move method. Is it the case?
As a workaround you may consider getting the entry ID and try to get the received item after it's been processed by Outlook. Or just handle the ItemSend event instead.
Anyway, you may find the following series of articles helpful:
Outlook NewMail event unleashed: the challenge (NewMail, NewMailEx, ItemAdd)
Outlook NewMail event: solution options
Outlook NewMail event and Extended MAPI: C# example
Outlook NewMail unleashed: writing a working solution (C# example)
This is a followup to a previous question I had asked. Thank you to the community for your help with that.
I'm trying to create WithEvents code for the first time to check a folder for new items. Eventual plan is to use the ItemsAdd event to trigger a bunch of other processing, but for now, just trying to save it to a folder and not getting that far.
When I run the Application_Startup code below, the immediate window shows that I've found the right clntFldrItms. Problem is, if I then drag an item into the folder in question, the ItemAdd macro doesn't fire. When I try to add a watch for clntFldrItms, the variable isn't set to anything. It looks like as soon as the Application_Startup sub finishes, the assignment stops.
All code is in the ThisOutlookSession object.
Could this be because I'm working with an SMTP email address (rather than Exchange, for example)?
Thanks again for your help.
EDIT Adding my response to Eugene's comment. I noticed that when I open the editor and step into the Application_Startup sub, clntFldrItms is properly assigned, even before I get to the Set clntFldrItms = clntFldr.Items line. As soon as I finish stepping through, it's gone again. I can't step into the ItemAdd sub, but when I step into other code clntFldrItms is Nothing.
FINAL EDIT Sorry, I realize I forgot to close this off. I wasn't able to solve the problem per se, but I realized it was due to my SMTP account. When I tried it at work with Exchange, it worked. It seems that the event doesn't fire unless I'm working in Exchange.
Option Explicit
Public WithEvents clntFldrItms As Outlook.Items
Private Sub Application_Startup()
Dim clntFldr As MAPIFolder
Set clntFldr = Application.Session.GetDefaultFolder(olFolderSentMail).Folders("Client Emails")
Set clntFldrItms = clntFldr.Items
Set clntFldr = Nothing
Debug.Print clntFldrItms.item(1).Subject
End Sub
Private Sub clntFldrItms_ItemAdd(ByVal item As Object)
Dim bChar As String
bChar = "\/:*?™""® <>|.&##_+`©~;-+=^$!,'" & Chr(34)
Dim saveName As String
If item.Class = olMail Then
saveName = item.Subject
For x = 1 To Len(bChar)
saveName = Replace(saveName, Mid(bChar, x, 1), "-")
Next x
item.SaveAs "C:\Users\User\Google Drive\8 - VBA work\Preparation for Assisted Responder\Sent Messages Folder\" & _
saveName & ".msg", olMSG
End If
End Sub
Try to set a breakpoint in the ItemAdd event handler and check out the clntFldrItms object there when the breakpoint is hit.
Be aware, the ItemAdd event is not fired when multiple items were added at the same time (more than 16 - this is a well-known issue in Outlook).
You may find the Getting Started with VBA in Outlook 2010 article hellpful.
EDIT The clntFldrItms is set because the Startup event handler is run when you start Outlook. So, the object is initialized at startup behind the scene.
Windows 7, Outlook 2010 Exchange.
I have an autoforward macro for incoming email that works flawlessly in forwarding all incoming email items to an external account I need for consolidation.
The only issue is every time I cold boot and START Outlook, the items that appear after the usual "Updating this folder..." in the ticker do NOT autoforward. From that point forward the macro starts working perfectly again.
It is located in the ThisOutlookSession.
In previous Outlook versions, a rule that ran a similar macro always fired upon startup.
Thanks for any help.
Private Sub Application_NewMailEx(ByVal EntryIDCollection As String)
Dim varEntryIDs
Dim objItem
Dim myItem As MailItem
Dim i As Integer
varEntryIDs = Split(EntryIDCollection, ",")
For i = 0 To UBound(varEntryIDs)
Set objItem = Application.Session.GetItemFromID(varEntryIDs(i))
If TypeOf objItem Is MailItem Then
Set myItem = objItem.Forward
myItem.Recipients.Add "bcc.hwb#gmail.com"
myItem.DeleteAfterSubmit = True
myItem.Send
Set myItem = Nothing
Else
Debug.Print "Skipping " & TypeName(objItem)
Set myItem = Nothing
End If
Next
End Sub
Try to set up a rule which calls a macro sub instead of handling the NewMailEx event.
If you are running Exchange in non-Cached mode the event will only fire as long as Outlook is running when a new e-mail arrives. If you close and re-open Outlook, all the e-mails that were in the queue waiting to be delivered will appear in Outlook but the event will not fire. This is a well-known issue in Outlook.
You can read about all possible ways for handling incoming emails in the Outlook NewMail event unleashed: the challenge (NewMail, NewMailEx, ItemAdd) article.
I have a set of macros that have worked in Outlook 2003, 2007, and 2010. In fact, it still works in 2013 except in a specific case.
The macro brings up a dialog box whenever you try to send an email - to tag the subject line with key words. The problem is, if I just started Outlook, and I bring up a new email or reply - the default in Outlook 2013 is to bring it into the former "Reading Pane" rather than in a new window. If I do not hit "Pop Out" and I try to send, my macro crashes with this error:
"Run-time error '91' Object variable or with block variable not set"
I tried to check for loading the form first - but it seem ANY call to my userform, even userform.show, generates this error.
Oddly, if I remember to "Pop Out" my first email, it runs fine everytime after until I close/reopen Outlook. Even if I don't "Pop Out" other emails. It's only on the very first one that this occurs.
Here's the beginning of my Initialize Event:
Dim Tags() As String
Dim T As Variant
Dim PC As Variant
Dim Rent As String
Dim Child As String
Dim nsourcefile As Integer
Dim email As MailItem
Dim PD As Variant
Dim Proj As String
Dim Desc As String
'Set email = Application.ActiveInspector.CurrentItem
Set email = Application.ActiveExplorer.Selection.Item(1)
'Checks to see if a project number (that's not on the list) may be in the subject already
If Val(email.Subject) > 10000 Then
TagMsg.Height = tall
TagMsg.NewProjID = Format(Val(email.Subject), "00000")
TagMsg.NewProjDesc.SetFocus
Else
'Set height of form (prior to pressing "More" button
TagMsg.Height = short
End If
Noticed I changed Set email = Application.ActiveInspector.CurrentItem to Set email = Application.ActiveExplorer.Selection.Item(1). This seems to have fixed it, but the VBA help states "Do not make any assumptions about the Item method return type; your code should be able to handle multiple item types or a ConversationHeader object."
Note that the form is being invoked by the ItemSend event.
First off, putting that code into the Initialize event wasn't a good move. Needed to be moved into a click event where it was actually needed.
Then, I found the code I needed from two other posts, combined and shortened them.
Working with current open email
https://superuser.com/questions/795831/outlook-2013-vba-refer-to-editor-in-reading-pane
Final result
Dim oInspector As Inspector
Dim email As MailItem
Dim oexp As Explorer
Set oInspector = Application.ActiveInspector
Set oexp = Application.ActiveExplorer
If oInspector Is Nothing Then
'Set email = Application.ActiveExplorer.Selection.Item(1)
Set email = oexp.ActiveInlineResponse
If email Is Nothing Then
'MsgBox "No active inspector or inline response"
Exit Sub
End If
Else
Set email = oInspector.CurrentItem
End If 'oInspector is Nothing
If email.Sent Then
'MsgBox "This is not an editable email"
Else
'Checks to see if a project number (that's not on the list) may be in the subject already
If Val(email.Subject) > 10000 Then
TagMsg.Height = tall
TagMsg.NewProjID = Format(Val(email.Subject), "00000")
TagMsg.NewProjDesc.SetFocus
Else
'Set height of form (prior to pressing "More" button
TagMsg.Height = short
End If
End If 'email.sent
Note: This still relies on the fact that it is called by the ItemSend event and the active or current item will be the email I just pressed "send" on.
Thank you, retailcoder for your comments.