外部程序通过COM启动AutoCAD时RPC_E_CALL_REJECTED的问题解决办法

摘要:
通常,您可能需要在自己的主界面程序中启动AutoCAD,并在AutoCAD开发期间执行自定义命令。如果您使用AutoCAD2010及更高版本,您可能会遇到执行组件的问题:Callwasrejectedbycallee我们将分析并解决以下问题。如果需要在主程序窗口中启动AutoCAD。但是,如果使用AutoCAD2010Update1或更高版本,则可能会遇到以下错误:执行组件时出现问题:Callwasrejectedbycall根据Kean的博客,AutoCAD引入此“问题”是为了解决接收COM消息时可能发生崩溃的问题。因此,上述错误消息RPCE_CALL_REJECTED

对AutoCAD的二次开发是采用插件的方式,即运行AutoCAD.net API编写dll文件,运行时在AutoCAD命令行中输入netload命令来加载你的自定义插件dll。一般AutoCAD开发过程中你可能需要在你自己的主界面程序里启动AutoCAD并执行你的自定义命令。这时可以通过下面的方式来做。如果你用AutoCAD 2010及以上版本可能会遇到Problem executing component: Call was rejected by callee. (Exception from HRESULT: 0x80010001 (RPC_E_CALL_REJECTED))的问题,下文一并分析解决。以下方法同时适用于Map 3D和Civil 3D。 

实现外部程序启动AutoCAD

~~~~~~~~~~~~~~~~~~~~~~~~~

在Visual Studio里新建一个Class library的工程,这里命名为myplugin, 编译生成myplugin.dll的程序集。这个项目是你对AutoCAD扩展的主要工作项目,你可以添加AutoCAD相关程序集的引用,并创建自定义命令等等。这个不用多说。如果你需要在自己的主程序窗口中启动AutoCAD。可以在解决方案里创建一个WinForm的项目,比如叫做StartCAD,在Form里放一个button,标题为Start AutoCAD。然后调整你的myplugin的输出路径到StartCAD的bin目录下,方便StartCAD找到你的自定义应用程序集。 如图:

image

下面实现StartCAD项目中启动AutoCAD并自动加载myplugin.dll . 在StartCAD项目中需要添加如下COM引用:

AutoCAD 2012 Type Library

AutoCAD/ObjectDBX Common 18.0 Type Library

下面是Button1.Click的代码:

        private void button1_Click(object sender, EventArgs e)
        {
            Autodesk.AutoCAD.Interop.AcadApplication cadApp = null;

            try
            {
                //Get the AutoCAD which is running,
                cadApp = (Autodesk.AutoCAD.Interop.AcadApplication)Marshal.GetActiveObject(programID);
            }
            catch
            {
                try
                {
                    //If AutoCAD is not running, start it
                    Type sType = Type.GetTypeFromProgID(programID);
                    cadApp = (Autodesk.AutoCAD.Interop.AcadApplication)Activator.CreateInstance(sType, true);
                    cadApp.Visible = true;

                }
                catch (Exception ex)
                {
                    MessageBox.Show("Cannot open AutoCAD. \n Error message : " + ex.Message);
                   
                }

            }

            //send command to AutoCAD to load our custom assembly
            if (cadApp != null)
            {
                cadApp.Visible = true; //[对应AutoCAD 2010 Update1 及以上] Problem executing component: Call was rejected by callee. (Exception from HRESULT: 0x80010001 (RPC_E_CALL_REJECTED))

                //Load my custom plugin assembly
                cadApp.ActiveDocument.SendCommand("filedia\r0\r"); // 关闭文件对话框模式
                //通过netload命令加载自定义程序集
                cadApp.ActiveDocument.SendCommand("netload\r" + Application.StartupPath + "\\myplugin.dll\r");
                //再打开文件对话框模式
                cadApp.ActiveDocument.SendCommand("filedia\r1\r");

                this.Close();
            }

        }

可能会遇到的问题及分析

~~~~~~~~~~~~~~~~~~~~~~~~~

如果你用AutoCAD 2010以前版本,上面代码应该没什么问题。但如果你用AutoCAD 2010 Update1及以后版本,你可能会遇到如下错误:

Problem executing component: Call was rejected by callee. (Exception from HRESULT: 0x80010001 (RPC_E_CALL_REJECTED))

