Moving an attachment from an email into an Outlook folder - vba

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.

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.

How to automatically print HTML attachments in outlook 32bit or 64bit

When a customer places an order from the site, an email with an attached file (HTML format) is sent to the management for his order and we want to print it automatically through Outlook, I searched all over the internet but the script I did not find a suitable one for this job.
Can anyone create a VBA Scriptfor Outlook 2016 32bit or Outlook 2019 64bit that can be saved and printed directly when a new email contains an attachment?
I tried many scripts but they did not help me and could not print the HTML file automatically, I made all the necessary settings but the script does not work.
To handle incoming emails in VBA you can handle the NewMailEx event of the Outlook Application class. The NewMailEx event fires when a new message arrives in the Inbox and before client rule processing occurs. This event fires once for every received item that is processed by Microsoft Outlook. The item can be one of several different item types, for example, MailItem, MeetingItem, or SharingItem. The EntryIDsCollection string contains the Entry ID that corresponds to that item. Use the Entry ID returned in the EntryIDCollection array to call the NameSpace.GetItemFromID method and process the item.
After an item is retrieved you could check the Attachments.Count property and decide whether to print it or not.
To print the content of the incoming email item you can use the MailItem.PrintOut method which prints the Outlook item using all default settings. The PrintOut method is the only Outlook method that can be used for printing.
But to print the attached HTML file you need to save it on the disk first and then call the print verb against the saved file, for example:
Sub PrintFile(FileToPrint as string)
Dim objShell
Set objShell = CreateObject("Shell.Application")
If FileToPrint = "" Then
GoTo MainLoop
Else
Debug.Print FileToPrint
objShell.Namespace(0).ParseName(FileToPrint).InvokeVerb ("Print")
End If
MainLoop:
Debug.Print "No files to print"
End Sub

Outlook VSTO Add-In using a Form and Button - Forward Email Won't Display and Can't get Button on Home Ribbon

I'm new to creating Add-In's for Outlook, but have managed to crash and bash my way through most of what I'm trying to do. I have been trying in C# as most examples and documentation use C#, however I'm not that comfortable writing in C#. I have used VBA quite a lot in the past so VB.Net is easier for me to use.
So far my Add-In does the following:
Adds a button when reading an email (OfficeID set to TabReadMessage).
When you click the button a form is displayed which asks for some information.
There's a button on the form to continue which validates the form and identifies the current MailItem.
Using the current MailItem, it appends some text and then attempts to Forward the email with the revised text.
Where I'm stuck is with the Forward() function. I can update the To and Subject, but I can't Display() it.
I think I'm ok up to here
' Create an application object which is needed to get to the ActiveInspector
Dim application As Outlook.Application '
application = Globals.ThisAddIn.Application
' Variables to inspector, needed to get the current item, current mail item, the forward email (so we can update the subject)
Dim mailItem As Microsoft.Office.Interop.Outlook.MailItem
Dim forwardMailItem As Microsoft.Office.Interop.Outlook.MailItem
Dim oInspector As Microsoft.Office.Interop.Outlook.Inspector
Dim currentSubject As String
Dim forwardSubject As String
oInspector = application.ActiveInspector
mailItem = oInspector.CurrentItem
' forward email gets created here
forwardMailItem = mailItem.Forward()
currentSubject = mailItem.Subject
forwardSubject = "some other stuff added - " & currentSubject
If I do a MsgBox at this point and reference forwardMailItem.Subject, the new MailItem must be there as it displays the revised subject.
Once I add the following line, it's like the MailItem object has gone - ie if I do MsgBox(forwardMailItem.Subject) nothing happens.
forwardMailItem.Display()
If I remove the above line and just put the following, the email gets sent.
forwardMailItem.Send()
Part 1 - I can't get the forwardMailItem to display and don't understand why?
Part 2 - I don't know how to get the new Ribbon I created to also show when on the Home Ribbon. I found how to change the OfficeID to show it on TabReadMessage instead of the default TabAddIn, but don't know what to search for to find how to add it to the Home Ribbon.

Is it possible to find related emails and loop the results in the background?

