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: 103
Joined: 2016-09-28
Ok .... I am going to find it difficult to describe this problem so please bear with me and don't hesitate to ask for clarification.

In our Add-in we process items in the Sent Items folder.

When the user composes an email we use the MailItem's PropertyAccessor to set a few header values. In the Sent Items these are subsequently picked up, and the user is then shown a dialog in which they can make a decision. One of the decisions the user can make is to Cancel out of the dialog, at which point we clear those properties out of the PropertyAccessor and Save the MailItem.

This is where it gets interesting. We look at the Sent Items folder periodically to see whether any recent mails in that folder have such properties in their PropertyAccessor, and if they do we present the user with that dialog.

Users are reporting that sometimes the dialog pops up for a Mail Item that they recently already got the dialog for and that they Canceled out of. So I know that we removed those properties from the PropertyAccessor and Saved the email, so our routine should actually ignore such a Mail Item.

One intriguing thing is that the problem seems to "go away" when the user unchecks Cached Exchange Mode.

So I am wondering whether I am observing an issue here where we do work on a "local" copy of a Mail Item which then somehow gets overwritten by Outlook after retrieving an "original" from the Mail Server and that that causes these Properties to "reappear"? If so... is there anything I can do about this, something to tell Outlook that the "local" version is the most current one and it should take precedence over the "server" version so that if anything gets updated by outlook it should be Local-> Server, not Server -> Local

Am I barking up the right tree?
Posted 25 Sep, 2019 05:13:53 Top
Andrei Smolin


Add-in Express team


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

Do you use Items.Restrict to get the items with your property? How do you filter out emails non-processed?

Regards from Belarus (GMT+3),

Andrei Smolin
Add-in Express Team Leader
Posted 25 Sep, 2019 06:41:21 Top
Pino Carafa




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

No we don't use that.... our approach is probably a lot less elegant. We sort the Sent Items folder by [SentOn] descending and then go through that list until we hit one that is older than a certain number of days and we ignore the rest. For every MailItem we find we check whether it has that property, and if it does, we'll process it.

Now in the meantime I have observed a few interesting things.

When the user hits cancel we remove a few properties from the propertyaccessor and we remove and set a few properties from the .UserProperties and then we perform a .Save() on the MailItem. Stepping through the code I noticed that the .Saved property at the point where we perform .Save() was still True despite us setting and removing these. I tried to "force" it by setting the .Body, .RTFBody c.q. .HTMLBody to itself (depending on the bodyformat) which does flip the value of .Saved back to False alright, and then .Save() it - but it made no change to this behaviour.

I DID however notice something interesting. I added a breakpoint in the code where it tries to reprocess the sent items, and I stopped it from performing this work when it tried to reproces a mailitem out of which I had canceled. When I then opened that mailitem I noticed this at the top:

"This is the most recent version but you have made changes to another copy. Click here to see other versions."

I wonder whether that is what is behind all this, and if so.... what can I do to prevent it?
Posted 26 Sep, 2019 03:57:33 Top
Andrei Smolin


Add-in Express team


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

Pino Carafa writes:
Stepping through the code I noticed that the .Saved property at the point where we perform .Save() was still True despite us setting and removing these.


This may be an indication of that item being non released by your add-in; possibly, by some other add-in (turn all of them off) or by another part of your add-in.

You should release every COM object created in your code. You create a COM object by mere calling a property or method returning an object type (not a string, Boolean, number, or enum). OutlookApp.ActiveExplorer().Selection[1] creates these COM objects:

- Outlook.Explorer
- Outlook.Selection
- object containing the currently selected item

Your add-in should release all of them.

In your scenario, you should have that email released by the moment you get it from the folder.

Recommended is: release a COM object as soon as you don't need it. If you need to cache a COM object, cache the information that needed to recreate it; e.g. MailItem.EntryId.

Restrict works faster than sorting. At https://www.add-in-express.com/creating-addins-blog/2011/11/10/outlook-newmail-custom-solution/, we use it in a similar scenario.

Regards from Belarus (GMT+3),

Andrei Smolin
Add-in Express Team Leader
Posted 26 Sep, 2019 04:18:24 Top
Pino Carafa




Posts: 103
Joined: 2016-09-28
Thanks Andrei I will look into that situation
Posted 26 Sep, 2019 04:34:36 Top
Andrei Smolin


Add-in Express team


Posts: 17493
Joined: 2006-05-11
You are welcome!

Regards from Belarus (GMT+3),

Andrei Smolin
Add-in Express Team Leader
Posted 26 Sep, 2019 04:55:05 Top
Pino Carafa




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

Ok I think I have a workable solution now but it's still a bit strange.

I've implemented some of the recommendations you gave me here and this is what I found.

