Some Outlook VBA code that saves email attachments according to their file names failed on an embedded bitmap image when it tried to access the FileName property of that "attachment."
How can I detect this? The FileName property is supposed to be a String but neither of these tests catches it:
If objAtt.FileName = "" Then ....
If objAtt.FileName = vbNullString Then ....
But every attempt to access this property throws a run-time error:
Outlook cannot perform this action on this type of attachment.
I would prefer not to use the DisplayName property if possible.
Also, it seems that I could filter the attachments according to the value of their FileType properties but I have not been able to find a list associating these Integer values with file types.
Advice is appreciated.
FileName property is only applicable to the regular olByValue type attachments. In your case you are most likely dealing with an olOLE type attachment, which is really a serialized embedded OLE object - the stream stores the bitmap (or metafile) that Outlook uses to represent the image as well as the actual data that the original application that created the data (MSPaint?) can use to edit it.
OLE attachment data is stored in the IStorage interface format. The actual format of the data depends on the application used to create it (MSPaint, Excel, etc.). Outlook Object Model does not let you save such attachments using Attachment.SaveAsFile.
Depending on the language you are using (C++ or Delphi would be best), you will need to open the PR_ATTACH_DATA_OBJ property as IStorage and then extract the data (IAttach::OpenProperty(PR_ATTACH_DATA_OBJ, IID_IStorage, ...)) . Take a look at the message with OutlookSpy (I am its author) - click IMessage button, go to the GetAttachmentTable tab, double click on the OLE attachment, select the PR_ATTACH_DATA_OBJ, right click, IMAPIProp::OpenProperty.
If using Redemption is an option (I am also its author), it extracts the the attachment data for a dozen or so most popular formats (MSPaint, Excel, Word, PDF, etc.):
set Session = CreateObject("Redemption.RDOSession")
Session.MAPIOBJECT = Application.Session.MAPIOBJECT
set OutlookMsg = Application.ActiveExplorer.Selection.Item(1)
set Msg = Session.GetRDOObjectFromOUtlookObject(OutlookMsg)
for each attach in Msg.Attachments
attach.SaveAsFile "c:\temp\" & attach.FileName
next
Related
I am creating a Form on Access. This Form has source controls to an underlying Access DB. This Form is ONLY INTENDED for insertion of a new record into the DB (No intention of modifying any of the DB's existing data).
In the Access DB, there is a field for Attachments. And on the Form, there is an Attachment Control that is Source Controlled to the Attachments field in the Access DB. The Attachment Control allows users to select files and attach to the form's current record.
I have tried closing the Form and re-opening, but that means the form goes through its life cycle, and does the BeforeUpdate()+AfterUpdate()! This means that the data on the Form that was just recently closed, was entered into the DB. I don't want that!
For the reason that this Form is only for creating new records into the DB, I want a button that can wipe the current Form clean, without the Form doing its BeforeUpdate()+AfterUpdate() methods, which means I can't close the Form and re-open it. I have got most of the Controls on the form figured out in terms of giving it default values. My only problem is resetting the Attachment Control.
What is a programmatic way (In VBA or some other language, DAO?) to remove entries on this Attachment Control (Not the Attachment Field in the DB, but the actual data on the Form currently being used)?
I am aware that in BeforeUpdate(), you may cancel the form's update by setting its Cancel = true in the definition of BeforeUpdate(). But there should still be a way to programmatically deal with Attachment Control (Not the attachment field, I know DAO can handle that easily), it just seem stupid not to have a way for it.
(EDIT) Addon 8/30/19:
It's a better idea to store the pathname of the file in your Access DB rather than the attachment it self, and then do a file copy from one location to another (Looks at the code below as my example). And this is exactly what I did, so I don't have to deal with the attachment control. I have found this control to be too limiting in terms of the programmatic-actions that it can offer to developers.
Dim fso As Object
Set fso = VBA.CreateObject("Scripting.FileSystemObject")
On Error GoTo DebugMsg ' DebugMsg is where I defined what happens on Error
fso.CopyFile srcPath, dstPath, False
The attachment field holds a subrecordset of type Recordset2, so you must deal with it as such:
Private Sub DeleteCurrentAttachment()
Dim Records As DAO.Recordset
Dim Attachments As DAO.Recordset2
Set Records = Me.RecordsetClone
Records.Bookmark = Me.Bookmark
Set Attachments = Records!YourAttachmentFieldName.Value
While Not Attachments.EOF
Attachments.Delete
Attachments.MoveNext
Wend
Attachments.Close
End Sub
Open the form in design mode.
Then in the Form property's > in Data Tab > set Data Entry to True.
add Event to the form
Private Sub Form_AfterInsert()
DoCmd.RunCommand acCmdRecordsGoToNew
End Sub
All this action will insure that you are all the time in new record mode .
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
Is it possible to get attachment content(eml file) in Outlook Add-in API?. If yes, please guide how to do it.
Call Attachment.SaveAsFile to save the attachment.
If you are trying to access the attachment contents without saving the attachment as a file, Outlook Object Model won't help you. You can use either Extended MAPI (C++ or Delphi only) and open the attachment data as a stream (IAttach::OpenProperty(PR_ATTACH_DATA_BIN, IID_IStream, ...)) or use Redemption (I am its author) - its attachment objects expose AsText, AsArray, etc properties.
set Session = CreateObject("Redemption.RDOSession")
Session.MAPIOBJECT = Application.Session.MAPIOBJECT
set item = Session.GetMessageFromID(Application.ActiveExplorer.Selection(1).EntryID)
for each attach in item.Attachments
MsgBox attach.AsText
next
There is an example on how to do this here:
Using:
var attachmentData =
attachment.PropertyAccessor.GetProperty(
PR_ATTACH_DATA_BIN);
But when I tried to do this I just got an exception.
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.
If certain users automate the Outlook Client to view bounce backs/ReportItems in a shared inbox, rather than returning the clear text of the message as indicated by the documentation there is a unicode string that has been parsed as a UTF-8 string - so it looks like Chinese.
I can get past that with some code, but the additional issue is that this change occurs in Outlook as well for all users with access to that inbox. The message itself as viewed in Outlook appears as Chinese characters - the original unicode html parsed as UTF-8.
We are using the normal methods to access the report item:
For Counter as Integer = Inbox.Items.Count To 1 Step -1
Dim Report As Outlook.ReportItem = Inbox.Items(Counter)
Dim Body As String = Report.Body
The last line is where we get the garbled text In VBA it attempts to parse it as ASCII and returns a large block of "?". In .Net it returns the value parsed as UTF-8 and we get the characters that appear Chinese. In either case the report item in the inbox begins displaying as Chinese characters and continues to do so for all users of that inbox.
I just had this happen to my VBA function in Outlook that processes email bounce backs for orders and marks those orders as requiring attention. The original email in outlook looks fine but when I attempt to process it, the characters change to Chinese and Report.Body just shows question marks.
I found using StrConv to convert to Unicode could get me the correct body contents for processing.
Dim strBody as String
strBody = StrConv(Report.Body, vbUnicode)
Are you sure that the Inbox.Items(Counter) call returns an instance of the ReportItem class? Did you have a chance to check out the MessageClass property?
Most probably you try to cast an instance of the MailItem class to the ReportItem class. Is that the case?
Also I'd suggest using any low-level property viewer such as MFCMAPI or OutlookSpy for observing properties at runtime. Do you see "chinese" charactere there?
I came across this issue and I've written a function that solves the issue for me. I thought I'd share it here in case it's of use to anyone else.
Private Sub Example()
Dim Item As Object
Set Item = Application.ActiveExplorer.Selection(1)
Debug.Print ItemBody(Item)
End Sub
Public Function ItemBody(Item As Variant) As String
On Error Resume Next
If TypeName(Item) = "ReportItem" Then
With Item.GetInspector
ItemBody = .WordEditor.Content
.Close 1
End With
Else
ItemBody = Item.Body
End If
End Function
Yes, there is a problem with ReportItem.Body property in the Outlook Object Model (present in Outlook 2013 and 2016) - you can see it in OutlookSpy (I am its author): select an NDR message, click Item button, select the Body property - it will be garbled. Worse than that, once the report item is touched with OOM, Outlook will display the same junk in the preview pane.
The report text is stored in various MAPI recipient properties (click IMessage button in OutlookSpy and go to the GetRecipientTable tab). The problem is the ReportItem object does not expose the Recipients collection. The workaround is to either use Extended MAPI (C++ or Delphi) or Redemption (any language - I am also its author) - its ReportItem.ReportText property does not have this problem:
set oItem = Application.ActiveExplorer.Selection(1)
set oSession = CreateObject("Redemption.RDOSession")
oSession.MAPIOBJECT = Application.Session.MAPIOBJECT
set rItem = oSession.GetRDOObjectFromOutlookObject(oItem)
MsgBox rItem.ReportText