VBA to select mailbox if an account has multiple mailbox's - vba

Here is my requirement.
I have multiple accounts in my OUTLOOK configured.
1) 1#email.com (only one mailbox)
2) 2#email.com (Multiple mailbox's are there. ex: Unix box, Windows Box, Mac box)
Here my 2nd email account has its own mailbox and linked to multiple mailbox's like UNIX, Windows etc. Each Mailbox has its own inbox and sub folders.
Now i need to select a folder in Unix box (inbox) and run the code to do something in side the folder.
Here's what i have
For Each oAccount In Application.Session.Accounts
If oaccount ="1#email.com" then
Set folder = ns.GetDefaultFolder(olFolderInbox) ' here it selects the inbox folder of account.
For each item in folder.items
Code goes here
next
end if
next
This works fine for single mailbox account, but when i do this for multiple mailbox account , it doesn't work.
any help would be appreciated.

You can use the DeliveryStore property of the Account to get its inbox. For example:
Dim ns As NameSpace
Set ns = Application.Session
Dim acc As Account
Dim f As Folder
For Each acc In ns.Accounts
... Preconditions here ...
Set f = acc.DeliveryStore.GetDefaultFolder(olFolderInbox)
... Now, do some looping ...
Next

Expanding on DanL's suggestion to loop through ns.Folders as I cannot tell whether you understood it.
Option Explicit
Sub accTopFolder()
Dim oAccount As Account
Dim ns As Namespace
Dim fldr As folder
Dim item As Object
Dim inbx As folder
Set ns = GetNamespace("MAPI")
For Each oAccount In Session.Accounts
Debug.Print vbCr & "oAccount: " & oAccount
'
For Each fldr In ns.Folders
' Shows all the names so you can replace "test"
Debug.Print " top folder: " & fldr.name
If fldr = "test" Then
Set inbx = fldr.Folders("Inbox")
'inbx.Display
For Each item In inbx.Items
Debug.Print " item .Subject: " & item.subject
Next
Exit For
End If
Next
Next
Set inbx = Nothing
Set ns = Nothing
End Sub

Related

Moving over 20,000 emails, based on email address, freezes Outlook

