OutlookItemEvents and Explorer Connection

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

OutlookItemEvents and Explorer Connection
Connecting explorer to OutlookItemEvents 
Alex Carter




Posts: 59
Joined: 2019-02-21
Hi All,
I have followed a couple of the blog articles to hook up the OutlookItemEvents class to the Inspector and Explorers and to make use of the events of mailitems.

I have used the following guides:
https://www.add-in-express.com/creating-addins-blog/2011/11/28/handle-outlook-item-events/
https://www.add-in-express.com/creating-addins-blog/2011/09/06/outlook-item-events/

The first guide has got my Inspector successfully hooked up and I am seeing the PropertyChange event fired and it works great.

I have followed the second guide to try and hook up the explorer (usually when it is an inline response), I can see it executing the ConnectToSelectedItem function but the events never fire when I change a property such as To or Subject.

Below is the code I am using to hook up my explorer:


    Private Sub ConnectToSelectedItem(ByVal explorer As Object)
        Dim sel As Outlook.Selection = Nothing
        Try
            Dim expl As Outlook._Explorer = CType(explorer, Outlook._Explorer)
            sel = expl.Selection
            If sel.Count = 1 Then
                Dim item As Object = sel(1)
                If TypeOf item Is Outlook._MailItem Then
                    OutlookItemEvents.ConnectTo(item, True)
                Else
                    Marshal.ReleaseComObject(item)
                End If
            End If

        Finally

        End Try
    End Sub

    Private Sub AdxOutlookAppEvents_ExplorerActivate(sender As Object, explorer As Object) Handles AdxOutlookAppEvents.ExplorerActivate
        ConnectToSelectedItem(explorer)
    End Sub

    Private Sub AdxOutlookAppEvents_ExplorerClose(sender As Object, explorer As Object) Handles AdxOutlookAppEvents.ExplorerClose
        Dim count As Integer = 0
        Dim expls As Outlook._Explorers = Nothing

        Try
            expls = OutlookApp.Explorers
            count = expls.Count
        Catch
        Finally
            If expls IsNot Nothing Then
                Marshal.ReleaseComObject(expls)
            End If
        End Try

        If count = 0 Then
            CanConnect = False
        End If
    End Sub


Can anyone assist with getting the events for my inline responses to fire correctly?

On a side not is it possible to get the MailItem from inside my ADXForm? I need to get the mailItem the ADXForm is currently embedded in and send it to another function, currently I have this bit of code in my ADXForm:


    Private Sub chkDoNotFile_Click(sender As Object, e As RoutedEventArgs) Handles chkDoNotFile.Click
        am.DoBackgroundPaint()
    End Sub


Would the sender be the active inspector/explorer? Could I just make use of this?

Thanks in advance!
Posted 19 Feb, 2020 09:47:33 Top
Andrei Smolin


Add-in Express team


Posts: 18844
Joined: 2006-05-11
Hello Alex,

The item selected and the item shown in an inline response are different items! See Explorer.ActiveInlineResponse. Also, you may need to use InlineResponse-related events of the Outlook Events component.


Andrei Smolin
Add-in Express Team Leader
Posted 19 Feb, 2020 10:05:20 Top
Alex Carter




Posts: 59
Joined: 2019-02-21
Ah OK I should know this by now! So I can just use the InlineResponseEx and CloseEx events to grab the inline response and connect that to the OutlookItemEvents Class then?
Posted 19 Feb, 2020 10:50:43 Top
Andrei Smolin


Add-in Express team


Posts: 18844
Joined: 2006-05-11
Hello Alex,

I think yes, you can. Note that that item is being composed, not viewed. In other words, you can't expect it fires the Reply event, for instance. It is okay to wait for the Write event though.


Andrei Smolin
Add-in Express Team Leader
Posted 20 Feb, 2020 02:27:41 Top
Alex Carter




Posts: 59
Joined: 2019-02-21
OK so I'm already using he ExplorerInlineResponseEx event as follows:


    Private Sub AdxOutlookAppEvents_ExplorerInlineResponseEx(sender As Object, itemObject As Object, sourceObject As Object) Handles AdxOutlookAppEvents.ExplorerInlineResponseEx
        Dim form As EmailAutoFilingHost = CType(Me.AdxOlFormsCollectionItem1.GetForm(sourceObject), EmailAutoFilingHost)

        If TypeOf itemObject Is Outlook.MailItem Then
            Dim item As Outlook._MailItem = CType(itemObject, Outlook._MailItem)
            OutlookItemEvents.ConnectTo(item, True)
        End If

        If form IsNot Nothing Then
            form.ShowFields()
        End If
    End Sub


