oultlook vba loop over inbox, not bulk scan - vba

We have a shared mailbox, and the alerts folder gets filled with alerts. Thousands of them - most from start of day and end of day. New alerts in the middle of the day might actually be something that we need look at.
Noone ever bulk marks the folder as read in the morning, because it takes too long - you can't highlight the who mailbox and click "mark as unread". The onely way to mark the emails is by highlighting a few hundred at a time - which takes along time manually.
I made thsi script because it will automagically mark the emails in the "alerts' folder. However, it seems to go after the whole folder at the same time. The script ist eh equivalent of highlightimg the whole folder, and marking a bulk delete. It takes to long and locks up the shared email box. I would like something that would start at the bottom of the folder, cycle through each email, and if unread, mark the email unread. pause for a second, then the next one.
Is that possible?
Sub Test2()
Dim objInbox As Outlook.MAPIFolder
Dim objOutlook As Object, objnSpace As Object, objMessage As Object
Dim objSubfolder As Outlook.MAPIFolder
Set objOutlook = CreateObject("Outlook.Application")
Set objnSpace = objOutlook.GetNamespace("MAPI")
Set objInbox = objnSpace.GetDefaultFolder(olFolderInbox)
Set objSubfolder = objInbox.Folders.Item("_ALERTS")
For Each objMessage In objSubfolder.Items
objMessage.UnRead = False
Next
Set objOutlook = Nothing
Set objnSpace = Nothing
Set objInbox = Nothing
Set objSubfolder = Nothing
End Sub

You can create a code in Outlook which get triggers when new Email entered in a target folder.
Public WithEvents objMails As Outlook.Items
Private Sub Application_Startup()
Set objMails = Outlook.Application.Session.GetDefaultFolder(olFolderInbox).Folders("_ALERTS").Items
End Sub
Private Sub objMails_ItemAdd(ByVal Item As Object)
'Do more stuff
End Sub
which will avoid looping through all the emails at a time

You could use Restrict to limit the items to be processed.
Option Explicit
Sub Test2()
Dim objInbox As Folder
Dim objnSpace As namespace
Dim objSubfolder As Folder
dim unreadItems As items
dim unreaditemsCount as long
Set objnSpace = GetNamespace("MAPI")
Set objInbox = objnSpace.GetDefaultFolder(olFolderInbox)
Set objSubfolder = objInbox.Folders.Item("_ALERTS")
set unreadItems = objSubfolder.Items.Restrict("[UnRead] = True")
unreaditemsCount = unreadItems.Count
If unreaditemsCount > 0 Then
' Reverse loop when changing the number of items in the collection
For i = unreaditemsCount to 1
unreadItems(i).UnRead = False
Next
end if
Set objInbox = Nothing
Set objnSpace = Nothing
Set objSubfolder = Nothing
Set unreadItems = Nothing
End Sub

Related

Move mails to folders with the sender's name

