RDO Session - loop through entire Inbox and move emails - vba

Thanks to the excellent assistance given on this site I found the code below - which works perfectly. I cannot (embarrassingly enough) figure out how to loop through the entire Inbox to move all emails (rather than selection as the code below does).
Any assistance most gratefully appreciated it.
John
Sub MoveWithRecDate()
' Moves selected emails with correct dates maintained
Dim objNS As Outlook.NameSpace
Dim Session As Redemption.RDOSession
Dim objRDOFolder As Redemption.RDOFolder
Dim objItem As Outlook.MailItem
Dim objRDOMail As Redemption.RDOMail
Set objNS = Application.GetNamespace("MAPI")
Set Session = CreateObject("Redemption.RDOSession")
Session.Logon
Set inbox = Session.GetDefaultFolder(olFolderInbox)
Set objRDOFolder = inbox.Parent.Folders("Cabinet")
For Each objItem In Application.ActiveExplorer.Selection
Set objRDOMail = Session.GetMessageFromID(objItem.EntryID)
objRDOMail.Move objRDOFolder
Next
End Sub

I had not heard of Redemption before reading your question. It looks very interesting so thank you for the information; I will try it next time I need to write a new Outlook macro.
I assume from the lack of an answer to your question that few others use Redemption either.
The Redemption website implies that the structure of Redemption code will be almost identical to standard Outlook code. I can only recall once writing a macro which operated on user selected items but my recollection is that the code looked like yours. The code below is standard Outlook but I hope that is enough for you to create the equivalent Redemption code.
You macro has the comment ' Moves selected emails with correct dates maintained. This implies you think there is a method by which emails can be moved so that dates are not maintained. I do not know such a method.
The code below examines every item in the Inbox. I did not want to move everything out of my Inbox so I have skipped items that are not mail items and are not from a specific sender.
I hope this is enough to get you going.
Sub MoveWithRecDate()
Dim FolderDest As MAPIFolder
Dim ItemToBeMoved As Boolean
Dim ItemCrnt As Object
Dim FolderSrc As MAPIFolder
Set FolderSrc = CreateObject("Outlook.Application"). _
GetNamespace("MAPI").GetDefaultFolder(olFolderInbox)
Set FolderDest = FolderSrc.Parent.Folders("Cabinet")
For Each ItemCrnt In FolderSrc.Items
ItemToBeMoved = True ' Assume item to be moved until discover otherwise
With ItemCrnt
If .Class = olMail Then
If .SenderEmailAddress <> "noreply#which.co.uk" Then
' Mail item not from Which
ItemToBeMoved = False
End If
Else
' Not mail item so do not move
ItemToBeMoved = False
End If
If ItemToBeMoved Then
.Move FolderDest
End If
End With
Next
End Sub

Related

Mark all emails read in specified folder

I have an Outlook 2019 IMAP account and I'm trying to use the following VBA code to mark all emails in the "Spam" folder as read automatically.
I found code here on Stack Overflow and changed first IF statement to get only the Spam folder.
It gives me
"Array index out of bounds"_ error (80020009)
after 3rd or 4th iteration (so up to three emails are being deleted) and the code breaks.
Why is it failing, and can I parameterize this code to choose an arbitrary folder name?
Sub MarkAllItemsAsRead()
Dim objStores As Outlook.Stores
Dim objStore As Outlook.Store
Dim objOutlookFile As Outlook.Folder
Dim objFolder As Outlook.Folder
'Process all Outlook files
Set objStores = Outlook.Application.Session.Stores
For Each objStore In objStores
Set objOutlookFile = objStore.GetRootFolder
For Each objFolder In objOutlookFile.Folders
'Process mail folders
If objFolder.DefaultItemType = olMailItem And objFolder.Name = "Spam" Then
Debug.Print objFolder.Name
Call ProcessFolders(objFolder)
End If
Next
Next
End Sub
Sub ProcessFolders(ByVal objCurFolder As Outlook.Folder)
Dim objUnreadItems As Outlook.Items
Dim i As Integer
Dim objItem As Object
Dim objSubFolder As Outlook.Folder
Set objUnreadItems = objCurFolder.Items.Restrict("[Unread]=True")
'Mark all unread emails as read
For i = 1 To objUnreadItems.Count
Set objItem = objUnreadItems.Item(i)
objItem.UnRead = False
objItem.Save
Next
End Sub
Simply reversing the order of iteration will likely fix your problem.
What's (probably) happening here is that when you change the messages, the server wants to be extra helpful, and moves them messages elsewhere, and once that is done, what was "message number 2" is now "message number 1". Processing them starting with the highest number ought to solve it, because if the highest-numbered message goes away, no other messages are renumbered.
IMAP offers better ways to solve it (either setting all read with one command, or specifying them by UID instead of by sequence number), but reversing the order of iteration is a small change and probably will work.

