none
Modal Outlook 2010 inspector with custom ribbon does not close properly RRS feed

  • Question

  • A modal Outlook inspector with a custom ribbon will, when its initiated by Excel/Word "Save and Send" event , not properly close after the item has been sent. After the message has been sent it causes the File Dialog Box to be hidden and blocking access to the document or sheet. Only by restarting Outlook will the Dialog appear.

    http://social.msdn.microsoft.com/Forums/en-US/vsto/thread/243e8465-a638-41b1-ac28-b4f7262457f9 suggsts using a pinvoke command but that does not seem to work.
    Appearently it is known bug and there is probably no elegant way but could someone please give any suggestions on how to proceed.

    I have tried Inspectorwrappers, Ribbon XML (only when you start altering the ribbon based on email content) interacting all without success. The problem occurs with even the simples of custom ribbons without any real funcitonality in the buttons themselves.

    It almost look like the ribbon is causing the Inspector not to close but since the ribbon is initialized per inspector you cant really dispose of it.

    Any suggestions would be very much appriciated.

    Saturday, May 11, 2013 8:13 PM

Answers

  • As I mentioned in that other thread you need to handle the item.Send() event and not the Application.ItemSend() event. The technique definitely works, I use it all the time and have it working in quite a few addins.

    Here is the solution you want:

    You can't close the window in Application.ItemSend() and that's pretty late in the process anyway.

    Handle the item.Send() event for the item and cancel the event by setting Cancel == true.

    Then you'd need to set a timer in the event handler to fire after the event ends. Then when the timer fires you need to synch the thread the timer fired in to the Outlook thread the addin run ins.

    Then and only then can you successfully close the window by sending a WM_CLOSE nessage and have it work and not fire various thread exceptions.

    Working code for that is in the other thread, and has been posted here and in the Outlook Developer forum a number of times.


    Ken Slovak MVP - Outlook

    • Marked as answer by RaginElmo Tuesday, May 14, 2013 8:20 AM
    Monday, May 13, 2013 2:40 PM