I am trying to move over 20,000 emails, based on email address, into desired folders.
The code I found freezes Outlook. The code does work before the freeze.
Using first code from the answer to this post
Option Explicit
Public Sub Move_Items()
' // Declare your Variables
Dim Inbox As Outlook.MAPIFolder
Dim SubFolder As Outlook.MAPIFolder
Dim olNs As Outlook.NameSpace
Dim Item As Object
Dim Items As Outlook.Items
Dim lngCount As Long
On Error GoTo MsgErr
' Set Inbox Reference
Set olNs = Application.GetNamespace("MAPI")
Set Inbox = olNs.GetDefaultFolder(olFolderInbox)
Set Items = Inbox.Items
' // Loop through the Items in the folder backwards
For lngCount = Items.Count To 1 Step -1
Set Item = Items(lngCount)
If Item.Class = olMail Then
Select Case Item.SenderEmailAddress
' // Email_One
Case "Email_One#email.com"
' // Set SubFolder of Inbox
Set SubFolder = Inbox.Folders("Folder One")
Set Item = Items.Find("[SenderEmailAddress] = 'Email_One#email.com'")
If TypeName(Item) <> "Nothing" Then
' // Mark As Read
Item.UnRead = False
' // Move Mail Item to sub Folder
Item.Move SubFolder
End If
' // Email_Two
Case "Email_Two#email.com"
' // Set SubFolder of Inbox
Set SubFolder = Inbox.Folders("Folder Two")
Set Item = Items.Find("[SenderEmailAddress] = 'Email_Two#email.com'")
If TypeName(Item) <> "Nothing" Then
' // Mark As Read
Item.UnRead = False
' // Move Mail Item to sub Folder
Item.Move SubFolder
End If
End Select
End If
Next lngCount
MsgErr_Exit:
Set Inbox = Nothing
Set SubFolder = Nothing
Set olNs = Nothing
Set Item = Nothing
Set Items = Nothing
Exit Sub
'// Error information
MsgErr:
MsgBox "An unexpected Error has occurred." _
& vbCrLf & "Error Number: " & Err.Number _
& vbCrLf & "Error Description: " & Err.Description _
, vbCritical, "Error!"
Resume MsgErr_Exit
End Sub
Also is it possible to filter not a specific email address e.g. dave#test.com but *#test.com?
I think at least your first problem might be the line 'Set Inbox = olNs.GetDefaultFolder(olFolderInbox)'
I have the similar line 'Set Items = objNS.GetDefaultFolder(olFolderInbox).Items' in my start-up routine Private Sub Application_Startup() . This worked fine ever since we switched to 365, but then circa February 2021 it started to crash on start-up. I got here by searching on this problem. Presumably they have changed something about the object model.
I also suppose it could be where olNs is set in the first place ' Set objNS = olApp.GetNamespace("MAPI"), if you mail doesn't use MAPI?
I've chucked the problem at out IT support, and I'll let you know if they come back with anything other than a mildly panicked 'what the hell you doing using VBA?'
The delay is caused by running a time-consuming task/code in Outlook. So, you need to optimize what and how is run in Outlook.
The problem is in the source code. I've noticed that you are iterating over all items in the folder:
// Loop through the Items in the folder backwards
For lngCount = Items.Count To 1 Step -1
That is completely a bad idea!
Instead, you need to use the Find/FindNext or Restrict methods to process all items that correspond to the specified search criteria. The Find method returns a single and first entry from the list of items. To get the second (if any) you need to use the FindNext method in the loop.
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 may consider using the AdvancedSearch method of the Application class. The key benefits of using the AdvancedSearch method in Outlook are:
The search is performed in another thread. You don’t need to run another thread manually since the AdvancedSearch method runs it automatically in the background.
Possibility to search for any item types: mail, appointment, calendar, notes etc. in any location, i.e. beyond the scope of a certain folder. The Restrict and Find/FindNext methods can be applied to a particular Items collection (see the Items property of the Folder class in Outlook).
Full support for DASL queries (custom properties can be used for searching too). You can read more about this in the Filtering article in MSDN. To improve the search performance, Instant Search keywords can be used if Instant Search is enabled for the store (see the IsInstantSearchEnabled property of the Store class).
You can stop the search process at any moment using the Stop method of the Search class.
See Advanced search in Outlook programmatically: C#, VB.NET for more information.
If processing every item there is no need for a Find. Find replaces the For loop item. It is more likely to run to completion when there are fewer items.
The simplest change is to remove the Find. This should fix any array out of bounds errors. Still it is inefficient.
// Email_One
Case "Email_One#email.com"
'// Set SubFolder of Inbox
Set SubFolder = Inbox.Folders("Folder One")
'// Mark As Read
Item.UnRead = False
'// Move Mail Item to sub Folder
Item.Move SubFolder
One way to limit processing to the applicable items.
Option Explicit
Public Sub Move_Items_Restrict()
'// Declare your Variables
Dim myInbox As Folder
Dim subFolder As Folder
Dim myItem As Object
Dim myItems As Items
Dim resItems As Items
Dim strfilter As String
Dim i As Long
' Not while developing
'On Error GoTo MsgErr
' Set Inbox Reference
Set myInbox = Session.GetDefaultFolder(olFolderInbox)
'// Email_One
Set myItems = myInbox.Items
strfilter = "[SenderEmailAddress] = 'Email_One#email.com'"
Debug.Print strfilter
' some of these work, fromemail does
' https://learn.microsoft.com/en-us/previous-versions/office/developer/exchange-server-2007/aa579702(v=exchg.80)
'strfilter = "#SQL=urn:schemas:httpmail:fromemail LIKE '%#test.com'"
'Debug.Print strfilter
Set resItems = myItems.Restrict(strfilter)
Debug.Print resItems.count
If resItems.count > 0 Then
'// Set SubFolder of Inbox
Set subFolder = myInbox.folders("Folder One")
For i = resItems.count To 1 Step -1
Set myItem = resItems(i)
With myItem
'// Mark As Read
.UnRead = False
'// Move Mail Item to sub Folder
.Move subFolder
End With
' If there is a memory error,
' release item when no longer necessary,
'Set myItem = Nothing
Next
End If
'// Email_Two
Set myItems = myInbox.Items
strfilter = "[SenderEmailAddress] = 'Email_Two#email.com'"
Debug.Print strfilter
Set resItems = myItems.Restrict(strfilter)
Debug.Print resItems.count
If resItems.count > 0 Then
'// Set SubFolder of Inbox
Set subFolder = myInbox.folders("Folder Two")
For i = resItems.count To 1 Step -1
Set myItem = resItems(i)
With myItem
' // Mark As Read
.UnRead = False
' // Move Mail Item to sub Folder
.Move subFolder
End With
' If there is a memory error,
' release item when no longer necessary,
'Set myItem = Nothing
Next
End If
MsgErr_Exit:
Exit Sub
'// Error information for users to advise the developer
MsgErr:
MsgBox "An unexpected Error has occurred." _
& vbCrLf & "Error Number: " & err.Number _
& vbCrLf & "Error Description: " & err.description _
, vbCritical, "Error!"
Resume MsgErr_Exit
End Sub

