none
Use OpenProperty to Add Attachment to A Message RRS feed

  • Question

  • Hi,

    I am studying of using OpenProperty to add attachment to a message. I use the following code from MSDN:

    LPSTREAM pStreamFile, pStreamAtt;
    HRESULT hr;

    hr = OpenStreamOnFile (MAPIAllocateBuffer, MAPIFreeBuffer,
                           STGM_READ, "myfile.doc", NULL, &pStreamFile);
    if (HR_SUCCEEDED(hr))
    {
        // Open the destination stream in the attachment object
        hr = pAttach->OpenProperty (PR_ATTACH_DATA_BIN,
                                    &IID_IStream,
                                    0,
                                    MAPI_MODIFY | MAPI_CREATE,
                                    (LPUNKNOWN *)&pStreamAtt);
        if (HR_SUCCEEDED(hr))
        {
            STATSTG StatInfo;
            pStreamFile->Stat (&StatInfo, STATFLAG_NONAME);
            hResult = pStreamFile->CopyTo (pStreamAtt, StatInfo.cbSize,
                                           NULL, NULL);
            pStreamAtt->Release();
        }
        pStreamFile->Release();
    }

    OpenProperty open the PR_ATTACH_DATA_BIN property in IID_IStream interface. Based on the description of IStream at https://docs.microsoft.com/en-us/windows/win32/api/objidl/nn-objidl-istream , it seems that IStream can only handle data in a structured storeage object, then:

    1. Will IStream be able to handle ANY binary data file? I have made a test, and it seems that IStream can work with ANY binary data file.

    2. To write data to the attachment stream, there are 2 methods:

    (1) Like in the sample codes, use OpenStreamOnFile and then invoke IStream::CopyTo to copy the data to the attachment stream.

    (2) Open the file as normal files, with CFile, then read data from the file, then invoke IStream::Write to write the data into the stream.

    which one is better in speed and memory consumption?



    • Edited by tempc Monday, January 6, 2020 11:16 AM
    Monday, January 6, 2020 6:54 AM

Answers

  • Hello,

    1. Yes, it will. 

    2. The IStream::Write is better because it allows to write data by small chunks. You need to read your file content into a buffer (partially or as a whole). Then you call IStream::Write(). For example:

    unsigned char * pBuffer = new unsigned char [BUFFERSIZE];
    
    HANDLE hFile = CreateFile(pszFileName, ...);
    
    // you can determine file size and set initially
    // pStream->SetSize(...)
    
    DWORD dwRead = 0;
    ULONG ulWritten = 0;
    do
    {
        if(!ReadFile(hFile, pBuffer, BUFFERSIZE, &dwRead, NULL))
            break;
    
        if(0 == dwRead) // means EOF
            break;
    
        pStream->Write(pBuffer, dwRead, &ulWritten);
    
        // you can also check (ulWritten == dwRead) just to make sure
    
    } while(BUFFERSIZE == dwRead);    // be careful (edit)
    
    CloseFile(hFile);
    
    delete [] pBuffer;


    profile for Eugene Astafiev at Stack Overflow, Q&A for professional and enthusiast programmers

    • Marked as answer by tempc Wednesday, January 8, 2020 3:31 AM
    Monday, January 6, 2020 11:40 AM
  • Call this method to preallocate space for the stream. If the NewSize parameter is larger than the current stream size, the stream is extended to the indicated size by filling the intervening space with bytes of undefined value. 

    It doesn't bring any performance advantages if the amount of bytes are equal. Use the IStream.Write method  to accomplish this rather than IStream.SetSize. IStream.Write automatically extends the stream, making IStream.SetSize unnecessary. Calling IStream.Write without IStream.SetSize can be up to three times faster than making the SetSize call prior to Write.


    profile for Eugene Astafiev at Stack Overflow, Q&A for professional and enthusiast programmers

    • Marked as answer by tempc Wednesday, January 8, 2020 3:31 AM
    Tuesday, January 7, 2020 7:42 PM

All replies

  • Hello,

    1. Yes, it will. 

    2. The IStream::Write is better because it allows to write data by small chunks. You need to read your file content into a buffer (partially or as a whole). Then you call IStream::Write(). For example:

    unsigned char * pBuffer = new unsigned char [BUFFERSIZE];
    
    HANDLE hFile = CreateFile(pszFileName, ...);
    
    // you can determine file size and set initially
    // pStream->SetSize(...)
    
    DWORD dwRead = 0;
    ULONG ulWritten = 0;
    do
    {
        if(!ReadFile(hFile, pBuffer, BUFFERSIZE, &dwRead, NULL))
            break;
    
        if(0 == dwRead) // means EOF
            break;
    
        pStream->Write(pBuffer, dwRead, &ulWritten);
    
        // you can also check (ulWritten == dwRead) just to make sure
    
    } while(BUFFERSIZE == dwRead);    // be careful (edit)
    
    CloseFile(hFile);
    
    delete [] pBuffer;


    profile for Eugene Astafiev at Stack Overflow, Q&A for professional and enthusiast programmers

    • Marked as answer by tempc Wednesday, January 8, 2020 3:31 AM
    Monday, January 6, 2020 11:40 AM
  • 1. Yes. You are probably thinking about IStorage. IStream only handles continuous data arrays.

    2. Use IStream.CopyTo - in most cases, it will do the same as your IStream::Write code. But it can contain implementation specific code that lets it optimize the operation: e.g. an Exchange provider can recognize that you are copying between two attachments residing on a server and copy the data on the server without first downloading data from one stream to the client side and then pushing it back to another attachment on the server.


    Dmitry Streblechenko (MVP)
    http://www.dimastr.com/redemption
    Redemption - what the Outlook
    Object Model should have been
    Version 5.5 is now available!

    Monday, January 6, 2020 2:39 PM
  • Hi, Eugene,

    Thank you for your answer. Will setting the size of the stream, i.e., 

    pStream->SetSize(...)

    beforehand give any advantages to the performance?

    Thanks


    • Edited by tempc Monday, January 6, 2020 11:10 PM
    Monday, January 6, 2020 11:10 PM
  • Call this method to preallocate space for the stream. If the NewSize parameter is larger than the current stream size, the stream is extended to the indicated size by filling the intervening space with bytes of undefined value. 

    It doesn't bring any performance advantages if the amount of bytes are equal. Use the IStream.Write method  to accomplish this rather than IStream.SetSize. IStream.Write automatically extends the stream, making IStream.SetSize unnecessary. Calling IStream.Write without IStream.SetSize can be up to three times faster than making the SetSize call prior to Write.


    profile for Eugene Astafiev at Stack Overflow, Q&A for professional and enthusiast programmers

    • Marked as answer by tempc Wednesday, January 8, 2020 3:31 AM
    Tuesday, January 7, 2020 7:42 PM
  • I suppose the second point is related to the online mode only.

    profile for Eugene Astafiev at Stack Overflow, Q&A for professional and enthusiast programmers

    Tuesday, January 7, 2020 7:42 PM
  • It is related to any provider specific optimization that may or may not be applicable.

    As a general rule of thumb, there is absolutely no reason to write more code than necessary - Write requires you to manage the buffer and the loop. CopyTo is just a single line of code.


    Dmitry Streblechenko (MVP)
    http://www.dimastr.com/redemption
    Redemption - what the Outlook
    Object Model should have been
    Version 5.5 is now available!

    Wednesday, January 8, 2020 4:18 AM