根据Kean的博客,这个“问题”是由于AutoCAD为了解决接受COM消息时可能出现崩溃的问题而引入的。由于微软决定在WPF中不支持嵌套消息循环,如果WPF正在进行布局处理操作时(这时会调用Dispatcher.DisableProcessing 停止处理消息)又接到COM调用,可能会造成AutoCAD崩溃。所以现在的改进是拒绝这个COM调用,以便让他过一会儿再重新调用。所以就有了上面的错误消息RPC_E_CALL_REJECTED。

解决的方法就是让我们的Form1类实现COM的IMessageFilter接口,这个接口是一个IUnknown接口,他的作用是使得COM服务器或者应用程序能够在等待同步调用响应时选择处理输入或者输入的COM消息。通过这个消息过滤机制,可以让COM 服务器来判定某个调用是否安全,不过造成死锁。COM会调用你的IMessageFilter的实现,从而使得你有机会来对消息做进一步的处理。

IMessageFilter 接口有下面3个方法:

 

HandleInComingCall 提供了一个输入调用的单一入口

Provides a single entry point for incoming calls.

他的返回值为:

SERVERCALL_ISHANDLED  应用程序也许能够处理这个调用The application might be able to process the call.
SERVERCALL_REJECTED   应用程序由于一些不可预计的问题处理不了这个调用。The application cannot handle the call due to an unforeseen problem, such as network unavailability, or if it is in the process of terminating.
SERVERCALL_RETRYLATER  应用程序现在处理不了The application cannot handle the call at this time. An application might return this value when it is in a user-controlled modal state.

 

MessagePending COM的等待远程调用响应的时候来了一个消息

Indicates that a message has arrived while COM is waiting to respond to a remote call.

他的返回值为:

PENDINGMSG_CANCELCALL  取消调用,只在极端情况下使用。
Cancel the outgoing call. This should be returned only under extreme conditions. Canceling a call that has not replied or been rejected can create orphan transactions and lose resources. COM fails the original call and returns RPC_E_CALL_CANCELLED.
 
PENDINGMSG_WAITNOPROCESS  不派发消息继续等待回应
Continue waiting for the reply, and do not dispatch the message unless it is a task-switching or window-activation message. A subsequent message will trigger another call to MessagePending. Leaving messages or events in the queue enables them to be processed normally, if the outgoing call is completed. Note that returning PENDINGMSG_WAITNOPROCESS can cause the message queue to fill.
 
PENDINGMSG_WAITDEFPROCESS 不再派发键盘和鼠标事件,但派发WM_PAINT消息,任务切换和计划消息正常处理
Keyboard and mouse messages are no longer dispatched. However there are some cases where mouse and keyboard messages could cause the system to deadlock, and in these cases, mouse and keyboard messages are discarded. WM_PAINT messages are dispatched. Task-switching and activation messages are handled as before.

 

RetryRejectedCall 提供给应用程序一个显示一个以对话框来选择重试,取消还是切换任务的选择。

Provides applications with an opportunity to display a dialog box offering retry, cancel, or task-switching options.

他的返回值是:

-1          调用会取消The call should be canceled. COM then returns RPC_E_CALL_REJECTED from the original method call.
0 ≤ value < 100   调用会立即重试The call is to be retried immediately.
100 ≤ value     调用会在指定时间后重试,以毫秒计。COM will wait for this many milliseconds and then retry the call.

解决办法

~~~~~~~~~~~~~~~~~~~~~~~~~

上面提到AutoCAD在WPF进行布局处理时拒绝了COM调用的消息,我们可以实现一个IMessageFilter的接口,等待一段时间再重新调用,下面是改进后的代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using Autodesk.AutoCAD.Interop;
using Autodesk.AutoCAD.Interop.Common;

