Outlook 2010: Move MailItems After Sending
Use VBA code to organize MailItems after sending
June 29, 2012
Microsoft Outlook’s default behavior is to retain a copy of sent items in the Sent Items folder. But in a business environment, in which hundreds of email messages might be dispatched each day, such a generic setup might not be satisfactory. Many people want to organize email messages by topic or line of business (LOB), which isn't the same as sorting by conversation, as each LOB can contain numerous conversation threads. Although moving received messages to a specified folder is simple enough, doing so with sent messages is another matter. The primary stumbling block is that, although Outlook provides an option for moving a copy of a MailItem after sending, it does not offer a rule or option setting to move the sent item itself.
Related: Outlook 2010: Reply to Autoforwarded Emails
Many an Outlook user has attempted to come up with a way to move messages after sending, usually relying on complicated timed processes and bug-prone Windows API calls via a third-party DLL such as Outlook Redemption. After some thought and a little experimentation, I have come up with a couple of Visual Basic for Applications (VBA) macro solutions that should suit your needs nicely.
Limitations of the Rule-Based Approach
When confronted by behavior that doesn't conform to my expectations or preferences, I tend to be a little hasty in looking for programmatic solutions. (It's the developer in me!) But before jumping into VBA code with both feet, I try to check out the Outlook Email options and rules. Only after you've exhausted those avenues should you go looking for more complex fixes. I followed my own advice in this instance.
Related: Q: How can I have Microsoft Outlook 2010 ignore future messages in an email thread?
Let's see what we can do with the Outlook Rules Wizard. If you start with a blank rule, you can choose whether to apply the rule to incoming or outgoing messages, as Figure 1 shows. After you select the conditions to identify the messages that you're looking for, you can select a folder to which to copy those messages, as Figure 2 shows.
Figure 1: Using the Rules Wizard to apply a rule on sent items
Figure 2: The Rules Wizard Select Folder dialog box
However, note that this rule creates and moves a message copy; the original message is left in the Sent Items folder. The only way to avoid this is to clear the Save copies of messages in Sent Items folder check box (in the Message handling section of the E-mail Options dialog box), which Figure 3 shows. The downside to this approach is that it prevents Outlook from keeping copies of any sent messages, so you won't have access to sent messages that aren't picked up by your rule.
Figure 3: The E-mail Options dialog box
VBA Fix: Using the MailItem's Send Event
Dissatisfied with the rules-and-options route, I rolled up my sleeves, made a pot of coffee and got to work.
Like those who have tread these murky waters before me, I began with the MailItem_Send() event. The approach seems straightforward enough: You send an email, and then move it. Only one problem: The email isn't moved into the Sent Items folder until the Send event has completed. Hence, any attempt to find the message in the Sent Items folder proves fruitless. Start on second coffee. Think harder.
Where to Put the Code?
One key factor in event-driven programming is where to place the event-handling code. Make the wrong choice, and you could have a brittle and flaky application on your hands. There might be more than one candidate, but more often than not, one choice is better than the others.
One event that crossed my mind is my destination folder's ItemAdd() event. This event fires whenever one or more items are added to its Items collection. But upon further investigation, I realized that this event is faced with the same timing problems as the MailItem_Send() event. You'd also need to duplicate the same code for all your destination folders. Duplication of code isn’t considered good style, so forget about that idea.
It seemed that the MailItem_Send() event was still my best choice for the code, since I could apply my rule to all outgoing messages. I just needed to approach the problem from a different angle.
Filtering Messages by Criteria
In a perfect world, we could call our VBA macro from a rule. That way, the rule would perform the mail filtering and the macro would take care of moving the message. Outlook 2002 added an option to run a script on incoming messages, but unfortunately, there's no such option for outgoing messages. No reason to get upset, though; that's an inefficient way to launch script code anyway. I've tried it a few times and found it to be highly unreliable. Every now and then, it causes an error and the rule is deactivated. We'll do our filtering right in the oMsg_Send() event.
Let's say that I have several business contacts at RobGravelleAndCo.com and want to move all messages addressed to those contacts to an Outlook folder called FOSS Export (CR-035), which Figure 4 shows. The Recipients object contains a collection of Recipient items, each of which contains the properties and methods that relate to one recipient. One Recipient property is the AddressEntry object, which houses the recipient's address details, including email address. The Recipient has a property called Address for the email address. We'll examine that property in the oMsg_Send code at callout B in Listing 1.
Figure 4: Outlook Folders
Instead of trying to delete a message manually after sending it, we can set the MailItem's DeleteAfterSubmit flag to true so that Outlook does it for us. Just keep in mind that turning on the DeleteAfterSubmit flag via the MailItem Properties dialog box will delete all sent messages! That's a bit of a sledgehammer solution when all you want to do is move certain messages.
On the subject of moving messages, you can't move the message from within the MailItem_Send() event because Outlook isn't done with it yet. (Attempting to do so results in a nasty runtime error.) According to Microsoft, the preferred way to manage this delicate operation is first to use the Copy() function to clone the message, then to move the clone. Although this is not a true message move, the result is the same: After the clone is moved, the original message is deleted, thanks to the DeleteAfterSubmit flag.
Now we need a reference to our folder. Working with custom folders is a bit more work than using Outlook's default folders. You can't just use the folder name to call the GetFolder function (there isn't one). Instead, we need to navigate to the custom folder from one of the default folders. In our case, the FOSS Export (CR-035) folder is parallel to the Inbox, in the mailbox root. To obtain a reference to a default Outlook folder, simply call the Application.Session GetDefaultFolder() function with one of the olDefaultFolders Outlook Library enumeration values. For example, the following code retrieves the Inbox:
Set olInbox = Application.Session.GetDefaultFolder(olFolderInbox)
We can get to our folder by using this code:
Set oBusinessFolder = Application.Session.GetDefaultFolder(olFolderInbox).Parent.Folders(BUSINESS_FOLDER)BUSINESS_FOLDER is a constant for our folder name. The oBusinessFolder can be passed directly to the MailItem.Move() sub, as it requires a MAPIFolder object. Similarly, we can get a folder's subfolder via its Folders collection property:Set ObjFolder = Application.Session.GetDefaultFolder(olFolderInbox).Folders("")
The Visual Basic Editor
All Microsoft Office applications come with a full-featured IDE called the Visual Basic Editor. It provides an interface for accessing application object models through code so that you can call object methods, set object properties, and respond to object events. The code that's used to accomplish these goals is VBA, a specialized subset of the Visual Basic language.
A Developer tab is available on the Office Ribbon, to access the Visual Basic Editor and other developer tools. However, this tab is disabled by default to help protect against viruses and other malicious code. You need to perform the following steps before you can use this tab:
1. In Outlook, select Outlook Options from the File tab to open the Outlook Options dialog box.
2. In the Outlook Options dialog box, click Trust Center.
3. Click Trust Center Settings, and then choose the Macro Settings option on the left.
4. Select the Macro security level that suits your comfort level, keeping in mind that the setting pertains to other people's macros as well as your own. If you don't want to give all macros carte blanche, you can have Outlook display a prompt each time a macro is about to run. That way, you can decide whether you want to let the macro run. That option is called Notifications for all macros.
5. Restart Outlook for the changes to take effect.
The Visual Basic button, which Figure 5 shows, will be on the far left in the Developer tab. Figure 6 shows the Visual Basic Editor.
Figure 5: Developer tab with Visual Basic button
Figure 6: Visual Basic Editor with Immediate window visible