Create a mail object and track it down in sent folder - vba

I need to create a mail via VBA, send it, and then export a .msg file of the sent mail to be archived (I know, it's weird, but that's what the boss asked for).
Creating the mail is straightforward:
Set OLK = Outlook.Session
Set ML = OLK.Createitem olMailItem
With ML
.Recipients.add "somebody#somedomain.com"
.Subject = "Great mail you have there"
.Body = "It would be a shame if somebody couldn't archive it"
End with
ML.Send
Problem is, after sending the mail is moved in the sent folder and the ML object points to nothing.
I could use the .saveas method before sending, but then the saved file is the unsent version, which can be edited and sent again.
How can I trace the mail in the sent folder?
The "brute force" way I found out implies saving the ConversationIndex before sending
IDX= ML.ConversationIndex
and then scan all the items in the sent folder for it:
For each ML in OLK.Session.GetDefaultFolder(olFolderSentMail).Items
If ML.ConversationIndex = IDX Then ML.SaveAs HomeDir & "\" & OutFileName: Exit For
Next
but it isn't exactly a smooth work.... (and may fail if some smartass replies to the automatic mail, even if nobody should)

Max,
You can handle the ItemAdd event of the Items class which belongs to the Sent Items folder. It is fired when one or more items are added to the specified collection.
In the ItemAdd event handler you can check out whether a particular item should be saved or not. For example, you can add a user property before calling the Send method. See the UserProperties class for more information.
Set myProp = myItem.UserProperties.Add("MyPropName", olText)
Be aware, the MailItem class provides the SaveSentMessageFolder property which allows to set a Folder object that represents the folder in which a copy of the e-mail message will be saved after being sent. So, you can assign a custom folder to save them.