And here is my ProcessPropertyChange event in the OutlookItemEvents class:

    Public Overrides Sub ProcessPropertyChange(ByVal Name As String)
        Dim props() As String = {"TO", "CC", "BCC", "SUBJECT"}

        If Not String.IsNullOrEmpty(Name) Then
            Dim found As String = Array.Find(props, Function(x) (x.Equals(Name.ToUpper)))

            If found IsNot Nothing Then
                If TypeOf Me.ItemObj Is Outlook._MailItem Then
                    Dim mailItem As Outlook._MailItem = CType(Me.ItemObj, Outlook._MailItem)
                    If Not mailItem.Sent Then
                        am.DoBackgroundPaint()
                    End If
                End If
            End If
        End If
    End Sub


It hooks in correctly and fires the ProcessPropertyChange event however on line

If TypeOf Me.ItemObj Is Outlook._MailItem Then


of the event I receive the following error:
System.Runtime.InteropServices.InvalidComObjectException: 'COM object that has been separated from its underlying RCW cannot be used.'

Any ideas on this?
Posted 20 Feb, 2020 07:39:58 Top
Andrei Smolin


Add-in Express team


Posts: 18844
Joined: 2006-05-11
Hello Alex,

The issue originates from the other method:


Private Sub AdxOutlookAppEvents_ExplorerInlineResponseEx(sender As Object, itemObject As Object, sourceObject As Object) Handles AdxOutlookAppEvents.ExplorerInlineResponseEx 
    Dim form As EmailAutoFilingHost = CType(Me.AdxOlFormsCollectionItem1.GetForm(sourceObject), EmailAutoFilingHost) 
 
    If TypeOf itemObject Is Outlook.MailItem Then 
        Dim item As Outlook._MailItem = CType(itemObject, Outlook._MailItem) 
        OutlookItemEvents.ConnectTo(item, True) 
    End If 


In this event handler, you connect to the COM object that the item and itemObject variables point to. The problem is: Add-in Express releases that COM object right after the method above completes.

To bypass this, create a different COM object representing the same email and ConnectTo that COM object:


Private Sub AdxOutlookAppEvents_ExplorerInlineResponseEx(sender As Object, itemObject As Object, sourceObject As Object) Handles AdxOutlookAppEvents.ExplorerInlineResponseEx 
    Dim form As EmailAutoFilingHost = CType(Me.AdxOlFormsCollectionItem1.GetForm(sourceObject), EmailAutoFilingHost) 
 
    If TypeOf itemObject Is Outlook.MailItem Then 
        Dim item As Outlook._MailItem = CType(itemObject, Outlook._MailItem)
        Dim newItemReference As Object = Marshal.GetUniqueObjectForIUnknown(Marshal.GetIUnknownForObject(itemObject)) 
        OutlookItemEvents.ConnectTo(newItemReference, True) 
    End If 


Please let me know whether this compiles and works.


Andrei Smolin
Add-in Express Team Leader
Posted 20 Feb, 2020 08:48:37 Top
Alex Carter




