none
Attachment info is not updated even the item is saved. RRS feed

  • Question

  • I used the following VB.NET code to get the attachment size.

    I open the attached file in Outlook, updated the file and saved it. I then also saved the task and close the Task window.

    However, the attachment.Size property doesn't get updated until I exit Outlook.

     For Each task In tasks
        For Each attachment In .Attachments
    writeLog("Size:" + attachment.Size.ToString())
        Next
      End With

    It looks like the updated version is cached somewhere in Outlook.

    Is that any way I can "flush the cache" to get the latest information of the attachments?

    or I have missed out something?

    Please advise.

    Thanks,

    Cowcow

    Friday, October 24, 2014 9:52 AM

Answers

  • It works on Win64

    ' For Win64
        <DllImport("MAPI32.DLL", CharSet:=CharSet.Ansi, EntryPoint:="HrGetOneProp")> _
        Private Shared Sub HrGetOneProp64(ByVal pmp As IntPtr, ByVal ulPropTag As UInteger, ByRef ppProp As IntPtr)
        End Sub

        <DllImport("MAPI32.DLL", CharSet:=CharSet.Ansi, EntryPoint:="HrSetOneProp")> _
        Private Shared Sub HrSetOneProp64(ByVal pmp As IntPtr, ByVal pprop As IntPtr)
        End Sub

        <DllImport("MAPI32.DLL", CharSet:=CharSet.Ansi, EntryPoint:="MAPIFreeBuffer")> _
        Private Shared Sub MAPIFreeBuffer64(ByVal lpBuffer As IntPtr)
        End Sub


        ' For Win32
        <DllImport("MAPI32.DLL", CharSet:=CharSet.Ansi, EntryPoint:="HrGetOneProp@12")> _
        Private Shared Sub HrGetOneProp32(ByVal pmp As IntPtr, ByVal ulPropTag As UInteger, ByRef ppProp As IntPtr)
        End Sub

        <DllImport("MAPI32.DLL", CharSet:=CharSet.Ansi, EntryPoint:="HrSetOneProp@8")> _
        Private Shared Sub HrSetOneProp32(ByVal pmp As IntPtr, ByVal pprop As IntPtr)
        End Sub

        <DllImport("MAPI32.DLL", CharSet:=CharSet.Ansi, EntryPoint:="MAPIFreeBuffer@4")> _
        Private Shared Sub MAPIFreeBuffer32(ByVal lpBuffer As IntPtr)
        End Sub


     Public Shared isWin64Exe As Boolean = (IntPtr.Size = 8)


        Private Shared Sub HrGetOneProp(ByVal pmp As IntPtr, ByVal ulPropTag As UInteger, ByRef ppProp As IntPtr)
            If (isWin64Exe) Then
                HrGetOneProp64(pmp, ulPropTag, ppProp)
            Else
                HrGetOneProp32(pmp, ulPropTag, ppProp)
            End If
        End Sub


        Private Shared Sub HrSetOneProp(ByVal pmp As IntPtr, ByVal pprop As IntPtr)
            If (isWin64Exe) Then
                HrSetOneProp64(pmp, pprop)
            Else
                HrSetOneProp32(pmp, pprop)
            End If

        End Sub

        Private Shared Sub MAPIFreeBuffer(ByVal lpBuffer As IntPtr)
            If (isWin64Exe) Then
                MAPIFreeBuffer64(lpBuffer)
            Else
                MAPIFreeBuffer32(lpBuffer)
            End If
        End Sub

    • Marked as answer by cowcow222 Saturday, October 25, 2014 6:36 AM
    Saturday, October 25, 2014 6:36 AM

