Getting an EntryID after an object is moved - vba

Summary
I'm trying to add hyperlinks to tasks created from emails that I have moved to another folder.
The goal is to have the task contain a hyperlink to the Outlook item that was moved to a "Processed Email" folder".
Problem
I don't understand how to move a MailItem and then get its new EntryID after it moves.
The "naive" way doesn't work. After using the Move method to move a MailItem object, the EntryID property does not reflect a change in ID.
Details
Creating a hyperlink to an Outlook item using the format outlook:<EntryID> is easy enough if the Outlook item remains in the Inbox, since I can just get the EntryID of the object that I am linking to. However, Outlook changes the EntryID when an object is moved.
I want to understand how to get the updated ID so that I can construct an accurate link.
Example
The message boxes show the EntryID property of objMail returns the same value despite the fact that the object has moved. However, running a separate macro on the mail in the destination folder confirms that the EntryID has changed with the move.
Sub MoveObject(objItem As Object)
Select Case objItem.Class
Case olMail
Dim objMail As MailItem
Set objMail = objItem
MsgBox (objMail.EntryID)
Dim inBox As Outlook.MAPIFolder
Set inBox = Application.ActiveExplorer().Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox)
Dim destFolder As Outlook.MAPIFolder
Set destFolder = inBox.Folders("Processed Email")
If (Application.ActiveExplorer().CurrentFolder.Name <> destFolder.Name) Then
objMail.Move destFolder
End If
MsgBox (objMail.EntryID)
End Select
End Sub

The Move method of the MailItem class returns an object that represents the item which has been moved to the designated folder. You need to check out the EntryID value of the returned object, not the source one.
Anyway, you may consider handling the ItemAdd event of the target folder to make sure that an updated entry ID value is used all the time.
Sub MoveItems()
Dim myNameSpace As Outlook.NameSpace
Dim myInbox As Outlook.Folder
Dim myDestFolder As Outlook.Folder
Dim myItems As Outlook.Items
Dim myItem As Object
Set myNameSpace = Application.GetNamespace("MAPI")
Set myInbox = myNameSpace.GetDefaultFolder(olFolderInbox)
Set myItems = myInbox.Items
Set myDestFolder = myInbox.Folders("Personal Mail")
Set myItem = myItems.Find("[SenderName] = 'Eugene Astafiev'")
While TypeName(myItem) <> "Nothing"
myItem.Move myDestFolder
Set myItem = myItems.FindNext
Wend
End Sub

Hello can you please elaborate your answer I am not able to understand it.
Anyway, you may consider handling the ItemAdd event of the target folder to make sure that an updated entry ID value is used all the time.
Here is my code and I need EntryID after moving.
Sub Movetest1()
Dim olApp As Outlook.Application
Dim olns As Outlook.NameSpace
Dim Fld As Folder
Dim ofSubO As Outlook.MAPIFolder
Dim myDestFolder As Outlook.Folder
Dim ofolders As Outlook.Folders
Dim objItems As Outlook.Items
Dim myRestrictItems As Outlook.Items
Dim i As Long
Dim myitem As Object
' Dim MailItem As Microsoft.Office.Interop.Outlook.MailItem
Dim MailItem, moveditem As Outlook.MailItem
Dim eid As String
Dim sid As Variant
Dim newEID As String
'---------------------------------------------------------------------------------------------------------
Set olApp = New Outlook.Application
Set olns = olApp.GetNamespace("MAPI")
For Each Fld In olns.Folders
If Fld.Name = "GSS Payables" Then
'
' MsgBox Fld.Name
' Debug.Print " - "; Fld.EntryID
Set Fld = olns.GetFolderFromID("000000009DA6D76FBE7A58489450CDF6094F592A0100A2457DC435B22448A832DB721D8185B1000000B6207D0000").Folders("Inbox")
Exit For
End If
Next
Set objItems = Fld.Items
eid = "000000009DA6D76FBE7A58489450CDF6094F592A0700A2457DC435B22448A832DB721D8185B1000000B620800000A2457DC435B22448A832DB721D8185B100007FF773270000"
sid = "000000009DA6D76FBE7A58489450CDF6094F592A0100A2457DC435B22448A832DB721D8185B1000000B6207D0000"
Set myDestFolder = Fld.Folders("Bhagyashri")
'Set myitem = objItems.Find("[SenderName]='Microsoft Outlook '")
Set MailItem = olns.GetItemFromID(eid)
Set moveditem = MailItem.Move(myDestFolder)
"giving error here
newID = moveditem.entryid
Debug.Print "newID -"; newID
' get mailitem.parent.storeid
MsgBox "done"
End

