Thursday, April 24, 2003 2:03 AM Shawn A. Van Ness

All quiet along the Toolbox front...

The saga continues.  Soon after I reported the terrible timing-sensitive bug in the VS automation layer, I got a friendly email from someone smarter than me, and with a microsoft.com email address.  That's the kind I like..!

I've forgotten how most of this COM/OLE goo works, but this kind soul reminded me that OLE message filters (ala IMessageFilter, but not the one you're thinking of!) are the key to avoiding call-rejected errors from COM plumbing.

Code follows.  Caveat emptor -- but it works for me!

using System;
using System.Runtime.InteropServices;

/*
This class implements an OLE message filter, appropriate for use with the VS automation clients.

Sample usage:

    [STAThread]
    private static void Main()
    {
        // We avoid instantiating the RCW as "new DTE()", because the both PIAs for v7.0 and v7.1 point 
        // to the same CLSID (VS 7.0's)... 
        Type latestDTE = Type.GetTypeFromProgID("VisualStudio.DTE"); //ver-indep progid
        EnvDTE.DTE dte = Activator.CreateInstance(latestDTE) as EnvDTE.DTE;

        // Register an OLE message filter to avoid "call rejected" exceptions
        MessageFilter.Register();

        // Drive the VS automation interface
        Installer1.AddToolBoxTab(dte); // or whatever

        // Unplug the message filter
        MessageFilter.Revoke();
    }
*/

class MessageFilter : IOleMessageFilter
{
    //
    // Public API

    public static void Register()
    {
        IOleMessageFilter newfilter = new MessageFilter(); 

        IOleMessageFilter oldfilter = null; 
        CoRegisterMessageFilter(newfilter, out oldfilter);
    }

    public static void Revoke()
    {
        IOleMessageFilter oldfilter = null; 
        CoRegisterMessageFilter(nullout oldfilter);
    }

    //
    // IOleMessageFilter impl
    
    int IOleMessageFilter.HandleInComingCall(int dwCallType, System.IntPtr hTaskCaller, int dwTickCount, System.IntPtr lpInterfaceInfo) 
    {
        System.Diagnostics.Debug.WriteLine("IOleMessageFilter::HandleInComingCall");

        return 0//SERVERCALL_ISHANDLED
    }

    int IOleMessageFilter.RetryRejectedCall(System.IntPtr hTaskCallee, int dwTickCount, int dwRejectType)
    {
        System.Diagnostics.Debug.WriteLine("IOleMessageFilter::RetryRejectedCall");

        if (dwRejectType == 2 ) //SERVERCALL_RETRYLATER
        {
            System.Diagnostics.Debug.WriteLine("Retry call later");
            return 99//retry immediately if return >=0 & <100
        }
        return -1//cancel call
    }

    int IOleMessageFilter.MessagePending(System.IntPtr hTaskCallee, int dwTickCount, int dwPendingType)
    {
        System.Diagnostics.Debug.WriteLine("IOleMessageFilter::MessagePending");

        return 2//PENDINGMSG_WAITDEFPROCESS 
    }

    //
    // Implementation

    [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 // deliberately renamed to avoid confusion w/ System.Windows.Forms.IMessageFilter
{
    [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);
}

Comments

# re: All quiet along the Toolbox front...

Monday, July 28, 2003 2:11 PM by Jon

Thanks, I was about to give up, and look for a new job.

You method of getting the DTE does not work for me. It must generate a new DTE and I need the current one. The following works for me.

DTE dte = (DTE)System.Runtime.InteropServices.Marshal.GetActiveObject("VisualStudio.DTE");

# re: All quiet along the Toolbox front...

Monday, July 28, 2003 3:14 PM by Shawn A. Van Ness

Be careful out there -- this will grab a DTE instance from the ROT, but remember that users can (and often do!) run multiple instances of devenv.exe.

Which one is represented in the ROT? The question is academic, in the case of toolbox automation: if the user closes yours first, and another one later, your changes to the toolbox will be lost.

At least that was the case w/ VS 7.0 -- can't honestly recall if I tried that test with VS 7.1. In either case, you can imagine some pretty crazy scenarios wherein the user modifies the toolbox manually, in the second running instance...

The only good way around this, IMHO, is to do the toolbox automation at install-time -- and precede it with a check to make sure there're no running instances of devenv.exe in the current session.

Here's the code I use to do that, as a simple standalone EXE run as a custom install action...

http://www.arithex.com/temp/IsVSRunning.cs.html

-S

# re: All quiet along the Toolbox front...

Friday, August 01, 2003 9:10 AM by DaveL

Thanks from me too. I had already found a new job but now I don't have to flip burgers...

We've got a large solution (30+ projects) that takes over 2 minutes to open. Sometimes it would time out and sometimes it wouldn't. This fixed the problem.

I'm working on beefing up the BuildIt sources to make it more suitable for our process. Do you happen to know of an automation interface to Rational ClearCase?

Thanks again.


# re: All quiet along the Toolbox front...

Monday, August 04, 2003 12:00 PM by Jeremy Marsch

Thanks a whole lot! I hadn't been able to find any fixes for this, not even from Microsoft!

# re: All quiet along the Toolbox front...

Tuesday, November 18, 2003 4:22 AM by Raghu

Thanks.

I think this has helped to fix the Call rejected by Callee errors that we were getting.

Cheers,
Raghu

# re: All quiet along the Toolbox front...

Monday, March 08, 2004 10:57 AM by Scott Munro

I don't fully understand what is going on here but it stopped the 'Call rejected by Callee' errors I was getting.

I translated the code to VB.Net and posted it on my blog if anyone is interested.

# re: All quiet along the Toolbox front...

Wednesday, March 31, 2004 4:24 AM by Sampath

Hi All,

I am using Word object library 9.0 in my project. I am creating an instance of the word object in my C# class and calling some methods related to mailmerge. I am getting the error "Call rejected by callee". But if i just put one messagebox, i am not getting any error. Can u kinldy advise me in solving this error.

I went through the sample code which is provided, but was not able to understand much and solve the issue.

Any help in this will be of great use.
Thanking you all in advance.
Waiting for a positive reply asap.

Regards
Sampath

# re: All quiet along the Toolbox front...

Wednesday, May 26, 2004 5:24 AM by The Hut

Excellent stuff... I was battling getting this error fixed.

# re: Toolbox Stupidity - Visual Studio 2003

Monday, July 12, 2004 5:19 AM by TrackBack

# A very useful trick for working with the DTE

Friday, February 18, 2005 5:20 AM by TrackBack

# public MattBerther : ISerializable &raquo; Blog Archive &raquo; How to install a component to the VS.NET toolbox