Searching Outlook email (and replying to it) using Excel VBA

I want to search ALL my outlook for latest message in a conversation (I use Subject name as search key).
This latest message can be in Inbox, Sent Items, in a sub folder of Inbox, a sub-sub folder of Inbox (anywhere).
I can achieve this by some very tedious code, going through every level of each major folder, but not only this method is very messy, I can't determine if this found message is the latest in this conversation.
I have the following code, which
--> Searches Inbox for "searchKey"
--> If finds it in Inbox folder, replies to it
--> If not, it moves into subfolders of Inbox, and continues the same process
Dim olApp As Outlook.Application
Dim olNs As Namespace
Dim Fldr As MAPIFolder
Dim olFldr As MAPIFolder
Dim olMail ' As Outlook.MailItem
Dim i As Integer
Set olApp = New Outlook.Application
Set olNs = olApp.GetNamespace("MAPI")
Set Fldr = olNs.GetDefaultFolder(olFolderInbox)
Set olFldr = Fldr
tryAgain:
For Each olMail In olFldr.Items
If InStr(olMail.Subject, searchKey) <> 0 Then
Set ReplyAll = olMail.ReplyAll
With ReplyAll
.HTMLBody = Msg & .HTMLBody
emailReady = True
.Display
End With
End If
Next olMail
If Not emailReady Then
i = i + 1
If i > Fldr.Folders.Count Then
MsgBox ("The email with the given subject line was not found!")
Exit Sub
Else
Set olFldr = Fldr.Folders(i)
GoTo tryAgain
End If
End If
This code might be confusing and long, so please let me know if you need any clarification.
The question is: How can I search through ALL Outlook, without going manually through every folder/subfolder/sub-subfolder... without this method, and find the LAST message in a specific conversation? Or, at least, how can I optimize this code so I don't miss any folder, and know the dates and times these emails were sent?
You can use the built in AdvancedSearch function, which returns a Search object containing items.
These should have date properties, so you only need your code to go through the search object mailItems and find that with the latest date ( ReceivedTime)?
I would suggest using the bottom example on that page - it gets a table object from the search, and then you use
Set MyTable = MySearch.GetTable
Do Until MyTable.EndOfTable
Set nextRow = MyTable.GetNextRow()
Debug.Print nextRow("ReceivedTime")
Loop
From there, you can do the comparison to find the latest time, and if you want to do something with the mailitem you would need to obtain the "EntryID" column from the table.
Then use the GetItemFromID method of the NameSpace object to obtain a full item, since the table returns readonly objects.
You can also apply a date filter to the search if you wish, if you knew a minimum date for instance.
To go through all folders do this:
Go once through all the primary folders in Outlook and then for each major folder go through each subfolder. If you have more branches then is guess you have to add more levels to the code "for each Folder3 in folder2.folders". Also in the if clause you can test the date of the mail and go from the newest to the oldest. Set oMsg.display to see what mail is being checked
Public Sub FORWARD_Mail_STAT_IN()
Dim Session As Outlook.NameSpace
Dim oOutLookObject As New Outlook.Application
Dim olNameSpace As NameSpace
Dim oItem As Object
Dim oMsg As Object
Dim searchkey As String
Set oOutLookObject = CreateObject("Outlook.Application")
Set oItem = oOutLookObject.CreateItem(0)
Set olNameSpace = oOutLookObject.GetNamespace("MAPI")
Set Session = Application.Session
Set Folders = Session.Folders
For Each Folder In Folders 'main folders in Outlook
xxx = Folder.Name
For Each Folder2 In Folder.Folders 'all the subfolders from a main folder
yyy = Folder2.Name
Set oFolder = olNameSpace.Folders(xxx).Folders(yyy) 'in each folder we search all the emails
For Z = oFolder.Items.Count To 1 Step -1 ' For Z = 1 To oFolder.Items.Count
With oFolder.Items(Z)
Set oMsg = oFolder.Items(Z)
If Format(oMsg.SentOn, "mm/dd/yyyy") = Format(Date, "mm/dd/yyyy") And InStr(1, LCase(oMsg.Subject), searchkey, vbTextCompare) > 0 Then
oMsg.display
' insert code
End If
End With
Next Z
Next Folder2
Next Folder