All replies

  • As I mentioned in that other thread you need to handle the item.Send() event and not the Application.ItemSend() event. The technique definitely works, I use it all the time and have it working in quite a few addins.

    Here is the solution you want:

    You can't close the window in Application.ItemSend() and that's pretty late in the process anyway.

    Handle the item.Send() event for the item and cancel the event by setting Cancel == true.

    Then you'd need to set a timer in the event handler to fire after the event ends. Then when the timer fires you need to synch the thread the timer fired in to the Outlook thread the addin run ins.

    Then and only then can you successfully close the window by sending a WM_CLOSE nessage and have it work and not fire various thread exceptions.

    Working code for that is in the other thread, and has been posted here and in the Outlook Developer forum a number of times.


    Ken Slovak MVP - Outlook

    • Marked as answer by RaginElmo Tuesday, May 14, 2013 8:20 AM
    Monday, May 13, 2013 2:40 PM
  • Ken thank you for your answer. Would you mind pointing to an example because I searched for what you just descibed and could not find it.

    Do I understand you correctly when saying that you need to cancel the regular sent even first, then close the item and then use another method (eg. Application.ItemSend) to send the item off?

    • Edited by RaginElmo Tuesday, May 14, 2013 9:39 AM
    Tuesday, May 14, 2013 9:33 AM
  • I posted an example of closing an Inspector window in that original thead, in C# code: http://social.msdn.microsoft.com/Forums/en-US/vsto/thread/243e8465-a638-41b1-ac28-b4f7262457f9

    Let me go over the sequence of events as I would handle it.

    1. I would discover the Simple MAPI Inspector by periodically checking the Inspectors collection to find instances of Inspectors that I'm not already handling. I use a tirmer on a Windows.Form that is instantiated but hidden in my startup code. That timer runs in the main thread so it can use the Outlook object model.

    2. I would mark my Inspector handler as being opened by Simple MAPI. Any handler added for an Inspector that didn't fire NewInspector() is such a Simple MAPI Inspector.

    3. I would set the Inspector handler to handle the Send() event on the Inspector.CurrentItem.

    4. In the Send() event if I wanted the item to go out but to be able to force the Inspector window to close after the item was sent I'd set a timer to run in the Send() event handler and let that event finish. The timer would fire maybe 100 ms after Send() ended.

    5. When the timer fired I'd check using FindWindow() to see if the window was still there. If so I'd force it closed by sending a WM_CLOSE message to the window. If the tirmer did not fire on the same thread as the addin runs in I'd synch the thread contexts so the close message was posted to the window in the main thread.

    That's it. Of course there are timing issues to handle, and you have to make sure that if you set up to handle an Inspector from the Inspector handler timer that the timer didn't fire and then NewInspector() fires on the same Inspector. That's all just detail though, the main prinicples are what I outlined above.


    Ken Slovak MVP - Outlook

    Tuesday, May 14, 2013 3:03 PM
  • Here is what I have done... Outlook 2010 actually does add the Inspector to the Inspectors Group. I checked this through looping through the inspectors and comparing to the active inspector.

    The moment you retrieve ActiveInspector, or in case of Ribbon XML rControl.Context the inspector is in this 'ghost' state after sending these Simple MAPI mails

    But I dont get the WM_CLOSE to work, even tried a while loop on the hWnd pointer.

    '''MailWrapper Class
    
    Private Sub Item_Send(ByRef Cancel As Boolean)
            'TODO: Implement something 
    'Remembering caption for later use        
    ThisAddIn.tbcInspCaption = Item.GetInspector.Caption
    'Invoking timer as last action
            ThisAddIn.invokeTimer()
        End Sub
    
    '''ThisAddin Class
    Public Shared Sub invokeTimer()
    'Create new timer instance
            Timer4Closing = New System.Timers.Timer(200)
    'Adding handler for elapsed timer
            AddHandler Timer4Closing.Elapsed, AddressOf elapsedTimer
    'Start countdown
            Timer4Closing.Start()
        End Sub
    
        Public Shared Sub elapsedTimer()
            Dim hWnd As System.IntPtr
    'Finding Windows with earlier saved caption
            hWnd = FindWindow("rctrl_renwnd32", tbcInspCaption)
    'Sending WM_Close
            PostMessage(hWnd, WM_CLOSE, System.IntPtr.Zero, System.IntPtr.Zero)
            Timer4Closing.Stop()
            Timer4Closing.Dispose()
            Timer4Closing = Nothing
            hWnd = Nothing
        End Sub
    Here

    Wednesday, May 15, 2013 2:04 PM
  • Have you set a breakpoint in the timer handler to make sure it's being executed?

    Are you sure the caption you have and the hWnd are correct for that Inspector window? Make sure of that using Spy++.

    Are any exceptions thrown by the use of PostMessage()? You can check the error in this way, I often use this as an override to PostMessage():

            [DllImport("user32", SetLastError = true)]
            [return: MarshalAs(UnmanagedType.Bool)]
            internal static extern bool PostMessage(IntPtr hWnd, uint wMsg, IntPtr wParam, IntPtr lParam);
            internal const int WM_CLOSE = 0x10;
            internal static int PostMessageSafe(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
            {
                int ex = 0;
                
                bool retVal = PostMessage(hWnd, msg, wParam, lParam);
                if (!retVal)
                {
                    // error occurred
                    ex = Marshal.GetLastWin32Error();
                }
                return ex;
            }
    I've never known the code to fail, and I've been using code like that since Outlook 2000.

    Ken Slovak MVP - Outlook

    Wednesday, May 15, 2013 6:35 PM
  • Dim Outl As Object
    Outl = CreateObject("Outlook.Application")
    Dim oMsg As Object
    oMsg = Outl.CreateItem(0) '=Outlook.OlItemType.olMailItem'
    oMsg.Attachments.Add(wBook.FullNameURLEncoded)
    oMsg.Display(True)
    oMsg = Nothing
    Outl = Nothing

    the above works as well in Excel, no hacks, searching for windows etc and stops the window being modal in the first place.

    It does require a seperate button as the build-in Save&Send still uses the old method.


    • Edited by RaginElmo Friday, June 21, 2013 9:12 PM *update
    Friday, June 21, 2013 9:08 PM