cad.net 定义Lisp和发送同步命令

通用枚举值

// 本枚举融合 Autodesk.AutoCAD.Runtime.LispDataType 
//          Autodesk.AutoCAD.EditorInput.PromptStatus 
public enum EnumBufCode
{ 
    // resbuf元素的类型  
    None           = 5000, //没有结果   回车耶
    Double         = 5001, //实数      Real  
    Point2d        = 5002, //2维点    
    Int16          = 5003, //短整数     Short
    Angle          = 5004, //角度     
    Text           = 5005, //字符串     String
    ObjectId       = 5006, //实体名称   Ename  
    SelectionSet   = 5007, //选择集名   PickSet     
    Orientation    = 5008, //方向     
    Point3d        = 5009, //3维点    
    Int32          = 5010, //长整数     Long 
    Void           = 5014, //空白符号
    ListBegin      = 5016, //list begin
    ListEnd        = 5017, //list end
    DottedPair     = 5018, //点对
    Nil            = 5019, //nil(空表)
    Dxf0           = 5020, //DXF代码0仅适用于ads_bListldlist
    T_atom         = 5021, //T(原子)

    Resbuf         = 5023, //resbuf

    Modeless       = 5027, //被无模式对话中断
    Other          = 5028,

    // 错误返回代码                       
    OK              = 5100, //请求成功,用户输入值有效  Norm
    Error          = -5001,//其他一些错误
    Cancel         = -5002,//用户取消请求-Ctl-C
    RejectRequest  = -5003,//AutoCAD拒绝请求-无效
    FailureLink    = -5004,//链接失败-Lisp可能已经死了
    Keyword        = -5005,//从getxxx()例程返回的关键字
    Inputtruncated = -5008,//输入并不都适合缓冲区 
}

Lisp定义

传参型Lisp定义

它在低版本有个问题,就是所有的返回值都要用表包裹着,而高版本就修复了这个问题.

为了所有版本统一,返回值应该统一成 ResultBuffer,在lisp再(car 返回值)....

我这里既有 ResultBuffer,又有object返回,供各位参考.

#if !HC2020
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using Acap = Autodesk.AutoCAD.ApplicationServices.Application;
#else
using GrxCAD.DatabaseServices;
using GrxCAD.EditorInput;
using GrxCAD.Geometry;
using GrxCAD.Runtime;
using Acap = GrxCAD.ApplicationServices.Application;
#endif
using System;
using System.Collections.Generic;

namespace JoinBox
{
    public class LispTest
    {

        //不传参的时候也要带参数
        //(getpath)
        [LispFunction("GetPath")]//注意:这里不是command!
        public object GetPath(ResultBuffer args=null) //注意:返回值:高版本object,但08只能ResultBuffer
       {
            return "你好美女";
        }

        // 传参型lisp的定义:传点
        // (LispTest_AddLine (getpoint))
        [LispFunction("LispTest_AddLine")]                       //注意:这里不是command!
        public ResultBuffer LispTest_AddLine(ResultBuffer args)  //注意:返回值:高版本object,但08只能ResultBuffer
        {
            if (args == null)
                return null;

            var dm = Acap.DocumentManager;
            var doc = dm.MdiActiveDocument;
            var db = doc.Database;
            var ed = doc.Editor;

            Point3d p1 = Point3d.Origin;
            Point3d p2 = Point3d.Origin;
            foreach (TypedValue rb in args)
            {
                //ed.WriteMessage(Environment.NewLine + "rb.ToString()=" + rb.ToString());
                //ed.WriteMessage(Environment.NewLine + "rb.TypeCode.ToString()=" + rb.TypeCode.ToString());
                //ed.WriteMessage(Environment.NewLine + "rb.Value.ToString()=" + rb.Value.ToString());
                var str = rb.Value.ToString();
                str = str.Substring(1).Substring(0, str.Length - 2);
                var pts = str.Split(',');
                double.TryParse(pts[0], out double x);
                double.TryParse(pts[1], out double y);
                double.TryParse(pts[2], out double z);
                p2 = new Point3d(x, y, z);
            }
            var line = new Line(p1, p2);
            db.Action(tr => {
                var acBlkTblRec = tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;
                // line.SetDatabaseDefaults();设置数据库默认值
                acBlkTblRec.AppendEntity(line);
                tr.AddNewlyCreatedDBObject(line, true);
            });

            var rbrtn = new ResultBuffer(new TypedValue[]
            {
                new TypedValue((int)EnumBufCode.ObjectId, line.ObjectId),
                new TypedValue((int)EnumBufCode.Point3d, p1),
                new TypedValue((int)EnumBufCode.Point3d, p2)
            });
            ed.WriteMessage(Environment.NewLine + " rbrtn=" + rbrtn.ToString());
            ed.WriteMessage(Environment.NewLine);
            return rbrtn; //返回值此处有图元名
        }

        // 传参型lisp的定义:加法
        // (LispTest_MyAdd 1 2 3)
        [LispFunction("LispTest_MyAdd")]                //注意:这里不是command!
        public object LispTest_MyAdd(ResultBuffer args) //注意:返回值:高版本object,但08只能ResultBuffer
        {
            if (args == null)
                return null;