How to automate saving an inbox message to a folder when closing after reading - Outlook 2010 VBA

I want to keep this in VBA. I'm seeking info on how to work around the following issue.
I get this error:
The item's properties and methods cannot be used inside this event procedure. MS has stopped people being able to use the .Close, .Move and .Delete methods in the Inspector_Close event.
I've seen suggestions to use threading to run a delayed macro, but can't find help on this, and suspect it may not be available in VBA.
My code is as follows:
Private Sub objInspector_Close()
Dim objNS As NameSpace
Dim objFolder As MAPIFolder
'On Error Resume Next
Set objNS = Application.Session
If Not mailSent Then
If objInspector.CurrentItem.Class = olMail Then
'Mail inspector is closing
If objInspector.CurrentItem.Parent = "Inbox" Then
Set objFolder = objNS.PickFolder
If Not objFolder Is Nothing And IsInDefaultStore(objFolder) _
And objFolder.DefaultItemType = olMailItem Then
Set objInspector.CurrentItem.Move = objFolder
End If
End If
End If
Else
mailSent = False
End If
Set objFolder = Nothing
Set objNS = Nothing
End Sub
The global mailSent Boolean is there to prevent this event code executing when I send / close an email.
The error occurs on Set objInspector.CurrentItem.Move = objFolder.
Is there a way for me to delay this until the event ends or perhaps to set some flags on the email item and then run a macro over all emails in my inbox later to move them to folders.
I work on multiple projects and maintain multiple email folders and am looking for ways to automate my email management. I've seen other pages where folder names are derived from email subjects but I don't want to do that.
Thanks for your help.
You may consider adding a user property which can mark the message for moving etc. Then you can use the Find/FindNext or Restrict methods for searching marked items. You can read more about these methods in the following articles:
How To: Use Find and FindNext methods to retrieve Outlook mail items from a folder (C#, VB.NET)
How To: Use Restrict method to retrieve Outlook mail items from a folder
Also you can use the GetTable method of the Folder class which obtains a Table object that contains items filtered by Filter.
As you probably know the Outlook object model can't be used from another threads. You need to use a low-level API - Extended MAPI which supports secondary threads. Or any other third-party wrappers around that API, for example - Redemption.
You could abandon the idea of using a trigger and move "manually"
Option Explicit
Private Sub MoveCurrentItem()
Dim objNS As Namespace
Dim objFolder As folder
Dim currItem As Object
Dim uPrompt As String
Set objNS = Application.Session
On Error Resume Next
Set currItem = ActiveInspector.currentItem
On Error GoTo 0
If currItem Is Nothing Then GoTo ExitRoutine
If currItem.Class = olMail Then
If currItem.Sent Then ' reading not composing
If currItem.Parent = "Inbox" Then
Set objFolder = objNS.PickFolder
If Not objFolder Is Nothing And IsInDefaultStore(objFolder) _
And objFolder.DefaultItemType = olMailItem Then
currItem.Move objFolder
End If
End If
End If
End If
ExitRoutine:
Set currItem = Nothing
Set objFolder = Nothing
Set objNS = Nothing
End Sub

send all "visible" drafts with VBA in Outlook 2007

How do I automatically send out multiple (currently visible) draft items with VBA?
Please help, thank you.
Edit: It's a tough case, none of the items are in the drafts folder yet. These are generated emails that are on your screen, waiting to be sent.
Edit2: nvm, it's not going to help anyway. My script creates approximately 500 emails, and displaying the first 100 causes out of memory error. I opted to auto send them without displaying (it breaks the layout this way, but it's my only option for now.)
It just so happens that I ran into the same issue before and have code handy. If you're not already in Outlook, you will need to add a reference in the VBA IDE, Tools ---> References... and check the box next to "Microsoft Outlook 14.0 Object Library".
Dim oFolder As Folder
Dim oNS As NameSpace
Dim olMail As MailItem
If (MsgBox("Are you sure you want to send ALL EMAILS IN YOUR DRAFTS FOLDER?", vbYesNo + vbCritical, "WARNING: THIS WILL SEND ALL DRAFTS")) = vbYes Then
Set oNS = Outlook.Application.GetNamespace("MAPI")
Set oFolder = oNS.GetDefaultFolder(olFolderDrafts)
For i = 1 To oFolder.Items.Count
oFolder.Items(1).Send
Next
End If
Set oNS = Nothing
Here's some code. Replace Your Name in myFolders("Mailbox - Your Name") with your actual name as it appears in the mailbox.
Public Sub EmailOutlookDraftsMessages()
Dim lDraftItem As Long
Dim myOutlook As Outlook.Application
Dim myNameSpace As Outlook.NameSpace
Dim myFolders As Outlook.Folders
Dim myDraftsFolder As Outlook.MAPIFolder
'Send all items in the "Drafts" folder that have a "To" address filled in.
'Setup Outlook
Set myOutlook = Outlook.Application
Set myNameSpace = myOutlook.GetNamespace("MAPI")
Set myFolders = myNameSpace.Folders
'Set Draft Folder.
Set myDraftsFolder = myFolders("Mailbox - Your Name").Folders("Drafts")
'Loop through all Draft Items
For lDraftItem = myDraftsFolder.Items.Count To 1 Step -1
'Check for "To" address and only send if "To" is filled in.
If Len(Trim(myDraftsFolder.Items.Item(lDraftItem).To)) > 0 Then
'Send Item
myDraftsFolder.Items.Item(lDraftItem).Send
End If
Next lDraftItem
'Clean-up
Set myDraftsFolder = Nothing
Set myNameSpace = Nothing
Set myOutlook = Nothing
End Sub
Source Code adapted from this Question's answer.

