Properties removed from PropertyAccessor sometimes "resurfacing"?

Add-in Express™ Support Service
That's what is more important than anything else

Properties removed from PropertyAccessor sometimes "resurfacing"?
Issue with Sent Items - Outlook 365 - Cached Exchange Mode? 
Pino Carafa




Posts: 81
Joined: 2016-09-28
Ah sorry I just realised I forgot to explain what I mean by Copy and Delete

After calling .Save on the MailItem I call .Copy and then .Delete

I then use Marshal.FinalReleaseCOMObject to release both the original MailItem and the Copy. As far as the user is concerned "nothing" has happened, but the Item they see in their Sent Items is now the Copy rather than the "original". This Copy has the UserProperty changes as well as the changes I made through the PropertyAccessor. And the message "This is the most recent version but you have made changes to another copy" is no longer shown. Bizarre. I guess some Marshaling reference must still remain unreleased *somewhere* but I'm at my wit's end here. This seems to work around the problem to my satisfaction and, hopefully, that of our customers.
Posted 26 Sep, 2019 10:17:17 Top
Andrei Smolin


Add-in Express team


Posts: 16673
Joined: 2006-05-11
Hello Pino,

Pino Carafa writes:
...I'm still releasing everything I need to release...


This isn't so.

'Pino Carafa writes:
If oProps.Find("SentItemChecked") Is Nothing Then 
   oProps.Add("SentItemChecked", Outlook.OlUserPropertyType.olText) 
   bAlreadyChecked = False 
End If 
Try 
    Marshal.FinalReleaseComObject(oProps) 
Catch
End Try 


This code fragment should be rewritten so that the UserProperty object is released as well:


If oProps IsNot Nothing Then
   Dim oProp as Outlook.UserProperty = oProps.Find("SentItemChecked")
   If oProp Is Nothing Then 
      oProp = oProps.Add("SentItemChecked", Outlook.OlUserPropertyType.olText) 
      bAlreadyChecked = False 
   End If 
   If oProp IsNot Nothing Then Marshal.FinalReleaseComObject(oProp)
   Marshal.FinalReleaseComObject(oProps)
   oProps = Nothing
End If


I strongly recommend that every Catch block informs you about getting the error; otherwise, you may spend a LOT of time on getting this code work correctly in ALL scenarios.

Regards from Belarus (GMT+3),

Andrei Smolin
Add-in Express Team Leader
Posted 27 Sep, 2019 02:22:04 Top
Pino Carafa




Posts: 81
Joined: 2016-09-28
"This isn't so."

Indeed.... I realised this after a while. I'm currently going through all the code to see where else I missed out on releasing such objects. It's a long hard slog but worth it - thank you so much for all your help.
Posted 27 Sep, 2019 05:11:59 Top
Andrei Smolin


Add-in Express team


Posts: 16673
Joined: 2006-05-11
Welcome!

Regards from Belarus (GMT+3),

Andrei Smolin
Add-in Express Team Leader
Posted 27 Sep, 2019 05:57:54 Top
Pino Carafa




Posts: 81
Joined: 2016-09-28
Hi Andrei.... me again.

I'm hitting the proverbial "Brick Wall".

I have now reduced the issue to this

I declare a variable to hold a reference to the "Sent Items" folder:

Public WithEvents moSentItems As Microsoft.Office.Interop.Outlook.Items


This is instantiated as follows

moSentItems = MAPINamespace.GetDefaultFolder(OlDefaultFolders.olFolderSentMail).Items


I was monitoring the _ItemAdd event, but even then I was getting a problem with a message stating that the message cannot be saved because it has been changed

I removed the WithEvents