Rule that prompts user once but applies results to multiple messages?

I want to apply an ad-hoc category to a selection of Outlook emails.
I have a VBA script that asks the user for a string and then sets this as the email's category. I run this by moving all emails to be updated to a temp folder, and then run the Outlook rule on all messages in that folder.
Public Sub PromptForCat(Email As Outlook.MailItem)
Category = InputBox("Please enter category to add :")
Email.Categories = Email.Categories & "," & Category
Email.Save
End Sub
However, this will prompt me for every message in the folder - is there any way to only prompt me once, yet apply the result to all messages in the folder?
If you need to run the code once for a selected folder you need to iterate over all items in the folder. The Items property of the Folder class returns an Items collection object as a collection of Outlook items in the specified folder:
Sub ContactDateCheck()
Dim myNamespace As Outlook.NameSpace
Dim myContacts As Outlook.Items
Dim myItem As Object
Set myNamespace = Application.GetNamespace("MAPI")
Set myContacts = myNamespace.GetDefaultFolder(olFolderContacts).Items
For Each myItem In myContacts
If (myItem.Class = olContact) Then
MsgBox myItem.FullName &; ": " &; myItem.LastModificationTime
End If
Next
End Sub
To get the selected items in Outlook you need to use the Selection property of the Explorer class:
Sub GetSelectedItems()
Dim myOlExp As Outlook.Explorer
Dim myOlSel As Outlook.Selection
Dim MsgTxt As String
Dim x As Integer
MsgTxt = "You have selected items from: "
Set myOlExp = Application.ActiveExplorer
Set myOlSel = myOlExp.Selection
For x = 1 To myOlSel.Count
MsgTxt = MsgTxt & myOlSel.Item(x).SenderName & ";"
Next x
MsgBox MsgTxt
End Sub

Switching between accounts then looping through email

