none
How to copy attachments. RRS feed

  • Question

  • I'm using outlook 2010 and C++. I want to copy an attachment from one mailItem to another. I know I can do it by going to disk but, from the documentation, it sounds like you can add the attachment object directly. Here' my code

    // source attachments
    AttachmentsPtr srcAttachments;
    mailItem1->get_Attachments(&srcAttachments);

    // source attachment - item(1)
    AttachmentPtr srcAttachment;
    srcAttachments->Item(variant_t(1), &srcAttachment);

    // destination attachments
    AttachmentsPtr dstAttachments;
    mailItem2->get_Attachments(&dstAttachments);

    // add attachment to destination
    AttachmentPtr att;
    IDispatch* disp = srcAttachment;
    hr = dstAttachments->Add(variant_t(disp), variant_t(olByValue), vtMissing, vtMissing, &att);

    Upon return, hr is 0x80020003 (Member not found). What am i doing wrong? Is this possible to do?

    Wednesday, November 3, 2010 7:23 PM

Answers

  • Note: that will only work for the by-value or by-reference attachments. Embedded message or OLE attachments won't work.

    <plug>Redemption allows to pass RDOAttachment object when calling RDOAttachments.Add </plug>


    Dmitry Streblechenko (MVP)
    http://www.dimastr.com/redemption
    Redemption - what the Outlook
    Object Model should have been
    • Marked as answer by Chuck Bohling Thursday, November 4, 2010 9:39 PM
    Thursday, November 4, 2010 9:27 PM
  • Thanks Dmitry, that's the conclusion I was coming to. You just solidified it.
    • Marked as answer by Chuck Bohling Monday, November 15, 2010 11:41 PM
    Monday, November 15, 2010 11:41 PM

All replies

  • Hi Chuck,

    Prior to Outlook 2010 this wasn't possible using the object model. However some binary property improvements were made for the PropertyAccessor, so you can use Attachment.PropertyAccessor to get and set attachment data without saving to disk. While it's C# and not exactly your scenario, this code sample should help you get started:

            private void DemoAttachmentStream()

            {

                const string PR_ATTACH_DATA_BIN =

                    "http://schemas.microsoft.com/mapi/proptag/0x37010102";

                //Create a mail item

                Outlook.MailItem mail =

                    Application.CreateItem(Outlook.OlItemType.olMailItem)

                    as Outlook.MailItem;

                mail.Subject = "Demo Attachment Stream";

                //Create the c:\demo folder if it doesn't exist

                if(!Directory.Exists(@"c:\demo"))

                {

                    Directory.CreateDirectory(@"c:\demo");

                }

                //Write attach.txt

                StreamWriter sw = new StreamWriter(@"c:\demo\attach.txt");

                char charA = 'A';

                string myString = new string(charA ,4096);

                sw.WriteLine(myString);

                sw.Close();

                //Add attach.txt as an attachment

                Outlook.Attachment attach =

                    mail.Attachments.Add(@"c:\demo\attach.txt",

                    Outlook.OlAttachmentType.olByValue,

                    Type.Missing,

                    Type.Missing);

                //Save the item

                mail.Save();

                //Use PropertyAccessor to retrieve attachment byte stream

                byte[] attachStream =

                    attach.PropertyAccessor.GetProperty(

                    PR_ATTACH_DATA_BIN) as byte[];

                //Iterate the stream and change "A" to "B"

                for (int i = 0; i < attachStream.Length; i++)

                {

                    attachStream[i] = 0x42; //Hex for "B"

                }

                //Set PR_ATTACH_DATA_BIN to attachStream

                attach.PropertyAccessor.SetProperty(PR_ATTACH_DATA_BIN,

                    attachStream);

                //Save the item again

                mail.Save();

            }

    Of course, this is just setting the binary data for an existing attachment, so for your scenario (clean message) you'd also need to add the other relevant attachment props.

    Bill Jacob
    Microsoft Customer Service and Support - Messaging Developer

    Thursday, November 4, 2010 7:26 PM
  • Note: that will only work for the by-value or by-reference attachments. Embedded message or OLE attachments won't work.

    <plug>Redemption allows to pass RDOAttachment object when calling RDOAttachments.Add </plug>


    Dmitry Streblechenko (MVP)
    http://www.dimastr.com/redemption
    Redemption - what the Outlook
    Object Model should have been
    • Marked as answer by Chuck Bohling Thursday, November 4, 2010 9:39 PM
    Thursday, November 4, 2010 9:27 PM
  • So I implemented this in C++ and it works great. Except if the attached file's extension is .doc (and I think .xls). Any file with that extension, a word doc or not, the PropertyAccessor->GetProperty(PR_ATTACH_DATA_BIN) call fails with MAPI_E_NOT_FOUND. All other file types work. Any ideas what's wrong? I'm using XP and outlook 2010. Same happens on Win7 and 2010. And with or without anti-virus. Sounds like a security thing.
    Sunday, November 14, 2010 9:49 PM
  • Can you see that property in OutlookSpy (click IMessage, go to the GetAttachmentTable tab, double click on the attachment)?
    Dmitry Streblechenko (MVP)
    http://www.dimastr.com/redemption
    Redemption - what the Outlook
    Object Model should have been
    Monday, November 15, 2010 1:53 PM
  • The problem occurs when I try to get a .doc attachment from a new message that is being sent from the compose window. In my ItemEvents_10::Write handler, I use OOM to access the object. The code snippet follows. In all cases but .doc (and .xls) the result of GetProperty is S_OK. But for .doc, the result is MAPI_E_NOT_FOUND.

    AttachmentPtr attachment;
    result = attachments->Item(varIndex, &attachment);

    _PropertyAccessorPtr propertyAccessor;
    result = attachment->get_PropertyAccessor(&propertyAccessor);

    variant_t sourceAttachment;
    result = propertyAccessor->GetProperty(L"http://schemas.microsoft.com/mapi/proptag/0x37010102", &sourceAttachment);

    If I look at the message in the compose window with outlookspy, the IMessage has no attachment table. I assume that's ok since the message hasn't been sent yet?? However, the outlookspy CurrentItem shows exactly what my code does. If I open the attachment then call GetProperty just like my code, outlookspy returns 0x80020009 "The property ... is unknown or cannot be found". If I open the attachment's MAPIOBJECT, PR_ATTACH_DATA_BIN is not found.

    Monday, November 15, 2010 7:09 PM
  • Do you see the property in OutlookSpy *after* the message is saved?
    Dmitry Streblechenko (MVP)
    http://www.dimastr.com/redemption
    Redemption - what the Outlook
    Object Model should have been
    Monday, November 15, 2010 9:16 PM
  • If in the compose window I save then send, outlookspy sees the attachment with both OOM MailItem and IMessage. And my code works. Why would everything but .doc work?
    Monday, November 15, 2010 9:30 PM
  • If you do not save the message first, uncommitted changes only exist in an internal Outlook buffer and are not visible to MAPI (and hence to PropertyAccessor).

    Outlook may decide to update some properties before you call Save, but as a general rule of thumb, you can expect the properties to be available only after the message is saved. 


    Dmitry Streblechenko (MVP)
    http://www.dimastr.com/redemption
    Redemption - what the Outlook
    Object Model should have been
    Monday, November 15, 2010 10:47 PM
  • Thanks Dmitry, that's the conclusion I was coming to. You just solidified it.
    • Marked as answer by Chuck Bohling Monday, November 15, 2010 11:41 PM
    Monday, November 15, 2010 11:41 PM