none
How I can handle the attachment(s) in TaskItem when Item_Send(Cancel As Boolean) or Application_Send(Item, Cancel) is triggered RRS feed

  • Question

  • I'm developing an add-in of outlook 2010 - 2016 (x86 and x64) on windows 7 - 10 (x86 and x64) using c++.

    How I can get TaskItem in TaskRequestItem and then reattach attachments in TaskItem?

    Description

    When Item_Send(Cancel As Boolean) or Application_Send(Item, Cancel) is triggered, the item is of TaskRequestItem, but I found Selected Item in the Explorer window is of TaskReuqestItem and CurrentItem in the Inspector window if of TaskItem.

    PS C:\Users\sam> $OutlookApp=[Runtime.InteropServices.Marshal]::GetActiveObject('Outlook.Application')
    PS C:\Users\sam> $item=$OutlookApp.ActiveInspector().CurrentItem.MessageClass
    IPM.Task
    PS C:\Users\sam> $OutlookApp.ActiveExplorer().Selection.Item(1).MessageClass
    IPM.TaskRequest

    Currently, I use GetAsscociatedTask(True) to create a new TaskItem and SaveAs a file, then reattachment the only attachment of TaskRequestItem.

    Thus, within the event Item_Send(Cancel As Boolean) or Application_Send(Item, Cancel), how I reattach each attachment after doing zip compression (I use Attachment.Remove and Attachment.Add because I need to change file name. Please tell me if there is a better approach, changing name and stream content of the original attached file?). However, When I 'm doing so (Removing, zip and Adding from end to begin), attachments will be resorted, which incurs some files will be not compressed or compressed file and original file coexist.

    My example code

    STDMETHOD_(void, ItemSend)(LPDISPATCH Item, VARIANT_BOOL *Cancel)
    {
    	if (IDCANCEL == MessageBoxW(NULL, L"Are you sure to send the email?", L"Outlook Addin", MB_ICONQUESTION | MB_OKCANCEL))
    		*Cancel = VARIANT_TRUE;
    
    	CComQIPtr<Outlook::_TaskRequestItem> spTaskRequestItem = Item;
    	if (spTaskRequestItem)
    	{
    		AttachementEmbeddedObject(spTaskRequestItem);
    		CComPtr<Outlook::_TaskItem> spTaskItem;
    		CComPtr<Outlook::Attachments> spAttachments;
    		if (spAttachments != NULL)
    		{
    			long nAttachCount=0;
    			spAttachments->get_Count(&nAttachCount);
    
    			for(int nAttachIndex=nAttachCount; nAttachIndex>0; nAttachIndex--)
    			{
    				// https://msdn.microsoft.com/en-us/vba/outlook-vba/articles/attachments-add-method-outlook
    				// To ensure consistent results, always save an item before adding or removing objects in the Attachments collection of the item.
    				spTaskItem.Save();
    				spAttachments.Remove(nAttachIndex); // remove xx.doc
    
    				// do zip
    
    				spTaskItem.Save();
    				spAttachments.Add("xx.zip"); // add xx.doc.zip
    			}
    		}
    	}
    }


    The known:

    MS-OXOTASK: 4.1 Sending a Task Request

    #define INITGUID

    #define USES_IID_IMessage // error LNK2001: unresolved external symbol _IID_IMessage

    #define USES_IID_IMAPIPropData

    #define USES_IID_IMAPITable

    #define USES_IID_IAttachment // error C2065: 'IID_IAttachment' : undeclared identifier

    #include <initguid.h>

    #include <MAPIGuid.h> // IID_xx

    #include <mapiform.h>

    #include <objbase.h>

    #include <mapix.h>

    #include <MAPITags.h> // PR_xx

    #include <MAPIDefS.h>

    #include <mapiutil.h>

    #include <imessage.h>

    #include <MAPICode.h> // HR_SUCCEEDED

    // [MS-OXOTASK: 4.1 Sending a Task Request](https://msdn.microsoft.com/en-us/library/ee219819(v=EXCHG.80).aspx)

    // https://social.msdn.microsoft.com/Forums/vstudio/en-US/d142e8ac-2630-4828-86a1-85c19bdf7531/convert-outlookattachment-to-outlookmailitem?forum=vsto

    // [Opening an Attachment](https://msdn.microsoft.com/en-us/library/office/cc842411.aspx)

    HRESULT AttachementEmbeddedObject(CComPtr<Outlook::_TaskRequestItem> spTaskRequestItem)

    {

      HRESULT hr;

      ATL::CComPtr<Outlook::Attachments> spAttachments;

      hr = spTaskRequestItem->get_Attachments(&spAttachments);

      if (SUCCEEDED(hr))

      {

        long nAttachments = 0;

        hr = spAttachments->get_Count(&nAttachments);

        if (SUCCEEDED(hr) && 0 < nAttachments)

        {

          ATL::CComPtr<Outlook::Attachment> spAttachment;

          hr = spAttachments->Item(CComVariant(1), &spAttachment);

          if (SUCCEEDED(hr))

          {

            ATL::CComPtr<IUnknown> spUnk = NULL;

            ATL::CComPtr<IAttach> spIAttach = NULL;

            //try to get MAPI interface from OOM (Outlook object model)

            if(SUCCEEDED(spAttachment->get_MAPIOBJECT( &spUnk )))

            {

              if( SUCCEEDED( spUnk->QueryInterface(IID_IAttachment, (void**) &spIAttach) ))

              {

                //[IMessage : IMAPIProp](https://msdn.microsoft.com/en-us/library/office/cc842097.aspx)

                ATL::CComPtr<IMessage> spIMessage = NULL;

                hr = spIAttach->OpenProperty(PR_ATTACH_DATA_OBJ, &IID_IMessage, 0, 0, (LPUNKNOWN *) &spIMessage);

                if (HR_SUCCEEDED(hr))

                {

                  ATL::CComPtr<Outlook::_TaskItem> spTaskItem;

                  hr = spIMessage->QueryInterface(Outlook::IID__TaskItem, (void**) &spTaskItem);

                  OutputDebugString(SUCCEEDED(hr) ? _T("succ OpenProperty(PR_ATTACH_DATA_OBJ)") : _T("failed OpenProperty(PR_ATTACH_DATA_OBJ)"));

                }else

                {

                  OutputDebugString(_T("failed OpenProperty(PR_ATTACH_DATA_OBJ)"));

                }

              }else

              {

                OutputDebugString(_T("failed QueryInterface(IID_IAttachment)"));

              }

            }else

            {

              OutputDebugString(_T("failed get_MAPIOBJECT"));

            }

          }else

          {

            OutputDebugString(_T("failed Item"));

          }

        }else

        {

          OutputDebugString(_T("failed get_Count"));

        }

      }else

      {

        OutputDebugString(_T("failed get_Attachments"));

      }

      return hr;

    }


    please verify my account.



    • Edited by kekkou Wednesday, August 2, 2017 8:11 AM IPM.TaskRequest
    Wednesday, August 2, 2017 7:35 AM