I am attempting to dump all emails in the junk email folder of a NON-Default outlook account into the inbox so that I can then perform additional logic on the email.
However I am unable to figure out how to reference the junk box or even the inbox of the non-default account, my code keeps going through my default account even with an account check in place.
Public Sub New_Mail()
Dim oAccount As Outlook.Account
Dim objSourceFolder As Outlook.MAPIFolder
Dim objDestFolder As Outlook.MAPIFolder
dim lngCount as long
lngcount = 0
For Each oAccount In Application.Session.Accounts ' cycle through accounts till we find the one we want
If oAccount = "desired.account#domain.ca" Then
Set objSourceFolder = objNamespace.GetDefaultFolder(olFolderJunk) ' select junk folder of the account
Set objDestFolder = objNamespace.GetDefaultFolder(olFolderInbox) ' select inbox of the account
For lngCount = objSourceFolder.Items.Count To 1 Step -1 ' Go through all items in inbox, if a mail object, move into inbox
Set objVariant = objSourceFolder.Items.Item(dblCount)
DoEvents
If objVariant.Class = olMail Then
Set objCurrentEmail = objVariant ' the inbox item is an email, so change object type to olMail (email object)
objCurrentEmail.Categories = "red category"
objCurrentEmail.Move objDestFolder ' Move the email to the required folder
End If
Next
End If
Next
End Sub
EDIT:
After Eric's Answer I'd like share my now working code.
Private Sub clearJunk()
Dim objVariant As Variant ' Variant object to handle and inbox item
Dim objCurrentEmail As Outlook.MailItem ' Temporary email object for logic
Dim dblCount As Double ' Double used to count email items in the inbox
Dim objStore As Outlook.Store ' Store Object to cycle through email accounts
Dim objRoot As Outlook.Folder ' Folder object to define Inbox of desired account
Dim folders As Outlook.folders ' FolderS object to holder folders...lol
Dim Folder As Outlook.Folder ' Temporary Folder object
Dim foldercount As Integer ' integer to count folders in account
Dim objInboxFolder As Outlook.MAPIFolder ' MAPI folder object to move emails to or from
Dim objJunkFolder As Outlook.MAPIFolder ' MAPI folder object to move emails to or from
Dim objRandomFolder As Outlook.MAPIFolder ' MAPI folder object to move emails to or from
'--------------------------------------------------------------------
' Cycle through each account in outlook client and find desired account
For Each objStore In Application.Session.Stores
If objStore = "desired.account#domain.ca" Then ' If we find the account
Set objRoot = objStore.GetRootFolder ' Store int objRoot Object
On Error Resume Next
Set folders = objRoot.folders ' Check if it has folders
foldercount = folders.Count
If foldercount Then ' if folders exist
For Each Folder In folders ' Go through each folder AND ....
' Look for Junk Email folder, Inbox Folder, and some random customer folder.
' Store in individual objects for future referencing
If Folder.FolderPath = "\\desired.account#domain.ca\Junk Email" Then
Set objJunkFolder = Folder
End If
If Folder.FolderPath = "\\desired.account#domain.ca\Inbox" Then
Set objInboxFolder = Folder
End If
If Folder.FolderPath = "\\desired.account#domain.ca\Random Custom Folder" Then
Set objRandomFolder = Folder
End If
Next
End If
' Now we have everything identified lets move emails!
For dblCount = objJunkFolder.Items.Count To 1 Step -1
Set objVariant = objJunkFolder.Items.Item(dblCount)
DoEvents
If objVariant.Class = olMail Then
Set objCurrentEmail = objVariant
objCurrentEmail.Categories = "Red Category"
objCurrentEmail.Move objInboxFolder
End If
Next
End If
Next
End Sub
You need to call Store.GetDefaultFolder(olFolderInbox) for the non-default accounts. Get the Store object from the Account.DeliveryStore property - in most cases that will be the correct store unless for example it is a PST account that has messages delivered to another account's store (perhaps even the default account's store).

Using Microsoft Access, need to pull attachments from Outlook emails of a different account

I have working code below to extract attachments from my own Outlook email account, however I need to do it for a different account that is setup as a default reply email box for an automated process.
I'm not entirely sure how to tell the code below to check for that mailbox instead of my own. I've tried different variations of setting the Inbox variable, but none of them have worked thus far. This is done within Access 2013.
Private Sub GetAttachments()
Dim ns As Namespace
Dim Inbox As Outlook.MAPIFolder
Dim Item As Object
Dim Atmt As Outlook.Attachment
Dim FileName As String
Set ns = GetNamespace("MAPI")
Set Inbox = ns.GetDefaultFolder(olFolderInbox)
If Inbox.Items.Count = 0 Then
MsgBox "There are no messages in the Inbox.", vbInformation, _
"Nothing Found"
Exit Sub
End If
For Each Item In Inbox.Items
For Each Atmt In Item.Attachments
If Atmt.Type = 1 And InStr(Atmt, "xlsx") > 0 Then
FileName = "C:\attachments\" & Atmt.FileName
Atmt.SaveAsFile FileName
End If
Next Atmt
Next Item
End Sub
Try this:
Set Inbox = ns.Folders("MailboxName").Folders("Inbox")
Use Namespace.GetSharedDefaultFolder
Set recip = ns.CreateRecipient("other mailbox owner")
recip.Resolve
Set Inbox = ns.GetSharedDefaultFolder(recip, olFolderInbox)