I'd like to find related emails to the email I have currently selected. Then I want to loop the results.
Using the ActiveExplorer.Search takes a moment, and at the same time the code keeps running. So it doesn't return any results, because of loading still happening in the background, I guess.
So my questions are:
How do I find related emails?
How do I loop the search results (in the background)?
To find related emails, maybe something like this:
Sub FindRelatedEmails()
Dim ns As Outlook.NameSpace
Set ns = myOlApp.GetNamespace("MAPI")
Dim oMail As Outlook.MailItem
Set oMail = ActiveExplorer.Selection.Item(1)
Dim strFrom As String
strFrom = oMail.SenderName
Dim strSubject As String
strSubject = oMail.ConversationTopic
Dim myOlApp As New Outlook.Application
Set myOlApp.ActiveExplorer.CurrentFolder = ns.GetDefaultFolder(olFolderInbox)
Dim txtSearch As String
txtSearch = "[Konversation]:=""" & strSubject & """"
myOlApp.ActiveExplorer.Search txtSearch, olSearchScopeAllFolders
' Problem occurs below, since the code keeps running but the search results haven't loaded yet.
myOlApp.ActiveExplorer.SelectAllItems
Dim i As Long
For i = ActiveExplorer.Selection.Count To 1 Step -1
Dim Item As MailItem
Set Item = ActiveExplorer.Selection.Item(i)
Debug.Print Item.Subject, Item.Sender, Item.Parent.FolderPath
Next
Set ns = Nothing
Set oMail = Nothing
Set myOlApp = Nothing
Set Item = Nothing
End Sub
Try to use Application.AdvancedSearch instead - it exposes Application.AdvancedSearchComplete event.
The Explorer.Search method is used to perform a Microsoft Instant Search on the current folder displayed in the Explorer using the given Query. Basically, it will use Outlook UI for searching items and the result is visible in Outlook. The functionality of Explorer.Search is analogous to the Search button in Instant Search. It behaves as if the user has typed the query string in the Instant Search user interface and then clicked Search. When calling Search, the query is run in the user interface, and there is no programmatic mechanism to obtain the search results. The Search method does not provide a callback to enable the developer to determine when the search is complete.
Instead, you may find the Find/FindNext or Restrict methods of the Items class helpful. Read more about them 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 helpful. 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.
The Outlook Object Model provides the AdvanvedSearchComplete event of the Application class. An instance of the Search class containing the search results is passed to the event handler (see the Results property).
See Advanced search in Outlook programmatically: C#, VB.NET for more information.

Why do Embedded Image Sizes Evaluate to 0 in Outlook VSTO Add-In?

We are building a VSTO Outlook Add-In that scans an outgoing mail message for attachments to alert users and noticed some unexpected behavior.
Considering the following ways of adding a file to an Outlook mail message:
A file attachment
Screen Shot
Screen Clipping
Mail Signature file
All four are recognized as attachments when the Item Send event fires:
Private Sub Application_ItemSend(ByVal Item As Object, ByRef Cancel As Boolean) Handles Application.ItemSend
In the following code example:
For Each attachment As Outlook.Attachment In Item.Attachments
'do some stuff like check attachment size
Next
We are checking for small embedded images in a signature file that don't want to notify the user of.
In the following cases:
Screen Shot
Screen Clipping
Mail Signature file
We've noticed that when the added files are embedded images (not attachments), we don't see a correct size property for the image using:
attachment.Size
IE: Say we are sending an Outlook email that has:
One Attachment.
One Screen Shot.
One Signature File with one image in it.
Our code seems to recognize the correct number of attachments, however if we check the attachment size for a screen shot or a signature file image, the attachment size property always evaluates to 0, which we're think is due to the fact that the file doesn't exist on disk and the attached file does.
For Each attachment As Outlook.Attachment In Item.Attachments
if attachment.size > 755 then
'ignore the image
end if
Next
Is there a way to check the image size in VB.Net or do we need to save the file off to a temp directory in order to do this?
EDIT
Outlook Spy Troubleshooting steps:
New Mail Message
Inserted screen shot and signature file:
OutlookSpy->IMessage
IMessage window blank (below)
Close IMessage window.
Re-Open IMessage Window
Inserted (attached) files appear (below)
8. Double Clicked on attachment
Selected Inspector Button
Current Item:
Browse:
Attachments:
Browse:
IEnumVariant:
I suspect that the differences between steps 4 and 7 may be due to the fact that Outlook may have saved a draft of the email message?
ADDITIONAL EDIT
Code added to save mail message before checking signature/embedded image size:
'convert generic object to Outlook.MailItem object.
Dim objMailItem As Outlook.MailItem = CType(Item, Outlook.MailItem)
'Save message
objMailItem.Save()
'quick check to see if message is saved (it is)
Dim saved As Boolean = objMailItem.Saved()
For Each attachment As Outlook.Attachment In objMailItem.Attachments
'all items still evaluate to 0.
If attachment.Size >= 20 Then
'do some stuff
End If
Next
Thanks.
Attachment size (which includes raw binary data as well as the per-attachment properties) is updated by the store provider when the mesage is saved. Make sure you call MailItem.Save first.
We ended up using Outlook Spy (awesome tool..) to find the PR_ATTACH_SIZE property:
Then set up using MS schema as follows:
'property access to get attachment sizes
Const PR_ATTACH_SIZE As String = "http://schemas.microsoft.com/mapi/proptag/0x0E200003"
Also great info from this SO Post.
Then iterated through our attachment collection as follows to find our attachment sizes:
For Each attachment As Outlook.Attachment In Item.Attachments
attSize = CType(attachment.PropertyAccessor.GetProperty(PR_ATTACH_SIZE), Integer)
if attSize.size > 755 then
'ignore the image
end if
Next
Thanks..