How to send multiple drafts from Outlook 2003

Outlook wont let me send multiple drafts at the same time. Is there an easy way to send multiple drafts at once in outlook? without having to open each one individually?
From what i've read, seen and tried; this is not possible from within outlook itself, and thus a programming solution would be required, probably some VB script
ok, i found a bit of VB that does it:
`Public Sub SendDrafts()
Dim lDraftItem As Long
Dim myOutlook As Outlook.Application
Dim myNameSpace As Outlook.NameSpace
Dim myFolders As Outlook.Folders
Dim myDraftsFolder As Outlook.MAPIFolder
'Send all items in the "Drafts" folder that have a "To" address filled
'in.
'Setup Outlook
Set myOutlook = Outlook.Application
Set myNameSpace = myOutlook.GetNamespace("MAPI")
Set myFolders = myNameSpace.Folders
'Set Draft Folder. This will need modification based on where it's
'being run.
Set myDraftsFolder = myFolders("$MAILBOX").Folders("$DRAFTS")
'Loop through all Draft Items
For lDraftItem = myDraftsFolder.Items.Count To 1 Step -1
'Check for "To" address and only send if "To" is filled in.
If Len(Trim(myDraftsFolder.Items.Item(lDraftItem).To)) > 0 Then
'Send Item
myDraftsFolder.Items.Item(lDraftItem).Send
End If
Next lDraftItem
'Clean-up
Set myDraftsFolder = Nothing
Set myNameSpace = Nothing
Set myOutlook = Nothing
End Sub
just replace $MAILBOX with your mailbox name and $DRAFTS with the name of your drafts folder.
This has been personnaly tested and seems to work fine.
Not very different from author's answer, but still:
Sub SendDrafts()
Dim ns As NameSpace
Dim drafts As MAPIFolder
Dim Item As MailItem
Set ns = Application.GetNamespace("MAPI")
Set drafts = ns.GetDefaultFolder(olFolderDrafts) ' 16
For Each Item In drafts.Items
'Item.Send
Next
End Sub
Please be careful as it really sends all emails in your default draft folder. After uncommenting the send line. Dim lines to allow for autocompletion when inside Outlook macro editor.
A useful version, which I just tested in Outlook 2000:
Drag the emails you wish to send to the Outbox. They won't be sent automatically, but using this version of the prior posting sends them:
Sub SendOutbox()
Dim ns As NameSpace
Dim outbox As MAPIFolder
Dim Item As MailItem
Set ns = Application.GetNamespace("MAPI")
Set outbox = ns.GetDefaultFolder(olFolderOutbox) ' 16
For Each Item In outbox.Items
Item.Send
Next
End Sub
That way, you can be selective.
Yes, you can write a macro or add-in to do that.