It is possible to create a rule which, for a sender, moves all the mails to the folder of your choice (for example, it creates a folder with the name of the sender).
If I want that for all the expeditors, I need to repeat the rule creation for each sender.
What I'd wish would be a macro "meta-rule" for each sender to have a folder with their name with the corresponding mails sorted.
I tried to start from the topic Outlook template rule to sort mails among directories.
I wrote this:
Sub RulesForFolders(m As MailItem)
Dim fldr As Outlook.Folder
For Each fldr In GetNamespace("MAPI").GetDefaultFolder(olFolderInbox).Folders
if fldr.Name Like m.SenderName Then m.MoveTo(SenderName)
else folders.add(m.SenderName)
Next
Set fldr = Nothing
End Sub
Option Explicit ' Consider this mandatory
' Tools | Options | Editor tab
' Require Variable Declaration
'
' If desperate declare as variant
Private Sub RulesForFolders(m As mailItem)
Dim targetFldr As folder
Dim myRoot As folder
Dim i As Long
Set myRoot = Session.GetDefaultFolder(olFolderInbox)
Debug.Print m.senderName
' This is often misused.
On Error Resume Next
' If folder exists the error is bypassed
' This is a rare beneficial use of On Error Resume Next
myRoot.folders.Add m.senderName
' Consider it mandatory to return to normal error handling
On Error GoTo 0
Set targetFldr = myRoot.folders(m.senderName)
m.Move targetFldr
End Sub
Private Sub RulesForFolders_test()
' Code requiring a parameter cannot run independently
Dim selItem As Object
' first select a mailitem
Set selItem = ActiveExplorer.Selection(1)
If selItem.Class = olMail Then
RulesForFolders ActiveExplorer.Selection(1)
End If
End Sub
First of all, I'd suggest starting from the NewMailEx event of the Application class which is fired when a new item is received in the Inbox. This event fires once for every received item that is processed by Microsoft Outlook. The item can be one of several different item types, for example, MailItem, MeetingItem, or SharingItem. The EntryIDsCollection string contains the Entry ID that corresponds to that item. 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.
To find the folder with a sender name you can iterate over all subfolders recursively:
Private Sub processFolder(ByVal oParent As Outlook.MAPIFolder)
Dim oFolder As Outlook.MAPIFolder
Dim oMail As Outlook.MailItem
For Each oMail In oParent.Items
'Get your data here ...
Next
If (oParent.Folders.Count > 0) Then
For Each oFolder In oParent.Folders
processFolder oFolder
Next
End If
End Sub
Finally, I'd recommend delving deeper with VBA by starting from the Getting started with VBA in Office article.
You can also use the following code if you don't need to iterate over all folders:
Sub RulesForFolders(m As MailItem)
Dim fldr As Outlook.Folder
Dim new_fldr As Outlook.Folder
Dim ns as Outlook.Namespace
Dim inbox as Outlook.Folder
Set ns = Application.GetNamespace("MAPI")
Set inbox = ns.GetDefaultFolder(olFolderInbox)
For Each fldr In inbox.Folders
if InStr(fldr.Name,m.SenderName) > 0 Then
m.MoveTo(fldr)
Return
End If
Next
Set new_fldr = folders.add(m.SenderName)
m.MoveTo(new_fldr)
Set fldr = Nothing
Set new_fldr = Nothing
Set inbox = Nothing
Set ns = Nothing
End Sub

Outlook Macro to Move entire conversations in a thread