Posts: 59
Joined: 2019-02-21
Thanks Andre,
This works great, I have taken a similar approach in my OutlookItemEvents class to maintain a new object in the DoBackgroundPaint function. Code snippets below:


    Private Sub AdxOutlookAppEvents_ExplorerInlineResponseEx(sender As Object, itemObject As Object, sourceObject As Object) Handles AdxOutlookAppEvents.ExplorerInlineResponseEx
        Dim form As EmailAutoFilingHost = CType(Me.AdxOlFormsCollectionItem1.GetForm(sourceObject), EmailAutoFilingHost)

        If TypeOf itemObject Is Outlook.MailItem Then
            Dim item As Object = Marshal.GetUniqueObjectForIUnknown(Marshal.GetIUnknownForObject(itemObject))
            OutlookItemEvents.ConnectTo(item, True)
            DoBackgroundPaint(item)
        End If

        If form IsNot Nothing Then
            form.ShowFields()
        End If
    End Sub

    Private Sub AdxOutlookAppEvents_ExplorerInlineResponseCloseEx(sender As Object, sourceObject As Object) Handles AdxOutlookAppEvents.ExplorerInlineResponseCloseEx
        Dim form As EmailAutoFilingHost = CType(Me.AdxOlFormsCollectionItem1.GetForm(sourceObject), EmailAutoFilingHost)
        If form IsNot Nothing Then
            form.HideFields()
            form.Clear()
        End If

        If OutlookItemEvents.IsConnected Then
            OutlookItemEvents.RemoveConnection()
        End If
    End Sub

    Public Overrides Sub ProcessPropertyChange(ByVal Name As String)
        Dim props() As String = {"TO", "CC", "BCC", "SUBJECT"}

        If Not String.IsNullOrEmpty(Name) Then
            Dim found As String = Array.Find(props, Function(x) (x.Equals(Name.ToUpper)))

            If found IsNot Nothing Then
                If TypeOf Me.ItemObj Is Outlook._MailItem Then
                    Dim mailItem As Object = Marshal.GetUniqueObjectForIUnknown(Marshal.GetIUnknownForObject(Me.ItemObj))
                    If Not mailItem.Sent Then
                        am.DoBackgroundPaint(mailItem)
                    End If
                End If
            End If
        End If
    End Sub

    Public Sub DoBackgroundPaint(Optional ByVal mail As Outlook.MailItem = Nothing)
        Dim form As EmailAutoFilingHost = CType(Me.AdxOlFormsCollectionItem1.GetCurrentForm(), EmailAutoFilingHost)
        Dim mailItem As Object = Marshal.GetUniqueObjectForIUnknown(Marshal.GetIUnknownForObject(mail))

        If mail IsNot Nothing And form IsNot Nothing Then
            If HasExternalRecipients(mail.Recipients) And StringHasCaseCodes(mail.Subject) And Not form.ShouldNotFile Then
                form.DoBackgroundPaint(True, OfficeColorScheme)
            Else
                form.DoBackgroundPaint(False, OfficeColorScheme)
            End If
        End If
    End Sub


One final question, when I tick a box on my ADXForm at the top of the mailitem, is it possible to get the mailitem that the ADX form is associated with from the ADX form. Or does this need to be done by looking at the active window/inspector/explorer? I ask as I need to fire off the DoBackgroundPaint function when this box is ticked & pass the mailitem to it.
Posted 20 Feb, 2020 10:34:04 Top
Andrei Smolin


Add-in Express team


Posts: 18844
Joined: 2006-05-11
Hello Alex,

Alex Carter writes:
HasExternalRecipients(mail.Recipients)

This call either leaves the Recipients collection unreleased or the HasExternalRecipients() function has a side effect: in addition to returning a Boolean it releases that COM object. I would do that call in this way:


Dim recipients as Outlook.Recipients = mail.Recipients
Dim hasRecipients as Boolean = HasExternalRecipients(recipients)
Marshal.ReleaseComObject(recipients): recipients = Nothing
If hasRecipients And StringHasCaseCodes(mail.Subject) And Not form.ShouldNotFile Then
...


Alex Carter writes:
is it possible to get the mailitem that the ADX form is associated with from the ADX form.


See ADXOlForm.InspectorObj. If it is not Nothing - and it is Nothing (null in C#) if the ADXOlForm is shown in an Explorer window - you cast it to Outlook.Inspector and retrieve Inspector.CurrentItem.

You shouldn't release what ADXOlForm.InspectorObj returns - Add-in Express relies on it. You should release the result of the Inspector.CurrentItem call.


Andrei Smolin
Add-in Express Team Leader
Posted 21 Feb, 2020 01:49:33 Top
Alex Carter




Posts: 59
Joined: 2019-02-21
Morning Andre,
Apologies for the late reply I've been on with several things! I've implemented your suggestions and also made use of InspectorObj & ExplorerObj, it works great.

As usual, thanks for all the help!
Posted 26 Feb, 2020 05:27:26 Top
Andrei Smolin


Add-in Express team


Posts: 18844
Joined: 2006-05-11
Hello Alex,

No problem at all! It's great to have this issue solved.


Andrei Smolin
Add-in Express Team Leader
Posted 26 Feb, 2020 05:38:32 Top