Let's say I have a Shared string variable containing an Entry ID and a Date variable containing the Sent On date. By doing this i can ensure that nothing "outlook" itself is doing could possibly "get in the way". I create a System.Windows.Forms.Timer and when it ticks I look at these variables. The timer "ticks" and at that point I have literally *nothing* other than those two variables. And if I then simply destroy the timer, do nothing, and I look at my sent items I can confirm that no item is showing this "This is the most recent version but you have made changes to another copy" message. So all is well.

Now I have a string containing an Entry ID. I find that I cannot use that in a .Restrict as per:
https://docs.microsoft.com/en-us/office/vba/api/outlook.items.restrict
"This method cannot be used and will cause an error with the following properties" - and EntryID is listed. I can confirm that this does indeed raise an error. That's why I got the Date variable. I use Restrict to find only the items sent on that Date and then go through the few of those to find the item that matches the Entry ID.

Try
oItemsMatch = moSentItems.Restrict(sFilter)
Catch ex As Exception

End Try
If Not oItemsMatch Is Nothing Then
Dim nItems As Integer = oItemsMatch.Count
Dim nItem As Integer = 1
While nItem <= nItems
Try
oItemObject = oItemsMatch.Item(nItem)
Catch

End Try
Try
If oItemObject.EntryID = oItem.EntryID Then
bFound = True
End If
Catch

End Try
If bFound Then
Exit While
End If
Try
Marshal.FinalReleaseComObject(oItemObject)
Catch

End Try
oItemObject = Nothing
nItem = nItem + 1
End While
Try
Marshal.FinalReleaseComObject(oItemsMatch)
Catch

End Try
oItemsMatch = Nothing
End If
End If

If oItemObject is not nothing after this, I get rid of the final reference
Try
Marshal.FinalReleaseComObject(oItemObject)
Catch

End Try

Because I'm doing all this in the "tick" handler of my own timer I am not worried that I may be releasing a handle on anything that Outlook is still using, so this is quite safe.

If I leave it at THIS, I find that the messages are still perfectly fine. None of them show the "This is the most recent version but you have made changes to another copy" message, so all is still well..

(more to follow)
Posted 26 Sep, 2019 09:26:19 Top
Pino Carafa




Posts: 103
Joined: 2016-09-28
I then add another piece of code to get an actual Outlook.Mailitem object:

If Not oItemObject Is Nothing Then
oMailItem = TryCast(oItemObject, Outlook.MailItem)
End If

and then I add code to check a User Property (for this example I used a User Property rather than using the PropertyAccessor)

If Not oMailItem Is Nothing Then
Dim bAlreadyChecked As Boolean = True
Dim oProps As Outlook.UserProperties = Nothing
Try
oProps = oMailItem.UserProperties
Catch

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

End Try
oProps = Nothing

If Not bAlreadyChecked Then
oMailItem.Save()
End If
Try
Marshal.FinalReleaseComObject(oMailItem)
Catch

End Try
oMailItem = Nothing
End If


As far as I can tell I'm still releasing everything I need to release but NOW it does show "This is the most recent version but you have made changes to another copy"

I eventually found what seems to be a workable solution. After doing oMailItem.Save I do oMailItem.Copy, then oMailItem.Delete - after releasing the references to the mailitem and its copy I no longer get the "This is the most recent version but you have made changes to another copy" message. But if I'm not releasing what I should be releasing I don't know what....
Posted 26 Sep, 2019 09:32:19 Top
Pino Carafa




Posts: 103
Joined: 2016-09-28
Ahhhhh. wait...... oProps.Add("SentItemChecked", Outlook.OlUserPropertyType.olText)

I bet I need to also release the object *it* returns.
Posted 26 Sep, 2019 09:50:46 Top
Pino Carafa




Posts: 103
Joined: 2016-09-28
Alas that didn't work either .... *but* copying and deleting does. Ah well. It'll have to do. :)
Posted 26 Sep, 2019 09:57:46 Top
Pino Carafa




Posts: 103
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: 17493
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: 103
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: 17493
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: 103
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: 103
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: 17493
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: 103
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: 17493
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: 103
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
Andrei Smolin


Add-in Express team


Posts: 17493
Joined: 2006-05-11
A great point it is!

I think the only way to bypass such issues is to get, cache, pass and release COM objects in an order; the order being obviously the matter of personal taste, experience and views.

We suggest that instead of holding a COM object for any significant time, you store the information required to recreate the COM object. This corresponds to the next rule that we actively use: Release COM objects as soon as you're done with them. We've met a rule further beautifying this one: Release COM objects in the order reverse to the order of their creation.

One more rule refers to passing a COM object: the caller is responsible for the COM object passed to a callee; the callee should not release COM objects received. This only works with Add-in Express components. Without them, if you connect to an Office event via

C#: {some object such as ExcelApp or Outlook.Explorer}.{event name} += {event handler}
VB.NET: AddHandler {some object such as ExcelApp or Outlook.Explorer}.{event name}, AddressOf {event handler} 