You can add a user property (MailItem.UserProperties.Add), but that would cause the message to be sent in the TNEF format unless the UseTnef property (DASL name http://schemas.microsoft.com/mapi/id/{00062008-0000-0000-C000-000000000046}/8582000B) is explicitly set to false.
You can set a named MAPI property using PropertyAccessor.SetProperty - just pick your custom GUID and property name. E.g.
ML.PropertyAccessor.SetProperty("http://schemas.microsoft.com/mapi/string/{3ADE3813-37A9-49C9-AD84-D49C8FF5D660}/MyOwnProp", "SomeValue")

Related

Reprocessing Outlook Undelivered Mail

I have an Exchange mailbox with a bunch of Outlook ReportItem Undelivered messages. I am attempting to reprocess the undelivered messages via an Outlook VBA script by invoking the "SendAgain" operation on the ReportItem messages. My issue is that the ReportItem does not have a send method, so I have no way of actually sending the reprocessed messages. I am using the following code to go through the messages:
Dim objApp As Outlook.Application
Dim objNameSpace As NameSpace
Dim journalAlertInbox As Folder
Dim objInspector As Inspector
Dim resendItem As ReportItem
Set objApp = CreateObject("Outlook.Application")
Set objNameSpace = objApp.GetNamespace("MAPI")
Set journalAlertInbox = objNameSpace.Stores.Item("thestore").GetDefaultFolder(olFolderInbox)
For Each folderItem In journalAlertInbox.Items
If TypeOf folderItem Is ReportItem Then
folderItem.Display
Set objInspector = folderItem.GetInspector
objInspector.CommandBars.ExecuteMso "SendAgain"
Set resendItem = Application.ActiveInspector.CurrentItem
Set objInspector = resendItem.GetInspector
''how do I send the item that is now displayed?
''resendItem.Close olSave
folderItem.Close olDiscard
End If
Next folderItem
I thought I might be able to save the displayed message as a draft, however If I uncomment the resendItem.close olSave line this results in a message in my Outlook Drafts folder of type ReportItem. I can open up the saved draft message it the Outlook GUI and click the send button, but I do not see a way to actually invoke the send operation programmatically. Examining the message in drafts shows it to be of type ReportItem, which does not have a .Send method.
How can I invoke the "Send" operation on the Report Item? I can clearly see the "Send" button, but there seems to be no programmatic way of actually clicking it.
OOM does not expose any functionality that allows to link a ReportItem object to the original MailItem, and, generally, there might not be any kind of link between the two. The best you can do is to retrieve PR_ORIGINAL_SEARCH_KEY MAPI property (or PR_REPORT_TAG, which includes both the search key and the store/Sent Items folder entry id) using ReportItem.PropertyAccess.GetProperty and try to find a matching message in the Sent Items folder. You can see these properties in OutlookSpy (I am its author).
Keep in mind that OOM does not allow to search on the binary (PT_BINARY) properties in Items.Find/Restrict.
If using Redemption is an option (I am also its author), it exposes RDOReportItem.FindOriginalItem method.
Once you have the original item, you can make a copy and try to send it again.
The ReportItem doesn't represent the original item which is failed to be sent. Also it doesn't contain any relationship with the original mail item, so you will not find any property or method available in the Outlook object model. Your existing solution looks good.
You may also try using the ReportItem.GetConversation method which obtains a Conversation object that represents the conversation to which this item belongs. So, you may try getting the previous item from the conversation, it could be the original item which has been submitted.

Reply, with template, using properties of original mail

I work at a Company that gets requests we filter in terms of
being eligibile for financing from our financing partner
requiring more information from the enquirer
There is a lot of monotonous work, in copy pasting email replies.
I looked into creating a Quick Action in Outlook, but because our mother company does not allow certain freedoms, like the Font has to be specifically Segoe Ui Light etc. I could not use this, so I thought about writing a macro.
I intended for a macro button to:
Open a new Reply email, replying to all in the original mail.
In this new email text body, use a template email that I already made and saved as a template. (This way it saved the Font, size and other formatting.)
Put in the original Sender and CC'ed mail addresses as well as the Subject from the original mail.
And then display the email, so I could make edits if I wanted to before sending.
Sub ReplyGewerbeanmeldung()
Dim origEmail As MailItem
Dim replyEmail As MailItem
Set origEmail = Application.ActiveWindow.Selection.Item(1)
Set replyEmail = Application.CreateItemFromTemplate(" "C:\Users\XYZ\AppData\Roaming\Microsoft\Templates\ReplyGewerbeanmeldung.oft"\ReplyGewerbeanmeldung.oft")
replyEmail.To = origEmail.Sender
replyEmail.CC = origEmail.CC
replyEmail.Subject = origEmail.Subject
replyEmail.HTMLBody = replyEmail.HTMLBody & origEmail.Reply.HTMLBody
replyEmail.Display
End Sub
First of all, it seems you need to correct the file path to the template message. The following string is not a valid file path:
" "C:\Users\XYZ\AppData\Roaming\Microsoft\Templates\ReplyGewerbeanmeldung.oft"\ReplyGewerbeanmeldung.oft"
Try to use the following string instead:
Set replyEmail = Application.CreateItemFromTemplate("C:\Users\XYZ\AppData\Roaming\Microsoft\Templates\ReplyGewerbeanmeldung.oft")
You can read more about that in the How To: Create a new Outlook message based on a template article.
Anyway, creating items from a template will not preserve the original message body. Moreover, in Outlook you will not get any visual appearance that a particular items was replied. So, you need to call the ReplyAll method on the original item in Outlook to avoid this artefacts in Outlook.
You can create a new mail item, but only for copying the message body response which is required to paste into the reply. The NameSpace.OpenSharedItem method can be used to to open iCalendar appointment (.ics) files, vCard (.vcf) files, and Outlook message (.msg) files. You may consider using it instead of creating a new item based on the template to grab the message body from there.

How to bulk export Attachments from emails (which are emails) to another folder within Outlook

I need to extract .msg attachments from emails in a range and save these into another outlook sub-folder. This works currently by dragging the attachment into a sub-folder of 'inbox' but is there a quicker way?
I have searched around a bit and found ways to extract them to a local folder but i need them to be contained within outlook.
I appreciate any help and suggestions.
Thanks.
There are two problems here - first is accessing embedded message attachments without saving them first as MSG file. Second is importing the MSG files back - you can use Application.CreateItemFromTemplate, but the item will be unsent. You can use Namespace.OpenSharedItem, and then use MailItem.Move, but it is still a kludge.
There issn't much much you can do in OOM alone. Extended MAPI would work, but it is C++ or Delphi only. If using Redemption is an option (I am its author), you can use EmbeddeedMsg property exposed by the Redemption RDOAttachment object. You can also use RDOMail.CopyTo and pass a folder as a parameter to copy an embedded message attachment to a folder:
Set Session = CreateObject("Redemption.RDOSession")
Session.MAPIOBJECT = Application.Session.MAPIOBJECT
set redItem = Session.GetMessageFromId(OutlookMessage.EntryID)
set redFolder = Session.GetFolderFromId(OutlookFolder.EntryID)
for each attach in redItem.Attachments
if attach.Type = olEmbeddeditem Then
attach.EmbeddedMsg.CopyTo OutlookFolder
End If
next

Moving an attachment from an email into an Outlook folder

I'm trying to take attachments from mails received and move them into a folder within Outlook.
I can move the entire message, and I've also worked out how to save the attachments to a drive, but neither of these things is what I'm looking for.
I was looking at something along the lines of the below, but I'm guessing there is no Attachment.Move similar to MailItem.Move.
Sub test1()
Dim olFolder As MAPIFolder
Set olFolder = Application.GetNamespace("MAPI").Folders("Mailbox - Test").Folders("Inbox")
Dim Item As Object
For Each Item In olFolder.Items
Set oMail = Item
For Each att In oMail.Attachments
att.Move Application.GetNamespace("MAPI").Folders("Enterprise Connect").Folders("Test")
Next
Next
End Sub
The attachments do not exist as standalone entities in folders - what you see if a message with a single attachment. The item's message class is IPM.Document.* - when you double click on an item like that, Outlook is smart enough to open the attachment instead of showing an inspector. Take a look at such an item with OutlookSpy (I am its author - click IMessage and Item buttons).
Outlook Object Model does not allow to create DocumentItem objects directly. But you can create a regular MailItem object, add an attachment using MailItem.Attachments.Add, then reset the MessageClass property appropriately - e.g. for a ".txt" attachment, look up HKEY_CLASSES_ROOT\.txt registry key, read the default value, append it to message class (IPM.Note.txtfile).
If using Redemption (I am also its author) is an option, it exposes the RDODocumentItem and allows to create document items directly (see the examples).
The Attachment class doesn't provide such methods. You need to save the attached file to the disk and then re-attach it anew to another Outlook item.
You may find the Getting Started with VBA in Outlook 2010 article helpful.

Getting new email senders address

I composed a new email. Before sending this email, I would like (using VBA) to get the senders email address.
I wrote the following example code. When I run this code, the first message box displays the email subject correctly, however the second message box shows nothing (the message box is empty).
Sub email_test()
Dim eSubject As String
Dim eSender As String
eSubject = Application.ActiveInspector.currentItem.subject
MsgBox eSubject
eSubject = Application.ActiveInspector.currentItem.SenderEmailAddress
MsgBox eSender
End Sub
A new mail item doesn't have the Sender* related properties set. They are set right after the message is processed by the transport provider and can be get, for example, from the Sent Items folder. You can handle the ItemAdd event of the Items class which comes from the Sent Items folder. Be aware, the SaveSentMessageFolder property of the MailItem class can be used to set a Folder object that represents the folder in which a copy of the e-mail message will be saved after being sent. Also the DeleteAfterSubmit property can be set, in that case a copy of the mail message is not saved after being sent.
You may be interested in the SendUsingAccount property which allows to get or set set an Account object that represents the account under which the MailItem is to be sent.
Use MailItem.SendUsingAccount.SmtpAddress. If MailItem.SendUsingAccount is null, you can asssume the default account will be used - the address can be accessed from Application.Session.CurrentUser.Address. In case of an Exchange mailbox, use Application.Session.CurrentUser.AdderssEntry.GetExchangeUser.PrimarySmtpAddress