I have written a macro which should move my email conversation to my "TO DO" folder whenever I flag the email as important. I find that the move function does happen, but I get a copy (i.e. the thread shows in both my "TODO"folder and still remains in the "Inbox".
What is also interesting is that in this line of code "For Each MailItem In Conversation.GetRootItems" I would have expected since all the messages do get moved that MailItem>1, but in fact that bit of code only executes one time and then the loop completes. Any thoughts on how to do a true move as opposed to be what appears to be a copy?
''''
Public WithEvents GExplorer As Outlook.Explorer
Public WithEvents GMailItem As Outlook.MailItem
Private WithEvents Items As Outlook.Items
Private Sub Application_Startup()
Set GExplorer = Outlook.Application.ActiveExplorer 'IGNORE THIS'
Dim olNameSpace As Outlook.NameSpace
Dim olFolder As Outlook.MAPIFolder
Set olNameSpace = Application.GetNamespace("MAPI")
Set olFolder = olNameSpace.GetDefaultFolder(olFolderInbox)
Set Items = olFolder.Items
End Sub
Private Sub Items_ItemChange(ByVal Item As Object)
'this item/macro is used to move an email message once it has been flagged
Dim olNameSpace As Outlook.NameSpace
Dim olFolder As Outlook.MAPIFolder
Dim olInbox As Outlook.MAPIFolder
Set olNameSpace = Application.GetNamespace("MAPI")
Set olFolder = olNameSpace.Folders("DEBUG").Folders("TODO")
Stop 'THIS WAS FOR DEBUGGING
If TypeOf Item Is Outlook.MailItem And Item.FlagStatus = olFlagMarked Then
Set Conversation = Item.GetConversation
If Not IsNull(Conversation) Then
' Set ItemsTable = conversation.GetTable
'MsgBox Conversation.GetRootItems.Count
For Each MailItem In Conversation.GetRootItems ' Items in the conversation. ONLY RUNS ONCE'
If TypeOf MailItem Is Outlook.MailItem Then
Item.Move olFolder
End If
Next
End If
End If
End Sub
''''
That is because the same action (move) is repeated for the item changed and passed as a parameter to the ItemChange event handler. Instead, you must run the Move method against the item object in the loop:
For Each MailItem In Conversation.GetRootItems ' Items in the conversation. ONLY RUNS ONCE'
If TypeOf MailItem Is Outlook.MailItem Then
MailItem.Move olFolder
End If
Next
In addition to Eugene's suggestion (use MailItem instead of item when calling Move), you should never use "for each" with Outlook objects in loops that modify the collection you are iterating over - use a down loop from Count to 1 step -1.
Maybe you need to GetChildren of the conversation.

Select a message based ont its subject from Outlook 2010, and run a macro that will copy data to Excel without manual intervention

Regretfully I have no formal background in VBA, but I have been able to learn quite a bit from sites like this.
Problem Statement:
I have a few emails with contain information that needs to be stored in excel. Fortunately I do have working script for that. Not provided to keep this somewhat shorter
The problem that I am facing is that capturing the right email from Microsoft Outlook 2010 and storing the data WITHOUT manual intervention.
The Email will contain a specific word/phrase, "EVEREST". Obviously it is not the only email received. It contains no attachments, and will come from various senders.
I have tried various macros I have found on-line to pull the message from the inbox, but none of them have worked for me.
So I have a macros that will pull messages from a personal folder, that macro then runs another macros that stores the contents of the email to excel, then it moves the message to its final resting place (another personal Folder) currently they all work fine together, but require manual intervention to complete the task. After the message is moved to the personal folder I simply click on a Quick Access Toolboar Icon mapped to a macro
To get the message moved over the personal folder i have a rule set up to move the message based on the word "EVEREST" and runs the initial script.
The problem with all of this is that the message will get moved to the folder, but needs manual intervention to complete the task. I would like it to run automatically.
I have been fumbling around with this for the past 2 months and seem to be in a stalemate. I would greatly appreciate your feedback and assistance.
The following is what I have so far.
My outlook rule set is:
Apply this rule after the message arrives
with "EVEREST" in the subject
and on this computer only
move it to the "EVEREST PRI" folder
and run "Project1.ThisOutlookSession.Everest"
' I believe these were put here when I was trying to run '
' everything via VBA macros, vice using the rule set above '
CLass Module (1)
Option Explicit
Private WithEvents Items As Outlook.Items
Private WithEvents olInboxItems As Items
' ThisOutlookSession contains the following scripts '
'This is the script that is run from the outlook rules '
' all it does is calls the "OCF" Sub below '
Sub Everest(email As MailItem)
OCF
End Sub
'This scipt opens the "EVEREST PRI" personal sub folder'
' and calls the "Prepwork" sub below '
Sub OCF()
Dim objOlApp As Outlook.Application
Dim Ns As Outlook.NameSpace
Dim objFolder As Outlook.Folder
Dim EmailCount As Integer
Set objOlApp = CreateObject("Outlook.Application")
Set Ns = Session.Application.GetNamespace("MAPI")
Set objFolder = Ns.Folders("Personal Folders").Folders("Archives").Folders("EVEREST PRI")
Set objOlApp.ActiveExplorer.CurrentFolder = objFolder
Set objFolder = Nothing
Set objOlApp = Nothing
Prepwork
End Sub
'I had hoped that the following routine would do the rest of the work '
'but it doesn't do it all the time. Most the time the message hasn't been '
'moved to the personal folder before its kicked off. '
'So I thought I would call another macro to play catch up "Wait" below '
Sub Prepwork()
Dim objOlApp As Outlook.Application
Dim Ns As Outlook.NameSpace
Dim objFolder As Outlook.Folder
Dim EmailCount As Integer
Set objOlApp = CreateObject("Outlook.Application")
Set Ns = Session.Application.GetNamespace("MAPI")
Set objFolder = Ns.Folders("Personal Folders").Folders("Archives").Folders("EVEREST PRI")
Set objOlApp.ActiveExplorer.CurrentFolder = objFolder
EmailCount = objFolder.Items.count
If EmailCount = 1 Then
'MsgBox "A COMSPOT has been recieved, acknowledge to update the chart'
' I tried adding this msgbox to provide some time delay, although '
' it has worked from time to time, it still requires manual '
' intervention, which is not desired. '
CopyToExcel
' CopyToExcel is the macro that writes my information to the '
' Spreadsheet. This script has been flawless and I have created '
' a Clickable ICON in the Quick Access Toolboar. '
ElseIf EmailCount = 0 Then
Wait
End If
End Sub
'The following "Wait Script was added, hoping to give time for the other '
'macros to finish, but i suspect they are all linked together, and wont '
'finish until all macroshave finished including the previously mentioned '
' "CopyToExcel" macro. '
' I have also tried to run this macro from the outlook rules, no joy......'
Sub Wait() '(email As MailItem)
' this provides a 5 second wait'
Sleep (5000)
Dim objOlApp As Outlook.Application
Dim Ns As Outlook.NameSpace
Dim objFolder As Outlook.Folder
Dim EmailCount As Integer
Set objOlApp = CreateObject("Outlook.Application")
Set Ns = Session.Application.GetNamespace("MAPI")
Set objFolder = Ns.Folders("Personal Folders").Folders("Archives").Folders("EVEREST PRI")
Set objOlApp.ActiveExplorer.CurrentFolder = objFolder
EmailCount = objFolder.Items.count
If EmailCount = 1 Then
'MsgBox "A COMSPOT has been recieved, acknowledge to update the chart"
CopyToExcel
ElseIf EmailCount = 0 Then
' MsgBox "The second Marco (Wait) did not locate a Message in the PRI Folder. Run the script from the Quick Access Toolboar"
End If
End Sub
' The following macro moves each of the selected items on the screen to an'
' Archive folder. I have not had any problems with this macro '
' This macro is called from the "CopyToExcel" macro. (not shown as it '
' has also worked fine since incorporating it '
Sub ArchiveItems() ' Moves each of the selected items on the screen to an Archive folder.
Dim olApp As New Outlook.Application
Dim olExp As Outlook.Explorer
Dim olSel As Outlook.Selection
Dim olNameSpace As Outlook.NameSpace
Dim olArchive As Outlook.Folder
Dim intItem As Integer
Set olExp = olApp.ActiveExplorer
Set olSel = olExp.Selection
Set olNameSpace = olApp.GetNamespace("MAPI")
' This assumes that you have an Inbox subfolder named Archive.
Set olArchive = olNameSpace.Folders("Personal Folders").Folders("Archives").Folders("EVEREST Archive")
For intItem = 1 To olSel.count
olSel.Item(intItem).Move olArchive
Next intItem
OIB
End Sub
' The following macro simply returns the view to the inbox folder, '
' Thus returning everything to Normal '
' The Ideal of returning to which every folder, or message was open at '
' the time the EVEREST message first arrived I thought would be to '
' complicated, but if any body could solve that... AMAZING.... '
Sub OIB()
Dim objOlApp As Outlook.Application
Dim Ns As Outlook.NameSpace
Dim objFolder As Outlook.Folder
Set objOlApp = CreateObject("Outlook.Application")
Set objFolder = Session.GetDefaultFolder(olFolderInbox)
Set objOlApp.ActiveExplorer.CurrentFolder = objFolder
Set objFolder = Nothing
Set objOlApp = Nothing
End Sub
There is no need to select, you already have the required "email" passed as a parameter by the rule.
The run a script code will look something like this.
Sub Everest(email As MailItem)
Dim Ns As NameSpace
'Dim inboxFolder As Folder
Dim olArchive As Folder
Set Ns = GetNamespace("MAPI")
CopyToExcelWithParameter email
'ArchiveItems
Set olArchive = Ns.Folders("Personal Folders")
Set olArchive = olArchive.Folders("Archives")
Set olArchive = olArchive.Folders("EVEREST Archive")
email.Move olArchive
' Edit: Just realized this was due to
' unnecessary folder selecting that is now gone
' This is unnecessary now as well
'OIB
'Set inboxFolder = Ns.GetDefaultFolder(olFolderInbox)
'Set ActiveExplorer.CurrentFolder = inboxFolder
Set Ns = Nothing
Set olArchive = Nothing
'Set inboxFolder = Nothing
End Sub
You will have to rewrite CopyToExcel to take email as a parameter
Sub CopyToExcelWithParameter (email as mailitem)
' code that processes "email" directly, not a selection
Debug.Print "Do something with " & email.subject
End Sub

Set the sender of a mail before it is sent in Outlook

I use the Application_ItemSend event to trigger actions on mails I send.
Under certain conditions the mail shall be moved to a new subfolder.
Since one can't move the mail before it is sent without jeopardizing the send, I copy the mail before sending and delete the original after.
Set myCopiedItem = objItem.Copy
myCopiedItem.Move olTempFolder
myCopiedItem.UnRead = False
myCopiedItem.SentOnBehalfOfName = olSession.CurrentUser
myCopiedItem.SendUsingAccount = olSession.Accounts(1)
'myCopiedItem.SenderName = olSession.CurrentUser
'myCopiedItem.SenderEmailAddress = olSession.CurrentUser.Address
objItem.DeleteAfterSubmit = True
I would like to have me as a sender on the copied mail.
I tried to set several different properties:
.SendOnBehalfOfName and .SendUsingAccount do not do what I am after.
.SenderName and .SenderEmailAddress showed to be "read only"
How can I avoid that the mail shows up in the folder without a sender?
Would this work for you:
Save the email in the Application_ItemSend event first:
Private Sub Application_ItemSend(ByVal Item As Object, Cancel As Boolean)
Item.Save
MoveEmail Item, "\\Mailbox - Darren Bartrup-Cook\Inbox\Some Folder\Some Sub Folder"
End Sub
In a separate module (excuse MoveEmail being a function - originally it returned the EmailID of the moved email):
'----------------------------------------------------------------------------------
' Procedure : MoveEmail
' Author : Darren Bartrup-Cook
' Date : 03/07/2015
'-----------------------------------------------------------------------------------
Public Function MoveEmail(oItem As Object, sTo As String) As String
Dim oNameSpace As Outlook.NameSpace
Dim oDestinationFolder As Outlook.MAPIFolder
Set oNameSpace = Application.GetNamespace("MAPI")
Set oDestinationFolder = GetFolderPath(sTo)
oItem.Move oDestinationFolder
End Function
'----------------------------------------------------------------------------------
' Procedure : GetFolderPath
' Author : Diane Poremsky
' Original : http://www.slipstick.com/developer/working-vba-nondefault-outlook-folders/
'-----------------------------------------------------------------------------------
Function GetFolderPath(ByVal FolderPath As String) As Outlook.MAPIFolder
Dim oFolder As Outlook.Folder
Dim FoldersArray As Variant
Dim i As Integer
On Error GoTo GetFolderPath_Error
If Left(FolderPath, 2) = "\\" Then
FolderPath = Right(FolderPath, Len(FolderPath) - 2)
End If
'Convert folderpath to array
FoldersArray = Split(FolderPath, "\")
Set oFolder = Application.Session.Folders.Item(FoldersArray(0))
If Not oFolder Is Nothing Then
For i = 1 To UBound(FoldersArray, 1)
Dim SubFolders As Outlook.Folders
Set SubFolders = oFolder.Folders
Set oFolder = SubFolders.Item(FoldersArray(i))
If oFolder Is Nothing Then
Set GetFolderPath = Nothing
End If
Next
End If
'Return the oFolder
Set GetFolderPath = oFolder
Exit Function
GetFolderPath_Error:
Set GetFolderPath = Nothing
Exit Function
End Function
Firstly, Move is a function, not a sub - it returns the newly created item. The original must be immediately discarded.
set myCopiedItem = myCopiedItem.Move(olTempFolder)
Secondly, sender related properties are set only after the message is sent and moved to the Sent Items folder. One solution is to wait until the Items.ItemAdd event fires on the Sent Items folder and make a copy then - the sender properties will be set by that time.
In theory, you can set a dozen or so PR_SENDER_* and PR_SENT_REPRESENTING_* MAPI properties, but if I remember my experiments correctly, MailItem.PropertyAccessor.SetProperty will not let you set sender related properties. If using Redemption is an option (I am its author), it allows to set the RDOMail.Sender and RDOMail.SentOnBehalfOf properties to an instance of an RDOAddressEntry object (such as that returned by RDOSession.CurrentUser).

For Each loop not deleting all items

I have a macro which is supposed to delete emails over 'x' amount of days old when I quit Outlook 2007 but it only seems to delete a few of them and when I open it and quit again it deleted the rest. Here is the code:
Private Sub Application_Quit()
Dim myOlApp, myNameSpace As Object
Dim MyItem As Object
Dim DeletedFolder As Object
Set myOlApp = CreateObject("Outlook.Application")
Set myNameSpace = myOlApp.GetNamespace("MAPI")
'Set DeletedFolder = myNameSpace.GetDefaultFolder(olFolderDeletedItems)
Set DeletedFolder = myNameSpace.GetDefaultFolder(olFolderInbox).Folders("Auto")
For Each MyItem In DeletedFolder.Items
If DateDiff("d", MyItem.ReceivedTime, Now) > 7 Then
MyItem.Delete
End If
Next
End Sub
In this example I chose greater than 7 days old in the Auto folder under my Inbox folder.
Any ideas why it does not delete them all the first time?
Thanks
Generally when deleting you need a different sort of iteration:
Dim m as Long
For m = DeletedFolder.Items.Count to 1 Step -1
Set myItem = DeletedFolder.Items(m)
If DateDiff("d", MyItem.ReceivedTime, Now) > 7 Then
MyItem.Delete
End If
Next
This is because, as you delete an element from the collection, the collection is re-indexed. So you need to step backwards through the collection, otherwise you will "skip" some items.