            var ds = new List<double>();
            foreach (TypedValue rb in args)
            {
                double.TryParse(rb.Value.ToString(), out double num);
                ds.Add(num);
            }
            double number = 0;
            foreach (var item in ds)
                number += item;

            var tv = new TypedValue[]
            {
                new TypedValue((int)EnumBufCode.Double, number)
            };
            var rbrtn = new ResultBuffer(tv);
#if AC2008
            return rbrtn;  //但是08只能返回ResultBuffer  会返回(6.0)
#else
            return number; //高版本这里可以返回double    会返回6.0
#endif
        }

        // 传参型lisp的定义:选择集
        // (LispTest_ssget (ssget))
        [LispFunction("LispTest_ssget")]               //注意:这里不是command!
        public object LispTest_ssget(ResultBuffer args)//注意:返回值:高版本object,但08只能ResultBuffer
        {
            if (args == null)
                return null;

            var dt = new List<object>();
            var ds = new List<object>();
            foreach (TypedValue rb in args)//{((5007,(((-2181752),Crossing,0,)((-2181760),Crossing,0,))))}
            {
                dt.Add(rb.TypeCode);
                ds.Add(rb.Value);
            }
            var tl = new List<TypedValue>();
            foreach (var item in ds)
                tl.Add(new TypedValue((int)EnumBufCode.SelectionSet, item));
            return new ResultBuffer(tl.ToArray());
        }

        // 传参型lisp的定义:点对
        // (LispTest_dotPair 0 100)
        [LispFunction("LispTest_dotPair")]               //注意:这里不是command!
        public object LispTest_dotPair(ResultBuffer args)//注意:返回值:高版本object,但08只能ResultBuffer
        {
            if (args == null)
                return null;

            var ds = new List<object>();
            foreach (TypedValue rb in args)
                ds.Add(rb.Value);

            var ls = new List<TypedValue>();
            ResultBuffer dotPair = null;
            if (ds.Count == 2)
            {
                ls.Add(new TypedValue((int)EnumBufCode.ListBegin, -1));
                ls.Add(new TypedValue((int)EnumBufCode.Int16, ds[0]));
                if (short.TryParse(ds[1].ToString(), out short num))
                {
                    ls.Add(new TypedValue((int)EnumBufCode.Int16, num));
                    ls.Add(new TypedValue((int)EnumBufCode.DottedPair, -1));
                    dotPair = new ResultBuffer(ls.ToArray());
                }
            }
            return dotPair; //返回点对表
        }
    }
}

img

读取和赋值Lisp变量的值

测试命令: Test_setLisp

#if !HC2020
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using Acap = Autodesk.AutoCAD.ApplicationServices.Application;
#else
using GrxCAD.DatabaseServices;
using GrxCAD.EditorInput;
using GrxCAD.Runtime;
using Acap = GrxCAD.ApplicationServices.Application;
#endif
using System.Runtime.InteropServices;
using System;