you are responsible for releasing a COM object(s) that the event handler receives! Not releasing such a COM object(s) can cause an issue as well.

Also, we suggest that you add an element of order to the way you release a COM object:

C#: Marshal.ReleaseComObject(someObject); someObject = null;
VB.NET: Marshal.ReleaseComObject(someObject): someObject = Nothing


Strictly speaking, nullifying the variable above is not required and it might be omitted. But there's a possibility that later on you'll modify the code so that someObject is used after it is released. If the nullifying statement is present, you'll get an acquaintance - NullReferenceException. Otherwise, you'll me a hostile stranger - InvalidComObject shouting at you that "COM object that has been separated from its underlying RCW cannot be used". Well, sort of personal, too.

Regards from Belarus (GMT+3),

Andrei Smolin
Add-in Express Team Leader
Posted 11 Oct, 2019 06:04:50 Top
Pino Carafa




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

This is exactly what I ended up doing and my addin as a result is now a lot more robust.

The only issue that still persists is effectively the same as the one described here:
https://www.add-in-express.com/forum/read.php?TID=15024

As Bert Sinnema already discovered, nothing you can do will stop the behaviour from happening. He had the problem with Outlook 2016, and I can now confirm that the same problem exists in Outlook 2013

As far as I can tell the behaviour ONLY manifests itself when adding a UserProperty to a mailitem, and even more specifically, it ONLY happens on items in the Sent Items

I tried the suggestion made in the thread in many different ways. I even went as far as taking an entry ID, kicking off a timer waiting 10 seconds and in the timer setting the User Property. No joy; still the same problem.

But what *does* work, as clunky as it may seem is this:

- Create a Copy object: oMailItemCopy = oMailItem.Copy
- Delete the Mail item: oMailItem.Delete
- Delete the most recent item in the Deleted Items folder (as described in many threads here, works perfectly)
- ReleaseComObject on oMailItem

The user won't be any the wiser, but now you no longer get that message.
Posted 11 Oct, 2019 15:28:29 Top
Andrei Smolin


Add-in Express team


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

Pino Carafa writes:
kicking off a timer


A System.Windows.Forms.Timer?

Are all other add-ins turned off? Is the Reading pane turned off? What is the error message? What is the caption of the error message window?

Regards from Belarus (GMT+3),

Andrei Smolin
Add-in Express Team Leader
Posted 14 Oct, 2019 00:57:24 Top
Pino Carafa




Posts: 103
Joined: 2016-09-28
It's not an error message, Andrei. The only symptom showing me that anything is wrong is when I open the message, and at the top of the message there's a little line that states "This is the most recent version, but you made changes to another copy. Click here to see the other versions."

The (pseudo) code in the timer "tick" boils down to this:

Use some EntryID to GetItemFromID into oMailItem

oProps = oMailItem.UserProperties
oProp = oProps.Find("<someproperty>")
If oProp Is Nothing Then
oProp = oProps.Add("<someproperty>")
End IF
Marshal.ReleaseComObject(oProp)
Marshal.ReleaseComObject(oProps)

oMailItem.Save()
Marshal.ReleaseComObject(oMailItem)