Use the following syntax:
Dim MoveToFolder As outlook.MAPIFolder
Dim MyItem As outlook.MailItem
Dim NewEntryID As String
NewEntryID = MyItem.Move(MoveToFolder).ENTRYID
After MyItem.Move is executed the new ENTRYID will be returned to the NewEntryID variable.

Related

Move mail folders and subfolders on shared mail box to delete shared folder

I have the following code in Outlook. On my first attempt the deleted mail was sent to my main account inbox and not the shared mailbox.
I would like to
1- pick the shared delete folder by default
2- avoid looping the delete folder
3- speed up the code if possible as size of mail box is > 1 Million mails.
It is error free but I can track the progress.
Dim objNameSpace As Outlook.NameSpace
Dim objMainFolder As Outlook.Folder
Dim olNs As NameSpace
Dim lngItem As Long
Dim Mails_itm As MailItem
Dim myNameSpace As Outlook.NameSpace
Dim myInboxDest As Outlook.Folder
Dim myInboxSc As Outlook.Folder
Dim myDestFolder As Outlook.Folder
Dim myItems As Outlook.Items
Dim myItem As Object
Set objNameSpace = Application.GetNamespace("MAPI")
Set objMainFolder = objNameSpace.PickFolder
Call ProcessCurrentFolder(objMainFolder)
End Sub
ProcessCurrentFolder(ByVal objParentFolder As Outlook.MAPIFolder)
Dim objCurFolder As Outlook.MAPIFolder
Dim objMail As Outlook.MailItem
Dim DeletedFolder As Outlook.Folder
Dim olNs As Outlook.NameSpace
Dim lngItem As Long
On Error Resume Next
Set olNs = Application.GetNamespace("MAPI")
Set DeletedFolder = olNs.GetDefaultFolder(olFolderDeletedItems)
For Each objMail In objParentFolder.Items
i = 0
For lngItem = objParentFolder.Items.Count To 1 Step -1
Set objMail = objParentFolder.Items(lngItem)
If TypeName(objMail) = "MailItem" Then
If ((objMail.ReceivedTime) < DateAdd("yyyy", -7, Date)) Then
objMail.Move DeletedFolder
i = i + 1
End If
End If
DoEvents
Next lngItem
Next
If (objParentFolder.Folders.Count > 0) Then
For Each objCurFolder In objParentFolder.Folders
Call ProcessCurrentFolder(objCurFolder)
Next
End If
End Sub
When placing a question, it is good to check it from time to time and answer the clarification questions, if any...
Supposing that your first required issue means replacing the folder picker option and directly setting objMainFolder, your first code should be adapted as:
Sub ProcessOldMails()
Dim objNameSpace As outlook.NameSpace
Dim objMainFolder As outlook.Folder
Set Out = GetObject(, "Outlook.Application")
Set objNameSpace = Out.GetNamespace("MAPI")
Set objNameSpace = Application.GetNamespace("MAPI")
'Set objMainFolder = objNameSpace.PickFolder 'uncomment if my supposition is wrong
'set the folder to be processed directly, if it is an InBox subfolder:
'Please use its real name instead of "MyFolderToProcess":
Set objMainFolder = objNameSpace.GetDefaultFolder(olFolderInbox).Folders("MyFolderToProcess")
ProcessCurrentFolder objMainFolder, Application
End Sub
In order to make the process faster, you can filter the folder content and iterate only between the remained mails:
Sub ProcessCurrentFolder(ByVal objParentFolder As outlook.MAPIFolder, app As outlook.Application)
Dim objCurFolder As outlook.MAPIFolder
Dim objMail As outlook.MailItem
Dim DeletedFolder As outlook.Folder
Dim olNs As outlook.NameSpace
Dim lngItem As Long, strFilter As String, oItems As items
Set olNs = app.GetNamespace("MAPI")
Set DeletedFolder = olNs.GetDefaultFolder(olFolderDeletedItems)
strFilter = "[ReceivedTime]<'" & Format(DateAdd("yyyy", -7, Date), "DDDDD HH:NN") & "'"
Set oItems = objParentFolder.items.Restrict(strFilter) 'extract only mails older then 7 years
Debug.Print "Mails to be moved to Deleted Items: " & oItems.count 'just to see how many such folders exist
For lngItem = oItems.count To 1 Step -1
oItems(lngItem).Move DeletedFolder
Next lngItem
If (objParentFolder.Folders.count > 0) Then
For Each objCurFolder In objParentFolder.Folders
Call ProcessCurrentFolder(objCurFolder, app)
Next
End If
End Sub
I used app second parameter only because I tried it as an Outlook automation from Excel, and it was easier to insert only two lines...
Please, test the suggested solution and send some feedback. If my understanding was not a correct one, do not hesitate to ask for clarifications, firstly answering my questions from the comment.
Now, I need to go out...
Use the Find/FindNext or Restrict methods to get items that correspond to your conditions instead of iterating over all items in the folder. 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
When you iterate over found items and move them to another folder you must use a reverse loop which allows prevent errors at runtime because decreasing the number of items by calling the Move method leads to decreasing the number of items.
Sub ProcessCurrentFolder(ByVal objParentFolder As outlook.MAPIFolder, app As outlook.Application)
Dim objCurFolder As outlook.MAPIFolder
Dim objMail As outlook.MailItem
Dim DeletedFolder As outlook.Folder
Dim olNs As outlook.NameSpace
Dim lngItem As Long, strFilter As String, oItems As items
Set olNs = app.GetNamespace("MAPI")
Set DeletedFolder = olNs.GetDefaultFolder(olFolderDeletedItems)
strFilter = "[ReceivedTime] < '" & Format(DateAdd("yyyy", -7, Date), "DDDDD HH:NN") & "'"
Set oItems = objParentFolder.items.Restrict(strFilter) 'extract only mails older then 7 years
Debug.Print "Mails to be moved to Deleted Items: " & oItems.count 'just to see how many such folders exist
For i = oItems.Count to 1 Step -1
Set objMail = oItems(i)
objMail.Move DeletedFolder
Next
' it makes sense to move this part to the beginning of the method to process subfolders first
If (objParentFolder.Folders.count > 0) Then
For Each objCurFolder In objParentFolder.Folders
Call ProcessCurrentFolder(objCurFolder, app)
Next
End If
End Sub
See For Each loop: Some items get skipped when looping through Outlook mailbox to delete items for more information.