Answers

  • Hi kekkou,

    >> for(int nAttachIndex=nAttachCount; nAttachIndex>0; nAttachIndex--)

    I am not familiar with C++, if there is something wrong in my post, please feel free to let me know.

    In my option, your issue is related with above code. You changed the attachments, but you loop the attachments one by one.

    I suggest you try to modify the code like below:

                    if (spAttachments != NULL)
    		{
    			long nAttachCount=0;
    			spAttachments->get_Count(&nAttachCount);
    
    			for(int nAttachIndex=nAttachCount; nAttachIndex>0; nAttachIndex--)
    			{
    		             //save the attachments and zip them
    			}
                            for(int nAttachIndex=nAttachCount; nAttachIndex>0; nAttachIndex--)
    			{
    		             //remove the attachments
    			}
                            for(int nAttachIndex=nAttachCount; nAttachIndex>0; nAttachIndex--)
    			{
    		             //attach the zip attachments
    			}
    		}

    Will the attachments be replaced by zip files?

    Best Regards,

    Edward


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    • Marked as answer by kekkou Friday, August 11, 2017 7:08 AM
    Wednesday, August 9, 2017 6:43 AM
  • Thanks.

    Wll, I have used another approch to resolve it.

    First to save all attachment COM objects, then loop them to remove, zip and zip one by one.

    std::vector< CComPtr<Outlook::Attachment> > vecAttachmentsNeedReattaching;
    
    for(int nAttachIndex=nAttachCount; nAttachIndex>0; nAttachIndex--)
    {		           
       vecAttachmentsNeedReattaching.push_back(spAttachment));
    }
    
    //
    
    std::vector< CComPtr<Outlook::Attachment> >::const_iterator itAtt;
    for( itAtt = vecAttachmentsNeedReattaching.begin(); itAtt != vecAttachmentsNeedReattaching.end(); ++itAtt)
    {
    	const CComPtr<Outlook::Attachment> &spAttachment = itAtt;
    	
    	// https://msdn.microsoft.com/en-us/vba/outlook-vba/articles/attachments-add-method-outlook
    	// To ensure consistent results, always save an item before adding or removing objects in the Attachments collection of the item.
    	
    	HRESULT hr = spTaskItem->Save();
    	
    	//spAttachment->SaveAsFile(CComBSTR(L"saved_attachment_file"));
    	
    	hr = spAttachment->Delete();
    	
    	//zip
    	
    	//Add  CComBSTR(L"saved_attachment_file")
    	
    }
    



    please verify my account.

    • Marked as answer by kekkou Friday, August 11, 2017 7:08 AM
    Friday, August 11, 2017 6:56 AM