(obviously there is some error handling and setting objects to Nothing in there too, but I've left that out here)

If I leave out the code between Setting the oProps variable and Releasing it, the issue doesn't happen.

What I also noticed is that if I allow the Timer to "tick" more than once for a particlar EntryID then even though it has ticked before and I set the Property on the previous "tick", the .Find returns Nothing on the next Tick. Which makes sense in the context of my GetItemFromID retrieving a "different copy" from the one I updated earlier.

To recap, and answering your questions:
- I haven't yet tried it will all other Add-ins turned off. Will do that now and I'll let you know what I find.
- The Reading Pane is off
- It's not an error message but a notification at the top of the Inspector window
Posted 14 Oct, 2019 03:17:31 Top
Pino Carafa




Posts: 103
Joined: 2016-09-28
Alas, with all other Add-ins disabled the same thing still happens :(
Posted 14 Oct, 2019 03:33:36 Top
Pino Carafa




Posts: 103
Joined: 2016-09-28
Please note that this happens in the same scenario as that described by Bert Sinnema in his issue. Exchange is online (Outlook 365) and Outlook is running with Cached Exchange Mode switched on. The only difference seems to be that Bert's client is Outlook 2016 while I'm reproducing this in Outlook 2013
Posted 14 Oct, 2019 04:31:50 Top
Andrei Smolin


Add-in Express team


Posts: 17493
Joined: 2006-05-11
http://temp.add-in-express.com/support/MyAddin136-PinoCarafa.zip

Can you reproduce the issue with this add-in?

Regards from Belarus (GMT+3),

Andrei Smolin
Add-in Express Team Leader
Posted 14 Oct, 2019 04:49:59 Top
Pino Carafa




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

Yes. The same thing happens with that add-in.
Posted 14 Oct, 2019 05:12:38 Top
Andrei Smolin


Add-in Express team


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

Could you please send me a video showing how do you test the project?

Regards from Belarus (GMT+3),

Andrei Smolin
Add-in Express Team Leader
Posted 14 Oct, 2019 05:26:14 Top
Pino Carafa




Posts: 103
Joined: 2016-09-28
Sure.... How can I do that? I don't seem to have the option to attach files here?
Posted 14 Oct, 2019 05:34:01 Top
Andrei Smolin


Add-in Express team


Posts: 17493
Joined: 2006-05-11
You can send it to the support email address; find it in {Add-in Express installation folder}\readme.txt; please make sure your email contains a link to this topic.

Regards from Belarus (GMT+3),

Andrei Smolin
Add-in Express Team Leader
Posted 14 Oct, 2019 05:34:49 Top
Pino Carafa




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

Thanks for that - I sent same to the support email address.
Posted 14 Oct, 2019 06:39:44 Top
Andrei Smolin


Add-in Express team


Posts: 17493
Joined: 2006-05-11
Thank you. I see. Let's do the following changes:
- don't release oMailItem; this was a mistake; still this doesn't influence the issue
- check oMailItem.Saved and only if it returns true, save the item; otherwise, do not save.

Public Overrides Sub ItemAdd(ByVal Item As Object, ByVal SourceFolder As Object)
    System.Diagnostics.Debug.WriteLine("!!! ItemAdd")
    Dim oMailItem As Outlook.MailItem = CType(Item, Outlook.MailItem)
    Dim oProps As Outlook.UserProperties
    Dim oProp As Outlook.UserProperty
    oProps = oMailItem.UserProperties
    oProp = oProps.Find("MyTestProperty")
    If oProp Is Nothing Then
        System.Diagnostics.Debug.WriteLine("!!! ItemAdd. UP not found")
        oProp = oProps.Add("MyTestProperty", Outlook.OlUserPropertyType.olText)
    End If
    Marshal.ReleaseComObject(oProp)
    Marshal.ReleaseComObject(oProps)

    If Not oMailItem.Saved Then
        oMailItem.Save()
        System.Diagnostics.Debug.WriteLine("!!! ItemAdd. email saved.")
    End If
End Sub


BTW, do you check the debug messages? The change above is made to handle several ItemAdd events occurring for the same item.

Regards from Belarus (GMT+3),

Andrei Smolin
Add-in Express Team Leader
Posted 14 Oct, 2019 07:35:31 Top
Pino Carafa




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

I took your code and expanded on it a little bit:


        System.Diagnostics.Debug.WriteLine("!!! ItemAdd")
        Dim bAlreadyThere As Boolean = False
        Dim oMailItem As Outlook.MailItem = CType(Item, Outlook.MailItem)
        Dim oProps As Outlook.UserProperties
        Dim oProp As Outlook.UserProperty
        oProps = oMailItem.UserProperties
        oProp = oProps.Find("SentItemChecked")
        If oProp Is Nothing Then
            System.Diagnostics.Debug.WriteLine("!!! ItemAdd. UP not found")
            oProp = oProps.Add("SentItemChecked", Outlook.OlUserPropertyType.olText)
        Else
            bAlreadyThere = True
        End If
        Marshal.ReleaseComObject(oProp)
        Marshal.ReleaseComObject(oProps)

        If Not oMailItem.Saved Then
            If Not bAlreadyThere Then
                System.Diagnostics.Debug.WriteLine("!!! ItemAdd. New Property added and Mailitem not Saved so this should be all good.")
                oMailItem.Save()
                System.Diagnostics.Debug.WriteLine("!!! ItemAdd. email saved.")
            Else
                System.Diagnostics.Debug.WriteLine("!!! ItemAdd. Mail flagged as ""Not Saved"" even though we added no new properties!?!?!?")
            End If
        Else
            If Not bAlreadyThere Then
                System.Diagnostics.Debug.WriteLine("!!! ItemAdd. Mail flagged as already saved even though we added a new Property!?!?!?")
            Else
                System.Diagnostics.Debug.WriteLine("!!! ItemAdd. Property is already there and Mailitem is already Saved so this should be all good.")
            End If
        End If


I sent 4 messages. This is the Debug output:
!!! ItemAdd
!!! ItemAdd. UP not found
!!! ItemAdd. New Property added and Mailitem not Saved so this should be all good.
!!! ItemAdd. email saved.
!!! ItemAdd
!!! ItemAdd. UP not found
!!! ItemAdd. New Property added and Mailitem not Saved so this should be all good.
!!! ItemAdd. email saved.
!!! ItemAdd
!!! ItemAdd. UP not found
!!! ItemAdd. New Property added and Mailitem not Saved so this should be all good.
!!! ItemAdd. email saved.
!!! ItemAdd
!!! ItemAdd. UP not found
!!! ItemAdd. New Property added and Mailitem not Saved so this should be all good.
!!! ItemAdd. email saved.

But it still shows that line at the top of sent emails
Posted 14 Oct, 2019 08:57:57 Top
Pino Carafa




Posts: 103
Joined: 2016-09-28
Here's something else that's rather intriguing....

The message is shown at the top of the four emails I sent. Then I exit from Outlook and I start Outlook again. When I then open each e-mail, the message is no longer there.
Posted 14 Oct, 2019 09:01:38 Top
Pino Carafa




Posts: 103
Joined: 2016-09-28
And I am wrong. Immediately after starting Outlook the message wasn't displayed. But I checked again a few minutes later.... and it's back.
Posted 14 Oct, 2019 09:06:55 Top
Pino Carafa




Posts: 103
Joined: 2016-09-28
Oh my word... I'm so sorry Andrei .... Ignore my last couple of comments. I was mixing up my Inbox and my Sent Items folder. Can happen when you test software by sending emails to yourself *grin*

No... Restarting Outlook makes no difference. The "This is the most recent version" message is already there immediately after restarting outlook.

So please ignore the two comments I sent *after* the one in which I posted the Debug output. They were mistaken.
Posted 14 Oct, 2019 09:15:05 Top
Pino Carafa




Posts: 103
Joined: 2016-09-28
Ok Andrei.... "the plot thickens"

I switched to an Outlook Profile with Cached Exchange Mode switched *off*. This led to some very interesting new issues.

Referring back to my comment of Posted 14 Oct, 2019 07:35:31

The statement oMailItem.Save() would sometimes - ONLY sometimes - fail:
"An exception of type 'System.Runtime.InteropServices.COMException' occurred in MyAddin136.dll but was not handled in user code
Additional information: The function cannot be performed because the message has been changed."

While stopped at that statement I tried to run the following command from the Immediate Window
Marshal.ReleaseComObject(oMailItem)
This led to
"Managed Debugging Assistant 'RaceOnRCWCleanup' has detected a problem in 'C:\Program Files (x86)\Microsoft Office\Office15\OUTLOOK.EXE'.
Additional Information: An attempt has been made to free an RCW that is in use. The RCW is in use on the active thread or another thread. Attempting to free an in-use RCW can cause corruption or data loss."

When I try the statement a second time, no error is raised
Before executing Marshal.ReleaseComObject I had done
?oMailItem.EntryID which gave me an Entry ID for the message, so now I tried:

oMailItem = DirectCast(AddinModule.CurrentInstance().HostApplication, Outlook.Application).GetNamespace("MAPI").GetItemFromID(....

This gives me a new oMailItem object. I then go through the steps again, and I find that now the .Find succeeds. And oMailItem.Saved is *False*.

Interesting.
Posted 14 Oct, 2019 09:45:15 Top
Pino Carafa




Posts: 103
Joined: 2016-09-28
It seems like, when I switch to NON-Cached Exchange Mode, I run into something similar to this:

https://www.add-in-express.com/forum/read.php?TID=8961
Posted 14 Oct, 2019 10:00:30 Top
Pino Carafa




Posts: 103
Joined: 2016-09-28
Ok - I thought I saw a topic on this forum once where a similar issue was resolved by using a "wait a while" method or something like that?

So in light of that vague memory I added a bit of code to your sample, Andrei - in the ItemAdd I simply take note of the Entry ID (I took Option Strict off, by the way)


    Public Overrides Sub ItemAdd(ByVal Item As Object, ByVal SourceFolder As Object)

        Dim sEntryID As String = String.Empty
        Try
            sEntryID = Item.EntryID
        Catch

        End Try

        If Not String.IsNullOrEmpty(sEntryID) Then
            Dim oFoo As System.Windows.Forms.Timer
            oFoo = New Timer
            oFoo.Interval = 20000
            oFoo.Tag = sEntryID
            AddHandler oFoo.Tick, AddressOf FooTimer_Tick
            oFoo.Start()
        End If

        'Marshal.ReleaseComObject(oMailItem)
    End Sub


The code that was originally in the ItemAdd I moved to the Tick event handler


    Private Shared Sub FooTimer_Tick(sender As Object, e As EventArgs)

        Dim oTimer As Windows.Forms.Timer = Nothing
        Dim sEntryID As String

        oTimer = TryCast(sender, Windows.Forms.Timer)

        If Not oTimer Is Nothing Then
            oTimer.Stop()
            Try
                sEntryID = oTimer.Tag
            Catch

            End Try
            oTimer.Dispose()
            oTimer = Nothing
        End If

        If Not String.IsNullOrEmpty(sEntryID) Then
            Dim oMailitem As Object = Nothing

            Try
                oMailitem = DirectCast(AddinModule.CurrentInstance().HostApplication, Outlook.Application).GetNamespace("MAPI").GetItemFromID(sEntryID)
            Catch

            End Try
            If Not oMailitem Is Nothing Then
                System.Diagnostics.Debug.WriteLine("!!! ItemAdd")
                Dim bAlreadyThere As Boolean = False

                Dim oProps As Outlook.UserProperties
                Dim oProp As Outlook.UserProperty
                oProps = oMailitem.UserProperties
                oProp = oProps.Find("SentItemChecked")
                If oProp Is Nothing Then
                    System.Diagnostics.Debug.WriteLine("!!! ItemAdd. UP not found")
                    oProp = oProps.Add("SentItemChecked", Outlook.OlUserPropertyType.olText)
                Else
                    bAlreadyThere = True
                End If
                If Not oProp Is Nothing Then
                    oProp.Value = "2"
                End If
                Marshal.ReleaseComObject(oProp)
                Marshal.ReleaseComObject(oProps)

                If Not oMailitem.Saved Then
                    If Not bAlreadyThere Then
                        System.Diagnostics.Debug.WriteLine("!!! ItemAdd. New Property added and Mailitem not Saved so this should be all good.")
                        oMailitem.Save()
                        System.Diagnostics.Debug.WriteLine("!!! ItemAdd. email saved.")
                    Else
                        System.Diagnostics.Debug.WriteLine("!!! ItemAdd. Mail flagged as ""Not Saved"" even though we added no new properties!?!?!?")
                    End If
                Else
                    If Not bAlreadyThere Then
                        System.Diagnostics.Debug.WriteLine("!!! ItemAdd. Mail flagged as already saved even though we added a new Property!?!?!?")
                    Else
                        System.Diagnostics.Debug.WriteLine("!!! ItemAdd. Property is already there and Mailitem is already Saved so this should be all good.")
                    End If
                    'Dim oMailItemCopy As Outlook.MailItem = DirectCast(oMailItem.Copy, Outlook.MailItem)
                    'oMailItem.Delete()
                    'Marshal.ReleaseComObject(oMailItem)
                    'DeleteDeletedItem()
                    'oMailItem = oMailItemCopy
                End If

                'If Not oMailitem.Saved Then
                '    Try
                '        oMailitem.Save()
                '    Catch ex As Exception

                '    End Try
                'End If
                Try
                    Marshal.ReleaseComObject(oMailitem)
                Catch

                End Try
                oMailitem = Nothing
            End If
        End If
    End Sub


The good news is that everything seems to be working perfectly after that. The bad news is that our clients are so trigger happy that they are quite likely to be clicking on Sent Items before the 20 seconds are up. They also expect a dialog giving them various options to handle a Sent Item immediately after sending so if this is delayed as it is in this example, they will be screaming at us. So this would not be an acceptable solution. Not sure where that leaves me as a developer.
Posted 14 Oct, 2019 10:51:18 Top
Andrei Smolin


Add-in Express team


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

Set oFoo.Interval = 500 instead. Does this work?

Regards from Belarus (GMT+3),

Andrei Smolin
Add-in Express Team Leader
Posted 15 Oct, 2019 01:41:07 Top
Pino Carafa




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

No... Or rather.... I sent 4 emails in quick succession. Of the four emails 3 are showing that message, one appears to be ok.

Then, to speed it up even further, I composed 4 emails simultaneously so that four windows were open ready to send and then I hit send,send,send,send

All four of those are showing the message.

Even more interesting. I added code to actually set a value in the User Property and I added the User Property to the View in the Sent Items folder. I composed 4 emails simultaneously and I hit send, send, send, send.

This is the Debug output which looks fine:

!!! ItemAdd
!!! ItemAdd. UP not found
!!! ItemAdd. New Property added and Mailitem not Saved so this should be all good.
!!! ItemAdd. email saved.
!!! ItemAdd
!!! ItemAdd. UP not found
!!! ItemAdd. New Property added and Mailitem not Saved so this should be all good.
!!! ItemAdd. email saved.
!!! ItemAdd
!!! ItemAdd. UP not found
!!! ItemAdd. New Property added and Mailitem not Saved so this should be all good.
!!! ItemAdd. email saved.
!!! ItemAdd
!!! ItemAdd. UP not found
!!! ItemAdd. New Property added and Mailitem not Saved so this should be all good.
!!! ItemAdd. email saved.


Now get this..... I'm looking at the Sent Items folder and the Debug messages are already displayed in the Immediate window. In the Sent Items folder I can see the value I put in the User Properties in the column against one of the messages.

And then.... after a few seconds .... the value disappears. Without me doing anything. No further messages in the Immediate window either. I'll send you another video illustrating this
Posted 15 Oct, 2019 02:33:48 Top
Andrei Smolin


Add-in Express team


Posts: 17493
Joined: 2006-05-11
Does that warning disappear if you perform Send/Receive?

Does that warning show up if you send emails to you from outside of your Exchange?

Regards from Belarus (GMT+3),

Andrei Smolin
Add-in Express Team Leader
Posted 15 Oct, 2019 02:38:08 Top
Pino Carafa




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

I just sent you another MP4. I tried the same thing first with Cached Exchange Mode ON and then with Cached Exchange Mode OFF.

I will now try your other suggestions.
Posted 15 Oct, 2019 03:20:14 Top
Pino Carafa




Posts: 103
Joined: 2016-09-28
> Does that warning disappear if you perform Send/Receive?

No. Once the value "disappears" from the Sent Items, it stays gone and the message shows at the top of the Inspector. I tried Send/Receive and I tried "Update Folders".

> Does that warning show up if you send emails to you from outside of your Exchange?
Actually.... that doesn't apply in this situation as the issue happens in my Sent Items. In my Inbox I have not had the problem. Instead, I tried sending the email from my Exchange to my gmail accounts. The same behaviour happens in Cached Exchange Mode, the User Property "disappears". I did notice something peculiar though while watching the Sent Items folder. When the email first appears there with the property set, it also shows the TO address as
'ro*******@gmail.com'
Then as the Userproperty is blanked out, the TO address changes to
ro*******@gmailcom - the single quotes disappear.
Posted 15 Oct, 2019 03:30:10 Top
Pino Carafa




Posts: 103
Joined: 2016-09-28
"In my Inbox I have not had the problem."

That's not right. I switched to our own add-in and I sent 4 emails from my gmail account to my Exchange account. With Cached Exchange switched OFF everything is fine. But when Cached Exchange is switched ON I do get the message on some of my inbox items. It just doesn't appear to be quite as bad as with the Sent Items, and as opposed to the Sent Items where I have a User Property named SentItemChecked, in the Inbox I have a User Property named ReceivedItemChecked and it is always set, even when the message is displayed at the top of the Inspector.
Posted 15 Oct, 2019 04:02:23 Top
Andrei Smolin


Add-in Express team


Posts: 17493
Joined: 2006-05-11
The warning suggest clicking it to see other versions of the email. Does clicking it opens any email? If yes, what folder does such an email originates from? does the email contain the UserProperty?

Also, try using _Mailitem instead of Mailitem in that code.

Regards from Belarus (GMT+3),

Andrei Smolin
Add-in Express Team Leader
Posted 15 Oct, 2019 05:05:52 Top
Pino Carafa




Posts: 103
Joined: 2016-09-28
> Does clicking it opens any email?

Yes it does - it opens the "other version" in a new Inspector. To my untrained eye the two copies look identical... *but* here is where it gets interesting.
Both the "original" message and the copy that is shown look identical, but when I click on the File tab in the Inspector window ...

> what folder does such an email originates from
On both messages, when I click on the File Tab in the Inspector window there is a button to "Move item to a different folder". In the original item, to the right of that Button it says "Current Folder: Sent Items". In the "other version", however, it says "Current Folder: Conflicts"

> does the email contain the UserProperty
*blush* ... I can't seem to find a way to show an email's "User Properties" through the Inspector Window. How do I do that?

I'll try your suggestion to use _Mailitem next
Posted 15 Oct, 2019 05:32:00 Top
Andrei Smolin


Add-in Express team


Posts: 17493
Joined: 2006-05-11
New info.

I've reproduced the issue.

The item opened from Sent Items:
- doesn't have the UserProperty object
- originates from the SentItems folder (this is expected)
- has an item in the MailItem.conflicts collection.

The other item:
- has the UserProperty
- originates from the folder Sync Issues\Conflicts
- has an item in the MailItem.conflicts collection.

Regards from Belarus (GMT+3),

Andrei Smolin
Add-in Express Team Leader
Posted 15 Oct, 2019 05:39:27 Top
Pino Carafa




Posts: 103
Joined: 2016-09-28
Ok that's brilliant.... Now .... can we avoid the problem?
Posted 15 Oct, 2019 06:05:17 Top
Pino Carafa




Posts: 103
Joined: 2016-09-28
By the way... I tried using _MailItem but had no joy with that.
Posted 15 Oct, 2019 06:07:37 Top
Andrei Smolin


Add-in Express team


Posts: 17493
Joined: 2006-05-11
In my case, the warning is gone on all the items after a while. The UserProperty is gone, too. That is, Outlook (or rather Exchange) has resolved the conflict in this way; meaning, you should not modify the item in the ItemAdd event.

What is your final goal? Would it be okay for you to add something to the interned header before the item is sent (say, in the ItemSend event) and then grab this something in the ItemAdd event? Note that that something will be available for the email recipients as well. I've googled out this topic: https://www.add-in-express.com/forum/read.php?FID=5&TID=14156 (HA-HA-HA).

Regards from Belarus (GMT+3),

Andrei Smolin
Add-in Express Team Leader
Posted 15 Oct, 2019 06:19:15 Top
Andrei Smolin


Add-in Express team


Posts: 17493
Joined: 2006-05-11
Another way to check is: use PropertyAccessor to create a custom named property, not a UserProperty.

Regards from Belarus (GMT+3),

Andrei Smolin
Add-in Express Team Leader
Posted 15 Oct, 2019 06:27:02 Top
Pino Carafa




Posts: 103
Joined: 2016-09-28
LOL that is very funny indeed but yes, there is the root cause of the problem.

We actually do exactly that - thank you once again for the suggestion you made at the time.

We set Internet headers in the ItemSend event, and we pick them up later in order to do something when the item appears in the Sent Items. All that is working very well.

We write software for solicitors who work with "cases" and each "case" has a "case code". When the user wants to send an email relating to a specific "case" they can select that "case" while composing the email, and we add information using the PropertyAccessor.

All of that is working very well, and when the user then Sends the email and it arrives in the Sent Items, we can use the Property Accessor to decide whether we need to process the email and store it in our "case management system".

BUT

At that point we DO need to set a UserProperty. The reason for that is that the user can then use the Field Chooser on the Sent Items View, and they can pick the User Properties we created - for example the "Case Code" - ... And that's why we can't have User Properties disappearing on us after we set them.
Posted 15 Oct, 2019 06:34:49 Top
Andrei Smolin


Add-in Express team


Posts: 17493
Joined: 2006-05-11
Considering the way Outlook synchronizes data with Exchange and the absence of clear indications of when the process starts and finishes, I think there's no solution to this.

I suppose that 20 seconds may not be enough if there's a connectivity issue external to that machine.

I've also found that you may observe the result of the conflict resolution using MailItem.AutoResolvedWinner and MailItemIsConflict; see https://docs.microsoft.com/en-us/office/vba/api/outlook.mailitem.autoresolvedwinner and https://docs.microsoft.com/en-us/office/vba/api/outlook.mailitem.isconflict. I don't know what to do with these, however.

At the moment, I don't have other ideas.

Regards from Belarus (GMT+3),

Andrei Smolin
Add-in Express Team Leader
Posted 15 Oct, 2019 10:21:11 Top
Andrei Smolin


Add-in Express team


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

When you search something in Outlook, it shows the Search context tab. There's the Indexing Status button under Search Tools. The button works as follows. Whenever Outlook finds many non-indexed items (say, after you delete an .OST file), clicking Indexing Status shows the number of item still to be indexed. That is, Outlook is indexing the items in the background; you click the button and the code behind the button finds how many items are to be indexed and returns the result. Note that the button doesn't reflect the status of indexing (="it doesn't receive an event when the indexing is complete"); instead, it creates an answer to this question "How many items to index do you have?" *whenever* you ask it.

A suggestion. You can make the users used to some items not having that user property. I would add it after 30 seconds and check if doing this creates a conflict. If there's a conflict, you try to create the user property next time/session/whatever. That is, the user property gets added to all the items but not immediately. To let the user ask what's the case ID of this item", you could provide a context menu button that would read the Internet header and supply the user with the case ID. In addition, if the user switches to that folder and that user property is listed in view fields, you could show a pane informing the user about the context menu button and that the user property would be added when possible.

Regards from Belarus (GMT+3),

Andrei Smolin
Add-in Express Team Leader
Posted 16 Oct, 2019 03:35:58 Top
Pino Carafa




Posts: 103
Joined: 2016-09-28
Thanks Andrei I think we'll have to take that kind of an approach alright. Our customers won't be perfectly happy but at least we can point them at this thread if they ever wonder why we can't give them *everything* they want :)
Posted 16 Oct, 2019 05:24:23 Top
Andrei Smolin


Add-in Express team


Posts: 17493
Joined: 2006-05-11
Alas, everything is not possible.

Regards from Belarus (GMT+3),

Andrei Smolin
Add-in Express Team Leader
Posted 16 Oct, 2019 06:38:18 Top
Andrei Smolin


Add-in Express team


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

I've found a solution - How to solve a synchronization conflict when modifying an Outlook item in ItemAdd event.

Regards from Belarus (GMT+3),

Andrei Smolin
Add-in Express Team Leader
Posted 26 Feb, 2020 02:48:10 Top
Pino Carafa




Posts: 103
Joined: 2016-09-28
Reading that I recognised so many of the issues I faced when I asked this and other questions! Thanks for the detailed explanation.
Posted 26 Feb, 2020 10:04:31 Top
Andrei Smolin


Add-in Express team


Posts: 17493
Joined: 2006-05-11
You are welcome !

Regards from Belarus (GMT+3),

Andrei Smolin
Add-in Express Team Leader
Posted 26 Feb, 2020 10:25:06 Top