How do I select an archive folder?

I have an email account "Fred.Smith#domain.co.uk" (domain being made up).
Outlook shows an archive named " Archive - Fred.Smith#domain.co.uk" where Outlook automatically moves emails after a certain period.
Current code:
Set olRecip = olNS.CreateRecipient("Archive - Fred.Smith#domain.co.uk")
olRecip.Resolve
Set olFolder = olNS.GetSharedDefaultFolder(olRecip, olFolderInbox)
This opens the main inbox. How do I select the archive folder?
"Archive" folder is usually at the root level - like inbox
in that case:
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")
Set olArchive = olNameSpace.Folders("myMail#mail.com").Folders("Archive")
For intItem = 1 To olSel.Count
olSel.Item(intItem).Move olArchive
Next intItem
End Sub
to get Inbox you could use default access:
Dim olInbox As Outlook.Folder
Set olInbox = olNameSpace.GetDefaultFolder(olFolderInbox)
Note - This will get you the default Inbox folder, if you have a few accounts in outlook you should verify it's really the folder you want - or use the mail specific approach like in Archive folder above
For Debugging - if you want to check all available subfolders
For i = 1 To olInbox.Folders.Count
Debug.Print olInbox.Folders(i).Name
Next i
Should be
Dim ARCHIVE_FOLDER As Outlook.MAPIFolder
Set ARCHIVE_FOLDER = olNs.Folders("Archive - Fred.Smith#domain.co.uk")
Full Example
Option Explicit
Public Sub Example()
Dim olNs As Outlook.NameSpace
Dim ARCHIVE_FOLDER As Outlook.MAPIFolder
Dim Items As Outlook.Items
Dim i As Long
Set olNs = Application.Session
Dim ARCHIVE_FOLDER As Outlook.MAPIFolder
Set ARCHIVE_FOLDER = olNs.Folders("Archive - Fred.Smith#domain.co.uk") _
.Folders("Inbox")
Debug.Print ARCHIVE_FOLDER.Name
Debug.Print ARCHIVE_FOLDER.FolderPath
Debug.Print ARCHIVE_FOLDER.Store.DisplayName
ARCHIVE_FOLDER.Display
Set Items = ARCHIVE_FOLDER.Items
For i = Items.Count To 1 Step -1
DoEvents
Debug.Print Items(i).Subject
Next
End Sub
MAPIFolder Object

