Outlook VBA AppointmentItem.Move creating a copy - vba

When using the Move method on an AppointmentItem in an Outlook macro, I lose the ability to receive updates because it is creating a copy of the item instead of truly moving it. This behavior causes the item to no longer be linked with the original and will not retain item updates as a result.
I want to replicate through VBA the cut/paste behavior you get which is able to maintain the original object and does not cause updates to be lost.
I believe this has something to do with the GlobalAppointmentID based on searching around, however I have not been able to find a way to actually move the appointment.
The code I'm using is below. GetFolderFromPath is a helper function to just return a folder object from the path, which works perfectly well.
Sub MoveItem()
Dim targetPath As String: targetPath = "\\tnolan#microsoft.com\Calendar\OOFS"
If Application.ActiveExplorer.Selection.Count = 0 Then
MsgBox ("No item selected")
Exit Sub
Else
Dim targetFolder As Outlook.Folder
Set targetFolder = GetFolderFromPath(targetPath)
For x = 1 To Application.ActiveExplorer.Selection.Count
Dim oSelected As Variant
Set oSelected = Application.ActiveExplorer.Selection.Item(x)
If oSelected.Class = olAppointment Then
Dim NS As NameSpace: Set NS = Application.GetNamespace("MAPI")
Dim oAppt As AppointmentItem: Set oAppt = NS.GetItemFromID(oSelected.EntryID)
oAppt.Move targetFolder
Set oAppt = Nothing
Set NS = Nothing
End If
Set oSelected = Nothing
Next x
Set targetFolder = Nothing
End If
End Sub

Outlook processes incoming meeting updates/deletions only against the default Calendar folder. If you move an appointment to a different folder, meeting update in your Inbox will create a new appointment in the default Calendar folder.

After playing around with my code for a little bit, I've found that this code works for me in a similar situation:
oAppt.CopyTo(targetFolder, olCopyAsAccept)
oAppt.Delete
I have a feeling that for some reason the AppointmentItem.Move command passes as olCreateAppointment which would always create a new GlobalAppointmentID.
However, this still has a side-effect of responding accept to the Appointment.

Related

VBA Outlook - Some MailItems produce runtime error 430

I am currently working on a simple VBA macro wich collects some metadata (e.g. EntryId, ReceivedTime, Recipients etc...) of mails in an Outlook mailbox.
To accomplish this it iterates through all folders recursively and collects the data from MailItems in every folder.
But I'm getting errors, which are not restricted to the same object (sometimes the error pops up earlier, but never later), stating the object does not support automation (runtime error 430).
The strange thing is, that roughly 14000 MailItems are processed without failure and usually at number 14232 it crashes.
I have two questions regarding this error:
I am working on a non local mailbox, therefore only a part of the data should be cached in the local .ost file.Could data missing in the cache be the cause for the error?
And if the cache is not the problem, then what is wrong with my code?
A simplified version of the code:
(Please note that all non MailItem objects are ruled out via an explicit typecheck)
Sub cache()
Dim objOl As Outlook.Application
Dim objNs As Outlook.NameSpace
Dim folder As Outlook.MAPIFolder
Dim vFolders As Outlook.Folders
Set objOl = New Outlook.Application
Set objNs = objOl.GetNamespace("MAPI")
Set vFolders = objNs.Folders
'This is where we're looking for the mailbox to work with
For i = 1 to vFolders.count
If StrComp(vFolders(i), "The Mailbox") = 0 Then
walk vFolders(i)
End If
Next
End Sub
Sub walk(folder As Outlook.MAPIFolder)
Dim item As Object
Dim vItems As Outlook.Items
Set vItems = folder.Items
If vItems.count > 0 Then
For i = 1 to vItems.Count
Set item = vItems(i)
If item.class = 43 Then
'This is where the debugger shows the runtime error 430
Debug.Print item.EntryID & vbCrLf & item.ReceivedTime
End If
Next
End If
Dim vFolders as Outlook.Folders
Set vFolders = folder.Folders
If (vFolders.count > 0) Then
For i = 1 To vFolders.Count
walk vFolders(i)
Next
End If
End Sub
UPDATE:
I updated the code according to the suggestions. No multi-dot notation and no For Each loops, the performance increased but the problem keeps occuring at the exact same item, as soon as I try to access data like (subject, entryID or else).
Since your error is happening in the same mailitem every time, I would validate what item 14232 is. From my experience just because it validates as enum 43 (or olMail) doesn't mean that all of the data will be valid. Is there anything special about 14232?
Edit:
I am currently working on a project using vb and outlook mailitems. I just identified the Item.MessageClass property defines the sub mailitem type. When I attempt to cast a message with a MessageClass other than IPM.Note it will give me a 430 error. Some of the MessageClass values that have given me problems include IPM.Note.Rues.ReplyTemplate.Microsoft and IPM.Note.Rules.OofTemplate.Microsoft. When I break on these messages I can see that most of the item's properties are not available. I would add an if check on your loop like this:
If item.class = 43 then
If item.messageclass = "IPM.Note" Then
Debug.Print item.EntryID & vbCrLf & item.ReceivedTime
End If
End If
this will then only print the info for normal messages. You may want to do some debugging on the MessageClass properties that you are currently able to process and see if they are all IPM.Note or if you can pinpoint the sub-type that is causing your problem.
Note: I do see that these mailitems still have a valid EntryID and ReceivedTime so I am not sure what the problem might be. What line of the code is your error occurring? The assignment of vItems(index) to Item? or is it somewhere else?
Firstly, avoid using multiple dot notation. Secondly, try not to use "for each" loops - they keep the collection items referenced until the loop exits. Do not use MailItem.Close - it does nothing unless you are actually showing the item in an Inspector.
dim vItems as Outlook.Items
vItems = folder.Items
for I = 1 to vItems.Count
set item = vItems.Item(I)
if item.Class = 43 Then
Debug.Print item.EntryID & vbCrLf & item.ReceivedTime
End If
set item = Nothing
Next

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.

RDO Session - loop through entire Inbox and move emails

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

outlook macro - why doesn't this delete all items from deleted folder?

I have the following macro in outlook to clear my deleted folder. its strange as it doesn't seem to delete all entries. I have to run this a few times for it to clear to deleted items folder. (usually 2 or 3 times). Each time the number of deleted items in the folder does get reduced but I don't understand why everything doesn't get wiped out in the first go.
Here is my code; Anything wrong?
Public Sub EmptyDeletedEmailFolder()
Dim outApp As Outlook.Application
Dim deletedFolder As Outlook.MAPIFolder
Dim item As Object
Dim entryID As String
Set outApp = CreateObject("outlook.application")
Set deletedFolder = outApp.GetNamespace("MAPI").GetDefaultFolder(olFolderDeletedItems)
For Each item In deletedFolder.Items
item.Delete ' Delete from mail folder
Next
Set item = Nothing
Set deletedFolder = Nothing
Set outApp = Nothing
End Sub
Try:
For i = deletedFolder.Items.Count To 1 Step -1
deletedFolder.Items(i).Delete '' Delete from mail folder
Next
There can be problems with deleting items from a collection.
By deleting the objects in the collection "underneath" the iterator, it couldn't really go over each item in the collection because the collection was changing. Remou came up with a really good way that will be guaranteed to continuously delete items in the collection as long as there are items left. Just don't be deleting items yourself while the method runs or you could run into prolems.

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.