All replies

  • Beautify the codes

    I use powershell to inspect or manipulate the email immediately.

    # [Launching Office Apps Programmatically](https://blogs.msdn.microsoft.com/andreww/2008/11/30/launching-office-apps-programmatically/)
    $OutlookApp=New-Object -ComObject Outlook.Application
    # downside is that you will only get the excel "instances" started by the same user that will initiate the ps1?
    # 1. New-Object -ComObject
    # 2. GetActiveObject will fail if not visiable after New-Object.
    # 3. AccessibleObjectFromWindow
    #http://stackoverflow.com/questions/779363/how-to-use-use-late-binding-to-get-excel-instance
    # Get reference to Outlook.Application from the ROT
    $OutlookApp=[Runtime.InteropServices.Marshal]::GetActiveObject('Outlook.Application')
    $item=$OutlookApp.ActiveInspector().CurrentItem
    
    PS C:\Users\sam> $item.Class
    48
    PS C:\Users\sam> [Enum]::ToObject([Microsoft.Office.Interop.Outlook.OlObjectClass], 48)
    olTask
    PS C:\Users\sam> $item.MessageClass
    IPM.Task
    
    PS C:\Users\sam> $item.Attachments | %{ New-Object psobject -property @{FileName=$_.FileName; Index=$_.Index} }
    FileName          Index
    --------          -----
    it_no.pot.zip       1
    it_no.ppt.zip       2
    it_no.pptm.zip      3
    it_no.pptx.zip      4
    it_no.pot           5
    it_no.potm.zip      6

    And I use VBA to register event and debug immediately

    'Track Message Headers
    
    ' [Using VBA to Manage Your Outlook Email Attachments)(http://www.fontstuff.com/outlook/oltut01.htm)
    
    'Option Explicit
    '' http://stackoverflow.com/questions/32149745/vba-run-time-error-2147221233-8004010f/32609779
    Const EXCHIVERB_REPLYTOSENDER = 102
    Const EXCHIVERB_REPLYTOALL = 103
    Const EXCHIVERB_FORWARD = 104
    
    Const PR_LAST_VERB_EXECUTED = "http://schemas.microsoft.com/mapi/proptag/0x10810003"
    Const PR_LAST_VERB_EXECUTION_TIME = "http://schemas.microsoft.com/mapi/proptag/0x10820040"
    Const PR_SMTP_ADDRESS = "http://schemas.microsoft.com/mapi/proptag/0x39FE001E"
    Const PR_RECEIVED_BY_ENTRYID = "http://schemas.microsoft.com/mapi/proptag/0x003F0102" ' As String
    
    Public ns As Outlook.NameSpace
    
    ' At the top of this OutlookSession Module (after option ... IF present)
    Public WithEvents LoadedMailItem As Outlook.MailItem
    Public WithEvents LoadedTaskItem As Outlook.TaskItem
    Public WithEvents LoadedTaskRequestItem As Outlook.TaskRequestItem
    Public WithEvents LoadedTaskRequestAcceptItem As Outlook.TaskRequestAcceptItem
    Private WithEvents oExpl As Explorer
    Private WithEvents oItem As MailItem
    
    
    'The Application ItemLoad event happens when the Item has not been fully initialized.
    'It is useful to hook up another Item Event, such as Open, Read (probably the one you want), etc.
    'We wold accomplish the Event you want with the code below, on the "ThisOutlookSession" class:
    'See https://msdn.microsoft.com/VBA/Outlook-VBA/articles/application-itemload-event-outlook
    Private Sub Application_ItemLoad(ByVal Item As Object)
        'If (TypeOf Item Is MailItem) Then ' or Item.Class = olMail
        '    Set LoadedMailItem = Item
        'ElseIf (TypeOf Item Is TaskItem) Then
        '    Set LoadedTaskItem = Item
        'ElseIf (TypeOf Item Is TaskRequestItem) Then
        '    Set LoadedTaskRequestItem = Item
        'End If
    End Sub
    
    Private Sub LoadedMailItem_Send(Cancel As Boolean)
        MsgBox LoadedMailItem.MessageClass
    End Sub
    
    Private Sub LoadedTaskRequestItem_Send(Cancel As Boolean)
        Cancel = True
        'MsgBox LoadedTaskRequestItem.MessageClass
        
        'Dim currentInspector As Outlook.Inspector
        'Set currentInspector = LoadedTaskRequestItem.GetInspector()
        'Set EmbeddedTaskItem = currentInspector.currentItem ' VBA cannot allow CurrentItem, always auto correct to currentItem
        'MsgBox EmbeddedTaskItem.MessageClass
    
        LoadedTaskRequestItem.Subject = LoadedTaskRequestItem.Subject & " LoadedTaskRequestItem@Item_Send"
        If Not TypeName(LoadedTaskRequestItem) = "Nothing" Then
            Set myNewTaskItem = LoadedTaskRequestItem.GetAssociatedTask(False)
            myNewTaskItem.Subject = myNewTaskItem.Subject & " myNewTaskItem@LoadedTaskRequestItem_Send"
            myNewTaskItem.Save
            'MsgBox myNewTaskItem.MessageClass
        End If
        LoadedTaskRequestItem.Save
        'Cancel = False
    End Sub
    Private Sub LoadedTaskItem_Send(Cancel As Boolean)
        MsgBox LoadedTaskItem.MessageClass
    End Sub
    
    
    
    Private Sub LoadedMailItem_AttachmentAdd(ByVal Attachment As Attachment)
        MsgBox ("LoadedMailItem_AttachmentAdd: " & LoadedMailItem.Subject)
    End Sub
    
    Private Sub LoadedMailItem_BeforeAttachmentWriteToTempFile(ByVal Attachment As Outlook.Attachment, ByRef Cancel As Boolean)
        MsgBox ("LoadedMailItem_BeforeAttachmentWriteToTempFile: " & Attachment.GetTemporaryFilePath)
    End Sub
    
    Private Sub LoadedMailItem_Reply(ByVal Response As Object, Cancel As Boolean)
        MsgBox "LoadedMailItem_Reply?" & LoadedMailItem.Subject
    End Sub
    
    Private Sub LoadedMailItem_ReplyAll(ByVal Response As Object, Cancel As Boolean)
        MsgBox "LoadedMailItem_ReplyAll?" & LoadedMailItem.Subject
    End Sub
    
    Private Sub LoadedMailItem_Forward(ByVal Forward As Object, Cancel As Boolean)
        MsgBox "LoadedMailItem_Forward?" & LoadedMailItem.Subject
    End Sub
    
    Private Sub LoadedMailItem_Open(ByRef Cancel As Boolean)
        MsgBox ("Item opened")
    End Sub
    
    Private Sub LoadedMailItem_Read()
        MsgBox "LoadedMailItem_Read " & LoadedMailItem.To
    End Sub
    
    Private Sub LoadedMailItem_UnLoad()
        MsgBox "LoadedMailItem_UnLoad"
    End Sub

    So, it's welcome for the above languages, i can understand


    please verify my account.




    • Edited by kekkou Wednesday, August 2, 2017 7:53 AM sam
    Wednesday, August 2, 2017 7:43 AM
  • So you re trying to attach a file to the task being sent as am embedded message attachment of the TaskRequestItem object? You are already have spIMessage from OpenProperty, call spIMessage::CreateAtach. MAPI system knowns nothing about  IID__TaskItem, so you cannot use any OOM interfaces.

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

    Wednesday, August 2, 2017 2:08 PM
  • Yes, I'm attaching some files to Task Request (See the following chart) in the send event programmatically.

    However, in the event send, the item is of type TaskRequestItem. So I call TaskRequestItem.GetAsscociatedTask(True) to create a new TaskItem, and then compose (remove or add attachments) in the new TaskItem, those changes will reflect on the UI. Afterward, SaveAs a file to disk and reattach (remove and add) the new TaskItem as the only attachment of TaskRequestItem. 

    I don't know how to indirectly get TaskItem from TaskRequestItem instead of reattaching. 

    Unfortunately, handling multiple files in the new TaskItem, e.g. remove a file and add a zipped file, within every step the order of attachments will be changed on windows 7 x64 and office x86 2010.

    The result is as following:

    Multiple files to zip as attachment of TaskRequestItem

    itar.potm.zip and itar.potm (zip and source) coexist and some files are not compressed


    please verify my account.



    • Edited by kekkou Thursday, August 3, 2017 12:39 PM result shotscreen
    Thursday, August 3, 2017 6:44 AM
  • I am confused - so you need to remove the original attachments? You can easily do that using Extended MAPI (or the RDO family of objects in Redemption if you don't want to deal with MAPI).

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

    Thursday, August 3, 2017 2:01 PM
  • Hi kekkou,

    >> for(int nAttachIndex=nAttachCount; nAttachIndex>0; nAttachIndex--)

    I am not familiar with C++, if there is something wrong in my post, please feel free to let me know.

    In my option, your issue is related with above code. You changed the attachments, but you loop the attachments one by one.

    I suggest you try to modify the code like below:

                    if (spAttachments != NULL)
    		{
    			long nAttachCount=0;
    			spAttachments->get_Count(&nAttachCount);
    
    			for(int nAttachIndex=nAttachCount; nAttachIndex>0; nAttachIndex--)
    			{
    		             //save the attachments and zip them
    			}
                            for(int nAttachIndex=nAttachCount; nAttachIndex>0; nAttachIndex--)
    			{
    		             //remove the attachments
    			}
                            for(int nAttachIndex=nAttachCount; nAttachIndex>0; nAttachIndex--)
    			{
    		             //attach the zip attachments
    			}
    		}

    Will the attachments be replaced by zip files?

    Best Regards,

    Edward


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    • Marked as answer by kekkou Friday, August 11, 2017 7:08 AM
    Wednesday, August 9, 2017 6:43 AM
  • Thanks.

    Wll, I have used another approch to resolve it.

    First to save all attachment COM objects, then loop them to remove, zip and zip one by one.

    std::vector< CComPtr<Outlook::Attachment> > vecAttachmentsNeedReattaching;
    
    for(int nAttachIndex=nAttachCount; nAttachIndex>0; nAttachIndex--)
    {		           
       vecAttachmentsNeedReattaching.push_back(spAttachment));
    }
    
    //
    
    std::vector< CComPtr<Outlook::Attachment> >::const_iterator itAtt;
    for( itAtt = vecAttachmentsNeedReattaching.begin(); itAtt != vecAttachmentsNeedReattaching.end(); ++itAtt)
    {
    	const CComPtr<Outlook::Attachment> &spAttachment = itAtt;
    	
    	// https://msdn.microsoft.com/en-us/vba/outlook-vba/articles/attachments-add-method-outlook
    	// To ensure consistent results, always save an item before adding or removing objects in the Attachments collection of the item.
    	
    	HRESULT hr = spTaskItem->Save();
    	
    	//spAttachment->SaveAsFile(CComBSTR(L"saved_attachment_file"));
    	
    	hr = spAttachment->Delete();
    	
    	//zip
    	
    	//Add  CComBSTR(L"saved_attachment_file")
    	
    }
    



    please verify my account.

    • Marked as answer by kekkou Friday, August 11, 2017 7:08 AM
    Friday, August 11, 2017 6:56 AM