Why can't I access subfolders of a shared mailbox?

My goal is to create a VBA script that fires when a new e-mail arrives to a shared mailbox and does the following things if the title contains specific text:
1. Moves the message to a specified subfolder
2. Saves all Excel attachments to a Desktop folder.
After doing considerable research I came up with the following code and pasted into ThisOutlookSession:
Private WithEvents Items As Outlook.Items
Private Sub Application_Startup()
Dim myOlApp As Outlook.Application
Dim myNms As Outlook.NameSpace
Dim myFolder As Outlook.MAPIFolder
Dim myitem As Object
Dim myRecipient As Outlook.Recipient
Dim myExplorer As Outlook.Explorer
Dim SharedFolder As Outlook.MAPIFolder
Dim oMoveTarget As Outlook.MAPIFolder
Set myOlApp = CreateObject("Outlook.Application")
Set myNms = myOlApp.GetNamespace("MAPI")
Set myFolder = myNms.GetDefaultFolder(olFolderInbox)
Set myExplorer = myOlApp.ActiveExplorer
Set myExplorer.CurrentFolder = myFolder
Set myRecipient = myNms.CreateRecipient("shared mailbox")
Set SharedFolder = myNms.GetSharedDefaultFolder(myRecipient, olFolderInbox)
Set oMoveTarget = SharedFolder.Folders("specific subfolder where messages should be moved")
Set Items = SharedFolder.Items
End Sub
Private Sub Items_ItemAdd(ByVal item As Object)
On Error GoTo ErrorHandler
Dim Msg As Outlook.MailItem
Dim att As Attachment
Dim FileName As String
Dim intFiles As Integer
Dim myOlApp As Outlook.Application
Dim myNms As Outlook.NameSpace
Dim myFolder As Outlook.MAPIFolder
Dim myitem As Object
Dim myRecipient As Outlook.Recipient
Dim myExplorer As Outlook.Explorer
Dim SharedFolder As Outlook.MAPIFolder
Dim oMoveTarget As Outlook.MAPIFolder
If TypeName(item) = "MailItem" Then
Set Msg = item
If InStr(1, item.Subject, "specific text in subject") > 0 Then
For Each att In item.Attachments
If InStr(att.DisplayName, ".xlsx") Then
FileName = "folderpath to desktop location\" & Trim(att.FileName)
att.SaveAsFile FileName
intFiles = intFiles + 1
End If
Next
item.Move oMoveTarget
End If
End If
ProgramExit:
Exit Sub
ErrorHandler:
MsgBox Err.Number & " - " & Err.Description
Resume ProgramExit
End Sub
I get the following error message when I try to run the code manually (F5) or when Outlook is restarted:
Run-time error '-2147221233 (8004010f)':
The attempted operation failed.
An object could not be found.
The line where the running is stopped is when the specific subfolder (oMoveTarget) is set in Private Sub Application_Startup().
If I omit (or comment out) the reference to a subfolder, the script works: Excel attachments from incoming e-mails to the shared mailbox with a specific subject are saved.
I am allowed to access and run a script on a shared mailbox, but I am denied access to its subfolders.
Is "Download shared folders" check box checked on the Advanced tab of your Exchange account properties dialog?
Try to uncheck it.