namespace StartAutoCAD
{
    // about IMessageFilter Interface http://msdn.microsoft.com/zh-cn/library/ms693740%28v=VS.85%29.aspx 


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

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

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

    }

    public partial class Form1 : Form, IMessageFilter
    {
        string programID = "AutoCAD.Application";

        [DllImport("ole32.dll")]
        static extern int CoRegisterMessageFilter(
            IMessageFilter lpMessageFilter,
            out IMessageFilter lplpMessageFilter
        );

        public Form1()
        {
            InitializeComponent();
            IMessageFilter oldFilter;
            CoRegisterMessageFilter(this, out oldFilter);
        }



        private void button1_Click(object sender, EventArgs e)
        {
            Autodesk.AutoCAD.Interop.AcadApplication cadApp = null;

            try
            {
                //Get the AutoCAD which is running
                cadApp = (Autodesk.AutoCAD.Interop.AcadApplication)Marshal.GetActiveObject(programID);
            }
            catch
            {
                try
                {
                    Type sType = Type.GetTypeFromProgID(programID);
                    cadApp = (Autodesk.AutoCAD.Interop.AcadApplication)Activator.CreateInstance(sType, true);
                    cadApp.Visible = true;

                }
                catch (Exception ex)
                {
                    MessageBox.Show("Cannot open AutoCAD. \n Error message : " + ex.Message);

                }

            }


            if (cadApp != null)
            {
                cadApp.Visible = true;

                //Load my custom plugin assembly
                cadApp.ActiveDocument.SendCommand("filedia\r0\r");
                cadApp.ActiveDocument.SendCommand("netload\r" + Application.StartupPath + "\\myplugin.dll\r");
                cadApp.ActiveDocument.SendCommand("filedia\r1\r");

                this.Close();
            }

        }


        #region IMessageFilter Members

        int IMessageFilter.HandleInComingCall(int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo)
        {
            return 0; // SERVERCALL_ISHANDLED
        }

        int IMessageFilter.MessagePending(IntPtr hTaskCallee, int dwTickCount, int dwPendingType)
        {
            return 1; // PENDINGMSG_WAITNOPROCESS
        }

        int IMessageFilter.RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType)
        {
            return 1000; // Retry in a second
        }

        #endregion
    }
}

免责声明:文章转载自《外部程序通过COM启动AutoCAD时RPC_E_CALL_REJECTED的问题解决办法》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇java中的线程安全队列图灵机器人聊天下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

AutoCAD ObjectARX和RealDWG的基本数据操作

.dwg文件的读取。拥有RealDWG或者ObjectARX不必多说。 安装RealDWG后RealDWG目录下的Samples看看。 新建工程配置好Inc,Lib,附加依赖等。 不必多说。 需要将AcDbHostApplicationServices实现一遍。 sample自带的即可,也可自己按照自己的来写。 1 int Verbose = 0...

AutoCAD 自动管理字体插件[使用ObjectARX C++]

概述: 使用AutoCAD的过程中,我们常常因为缺失字体而烦恼,本插件就是为了解决这个问题。 插件采用WEB服务器 + CAD插件方式。WEB服务器使用Python编写,部署在百度BAE上;CAD插件使用C++开发,在AutoCAD中使用命令“APPLOAD”加载该插件。 在CAD中打开新的DWG文档后,插件会自动比较DWG文档所需字体以及CAD的Font...

C# 实现预览dwg文件完整源代码(无需autocad环境)

using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; using System.IO; namespace WindowsApplica...

Word及Autocad中中文字号与字体大小的关系

对于使用Word、Excel和AutoCAD的人,经常会碰上字体大小的问题。事实上他们是有迹可循的。 1,Word和Excel中的字号和磅数 Word中的字体大小,同时采用了中文的字号和磅数字号。Excel中只采用磅数,它们的对应关系如下: 2,AutoCAD中的字体大小   Autocad中用“字高”来作为文字的度量单位,CAD中的“字高”的数值约等于...

AutoCAD批量导出点坐标

需求背景: 需要批量导出DWG文件中的散点树的位置信息,以Excel文件格式存储。 实现方法: 在AutoCAD2012打开dwg文件,点击“插入”选项卡中的“提取数据”工具(或输入DATAEXTRACTION命令): 选择“创建新数据提取” 选择需要导出的对象 设置导出参数 选择输出Excel文件保存路径 导出结果: 总结: 该工具...

AutoCAD自动加载DLL文件的方法

让CAD打开以后自动加载DLL文件的步骤如下:      1.找到你CAD目录的安装目录的启动支持文件,例如:C:\Program Files\AutoCAD 2006\Support\acad2006.lsp      2.打开acad2006.lsp文件 ,在最后输入(command "netload" "E:\\PowerSupplyManager\...