Instead of monitoring the event I now use a Timer


    Private WithEvents mtSentItemsMonitor As System.Windows.Forms.Timer
    Private mdLastSentItem As Date = System.DateTime.Now

    Public Sub StartSentItemsMonitor()

        If mtSentItemsMonitor Is Nothing Then
            mtSentItemsMonitor = New Forms.Timer
            mtSentItemsMonitor.Interval = 10000
            mtSentItemsMonitor.Start()
        End If

    End Sub

    Public Sub StopSentItemsMonitor()

        If Not mtSentItemsMonitor Is Nothing Then
            Try
                mtSentItemsMonitor.Stop()
            Catch

            End Try
            Try
                mtSentItemsMonitor.Dispose()
            Catch

            End Try
            mtSentItemsMonitor = Nothing
        End If

    End Sub


when the AddinModule starts I call the StartSentItemsMonitor() procedure

Globals.goEventHandler.moSentItems = OutlookCode.SentItemsFolder.Items
Globals.goEventHandler.StartSentItemsMonitor()


This is the code of the event handler

    Private Sub mtSentItemsMonitor_Tick(sender As Object, e As EventArgs) Handles mtSentItemsMonitor.Tick

        If moSentItems Is Nothing Then
            Exit Sub
        End If

        Dim sFilter As String = String.Empty
        Dim dLastSentItem As Date

        dLastSentItem = mdLastSentItem
        sFilter = "[SentOn] >= '" + mdLastSentItem.ToString("yyyy-MM-dd HH:mm") + "'"

        Dim oItemsMatch As Outlook.Items = Nothing
        Try
            oItemsMatch = moSentItems.Restrict(sFilter)
        Catch ex As Exception

        End Try

        If oItemsMatch.Count > 0 Then
            Dim nItems As Integer = oItemsMatch.Count
            Dim nItem As Integer = 0
            While nItem < nItems
                nItem = nItem + 1

                Dim oItem As Object = Nothing

                Try
                    oItem = oItemsMatch.Item(nItem)
                Catch ex As Exception

                End Try

                If Not oItem Is Nothing Then
                    Dim oMailItem As Outlook.MailItem = Nothing
                    Try
                        oMailItem = TryCast(oItem, Outlook.MailItem)
                    Catch ex As Exception

                    End Try

                    If Not oMailItem Is Nothing Then
                        If oMailItem.SentOn > mdLastSentItem Then
                            Dim bNew As Boolean = False
                            Dim oUserProps As Outlook.UserProperties = Nothing

                            Try
                                oUserProps = oMailItem.UserProperties
                            Catch ex As Exception

                            End Try

                            If Not oUserProps Is Nothing Then
                                Dim oUserProp As Outlook.UserProperty = Nothing
                                Try
                                    oUserProp = oUserProps.Find("SentItemChecked")
                                Catch ex As Exception

                                End Try
                                If oUserProp Is Nothing Then
                                    Try
                                        oUserProp = oUserProps.Add("SentItemChecked", OlUserPropertyType.olText)
                                    Catch ex As Exception

                                    End Try
                                    Try
                                        oMailItem.Save()
                                        If oMailItem.SentOn > dLastSentItem Then
                                            dLastSentItem = oMailItem.SentOn
                                        End If
                                        bNew = True
                                    Catch ex As Exception 'This is where it goes wrong but only sometimes

                                    End Try
                                Else
                                    If oMailItem.SentOn > dLastSentItem Then
                                        dLastSentItem = oMailItem.SentOn
                                    End If
                                End If
                                If Not oUserProp Is Nothing Then
                                    Try
                                        Marshal.FinalReleaseComObject(oUserProp)
                                    Catch ex As Exception

                                    End Try
                                    oUserProp = Nothing
                                End If
                                Try
                                    Marshal.FinalReleaseComObject(oUserProps)
                                Catch ex As Exception

                                End Try
                                oUserProps = Nothing
                            End If
                            If bNew Then
                                MsgBox("hello world")
                            End If
                        End If
                        Try
                            Marshal.FinalReleaseComObject(oMailItem)
                        Catch ex As Exception

                        End Try
                        oMailItem = Nothing
                    End If
                    Try
                        Marshal.FinalReleaseComObject(oItem)
                    Catch ex As Exception

                    End Try
                End If
            End While
        End If

        If dLastSentItem > mdLastSentItem Then
            mdLastSentItem = dLastSentItem
        End If

        If Not oItemsMatch Is Nothing Then
            Try
                Marshal.FinalReleaseComObject(oItemsMatch)
            Catch ex As Exception

            End Try
        End If
    End Sub