namespace JoinBox
{
    // 操作lisp赋值
    // https://blog.csdn.net/qq_21489689/article/details/80630817
    public static class LispStruct
    {
        //LSP变量的读取-32位
        [System.Security.SuppressUnmanagedCodeSecurity]
        [DllImport("acad.exe", EntryPoint = "acedGetSym", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
        extern static private int AcedGetSym32(string args, out IntPtr result);

        //LSP变量的读取-64位
        [System.Security.SuppressUnmanagedCodeSecurity]
        [DllImport("accore.dll", EntryPoint = "acedGetSym", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
        extern static private int AcedGetSym64(string args, out IntPtr result);

        //LSP变量的写入-32位
        [System.Security.SuppressUnmanagedCodeSecurity]
        [DllImport("acad.exe", EntryPoint = "acedPutSym", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
        extern static private int AcedPutSym32(string args, IntPtr result);

        //LSP变量的写入-64位
        [System.Security.SuppressUnmanagedCodeSecurity]
        [DllImport("accore.dll", EntryPoint = "acedPutSym", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
        extern static private int AcedPutSym64(string args, IntPtr result);

        /// <summary>
        /// 获取Lisp变量值
        /// </summary>
        /// <param name="name">变量名</param>
        /// <returns>变量值,如果失败则为空</returns>
        static ResultBuffer GetLispSym(string name)
        {
            IntPtr ip;
            //判断系统是32位还是64位
            switch (Marshal.SizeOf(typeof(IntPtr)))
            {
                case 4:
                {
                    int status = AcedGetSym32(name, out ip);
                    if (status == (int)EnumBufCode.OK && ip != IntPtr.Zero)
                        return ResultBuffer.Create(ip, true);
                }
                break;
                case 8:
                {
                    int status = AcedGetSym64(name, out ip);
                    if (status == (int)EnumBufCode.OK && ip != IntPtr.Zero)
                        return ResultBuffer.Create(ip, true);
                }
                break;
            }
            return null;
        }

        /// <summary>
        /// 设置Lisp变量值
        /// </summary>
        /// <param name="name">变量名</param>
        /// <param name="rb">变量值</param>
        static int SetLispSym(string name, ResultBuffer rb)
        {
            int ret = 0;
            //判断系统是32位还是64位
            switch (Marshal.SizeOf(typeof(IntPtr)))
            {
                case 4:
                    ret = AcedPutSym32(name, rb.UnmanagedObject);
                    break;
                case 8:
                    ret = AcedPutSym64(name, rb.UnmanagedObject);
                    break;
            }
            return ret;
        }

        /// <summary>
        /// lisp变量读取
        /// </summary>
        /// <param name="varName">变量名</param>
        /// <returns></returns>
        public static object GetLspVar(string varName)
        {
            object varValue = null;
            var rb = GetLispSym(varName);
            if (rb != null)
            {
                foreach (var val in rb)
                    varValue = val.Value;
            }
            return varValue;
        }

        /// <summary>
        /// lisp变量赋值
        /// </summary>
        /// <param name="varName">变量名</param>
        /// <param name="varValue">变量值</param>
        /// <param name="cEnumRes">变量值类型,原子,字符串等等</param>
        public static void SetLspVar(EnumBufCode cEnumRes, string varName, object varValue = null)
        {
            using (var rb = new ResultBuffer())
            {
                rb.Add(new TypedValue((int)cEnumRes, varValue));
                SetLispSym(varName, rb);
            }
        }
    }

    public static class CmdTest_LispStructure
    {
        [CommandMethod("Test_setLisp")]
        public static void Test_setLisp()
        {
            string varName = "a";
            object varValue;
            // 赋值例子
            // (setq a "35432")
            LispStruct.SetLspVar(EnumBufCode.Text, varName, "35432");   //返回  "3543252351515"
            varValue = LispStruct.GetLspVar(varName);
            Princ(varValue);
            // (setq a 35432)
            LispStruct.SetLspVar(EnumBufCode.Double, varName, "35432"); //返回   3543252351515
            varValue = LispStruct.GetLspVar(varName);
            Princ(varValue);
            // (setq a T)
            LispStruct.SetLspVar(EnumBufCode.T_atom, varName, true);    //返回   T
            LispStruct.SetLspVar(EnumBufCode.T_atom, varName, false);   //返回   T
            varValue = LispStruct.GetLspVar(varName);
            Princ(varValue);
            // (setq a nil)
            LispStruct.SetLspVar(EnumBufCode.Nil, varName);             //返回   nil
            varValue = LispStruct.GetLspVar(varName);
            Princ(varValue);
        }
        public static void Princ(object varValue)
        {
            var ed = Acap.DocumentManager.MdiActiveDocument.Editor;
            string valueString = "";
            if (varValue == null)
                valueString = "nil";
            else
            {
                string type = varValue.GetType().Name;
                switch (type)
                {
                    case "String":
                    {
                        valueString = varValue.ToString();
                        valueString = "\"" + valueString + "\"";
                    }
                    break;
                    case "Double":
                    {
                        valueString = varValue.ToString();
                    }
                    break;
                    case "Boolean":
                    {
                        valueString = "T"; //返回布尔值都是为true的,不返回才是nil
                    }
                    break;
                }
            }
            ed.WriteMessage("\n" + valueString);
        }
    }
}

发送

发送Lisp(非明文,同步)

通过arx接口发送Lisp,这个可以避免用明文方式发送到命令栏,而且它在自动执行函数上面也是同步发送的,

#if !HC2020
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;
using Acap = Autodesk.AutoCAD.ApplicationServices.Application;
#else
using GrxCAD.DatabaseServices;
using GrxCAD.Runtime;
using Acap = GrxCAD.ApplicationServices.Application;
#endif
using System;
using System.Runtime.InteropServices;
using Exception = System.Exception;

namespace JoinBox
{
    public class SendLisp
    {

#if NET35
    [DllImport("acad.exe", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl,
        EntryPoint = "?acedEvaluateLisp@@YAHPB_WAAPAUresbuf@@@Z")]
#else
    // 高版本此接口不能使用lisp(command "xx"),但是可以直接在自动运行接口上
    [DllImport("accore.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl,
        EntryPoint = "?acedEvaluateLisp@@YAHPEB_WAEAPEAUresbuf@@@Z")]
#endif
    [System.Security.SuppressUnmanagedCodeSecurity]//初始化默认值
    static extern int AcedEvaluateLisp(string lispLine, out IntPtr result);

#if NET35
    [DllImport("acad.exe", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl,
        EntryPoint = "ads_queueexpr")]
#else
    [DllImport("accore.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl,
        EntryPoint = "ads_queueexpr")]
#endif
    static extern int Ads_queueexpr(string strExpr);

    public enum RunLispFlag : byte
    {
        AdsQueueexpr = 1,
        AcedEvaluateLisp = 2,
        SendStringToExecute = 4,
    }

    /// <summary>
    /// 发送lisp语句字符串到cad执行
    /// </summary>
    /// <param name="ed">编辑器对象</param>
    /// <param name="lispCode">lisp语句</param>
    /// <returns>缓冲结果,返回值</returns>
#pragma warning disable IDE0060 // 删除未使用的参数
    public static ResultBuffer? RunLisp(this Editor ed, string lispCode, RunLispFlag flag = RunLispFlag.AdsQueueexpr)
#pragma warning restore IDE0060 // 删除未使用的参数
    {
        /*
         * 测试命令:
         *   [CommandMethod("CmdTest_RunLisp")]
         *   public static void CmdTest_RunLisp()
         *   {
         *       var res = SendLisp.RunLisp("(setq abc 10)");
         *   }
         * 调用方式:
         *    (command "CmdTest_RunLisp1")
         * bug说明: 
         *    AcedEvaluateLisp接口在高版本调用时候没有运行成功,使得 !abc 没有值
         *    经过测试,cad08调用成功,此bug与CommandFlags无关
         * 解决方案:
         *   0x01 用异步接口,但是这样是显式调用了:
         *        (setq thisdrawing (vla-get-activedocument (vlax-get-acad-object)))(vla-SendCommand thisdrawing "CmdTest_RunLisp1 ")
         *   0x02 使用 Ads_queueexpr 接口
         */
        if ((flag & RunLispFlag.AdsQueueexpr) == RunLispFlag.AdsQueueexpr)
        {
            // 这个在08/12发送lisp不会出错,但是发送bo命令出错了.
            // 0x01 设置 CommandFlags.Session 可以同步,
            // 0x02 自执行发送lisp都是异步,(用来发送 含有(command)的lisp的)
            _ = Ads_queueexpr(lispCode + "\n");
        }
        if ((flag & RunLispFlag.AcedEvaluateLisp) == RunLispFlag.AcedEvaluateLisp)
        {
            _ = AcedEvaluateLisp(lispCode, out IntPtr rb);
            if (rb != IntPtr.Zero)
                return DisposableWrapper.Create(typeof(ResultBuffer), rb, true) as ResultBuffer;
        }
        if ((flag & RunLispFlag.SendStringToExecute) == RunLispFlag.SendStringToExecute)
        {
            var dm = Application.DocumentManager;
            var doc = dm.MdiActiveDocument;
            doc.SendStringToExecute(lispCode + "\n", false, false, false);
        }
        return null;
    }

    #region __发送lisp接口测试
    public class TestSendLisp
    {
        [LispFunction("LispTest_RunLisp")]
        public static object LispTest_RunLisp(ResultBuffer rb)
        {
            CmdTest_RunLisp();
            return null;
        }

        [CommandMethod("CmdTest_RunLisp1", CommandFlags.Modal)]
        [CommandMethod("CmdTest_RunLisp2", CommandFlags.Transparent)]
        [CommandMethod("CmdTest_RunLisp3", CommandFlags.UsePickSet)]
        [CommandMethod("CmdTest_RunLisp4", CommandFlags.Redraw)]
        [CommandMethod("CmdTest_RunLisp5", CommandFlags.NoPerspective)]
        [CommandMethod("CmdTest_RunLisp6", CommandFlags.NoMultiple)]
        [CommandMethod("CmdTest_RunLisp7", CommandFlags.NoTileMode)]/*不允许模型使用命令*/
        [CommandMethod("CmdTest_RunLisp8", CommandFlags.NoPaperSpace)]/*不允许布局使用命令*/
        [CommandMethod("CmdTest_RunLisp9", CommandFlags.NoOem)]
        [CommandMethod("CmdTest_RunLisp10", CommandFlags.Undefined)]/*不允许调用*/
        [CommandMethod("CmdTest_RunLisp11", CommandFlags.Defun)]/*不允许调用*/
        [CommandMethod("CmdTest_RunLisp12", CommandFlags.NoNewStack)]
        [CommandMethod("CmdTest_RunLisp13", CommandFlags.NoInternalLock)]
        [CommandMethod("CmdTest_RunLisp14", CommandFlags.DocReadLock)]
        [CommandMethod("CmdTest_RunLisp15", CommandFlags.DocExclusiveLock)]
        [CommandMethod("CmdTest_RunLisp16", CommandFlags.Session)]
        [CommandMethod("CmdTest_RunLisp17", CommandFlags.Interruptible)]
        [CommandMethod("CmdTest_RunLisp18", CommandFlags.NoHistory)]
        [CommandMethod("CmdTest_RunLisp19", CommandFlags.NoUndoMarker)]
        [CommandMethod("CmdTest_RunLisp20", CommandFlags.NoBlockEditor)]
#if !NET35
        [CommandMethod("CmdTest_RunLisp21", CommandFlags.NoActionRecording)]
        [CommandMethod("CmdTest_RunLisp22", CommandFlags.ActionMacro)]
        [CommandMethod("CmdTest_RunLisp23", CommandFlags.NoInferConstraint)]
        [CommandMethod("CmdTest_RunLisp24", CommandFlags.TempShowDynDimension)]
#endif
        public static void CmdTest_RunLisp()
        {
            // 测试方法1: (command "CmdTest_RunLisp1")
            // 测试方式2: (LispTest_RunLisp)

            var dm = Acap.DocumentManager;
            var doc = dm.MdiActiveDocument;
            var ed = doc.Editor;
            var option = new PromptIntegerOptions("输入RunLispFlag枚举值");
            var ppr = ed.GetInteger(option);
            if (ppr.Status != PromptStatus.OK)
                return;
            var flag = (EditorEx.RunLispFlag)ppr.Value;

            if (flag == EditorEx.RunLispFlag.AdsQueueexpr)
            {
                // 同步
                ed.RunLisp("(setq a 10)(princ)",
                    EditorEx.RunLispFlag.AdsQueueexpr);
                ed.RunLisp("(princ a)",
                    EditorEx.RunLispFlag.AdsQueueexpr);//成功输出 
            }
            else if (flag == EditorEx.RunLispFlag.AcedEvaluateLisp)
            {
                // 使用(command "CmdTest_RunLisp1")发送,然后 !b 查看变量,acad08是有值的,高版本是null
                var strlisp0 = "(setq b 20)";
                var res0 = ed.RunLisp(strlisp0,
                    EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值

                var strlisp1 = "(defun f1( / )(princ \"aa\"))";
                var res1 = ed.RunLisp(strlisp1,
                    EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值

                var strlisp2 = "(defun f2( / )(command \"line\"))";
                var res2 = ed.RunLisp(strlisp2,
                    EditorEx.RunLispFlag.AcedEvaluateLisp); //有lisp的返回值
            }
            else if (flag == EditorEx.RunLispFlag.SendStringToExecute)
            {
                //测试异步
                //(command "CmdTest_RunLisp1")和(LispTest_RunLisp)4都是异步
                var str = "(setq c 40)(princ)";
                ed.RunLisp(str,
                    EditorEx.RunLispFlag.SendStringToExecute); //异步,后发送
                ed.RunLisp("(princ c)",
                    EditorEx.RunLispFlag.AdsQueueexpr); //同步,先发送了,输出是null 
            }
        }
    } 
    #endregion

}

发送cad命令(同步/异步)

#if !HC2020
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using Acap = Autodesk.AutoCAD.ApplicationServices.Application;
#else
using GrxCAD.DatabaseServices;
using GrxCAD.EditorInput;
using GrxCAD.Runtime;
using Acap = GrxCAD.ApplicationServices.Application;
#endif
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Reflection;
using System.Text;
using Exception = System.Exception;


namespace JoinBox
{
    public class PostCmd
    {
        /*
         *[DllImport("accore.dll", EntryPoint = "acedCommand")]
         *private static extern int AcedCommand(IntPtr vlist);
         */
        delegate int DelegateAcedCommand(IntPtr strExpr);
        static DelegateAcedCommand _AcedCommand;
        /// <summary>
        /// 发送命令(同步)
        /// </summary>
        public static int AcedCommand(IntPtr strExpr)
        {
            if (_AcedCommand == null)
            {
                string str   = "acedCommand";
                var pe       = new AcadPeInfo(str);
                _AcedCommand = pe.GetDelegate<DelegateAcedCommand>();
            }
            return _AcedCommand.Invoke(strExpr);//调用方法
        }

        /*
         *[DllImport("acad.exe", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl,
         *    EntryPoint = "?acedPostCommand@@YAHPB_W@Z")]
         *public static extern int AcedPostCommand(string strExpr);
        */
        delegate int DelegateAcedPostCommand(byte[] strExpr);
        static DelegateAcedPostCommand _AcedPostCommand;
        /// <summary>
        /// 发送命令(同步)
        /// </summary>
        public static int AcedPostCommand(string strExpr)
        {
            if (_AcedPostCommand == null)
            {
                string str       = "acedPostCommand";
                var pe           = new AcadPeInfo(str);
                _AcedPostCommand = pe.GetDelegate<DelegateAcedPostCommand>();
            }
            //不然到CAD之后会乱码
            byte[] bytes = Encoding.Unicode.GetBytes(strExpr);
            return _AcedPostCommand.Invoke(bytes);//调用方法
        }

        delegate int DelegateAcedInvoke(byte[] strExpr);
        static DelegateAcedInvoke _AcedInvoke;
        /// <summary>
        /// 发送命令(同步)
        /// </summary>
        public static int AcedInvoke(string strExpr)
        {
            if (_AcedInvoke == null)
            {
                string str  = "acedInvoke";
                var pe      = new AcadPeInfo(str);
                _AcedInvoke = pe.GetDelegate<DelegateAcedInvoke>();
            }
            //不然到CAD之后会乱码
            byte[] bytes = Encoding.Unicode.GetBytes(strExpr);
            return _AcedInvoke.Invoke(bytes);//调用方法
        }

        /*
#if NET35 || NET40
        [DllImport("acad.exe", EntryPoint = "acedCmd", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
#else
        //cad2015的AcedCmd改为AcedCmdS.参数也改了,不过不影响pe读取,accore.dll是2013版本后才开始改
        [DllImport("accore.dll", EntryPoint = "acedCmdS", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        private static extern int AcedCmd(IntPtr rbp, bool forFutureUse = false, IntPtr pForFutureUse = IntPtr.Zero);
#endif
        private static extern int AcedCmd(IntPtr rbp);
        public static int AcedCmd(ResultBuffer args)
        {
            if (Acap.DocumentManager.IsApplicationContext)
                return 0;
            else
                return AcedCmd(args.UnmanagedObject);
        }
         */
        delegate int DelegateAcedCmd(IntPtr rb);
        static DelegateAcedCmd _AcedCmd;
        /// <summary>
        /// 发送命令(同步)如果2015.+这里报错,那么表示vs需要提权测试
        /// </summary>
        public static int AcedCmd(ResultBuffer args)
        {
            if (Acap.DocumentManager.IsApplicationContext)
                return 0;
            if (_AcedCmd == null)
            {
                string str = "acedCmd";
                if (Acap.Version.Major >= 20)//2015.+
                    str += "S";
                var pe = new AcadPeInfo(str);
                _AcedCmd = pe.GetDelegate<DelegateAcedCmd>();
            }
            var reNum = _AcedCmd(args.UnmanagedObject);
            if (reNum != 5100)//5100正确
                throw new ArgumentNullException("发送命令出错,是否vs权限不足?");
            return reNum;
        }

        [CommandMethod("TestCmd_Aced")]
        public void TestCmd_Aced()
        {
            var dm = Acap.DocumentManager;
            var doc = dm.MdiActiveDocument;
            var db = doc.Database;
            var ed = doc.Editor;

            var options = new PromptPointOptions("点位置");
            var pot = ed.GetPoint(options);
            if (pot.Status != PromptStatus.OK)
                return;
            using (var rb = new ResultBuffer())
            {
                rb.Add(new TypedValue((int)EnumBufCode.Text, "INSERT"));
                rb.Add(new TypedValue((int)EnumBufCode.Text, "_ArchTick"));
                rb.Add(new TypedValue((int)EnumBufCode.Point3d, pot.Value));
                rb.Add(new TypedValue((int)EnumBufCode.Text, "1"));
                rb.Add(new TypedValue((int)EnumBufCode.Text, "1"));
                rb.Add(new TypedValue((int)EnumBufCode.Text, "0"));
                AcedCmd(rb);
            }
            using (var rb = new ResultBuffer())
            {
                rb.Add(new TypedValue((int)EnumBufCode.Text, "_.copyhist"));
                AcedCmd(rb);
            }
            if (Clipboard.ContainsText(TextDataFormat.Text))
            {
                string clipboardText = Clipboard.GetText(TextDataFormat.Text);
                // 现在剪贴板内容存在 clipboardText 里
                ed.WriteMessage(clipboardText);
            }
        }

        /// <summary>
        /// 发送命令(异步)
        /// </summary>
        /// <param name="str">命令</param>
        /// <returns></returns>
        public static bool ActiveCmd(string str)
        {
            //ActiveDocument 加载lisp第二个文档有问题,似乎要切换了才能
            object ActiveDocument = Com.App.GetType().InvokeMember("ActiveDocument", BindingFlags.GetProperty, null, Com.App, null);
            object[] commandArray = { str + "\n" };
            ActiveDocument.GetType().InvokeMember("SendCommand", BindingFlags.InvokeMethod, null, ActiveDocument, commandArray);
            return true;
        }
    }
}

子函数

1.此文使用了读取PE技术

2.AcadPeInfo

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;

using JoinBox.BasalCurrency;
using static JoinBox.IntPrtHelper;

namespace JoinBox
{
    // 自动获取本工程上面的发送命令的接口
    public class AcadPeInfo
    {
        #region 成员
        //这里的枚举对应 GetMethodException 错误值
        enum GetMethodErrorNum
        {
            Ok         = 0,
            NoModule   = 1,
            NoFuncName = 2,
        }

        static PeInfo _PeForAcadExe;
        public static PeInfo PeForAcadExe
        {
            get
            {
                if (_PeForAcadExe == null)
                {
                    // 拿到exe/dll路径,获取所有的函数名
                    var acadexeFullName = Process.GetCurrentProcess().MainModule.FileName;
                    _PeForAcadExe       = new PeInfo(acadexeFullName);
                }
                return _PeForAcadExe;
            }
        }

        static PeInfo _PeForAccoreDll;
        public static PeInfo PeForAccoreDll
        {
            get
            {
                if (_PeForAccoreDll == null)
                {
                    // 拿到exe/dll路径,获取所有的函数名
                    var acadexeFullName = Process.GetCurrentProcess().MainModule.FileName;
                    var accore = Path.GetDirectoryName(acadexeFullName) + "\\accore.dll";
                    if (File.Exists(accore))
                        _PeForAccoreDll = new PeInfo(accore);
                }
                return _PeForAccoreDll;
            }
        }

        /// <summary>
        /// 同名函数指针们
        /// </summary>
        static ProcInfo[] _Methods;
        public ProcInfo[] Methods
        {
            get
            {
                if (_Methods == null)
                {
                    List<ProcInfo> methodIntPtr = new();
                    if (_peInfo != null)
                        GetMethod(_methodName, _peInfo, methodIntPtr);
                    else
                    {
                        GetMethod(_methodName, PeForAcadExe, methodIntPtr);
                        if (methodIntPtr.Count == 0)
                            GetMethod(_methodName, PeForAccoreDll, methodIntPtr);
                    }
                    _Methods = methodIntPtr.ToArray();
                }
                return _Methods;
            }
        }

        string _methodName;
        PeInfo _peInfo;

        static Dictionary<string, List<ProcInfo>> _Dict;
        #endregion

        #region 构造
        static AcadPeInfo()
        {
            _Dict = new();
        }

        /// <summary>
        /// 通过函数名获取指针,指定类型
        /// </summary>
        /// <param name="methodName"></param>
        /// <param name="peInfo"></param>
        public AcadPeInfo(string methodName, JoinBox.BasalCurrency.PeInfo peInfo = null)
        {
            _methodName = methodName;
            _peInfo     = peInfo;
        }
        #endregion

        #region 方法
        /// <summary>
        /// 返回函数指针
        /// </summary>
        /// <param name="peInfo">Pe信息</param>
        /// <param name="funcName">方法名</param>
        /// <returns>错误信息</returns>
        GetMethodErrorNum GetMethod(string funcName, PeInfo peInfo, List<ProcInfo> funcAdress)
        {
            var identifyStr = funcName + ";" + peInfo.FullName;
            if (_Dict.ContainsKey(identifyStr))//如果已经找过,直接返回
                funcAdress.AddRange(_Dict[identifyStr]);
            else
            {
                try
                {
                    IntPrtToMethod(peInfo.FullName,
                                   peInfo.ExportDirectory.NameListToString,
                                   funcName,
                                   funcAdress);

                    if (funcAdress.Count != 0 &&
                        !_Dict.ContainsKey(identifyStr))
                        _Dict.Add(identifyStr, new List<ProcInfo>(funcAdress));
                }
                catch (GetMethodException ex)
                {
                    return (GetMethodErrorNum)ex.ErrorNum;
                }
            }
            return GetMethodErrorNum.Ok;
        }

        /// <summary>
        /// 转为委托
        /// </summary>
        /// <typeparam name="TDelegate">委托对象</typeparam>
        /// <returns></returns>
        public TDelegate GetDelegate<TDelegate>() where TDelegate : class
        {
            if (Methods.Count() == 0)
                return null;
            TDelegate func = null;
            foreach (var item in Methods)
            {
                //这里永远不报错,但是不代表不会出错.
                //调用C盘exe/dll时需要权限,
                //所以会出现:[DLLImport]可以,直接运行cad也可以,但是调试不行.
                //此时可以提权vs再调试,调试虽然不显示东西,但是运行是对的.
                func = Marshal.GetDelegateForFunctionPointer(item.MethodIntPtr, typeof(TDelegate)) as TDelegate;
                break;
            }
            return func;
        }
        #endregion
    }

    public class IntPrtHelper
    {
        public class ProcInfo
        {
            string _CName;
            /// <summary>
            /// 纯c语言名
            /// </summary>
            public string CName
            {
                get
                {
                    if (_CName == null)
                    {
                        _CName = MethodName.Replace("?", string.Empty); //剔除cpp前缀
                        int num = _CName.IndexOf("@");
                        if (num > -1)
                            _CName = _CName.Substring(0, num); //剔除参数部分
                    }
                    return _CName;
                }
            }

            /// <summary>
            /// 模块文件路径
            /// </summary>
            public string ModuleFullName;
            /// <summary>
            /// 模块指针
            /// </summary>
            public IntPtr ModuleIntPtr;
            /// <summary>
            /// 函数名
            /// </summary>
            public string MethodName;
            /// <summary>
            /// 函数指针
            /// </summary>
            public IntPtr MethodIntPtr;

            public ProcInfo(string methodName)
            {
                MethodName = methodName;
            }
        }

        /// <summary>
        /// 通过名字查找exe/dll内所有名字
        /// </summary>
        /// <param name="hModuleFullName">文件路径名</param>
        /// <param name="methodNames">函数集合在这找</param>
        /// <param name="findFuncName">函数</param>
        /// <param name="funcAdress">返回相关的函数指针集合</param>
        public static void IntPrtToMethod(
            string hModuleFullName,
            IEnumerable<string> methodNames,
            string findFuncName,
             List<ProcInfo> funcAdress)
        {
            IntPtr hModule = Win32API.WinApi.GetModuleHandle(hModuleFullName); // 执行前必须加载了先,acad.exe/accore.dll
            if (hModule == IntPtr.Zero)
                throw new GetMethodException(1, "找不到模块:" + hModuleFullName + "当前程序没有加载这个东西?");

            //遍历函数接口名单
            var fns = new List<ProcInfo>();
            foreach (var methodName in methodNames)
            {
                if (methodName.Contains(findFuncName))
                    fns.Add(new ProcInfo(methodName));
            }
            if (fns.Count == 0)
                throw new GetMethodException(2, "没有找到对应的函数:" + findFuncName);

            //排序,最少长度原则本身就是让完全相同字符串在最前面
            fns = fns.OrderBy(str => str.CName.Length)
                     .ThenBy(str => str.MethodName.Length).ToList();
            //获取函数指针
            foreach (var item in fns)
            {
                item.ModuleFullName = hModuleFullName;
                item.ModuleIntPtr = hModule;
                item.MethodIntPtr = Win32API.WinApi.GetProcAddress(item.ModuleIntPtr, item.MethodName);
                funcAdress.Add(item);
            }
        }
    }

    /// <summary>
    /// 封装错误
    /// </summary>
    public class GetMethodException : ApplicationException
    {
        public int ErrorNum;
        public string ErrorMsg;
        public Exception InnerException1;

        public GetMethodException() { }
        public GetMethodException(int errorNum, string msg) : base(msg)
        {
            ErrorNum = errorNum;
        }
        public GetMethodException(string msg) : base(msg)
        {
            ErrorMsg = msg;
        }
        public GetMethodException(string msg, Exception innerException) : base(msg, innerException)
        {
            InnerException1 = innerException;
            ErrorMsg        = msg;
        }
    }
}

发送多线程同步命令

#if !HC2020
using Acap = Autodesk.AutoCAD.ApplicationServices.Application;
using Autodesk.AutoCAD.Runtime;
#else
using Acap = GrxCAD.ApplicationServices.Application;
using GrxCAD.Runtime;
#endif
using System.Threading;
using System.Diagnostics;

namespace JoinBox
{
    public class 多线程命令测试
    {
        public static int aaaa = 0;
        const string CmdTest_CCCCC = "CmdTest_CCCCC";

        [CommandMethod(CmdTest_CCCCC)]
        public void CmdTest()
        {
            var dm = Acap.DocumentManager;
            var doc = dm.MdiActiveDocument;
            var db = doc.Database;
            var ed = doc.Editor;
            ed.WriteMessage($"\n命令内发送了{aaaa}");
        }

        [CommandMethod("CmdTest_ThreadSendCmd", CommandFlags.Session)]
        public void CmdTest_ThreadSendCmd()
        {
            new Thread(() => {
                while (true)
                {
                    // AutoGo.SyncManager见 
                    // https://www.cnblogs.com/JJBox/p/14179921.html
                    // https://gitee.com/inspirefunction/CadLabelBar
                    AutoGo.SyncManager.SyncContext.Post(() => {
                        try
                        {
                            var dm = Acap.DocumentManager;
                            var doc = dm.MdiActiveDocument;
                            var db = doc.Database;//关闭SyncContext.Post会出现:“doc.Database”引发了类型“System.NullReferenceException”的异常
                            var ed = doc.Editor;

                            ed.WriteMessage("刷新执行了\n" + aaaa++);

                            //多线同步命令1:
                            // doc.SendStringToExecute($"{CmdTest_CCCCC}\n", true, false, true); //第二参数必须true

                            //多线同步命令2:
                            var result = PostCmd.AcedPostCommand($"{CmdTest_CCCCC}\n");
                            if (result == 0)
                                ed.WriteMessage($"发送命令失败!");

                            //失败例子:
                            //PostCmd.AcedCmd 这个接口是命令的同步接口,但是不知道为什么多线程却永远失败
                            //dm.ExecuteInApplicationContext((arg) => {
                            //    var rb = new ResultBuffer
                            //    {
                            //        new TypedValue((int)EnumBufCode.Text, CmdTest_CCCCC)
                            //    };
                            //    int result = PostCmd.AcedCmd(rb);
                            //    if (result == 0)
                            //        ed.WriteMessage($"发送命令失败!");
                            //}, null);

                            ed.UpdateScreenEx();
                        }
                        catch (System.Exception)
                        {
                            Debugger.Break();
                        }
                    });
                    Thread.Sleep(100);//这里是子线程等待
                }
            }).Start();
        }
    }
}

发送Esc停止

田草博客转发了此问题:
http://www.tiancao.net/blogview.asp?logID=1871&cateID=3&tdsourcetag=s_pcqq_aiomsg

它的来源是官博:
https://adndevblog.typepad.com/autocad/2012/07/start-command-with-escape-characters-cc.html

我想从 非模态对话框 的按钮中启动命令,发现对于SendStringToExecute,应该使用字符 \x03而不是 ^C
这种情况下,
如果没有命令正在运行,那么 命令窗口 中会显示两个*Cancel* 字符串,
而如果单击菜单项时未运行命令(点击时候强制停止已有命令),则菜单的 ^C^C 不会导致 *Cancel* 出现.

我怎样才能达到同样的目的呢?
解:
你可以查 cmdnames 系统变量,根据当前运行的命令数量,可以在命令字符串的开头添加许多转义字符.

[CommandMethod("CmdTest_SendEsc")]
public void CmdTest_SendEsc()
{
    //c#非模态窗体发命令
    string esc = "";
    string cmds = CadSystem.Getvar("cmdnames");
    if (cmds.Length > 0)
    {
        int cmdNum = cmds.Split(new char[] { '\'' }).Length;
        for (int i = 0; i < cmdNum; i++)
            esc += '\x03'; //官博回答是这个,但是0x1B才是ESC
    }
    var doc = Application.DocumentManager.MdiActiveDocument;
    doc.SendStringToExecute(esc + "_.LINE ", true, false, true);
}

相关阅读

cad的com接口新旧写法
cad.net 仿lisp函数专篇

(完)

posted @ 2019-09-17 11:50  惊惊  阅读(2411)  评论(3编辑  收藏  举报