All replies

  • Hello,

    What code do you use for modifying the attachment? Did you try to call the Save method after?

    Outlook doesn't reflect changes immediately. Sometimes you need to close the inspector window and release all references in the code to get changes visible.

    For Each task In tasks

    Use the for loop instead. Also I'd suggest releasing underlying COM objects instantly. Use System.Runtime.InteropServices.Marshal.ReleaseComObject to release an Outlook object when you have finished using it. Then set a variable to Nothing in Visual Basic (null in C#) to release the reference to the object.
    Friday, October 24, 2014 10:31 AM
  •    

    It's not that case.

    I open a Outlook 2013 and update the attachment in the task, save the task and then close the Outlook Task window.(There is no any custom program). 
    I then open the task again and use the OutlookSpy to check the attachment.size properties. It's still the old value.

    I have another standalone VB.NET program. I run that program, and retrieve the task and it's attachments. I check the attachments size using  attachment.Size. 
    It still the old value. I then, exit Outlook. 

    In my VB.NET program, I run that function again to get the attachment size. It returns the correct value after I exit Outlook. 

    I have released the object like this, but I don't think this is the issue. Because I update the value using Oultook, not in my VB.NET program. Even without my program, the OutlookSpy shows that the attachment.size doesn't update until I exit Outlook and start Outlook again.

    Here is my code segment for refernece:

    With task
    attachments = .Attachments
        For Each attachment In attachments

    writeLog("Size:" + attachment.Size.ToString())

    Marshal.ReleaseComObject(attachment)
    attachment = Nothing
        Next
    Marshal.ReleaseComObject(attachments)
         attachments = Nothing
    End With

    Marshal.ReleaseComObject(task)
    task = Nothing

    I am sorry that I don't know what you mean Inspector Window

    Thanks,

    Cowcow

    Friday, October 24, 2014 11:57 AM
  • Do you have any add-ins installed for Outlook?
    Friday, October 24, 2014 12:23 PM
  • No.

    I have already clean my own add-in. All are default Add-Ins when install Outlook, except the OutlookSpy that I just installed to test it.

    Friday, October 24, 2014 1:08 PM
  • I think I found the solution.

    I use this code and it works:


    With task
    attachments = .Attachments
        For Each attachment In attachments

    Dim aSize As Long = GetMAPIProperty(attachment.MAPIOBJECT, PR_ATTACH_SIZE)


    Marshal.ReleaseComObject(attachment)
    attachment = Nothing
        Next
    Marshal.ReleaseComObject(attachments)
         attachments = Nothing 
    End With

    Marshal.ReleaseComObject(task)
    task = Nothing


     It retuns the correct value even without having to exit Outlook.


    My last quesiton is since I called the "MAPI32.DLL", what happen if the  Outlook client is 64bit? Will it work?




    In case anyone need it, here is the code:


    ===========================================

        Private Const PR_ATTACH_SIZE As Integer = &HE200003


        <DllImport("MAPI32.DLL", CharSet:=CharSet.Ansi, EntryPoint:="HrGetOneProp@12")> _
        Private Shared Sub HrGetOneProp(ByVal pmp As IntPtr, ByVal ulPropTag As UInteger, ByRef ppProp As IntPtr)
        End Sub

        <DllImport("MAPI32.DLL", CharSet:=CharSet.Ansi, EntryPoint:="HrSetOneProp@8")> _
        Private Shared Sub HrSetOneProp(ByVal pmp As IntPtr, ByVal pprop As IntPtr)
        End Sub

        <DllImport("MAPI32.DLL", CharSet:=CharSet.Ansi, EntryPoint:="MAPIFreeBuffer@4")> _
        Private Shared Sub MAPIFreeBuffer(ByVal lpBuffer As IntPtr)
        End Sub

        <DllImport("MAPI32.DLL", CharSet:=CharSet.Ansi)> _
        Private Shared Function MAPIInitialize(ByVal lpMapiInit As IntPtr) As Integer
        End Function

        <DllImport("MAPI32.DLL", CharSet:=CharSet.Ansi)> _
        Private Shared Sub MAPIUninitialize()
        End Sub

        Private Structure SPropValue
            Public ulPropTag As UInteger
            Public dwAlignPad As UInteger
            Public Value As Long
        End Structure

        ' return codes
        Private Const S_OK As Integer = 0


        Private Shared Function GetMAPIProperty(ByVal oMAPIObject As Object, ByVal uiPropertyTag As UInteger) As Long

            If oMAPIObject.Equals(Nothing) Then
                'No MAPI Object
                Return ""
            End If

            Dim sProperty As Long = -1
            Dim pPropValue As IntPtr = IntPtr.Zero

            Dim IUnknown As IntPtr = IntPtr.Zero
            Dim IMAPIProperty As IntPtr = IntPtr.Zero

            Try

                ' initialize MAPI
                MAPIInitialize(IntPtr.Zero)

                ' get the unknown object.
                IUnknown = Marshal.GetIUnknownForObject(oMAPIObject)

                'get the property
                Dim guidIMAPIProp As New Guid(IID_IMAPIProp)
                If Marshal.QueryInterface(IUnknown, guidIMAPIProp, IMAPIProperty) <> S_OK Then
                    'Failed to get IMAPIProperty
                    Return ""
                End If

                Try

                    ' get the field from the MAPI Property
                    HrGetOneProp(IMAPIProperty, uiPropertyTag, pPropValue)
                    ' Is the property actually there?
                    If pPropValue = IntPtr.Zero Then
                        Return ""
                    End If
                    ' Get the value back
                    Dim propValue As SPropValue = DirectCast(Marshal.PtrToStructure(pPropValue, GetType(SPropValue)), SPropValue)
                    ' convert to string
                    sProperty = propValue.Value
                    '  sProperty = Marshal.PtrToStringAnsi(New IntPtr(propValue.Value))
                Catch ex As System.Exception
                    Throw ex
                End Try
            Finally
                ' CLEAN UP
                If pPropValue <> IntPtr.Zero Then
                    MAPIFreeBuffer(pPropValue)
                End If

                If IMAPIProperty <> IntPtr.Zero Then
                    Marshal.Release(IMAPIProperty)
                End If
                If IUnknown <> IntPtr.Zero Then
                    Marshal.Release(IUnknown)
                End If

                MAPIUninitialize()
            End Try

            Return sProperty
        End Function




    ===========================================

    • Proposed as answer by Deric Ferreira Friday, October 24, 2014 1:44 PM
    Friday, October 24, 2014 1:44 PM
  • It works on Win64

    ' For Win64
        <DllImport("MAPI32.DLL", CharSet:=CharSet.Ansi, EntryPoint:="HrGetOneProp")> _
        Private Shared Sub HrGetOneProp64(ByVal pmp As IntPtr, ByVal ulPropTag As UInteger, ByRef ppProp As IntPtr)
        End Sub

        <DllImport("MAPI32.DLL", CharSet:=CharSet.Ansi, EntryPoint:="HrSetOneProp")> _
        Private Shared Sub HrSetOneProp64(ByVal pmp As IntPtr, ByVal pprop As IntPtr)
        End Sub

        <DllImport("MAPI32.DLL", CharSet:=CharSet.Ansi, EntryPoint:="MAPIFreeBuffer")> _
        Private Shared Sub MAPIFreeBuffer64(ByVal lpBuffer As IntPtr)
        End Sub


        ' For Win32
        <DllImport("MAPI32.DLL", CharSet:=CharSet.Ansi, EntryPoint:="HrGetOneProp@12")> _
        Private Shared Sub HrGetOneProp32(ByVal pmp As IntPtr, ByVal ulPropTag As UInteger, ByRef ppProp As IntPtr)
        End Sub

        <DllImport("MAPI32.DLL", CharSet:=CharSet.Ansi, EntryPoint:="HrSetOneProp@8")> _
        Private Shared Sub HrSetOneProp32(ByVal pmp As IntPtr, ByVal pprop As IntPtr)
        End Sub

        <DllImport("MAPI32.DLL", CharSet:=CharSet.Ansi, EntryPoint:="MAPIFreeBuffer@4")> _
        Private Shared Sub MAPIFreeBuffer32(ByVal lpBuffer As IntPtr)
        End Sub


     Public Shared isWin64Exe As Boolean = (IntPtr.Size = 8)


        Private Shared Sub HrGetOneProp(ByVal pmp As IntPtr, ByVal ulPropTag As UInteger, ByRef ppProp As IntPtr)
            If (isWin64Exe) Then
                HrGetOneProp64(pmp, ulPropTag, ppProp)
            Else
                HrGetOneProp32(pmp, ulPropTag, ppProp)
            End If
        End Sub


        Private Shared Sub HrSetOneProp(ByVal pmp As IntPtr, ByVal pprop As IntPtr)
            If (isWin64Exe) Then
                HrSetOneProp64(pmp, pprop)
            Else
                HrSetOneProp32(pmp, pprop)
            End If

        End Sub

        Private Shared Sub MAPIFreeBuffer(ByVal lpBuffer As IntPtr)
            If (isWin64Exe) Then
                MAPIFreeBuffer64(lpBuffer)
            Else
                MAPIFreeBuffer32(lpBuffer)
            End If
        End Sub

    • Marked as answer by cowcow222 Saturday, October 25, 2014 6:36 AM
    Saturday, October 25, 2014 6:36 AM