'Save OLE Embedded documents in Outlook email to file
I am using late binding to connect to MS Outlook and to open and extract info from outlook emails using the MailItem
object.
I am trying to save attachments to file. This is fairly straightforward in most instances using the Attachment
object and its SaveAsFile
method.
However, it does not work where the Attachment
Type
is olOLE
. I believe this only relates to documents embedded in emails created in RTF format (hopefully few and far between nowadays).
Via the Attachment
object it is possible to access MAPI
properties not exposed by the object model using its PropertyAccessor
.
The relevant MAPI
property for OLE objects is PR_ATTACH_DATA_OBJ
, which can be accessed using the PropertyInspector as in the following example:
Function SaveOLEAttachmentToFile(Attachment:Variant; fn:String): boolean;
var
OPA, PropName : Variant;
begin
Result := false;
OPA := Attachment.PropertyAccessor;
PropName := 'http://schemas.microsoft.com/mapi/proptag/0x3701000D '; //PR_ATTACH_DATA_OBJ
?????? := OPA.GetProperty(PropName);
end;
I am stuck at this point as I can't know work out what Delphi type to save the data to and I am not even sure this is possible having read the MS documentation (Click here). PR_ATTACH_DATA_OBJ
returns a PT_OBJECT
. I am hoping that this object contains the raw data which (if I could work out how to access it in Delphi
) can be simply saved to a file. However, the documentation suggests it may not be that simple and it's possible I may have to work with Extended MAPI
. I have spent a few hours researching the latter with no concrete result other than a headache. I appreciate I could use Redemption, but I don't want to use a third party tool for something which is fairly minor in the round.
If anyone can advise as to a data type to hold the PT_OBJECT
from which it can be simply saved to file that would be my route one.
Failing that, if I need to dig deeper into MAPI
, I would be grateful if anyone could clarify/amplify my research so far. I have the following steps:
- Initialize
MAPI
. - Get an
IMAPIPROP
interface. I think I should be getting the interface from myAttachment
object and the following seems to work (ie compiles and executes without problems):MAPIPROP := IUnknown(Attachment.MAPIObject) as IMAPIPROP
. Failing that, I would have to cast the parentMailItem
toIMAPIPROP
interface and work my way down to the attachment viaGetAttachmentTable
. - Load the attachment data into an
IStream
:if Succeeded(MAPIPROP.OpenProperty(PR_ATTACH_DATA_OBJ, IStream, STGM_READ, 0, IUnknown(SourceStream)) then
- Extract the data from the
IStream
and save to file
I have failed to get as far as point 3 as something would seem to be wrong with my initial casting to IMAPIPROP
albeit it does not cause any violations. I have tried reading a single property from the MailItem
cast to IMAPIPROP
using the following code:
if (Succeeded(HrGetOneProp(MAPIPROP, PR_SUBJECT, Prop))) then
And I get an access violation. Likewise if I cast the Attachment
object and query an attachment property I also get a violation. I don't think the problem lies with the call to HrGetOneProp
, I think it has to be the casting to IMAPIPROP.
Any pointers re the above would be greatly appreciated.
Solution 1:[1]
Not quite an answer to my question, but I have thought of an alternative solution. What I am ultimately trying to do is convert a msg email as a pdf. To do that I need to extract the body and then somehow insert the embedded images. With an html email this seemed pretty straightforward ((1) extract all the attachments to a folder, (2) parse the html body for references to SRC IMG and update the location of the image to reference the saved files and (3) save the edited html body to file and open it in Word and save as PDF).
RTF emails cannot be handled in this way. However, for my specific problem there is a much easier way to achieve what I need for all email types using Outtlook
and Word
.
Use the MailItem.SaveAs
function and save the email in either html format or mthml. The former format will save all embedded images to a sub-folder (in png and jpg formats) should you need them for any other reason. once you have your html file, open it with Word and save to PDF.
If Office is not a solution then you need to figure Istorage or use one of the Extended MAPI solutions such as Redemption.
For Delphi users there are also the following commercial offerings that I have come across in my recent travels:
I did come across one more solution which I can't find at the moment! Will post an update if I do.
Solution 2:[2]
PropertyAccessor
(and the Outlook Object Model in general) does not handle PT_OBJECT
type properties.
What you need to do is open the PR_ATTACH_DATA_OBJ
property as IStorage
, and then extract the data from there (it depends on the actual type of the attachment). You can see the data in the streams in OutlookSpy (I am its author) - select the message, click IMessage
button on the OutlookSpy rubbon, go to the GetAttachmentTable
tab, double click on the attachment to open it, select the PR_ATTACH_DATA_OBJ
property, right click, select IMAPIProp::OpenProperty
, then IStorage
.
If using Redemption (I am also its author) is an option, its version of RDOAttachment.SaveAsFile
handles OLE attachment for the most popular formats (Word, Excel, bitmap, Power Point, Adobe PDF, etc.) - create an instance of the RDOSession object (using either CrealeOleObject
or RedemptionLoader) and use RDOSession.GetRDOObjectFromOutlookObject
method (pass either Attachment
or MailItem
object) to get back RDOMail or RDOAttachment object respectively.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Mark Williams |
Solution 2 |