When it *does* go wrong, this is the message I'm now getting
{System.Runtime.InteropServices.COMException (0x80040109): The function cannot be performed because the message has been changed.
at Microsoft.Office.Interop.Outlook._MailItem.Save()
at KeyhouseOutlookAddin.EventHandler.mtSentItemsMonitor_Tick(Object sender, EventArgs e) in C:\GitSC\Framework\KeyhouseOutlookAddin\BL\EventHandler.vb:line 260}


When I had similar code in the _ItemAdd event handler before, I was unable to do anything about it. In this Tick() event, however - as you can see - I clean up after myself and allow it to "tick" again, and on the next tick the problem is gone.

As I have stripped out anything *our* add-in is doing, the cause of the problem seems to be external to our code. Any idea who or what could be the culprit here? Is there a way I can programmatically check whether the "message has been changed" *without* trying to execute the Save() method?
Posted 27 Sep, 2019 07:14:06 Top
Pino Carafa




Posts: 81
Joined: 2016-09-28
by the way - that is with Cached Exchange Mode switched *off*
Posted 27 Sep, 2019 07:20:31 Top
Andrei Smolin


Add-in Express team


Posts: 16673
Joined: 2006-05-11
Hello Pino,

I think you've overreacted with Try/Catch blocks and FinalReleaseComObject. You shouldn't use the latter at all; use ReleaseComObject instead. As far as I remember TryCast(oItem, Outlook.MailItem) can't produce an exception.

Is the issue reproducible if you turn off all other COM add-ins and the Reading Pane?

Regards from Belarus (GMT+3),

Andrei Smolin
Add-in Express Team Leader
Posted 27 Sep, 2019 10:05:28 Top
Pino Carafa




Posts: 81
Joined: 2016-09-28
Hello Andrei,

Sorry for the long silence but I have finally been able to work around the problem keeping your various recommendations in mind. Thanks again for all the help. I have one new issue but I'll raise that separately.
Posted 10 Oct, 2019 09:01:44 Top
Andrei Smolin


Add-in Express team


Posts: 16673
Joined: 2006-05-11
No problem at all.

Regards from Belarus (GMT+3),

Andrei Smolin
Add-in Express Team Leader
Posted 10 Oct, 2019 10:09:24 Top
Pino Carafa




Posts: 81
Joined: 2016-09-28
Just a general observation... I think this is a trap that developers like myself - people who normally work within the context of "ordinary" .NET development with its lovely "managed" classes and its Garbage Collector can easily fall into. For example, I think I made a lot of mistakes with UserProperties by writing code like this:

oProps = .UserProperties

oProps.Add <something>

mailitem.Save

and THEN I would use Marshal.ReleaseComObject to release oProps ...

This was when I was getting strange phantom "copies" of mail items - where Outlook would say that "this is the most recent copy" and that sort of weirdness.

When a "better" way to do this would be:

oProps = .UserProperties

Any time you need to add, delete or modify a UserProperty do something like

oProp = .... e.g. an .Add or a .Find

When you get an oProp and you are finished with it:

Marshal.ReleaseComObject(oProp)

finally when you're done and you don't need the oProps object any longer:

Marshal.ReleaseComObject(oProps)

and only THEN

mailitem.save

and eventually - (but ONLY if the mail item isn't something that was - say - passed into an event procedure)

Marshal.ReleaseComObject on the mail item itself


Talk about learning things the hard way *smile*
Posted 11 Oct, 2019 03:40:43 Top