Error trying to save email attachment

I try to write some VBA to save the attachment files from some email to a folder
But I get the error
Run Time Error '424'
Object Required
This is the code I am trying to use
Sub test_extraer()
Dim olApp As Outlook.Application
Dim objNS As Outlook.NameSpace
Set olApp = Outlook.Application
Set objNS = olApp.GetNamespace("MAPI")
Set Items = objNS.GetDefaultFolder(olFolderInbox).Items
If (Msg.SenderName = "sender#email.com") And _
(Msg.Subject = "subject of the email") And _
(Msg.Attachments.Count >= 1) Then
'Set folder to save in.
Dim olDestFldr As Outlook.MAPIFolder
Dim myAttachments As Outlook.Attachments
Dim Att As String
Const attPath As String = "C:\temp\"
Set myAttachments = item.Attachments
Att = myAttachments.item(1).DisplayName
myAttachments.item(1).SaveAsFile attPath & Att
End If
End Sub
The error is triggered when the script enter to this if
If (Msg.SenderName = "sender#email.com") And _
(Msg.Subject = "subject of the email") And _
(Msg.Attachments.Count >= 1) Then
Any advice
Thanks in advance
Ok... where to start.
You definitely have some basic issues you need to work out here. You have a couple of variables that are not declared. The first of which is the cause of your title. msg in context is most likely supposed to be an Outlook.MailItem. Just declaring that variable is not the sole source of your problems. Next you have item which much like msg in context should be an Outlook.MailItem. You are missing a loop that would navigate through all the items in the Inbox as well.
So you are just trying to navigate the Inbox looking for a particular item correct? Just adding the loop would create another issue. Some of the items in the inbox are not mail items. To address this we navigate every object in the inbox and examine every mailitem we come across. If that matches the criteria of sender,subject and number of items we proceed to .SaveAsFile to the destination directory.
Sub Test_ExtraER()
Const strAttachmentPath As String = "C:\temp\"
Dim olApp As Outlook.Application
Dim objNS As Outlook.NameSpace
Dim objFolder As Outlook.MAPIFolder
Dim objItem As Object
Dim strFileName As String
Set olApp = Outlook.Application
Set objNS = olApp.GetNamespace("MAPI")
Set objFolder = objNS.GetDefaultFolder(olFolderInbox)
For Each objItem In objFolder.Items
If TypeName(objItem) = "MailItem" Then
If (objItem.Attachments.Count >= 1) And (objItem.Subject = "Some Subject") And (objItem.SenderName = "sender#email.com") Then
With objItem.Attachments.Item(1)
strFileName = strAttachmentPath & .DisplayName
Debug.Print strFileName
.SaveAsFile strFileName
End With
End If
End If
Next
End Sub
This is mostly preference but, as you can see, I made some other coding changes. I renamed some of the other variables to be a little more descriptive of the object it was. Also moved all the Dims and Const together for better readability.
One last thing. It would seem you are navigating you entire inbox looking for a small subset of mails. You could create a rule that would process these mails as they come into your mailbox. An example of this would be: Save Outlook attachment to disk
Sub test_extraer()
Dim olApp As Outlook.Application
Dim objNS As Outlook.NameSpace
Dim MailItems As Outlook.MAPIFolder 'Add this one
Dim Msg As Outlook.MailItem 'Add this one
Set olApp = Outlook.Application
Set objNS = olApp.GetNamespace("MAPI")
Set MailItems = objNS.GetDefaultFolder(olFolderInbox)
For Each Msg In MailItems.Items 'loop thru the inbox folder to match the exact sender name and subject
If (Msg.SenderName = "Sender Name Here") And _
(Msg.Subject = "Subject Here") And _
(Msg.Attachments.Count >= 1) Then
'Set folder to save in.
Dim olDestFldr As Outlook.MAPIFolder
Dim myAttachments As Outlook.Attachments
Dim Att As String
Const attPath As String = "C:\temp\"
Set myAttachments = Msg.Attachments
Att = myAttachments.Item(1).DisplayName
myAttachments.Item(1).SaveAsFile attPath & Att
End If
Next
End Sub

