Office Interop and Call was rejected by callee

28. September 2012 13:14 by Mrojas in ActiveX, C#, Memory, VB6 Migration, WinForms  //  Tags: , ,   //   Comments (0)

This issue is mostly cause by timing issues when calling a multi-threaded app.

Some workarounds:

 

1. Set the application visible property to false. For example if using Word make word visible property false at the start of the method and set it back to true at the end.

This will delay some GUI changes avoiding timing issues.

 

2. Insert some Thread.Sleep calls (yes this is ugly)

 

3. Register and IOleMessageFilter. I have copied an implementation from the MSDN

 

Just copy this class in your code.

At the start of the method call 

 

            MessageFilter.Register();
            // This registers the IOleMessageFilter to handle any threading 
            // errors.

 

And at the end

MessageFilter.Revoke();

public class MessageFilter : IOleMessageFilter
{

        //
        // Class containing the IOleMessageFilter
        // thread error-handling functions.

        // Start the filter.
        public static void Register()
        {
            IOleMessageFilter newFilter = new MessageFilter(); 
            IOleMessageFilter oldFilter = null; 
            CoRegisterMessageFilter(newFilter, out oldFilter);
        }


        // Done with the filter, close it.
        public static void Revoke()
        {
            IOleMessageFilter oldFilter = null; 
            CoRegisterMessageFilter(null, out oldFilter);
        }


        //
        // IOleMessageFilter functions.
        // Handle incoming thread requests.
        int IOleMessageFilter.HandleInComingCall(int dwCallType, 
        System.IntPtr hTaskCaller, int dwTickCount, System.IntPtr lpInterfaceInfo) 
        {

            //Return the flag SERVERCALL_ISHANDLED.
            return 0;
        }


        // Thread call was rejected, so try again.
        int IOleMessageFilter.RetryRejectedCall(System.IntPtr 
        hTaskCallee, int dwTickCount, int dwRejectType)
        {

            if (dwRejectType == 2)
            // flag = SERVERCALL_RETRYLATER.
            {
                // Retry the thread call immediately if return >=0 & 
                // <100.
                return 99;
            }
            // Too busy; cancel call.
            return -1;
        }


        int IOleMessageFilter.MessagePending(System.IntPtr hTaskCallee, 
        int dwTickCount, int dwPendingType)
        {
            //Return the flag PENDINGMSG_WAITDEFPROCESS.
            return 2; 
        }


        // Implement the IOleMessageFilter interface.
        [DllImport("Ole32.dll")]
        private static extern int 
          CoRegisterMessageFilter(IOleMessageFilter newFilter, out 
          IOleMessageFilter oldFilter);
    }



    [ComImport(), Guid("00000016-0000-0000-C000-000000000046"), 
    InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
    interface IOleMessageFilter 
    {
        [PreserveSig]
        int HandleInComingCall( 
        int dwCallType, 
        IntPtr hTaskCaller, 
        int dwTickCount, 
        IntPtr lpInterfaceInfo);

        [PreserveSig]
        int RetryRejectedCall( 
        IntPtr hTaskCallee, 
        int dwTickCount,
        int dwRejectType);


        [PreserveSig]
        int MessagePending( 
            IntPtr hTaskCallee, 
            int dwTickCount,
            int dwPendingType);
    }

 

STAThread and Memory Issues

15. February 2012 15:39 by Mrojas in COM Interop, Memory  //  Tags: , , , , , ,   //   Comments (0)


VB6 Application where STAThread. And that is the reason that Winforms applications are
by default STAThread. Using MTAThread causes problems with some ActiveX Controls.

However STAThread has a nasty implication
see: http://social.msdn.microsoft.com/Forums/en/clr/thread/835db88e-db51-4f83-bd4f-a10d126effa6
 
"inside a STA thread, the finalizer thread must reenter the STA thread in order to finalize the component.
If the STA is blocked and isn't pumping, the finalizer has to wait in line until it does"

This can then cause leaks of components affecting the memory use.


"To get around this issue, you have some options (from best to worst), e.g.:"

1) Create your components in an MTA. ... Unless you have an explicit reason to use an STA, you shouldn't. I realize that Visual Studio adds these to some entrypoints automatically for you.
For example, most GUI applications have to start life inside an STA, e.g. WinForms, but Console applications"  or services "certainly do not."

"2) Deterministically release your resources. If you are using components which implement IDisposable, wrap them in
a C# 'using' statement or call Dispose() on them explicitly when you're done.
RCW's done have Dispose on them. You can consider doing a Marshal.ReleaseComObject on them directly,
but realize that this can cause problems if you're not really done using the COM object."


"3) Use another form of blocking to prevent the primary thread from exiting."

"Chris Brumme writes about this" (COM Apartments)
" at http://blogs.msdn.com/cbrumme/archive/2004/02/02/66219.aspx; caution: that's a fairly lengthy post"