I have a macro that prompts you to save an email to a specified folder before you send it, but I can't get the macro to work for receiving emails.
This is what I have for sending emails:
Private Sub Application_ItemSend(ByVal Item As Object, Cancel As Boolean)
If TypeOf Item Is Outlook.MailItem Then
Cancel = Not SaveSentMail(Item)
End If
End Sub
Private Function SaveSentMail(Item As Outlook.MailItem) As Boolean
Dim F As Outlook.MAPIFolder
If Item.DeleteAfterSubmit = False Then
Set F = Application.Session.PickFolder
If Not F Is Nothing Then
Set Item.SaveSentMessageFolder = F
SaveSentMail = True
End If
End If
End Function
Work with Application.Startup Event (Outlook) and Items.ItemAdd Event (Outlook)
Items.ItemAdd Event (Outlook) Occurs when one or more items are added to the specified collection. This event does not run when a large number of items are added to the folder at once. This event is not available in Microsoft Visual Basic Scripting Edition (VBScript).
Application.Startup Event (Outlook) Occurs when Microsoft Outlook is starting, but after all add-in programs have been loaded.
Code Example
Private WithEvents Items As Outlook.Items
Private Sub Application_Startup()
Dim olNs As Outlook.NameSpace
Dim Inbox As Outlook.MAPIFolder
Set olNs = Application.GetNamespace("MAPI")
Set Inbox = olNs.GetDefaultFolder(olFolderInbox)
Set Items = Inbox.Items
End Sub
Private Sub Items_ItemAdd(ByVal Item As Object)
Dim Folder As Outlook.MAPIFolder
If TypeOf Item Is Outlook.mailitem Then
Set Folder = Application.Session.PickFolder
If Folder Is Nothing Then Exit Sub
Item.Move Folder
End If
End Sub
You can handle the NewMailEx event of the Application class 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.
Be aware, for users with an Exchange Server account (non-Cached Exchange Mode or Cached Exchange Mode), the event will fire only for messages that arrive at the server after Outlook has started. The event will not fire for messages that are synchronized in Cached Exchange Mode immediately after Outlook starts, nor for messages that are already on the server when Outlook starts in non-Cached Exchange Mode.
Another possible option is to handle the ItemAdd event of the Items class which is fired when one or more items are added to the specified collection. Be aware, this event does not run when a large number of items are added to the folder at once (more than 16 - this is a well-known issue in OOM).
A better solution is to check for new items in the folder using the timer's event. Read more about possible solutions in the following articles:
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)
Related
I'm trying to record a specific next-numbering on certain e-mails after I send them. However Application_ItemSend executes the code before it sends the e-mail.
I'm adding a reference number to the Subject, but I don't want to confuse everyone with random numbers appearing in their inbox.
Is there a way to execute code after the send?
I think I got it:
Declare a new type of events-related variable to ensure every time an e-mail is added to the "Received Items" folder an action is executed.
Private WithEvents Rcvditems As Outlook.Items
The method I used to avoid variables being restored to 'nothing' is using the same Application.Item_Send event. This option will ensure I activate my reader every time I send an e-mail.
Private Sub Application_ItemSend(ByVal Item As Object, Cancel As Boolean)
Dim olApp As Outlook.Application
Dim objNS As Outlook.NameSpace
Set olApp = Outlook.Application
Set objNS = olApp.GetNamespace("MAPI")
Set Rcvditems = objNS.GetDefaultFolder(olFolderSentMail).Items
End Sub
... what it is doing is recalculating my variable 'Rcvditems'.
Finally, as Rcvd_Items is an event-related variable, I can assign event 'AddItem' to it.
Private Sub Rcvditems_ItemAdd(ByVal Item As Object)
On Error GoTo EscapeSub
Call MyMacro(Item)
EscapeSub:
End Sub
Like this I'm ensuring that I won't alter the e-mail when prior sending it, but just the e-mail that is added in the Received-Items folder.
By default, the sent items are placed to the Sent Items folder in Outlook. If so, you can hook to the ItemAdd event of the Items class which belongs to the Sent Items folder. It is fired when one or more items are added to the specified collection. This event does not run when a large number of items are added to the folder at once (more than sixteen) - this is a known issue in OOM and exists in all Outlook versions.
Public WithEvents myOlItems As Outlook.Items
Public Sub Initialize_handler()
Set myOlItems = Application.GetNamespace("MAPI").GetDefaultFolder(olFolderSentItems).Items
End Sub
Private Sub myOlItems_ItemAdd(ByVal Item As Object)
MsgBox Item.Subject
End Sub
Note, the MailItem.SaveSentMessageFolder property allows to set a Folder object that represents the folder in which a copy of the email message will be saved after being sent. So, it is not necessary that an Outlook item will be saved to the Sent Items folder if an add-in sets this property in the ItemSend event handler.
This question looks like it should have been answered here:
Outlook VBA Save Copy Sent Item
I am unable to find the referenced "Items.ItemAdd" event amongst the list of events in the VB Editor for Outlook 2016.
Application.ItemSend is located in "ThisOutLookSession". The script I came up with looks 90% similar to the one posted in the original question and I have the same problem. The mail item is saved in it's pre-send condition.
I can't use "MailItem.SaveSentMessageFolder" as I am trying to move the sent mail to a PST.
The Event is explained here:
https://msdn.microsoft.com/en-us/vba/outlook-vba/articles/items-itemadd-event-outlook
In case someone asks "Why not use the AutoArchive Function?", my organization has some terrible group policies set up for both Sent Items and AutoArchive. I can't wade through the mess to get AutoArchive for sent items set up.
Work with Items.ItemAdd Event (Outlook)
Example
Option Explicit
Private WithEvents Items As Outlook.Items
Private Sub Application_Startup()
Dim olNs As Outlook.NameSpace
Dim Inbox As Outlook.MAPIFolder
Set olNs = Application.GetNamespace("MAPI")
Set Inbox = olNs.GetDefaultFolder(olFolderSentMail)
Set Items = Inbox.Items
End Sub
Private Sub Items_ItemAdd(ByVal Item As Object)
If TypeOf Item Is Outlook.mailitem Then
' code to copy the sent Item here
End If
End Sub
Why am I facing the below problem?
I have written a code to open sent email after sending email. The VBA code doesn't open the latest sent email but the previous one.
Private Sub Application_ItemSend(ByVal Item As Object, Cancel As Boolean)
Dim myItem As MailItem
Dim myNamespace As NameSpace
Dim myFolder As Folder
Set myNamespace = Application.GetNamespace("MAPI")
Set myFolder = myNamespace.GetDefaultFolder(olFolderSentMail)
Set myItem = myFolder.Items(myFolder.Items.Count)
myItem.Display
End Sub
This is the answer why this won't work with the Application_ItemSend event:
Application_ItemSend is called before the email is sent. You can see that because it has a Cancel parameter. This means the email only gets sent if Cancel = False else the email is dropped.
So you just cannot display the email because it isn't sent at this time in the Application_ItemSend event. It gets sent after the Application_ItemSend is finished.
Note: If you put a break point within the Application_ItemSend you will see that the "New Email" window remains open/visible until the ItemSend event is finished. Therefore you cannot open that email within that event.
Workaround
You might try the following code. This creates an event for the default folder of sent items which is called when an item is added to this folder.
A restart of Outlook might be needed after adding the code (or at least running the Application_Startup procedure once).
Option Explicit
Public WithEvents myOlItems As Outlook.Items
Private Sub Application_Startup()
Set myOlItems = Application.GetNamespace("MAPI").GetDefaultFolder(olFolderSentMail).Items
End Sub
Private Sub myOlItems_ItemAdd(ByVal Item As Object)
Item.Display
End Sub
If the folder where your sent items are moved to is not the default Outlook sent mail folder then you need to find the right folder to set it in Application_Startup.
I have a rule in Outlook which sends a daily email into a particular folder. I then have a VBA script which upon noticing a new unread message in that folder goes in and saves the attachment to a folder on my hard drive and does a few other formatting type things (on the attachment).
I then just linked up the script to the rule in the Outlook rules wizard so it runs as a package.
The problem is as follows: the script is kicked off BEFORE the message is sorted into the appropriate folder. In reality it should run after the message is sorted (otherwise there is nothing for it to act upon). Any ideas on how to rectify?
The code currently begins as follows:
sub saveattachment()
Should it be this instead?
private sub saveattachment()
or
public sub saveattachment()
Would it be better to have the "rule" embedded in the macro instead and then just run it as a private sub anytime the daily email appears in my Inbox?
If you need to assign a VBA macro sub to the Outlook rule, the VBA sub should look like the following one:
Public Sub Test(mail as MailItem)
' your code goes there
End Sub
An instance of the MailItem class is passed as a parameter and stands for the email arrived to the Inbox.
But in case if you need to be sure that your code is triggered when a mail is moved to a particular folder you need to handle the ItemAdd event of the Items class which comes from that folder. Be aware, the event is not fired when more than 16 items are added to the folder simultaneously.
Public WithEvents myOlItems As Outlook.Items
Public Sub Initialize_handler()
Set myOlItems = Application.GetNamespace("MAPI").GetDefaultFolder(olFolderContacts).Items
End Sub
Private Sub myOlItems_ItemAdd(ByVal Item As Object)
Dim myOlMItem As Outlook.MailItem
Dim myOlAtts As Outlook.Attachments
Set myOlMItem = myOlApp.CreateItem(olMailItem)
myOlMItem.Save
Set myOlAtts = myOlMItem.Attachments
' Add new contact to attachments in mail message
myOlAtts.Add Item, olByValue
myOlMItem.To = "Sales Team"
myOlMItem.Subject = "New contact"
myOlMItem.Send
End Sub
Finally, you may find the Getting Started with VBA in Outlook 2010 article helpful.
I use event handlers in VBA and Outlook frequently. One of them is one which marks any item which is deleted to be marked as read.
Private Sub deletedItems_ItemAdd(ByVal Item As Object)
Application_Startup
On Error Resume Next
Item.UnRead = False
End Sub
Declared via:
Private WithEvents deletedItems As Outlook.Items
and initialized in Application_Startup as:
Dim olNameSpace As Outlook.NameSpace
Set olNameSpace = olApp.GetNamespace("MAPI")
Set deletedItems = olNameSpace.GetDefaultFolder(olFolderDeletedItems).Items
Unfortunately, this does not affect all the items if I delete multiple items at once.
Is there a way I can do something to hijack this process somehow? I looked into using the _beforeDelete event but you have to set the item correctly each time, which if I could do this problem wouldn't exist anyways.
Apparently I wasn't clear - the use case I have is when I delete messages via the delete key from my inbox, drafts, whatever.
You don't have to.
I was curious about your question so I opened up Outlook and wrote this code in ThisOutlookSession:
Private WithEvents items As Outlook.items
Public Sub SetItems()
Set items = Application.GetNamespace("MAPI") _
.GetDefaultFolder(olFolderDeletedItems) _
.items
End Sub
Private Sub items_ItemAdd(ByVal Item As Object)
Dim mail As MailItem
On Error Resume Next
Set mail = Item
Err.Clear
On Error GoTo 0
If Not mail Is Nothing Then
MsgBox mail.Subject
mail.UnRead = False
End If
End Sub
Then I ran SetItems from the immediate pane, went to my inbox and deleted a SMS message - as expected mail was Nothing. Then I deleted a single email, and got the message with the mail's subject.
When I selected two emails and hit Delete, the event was fired once for each selected email, so I saw two message boxes - it just works! :)
The Outlook API doesn't seem to offer an event which would handle all deletions at once.
i have (almost) exactly the same code and it works also for multiple items - only after sleep-mode Outlook seems to forget how to handle deleted items...
Option Explicit
Public WithEvents itDeleted As items
Private Sub Application_Startup()
Set itDeleted = Application.GetNamespace("MAPI").GetDefaultFolder(olFolderDeletedItems).items
End Sub
Private Sub itDeleted_ItemAdd(ByVal Item As Object)
'MsgBox "deleted-sub fired" 'this only for test-purposes
If TypeOf Item Is MailItem Then
Item.UnRead = False
End If
End Sub
I think the difference in the definition of "deletedItems" is the problem; that you are not checking the mailitem-property is also not optimal.
Hope this helps,
Max