Move outlook mail from one mailbox inbox to different folder in same mailbox

I have several mailboxes which I can see in my Outlook profile. One of the mailboxes, let's call it "Mailbox - HUR" receives messages constantly. presently one of my team goes into the inbox of this mailbox every day and moves (drag and drop) the messages into a subfolder of the inbox called Archive (we're an imaginative lot!) if the messages are greater than 24 hours old.
Is there any way that a macro can be set up to do this task? I know my simple way around VBA but have never used it with Outlook and can't figure out the namespace details to point me to the correct mailbox instead of my mailbox.
Unfortunately I do not have access to Exchange server, only using outlook client.
Any help anyone could give would be great.
You might like to try:
Sub MoveOldEmail()
Dim oItem As MailItem
Dim objMoveFolder As MAPIFolder
Dim objInboxFolder As MAPIFolder
Dim i As Integer
Set objMoveFolder = GetFolder("Personal Folders\Inbox\Archive")
Set objInboxFolder = GetNamespace("MAPI").GetDefaultFolder(olFolderInbox)
For i = objInboxFolder.Items.Count - 1 To 0 Step -1
With objInboxFolder.Items(i)
''Error 438 is returned when .receivedtime is not supported
On Error Resume Next
If .ReceivedTime < DateAdd("h", -24, Now) Then
If Err.Number = 0 Then
.Move objMoveFolder
Else
Err.Clear
End If
End If
End With
Next
Set objMoveFolder = Nothing
Set objInboxFolder = Nothing
End Sub
Public Function GetFolder(strFolderPath As String) As MAPIFolder
'' strFolderPath needs to be something like
'' "Public Folders\All Public Folders\Company\Sales" or
'' "Personal Folders\Inbox\My Folder"
Dim objNS As NameSpace
Dim colFolders As Folders
Dim objFolder As MAPIFolder
Dim arrFolders() As String
Dim i As Long
On Error GoTo TrapError
strFolderPath = Replace(strFolderPath, "/", "\")
arrFolders() = Split(strFolderPath, "\")
Set objNS = GetNamespace("MAPI")
On Error Resume Next
Set objFolder = objNS.Folders.Item(arrFolders(0))
If Not objFolder Is Nothing Then
For i = 1 To UBound(arrFolders)
Set colFolders = objFolder.Folders
Set objFolder = Nothing
Set objFolder = colFolders.Item(arrFolders(i))
If objFolder Is Nothing Then
Exit For
End If
Next
End If
On Error GoTo TrapError
Set GetFolder = objFolder
Set colFolders = Nothing
Set objNS = Nothing
Exit_Proc:
Exit Function
TrapError:
MsgBox Err.Number & " " & Err.Description
End Function
You should setup a mailbox rule. Tools | Rules Wizard
If you are using Exchange server have an Outlook rule to move the emails to the specific folder, then use the Mailbox Manager in Exchange to delete messages from that folder after a specific period of time. See this article for more information.
Fionnuala you rock!
I've been looking for a solution to a similar issue for months. With my corporate restrictions, I wasn't able to use the UDF (worked just fine on my personal); Within the sub MoveOldEmail, I instead used:
Set objMoveFolder = GetNamespace("MAPI").PickFolder
Cool thing is that this seems to let me move between email accounts that I have associated with my Outlook (until corp figures out at least).