Kean Walmsley


  • About the Author
    Kean on Google+

August 2014

Sun Mon Tue Wed Thu Fri Sat
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31            








« Updated DevTV: AutoCAD VBA to VB.NET Migration Basics | Main | March’s Plugin of the Month live on Autodesk Labs: Batch Publish for AutoCAD »

February 26, 2010

Handling COM calls rejected by AutoCAD from an external .NET application

We’ve had a few reports from people implementing external .NET applications to drive AutoCAD – as shown in this previous post – experiencing intermittent failures once AutoCAD 2010 Update 1 has been applied. Here’s a typical error message:

Exception after installing AutoCAD 2010 Update 1

It contains the text “Problem executing component: Call was rejected by callee. (Exception from HRESULT: 0x80010001 (RPC_E_CALL_REJECTED))” (to help people Googling this error message :-).

This “problem” was introduced as we addressed an issue with the way our WPF components in AutoCAD handle inbound messages, largely due to Microsoft’s decision not to support nested message loops in WPF. If WPF is in the middle of performing some kind of layout processing operation (which leads to Dispatcher.DisableProcessing() being called) and there’s an incoming COM call, then we were previously respecting it which could lead to a crash. Now we do the right thing and reject the COM call: effectively asking the caller to try again later.

The problem is that – while the VB6 runtime was very good at automatically retrying calls such as CreateObject() – WinForm applications are not. We need to implement an additional interface from our WinForm application to make sure it can handle failure (I could probably do with adding that one myself ;-).

As it’s hopefully clear: while the problem is likely to be more visible in AutoCAD 2010 once Update 1 has been applied, this is ultimately about correctly inappropriate expectations on the side of the calling application and teaching it to do the right thing.

Here’s the updated C# code from the previous post, with the changed lines in red, and here’s the source project.

    1 using Autodesk.AutoCAD.Interop;

    2 using System.Windows.Forms;

    3 using System.Runtime.InteropServices;

    4 using System.Reflection;

    5 using System;

    6 using LoadableComponent;

    7 

    8 // For more information on IMessageFilter:

    9 // http://msdn.microsoft.com/en-us/library/ms693740(VS.85).aspx

   10 

   11 namespace DrivingAutoCAD

   12 {

   13   [ComImport,

   14    InterfaceType(ComInterfaceType.InterfaceIsIUnknown),

   15    Guid("00000016-0000-0000-C000-000000000046")]

   16   public interface IMessageFilter

   17   {

   18     [PreserveSig]

   19     int HandleInComingCall(

   20       int dwCallType, IntPtr hTaskCaller,

   21       int dwTickCount, IntPtr lpInterfaceInfo

   22     );

   23     [PreserveSig]

   24     int RetryRejectedCall(

   25       IntPtr hTaskCallee, int dwTickCount, int dwRejectType

   26     );

   27     [PreserveSig]

   28     int MessagePending(

   29       IntPtr hTaskCallee, int dwTickCount, int dwPendingType

   30     );

   31   }

   32 

   33   public partial class Form1 : Form, IMessageFilter

   34   {

   35     [DllImport("ole32.dll")]

   36     static extern int CoRegisterMessageFilter(

   37       IMessageFilter lpMessageFilter,

   38       out IMessageFilter lplpMessageFilter

   39     );

   40 

   41     public Form1()

   42     {

   43       InitializeComponent();

   44       IMessageFilter oldFilter;

   45       CoRegisterMessageFilter(this, out oldFilter);

   46     }

   47 

   48     private void button1_Click(object sender, EventArgs e)

   49     {

   50       const string progID = "AutoCAD.Application.18";

   51 

   52       AcadApplication acApp = null;

   53       try

   54       {

   55         acApp =

   56           (AcadApplication)Marshal.GetActiveObject(progID);

   57       }

   58       catch

   59       {

   60         try

   61         {

   62           Type acType =

   63             Type.GetTypeFromProgID(progID);

   64           acApp =

   65             (AcadApplication)Activator.CreateInstance(

   66               acType,

   67               true

   68             );

   69         }

   70         catch

   71         {

   72           MessageBox.Show(

   73             "Cannot create object of type \"" +

   74             progID + "\""

   75           );

   76         }

   77       }

   78       if (acApp != null)

   79       {

   80         try

   81         {

   82           // By the time this is reached AutoCAD is fully

   83           // functional and can be interacted with through code

   84 

   85           acApp.Visible = true;

   86 

   87           INumberAddition app =

   88             (INumberAddition)acApp.GetInterfaceObject(

   89               "LoadableComponent.Commands"

   90             );

   91 

   92           // Now let's call our method

   93 

   94           string res = app.AddNumbers(5, 6.3);

   95 

   96           acApp.ZoomAll();

   97 

   98           MessageBox.Show(

   99             this,

  100             "AddNumbers returned: " + res

  101           );

  102         }

  103         catch (Exception ex)

  104         {

  105           MessageBox.Show(

  106             this,

  107             "Problem executing component: " +

  108             ex.Message

  109           );

  110         }

  111       }

  112     }

  113     #region IMessageFilter Members

  114 

  115     int IMessageFilter.HandleInComingCall(

  116       int dwCallType, IntPtr hTaskCaller,

  117       int dwTickCount, IntPtr lpInterfaceInfo

  118     )

  119     {

  120       return 0; // SERVERCALL_ISHANDLED

  121     }

  122 

  123     int IMessageFilter.RetryRejectedCall(

  124       IntPtr hTaskCallee, int dwTickCount, int dwRejectType

  125     )

  126     {

  127       return 1000; // Retry in a second

  128     }

  129 

  130     int IMessageFilter.MessagePending(

  131       IntPtr hTaskCallee, int dwTickCount, int dwPendingType

  132     )

  133     {

  134       return 1; // PENDINGMSG_WAITNOPROCESS

  135     }

  136 

  137     #endregion

  138   }

  139 }

There’s nothing really to see, in terms of modified behaviour, other than that you should no longer experience the crash.

blog comments powered by Disqus

10 Random Posts