Get email from non default inbox?

I am using the following vba code to get emails from my inbox folder and move them to a sub folder called suppliers. At the moment the emails are moved from my default email inbox, but I have an account called purcashing#hewden.co.uk and I want it to get the emails from this inbox and move it to the subfolder called Suppliers in this account.
can someone show me how I would alter GetDefaultFolder to make this happen. thanks
Sub MoveItems()
Dim myNameSpace As Outlook.NameSpace
Dim myInbox As Outlook.Folder
Dim myDestFolder As Outlook.Folder
Dim myItems As Outlook.Items
Dim myItem As Object
Set myNameSpace = Application.GetNamespace("MAPI")
Set myInbox = myNameSpace.GetDefaultFolder(olFolderInbox)
Set myItems = myInbox.Items
Set myDestFolder = myInbox.Folders("Supplier")
Set myItem = myItems.Find("[Subject] = 'Introduction'")
While TypeName(myItem) <> "Nothing"
myItem.Move myDestFolder
Set myItem = myItems.FindNext
Wend
End Sub
Instead of using Namespace.GetDefaultFolder, retrieve the appropriate store from the Namespace.Stores collection and use Store.GetDefaultFolder.
I just used Dmitry's suggestion and it works like a charm.
Hope it helps \o/
Sub GetEmailFromNonDefaultInbox()
Dim myOlApp As New Outlook.Application
Dim myNameSpace As Outlook.Namespace
Dim myInbox As Outlook.MAPIFolder
Dim myitems As Outlook.Items
Dim strFilter As String
' let the user choose which account to use
Set myAccounts = myOlApp.GetNamespace("MAPI").Stores
For i = 1 To myAccounts.count
res = MsgBox(myAccounts.Item(i).DisplayName & "?", vbYesNo)
If res = vbYes Then
Set myInbox = myAccounts.Item(i).GetDefaultFolder(olFolderInbox)
Exit For
End If
Next
If myInbox Is Nothing Then Exit Sub ' avoid error if no account is chosen
' query emails by subject
strFilter = "#SQL=""urn:schemas:httpmail:subject"" like '%YOUR SUBJECT%'"
Set myitems = myInbox.Items.Restrict(strFilter)
' show some feedback if no email is found
If myitems.count = 0 Then
MsgBox "Nothing found. Try another account."
Exit Sub
End If
' get the most recent email
myitems.Sort "ReceivedTime", True
Set myitem = myitems.GetFirst
If myitem.Class = olMail Then
' and now you can do whatever you want
MsgBox (myitem.Subject)
End If
End Sub