cad.net 定义lisp
阅读原文时间:2023年07月16日阅读:5

通用枚举值…

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

    Resbuf         = , //resbuf

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

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

首先是传参型lisp的定义,它在低版本有个问题,就是所有的返回值都要用表包裹着,而高版本就修复了这个问题.

为了所有版本统一,返回值应该统一成 ResultBuffer ,在lisp再(car 返回值)…. 我这里既有 ResultBuffer,又有object返回,供各位参考.

    //传参型lisp的定义:传点  
    //复制到命令栏运行:  (Test\_AddLine (getpoint))  
    \[LispFunction("Test\_AddLine")\] //注意: 这里不是command!  
    public static ResultBuffer AddLine(ResultBuffer rbArgs)  
    {  
        Database db = Acap.DocumentManager.MdiActiveDocument.Database;  
        Editor ed = Acap.DocumentManager.MdiActiveDocument.Editor;

        Point3d p1 = Point3d.Origin;  
        Point3d p2 = Point3d.Origin;  
        if (rbArgs != null)  
        {  
            foreach (TypedValue rb in rbArgs)  
            {  
                try  
                {  
                    //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().Substring(, str.Length - );

                    var pts = str.Split(',');  
                    p2 = new Point3d(double.Parse(pts\[\]), double.Parse(pts\[\]), double.Parse(pts\[\]));  
                }  
                catch  
                { }  
            }  
        }  
        var line = new Line(p1, p2);  
        using (Transaction tr = db.TransactionManager.StartTransaction())  
        {  
            var acBlkTbl = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;  
            var acBlkTblRec = tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;  
            // line.SetDatabaseDefaults();设置数据库默认值  
            acBlkTblRec.AppendEntity(line);  
            tr.AddNewlyCreatedDBObject(line, true);  
            tr.Commit();  
        }  
        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的定义:加法  
    //复制到命令栏运行:  (Test\_MyAdd 1 2 3)  
    \[LispFunction("Test\_MyAdd")\] //注意: 这里不是command!  
    public static object Test\_MyAdd(ResultBuffer args) //高版本这里可以返回double,但是08只能返回ResultBuffer  
    {  
        var ds = new List<double>();  
        if (args != null)  
        {  
            foreach (TypedValue rb in args)  
            {  
                try  
                {  
                    var a = double.Parse(rb.Value.ToString());  
                    ds.Add(a);  
                }  
                catch  
                { }  
            }  
        }

        double number = ;  
        foreach (var item in ds)  
        {  
            number += item;  
        }

        //08只能返回ResultBuffer 会返回表内数字(6.0)  
        //高版本可以返回其他的  
        var rbrtn = new ResultBuffer(new TypedValue\[\]  
        {  
             new TypedValue((int)EnumBufCode.Double, number),  
        });

        var dotPair = new ResultBuffer(new TypedValue\[\] {  
        new TypedValue((int)EnumBufCode.ListBegin, -),  
        new TypedValue((int)EnumBufCode.Int16, ),  
        new TypedValue((int)EnumBufCode.Int16, ),  
        new TypedValue((int)EnumBufCode.DottedPair, -),  
        });

#if AC2008
return rbrtn; //但是08只能返回ResultBuffer 会返回(6.0)
#else
return number; //高版本这里可以返回double 会返回6.0
#endif

    }

    //传参型lisp的定义:选择集  
    //复制到命令栏运行:  (Test\_ssget (ssget))  
    \[LispFunction("Test\_ssget")\] //注意: 这里不是command!  
    public static object Test\_ssget(ResultBuffer args)  
    {  
        //var dt = new List<object>();  
        var ds = new List<object>();  
        if (args != null)  
        {  
            foreach (TypedValue rb in args)//{((5007,(((-2181752),Crossing,0,)((-2181760),Crossing,0,))))}  
            {  
                try  
                {  
                    //dt.Add(rb.TypeCode);  
                    ds.Add(rb.Value);  
                }  
                catch  
                { }  
            }  
        }

        var tl = new List<TypedValue>();  
        foreach (var item in ds)  
        {  
            tl.Add(new TypedValue((int)EnumBufCode.SelectionSet, item));  
        }  
        var rbrtn = new ResultBuffer(tl.ToArray());  
        return rbrtn;  
    }

    //传参型lisp的定义:点对  
    //复制到命令栏运行:  (Test\_dotPair 0 100)  
    \[LispFunction("Test\_dotPair")\] //注意: 这里不是command!  
    public static object Test\_dotPair(ResultBuffer args)  
    {  
        var ds = new List<object>();  
        if (args != null)  
        {  
            foreach (TypedValue rb in args)//{((5007,(((-2181752),Crossing,0,)((-2181760),Crossing,0,))))}  
            {  
                try  
                {  
                    ds.Add(rb.Value);  
                }  
                catch  
                { }  
            }  
        }

        var ls = new List<TypedValue>();

        ResultBuffer dotPair = null;  
        if (ds.Count == )  
        {  
            ls.Add(new TypedValue((int)EnumBufCode.ListBegin, -));

            ls.Add(new TypedValue((int)EnumBufCode.Int16, ds\[\]));  
            if (short.TryParse(ds\[\].ToString(), out short num))  
            {  
                ls.Add(new TypedValue((int)EnumBufCode.Int16, num));  
                ls.Add(new TypedValue((int)EnumBufCode.DottedPair, -));  
                dotPair = new ResultBuffer(ls.ToArray());  
            }  
        }  
        return dotPair; //返回点对表  
    }

在c#读取和赋值lisp变量的值 ,测试命令: Test_setLisp

#if !HC2020
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using Application = Autodesk.AutoCAD.ApplicationServices.Application;
#else
using GrxCAD.DatabaseServices;
using GrxCAD.EditorInput;
using GrxCAD.Geometry;
using GrxCAD.ApplicationServices;
using GrxCAD.Runtime;
using GrxCAD.Colors;
using GrxCAD.GraphicsInterface;
using Viewport = GrxCAD.DatabaseServices.Viewport;
using Application = GrxCAD.ApplicationServices.Application;
#endif

using System.Runtime.InteropServices;
using System;

namespace JoinBox.CommandLisp
{
// 操作lisp赋值
// https://blog.csdn.net/qq_21489689/article/details/80630817
// 我验证了2008和2019,32位 DllImport("accore.dll" 好像不对,没有环境验证

public static class LStructure  
{  
    //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);

#if AC2008
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("acad.exe", EntryPoint = "acedPutSym", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
extern static private int AcedPutSym32(string args, IntPtr result);
#else
//没有环境验证
//LSP变量的写入-32位
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("accore.dll", EntryPoint = "_acedPutSym", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
extern static private int AcedPutSym32(string args, IntPtr result);
#endif

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

    //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);

#if AC2008
//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);
#else
//没有环境验证
//LSP变量的读取-32位
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("accore.dll", EntryPoint = "_acedGetSym", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
extern static private int AcedGetSym32(string args, out IntPtr result);
#endif

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

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

    \[CommandMethod("Test\_setLisp")\]  
    public static void Test\_setLisp()  
    {  
        Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;  
        string varName = "a";  
        object v;  
        // 赋值例子  
        // (setq a "35432")  
        SetLspVar(EnumBufCode.Text, varName, "");   //返回  "3543252351515"  
        v = GetLspVar(varName);  
        ed.Princ(v);

        // (setq a 35432)  
        SetLspVar(EnumBufCode.Double, varName, ""); //返回   3543252351515  
        v = GetLspVar(varName);  
        ed.Princ(v);

        // (setq a T)  
        SetLspVar(EnumBufCode.T\_atom, varName, true);    //返回   T  
        SetLspVar(EnumBufCode.T\_atom, varName, false);   //返回   T  
        v = GetLspVar(varName);  
        ed.Princ(v);

        // (setq a nil)  
        SetLspVar(EnumBufCode.Nil, varName);             //返回   nil  
        v = GetLspVar(varName);  
        ed.Princ(v);  
    }

    static void Princ(this Editor ed, object var)  
    {  
        string varString = "";  
        if (var == null)  
        {  
            varString = "nil";  
        }  
        else  
        {  
            string type = var.GetType().Name;  
            switch (type)  
            {  
                case "String":  
                    {  
                        varString = var.ToString();  
                        varString = "\\"" + varString + "\\"";  
                    }  
                    break;  
                case "Double":  
                    {  
                        varString = var.ToString();  
                    }  
                    break;  
                case "Boolean":  
                    {  
                        varString = "T"; //返回布尔值都是为true的,不返回才是nil  
                    }  
                    break;  
            }  
        }  
        ed.WriteMessage("\\n" + varString);  
    }  
}  

}

再来是通过接口发送lisp,这个可以避免用明文方式发送到命令栏,而且它在自动执行函数上面也是同步发送的,很有趣哟: testLisp

#if AC2008 || AC2012
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("acad.exe", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl,
EntryPoint = "?acedEvaluateLisp@@YAHPB_WAAPAUresbuf@@@Z")]
private static extern int AcedEvaluateLisp(string lispLine, out IntPtr result);

    \[DllImport("acad.exe", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedInvoke")\]  
    private static extern int AcedInvoke(IntPtr args, out IntPtr result);  

#else
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("accore.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl,
EntryPoint = "?acedEvaluateLisp@@YAHPEB_WAEAPEAUresbuf@@@Z")]
private static extern int AcedEvaluateLisp(string lispLine, out IntPtr result);

    \[DllImport("accore.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedInvoke")\]  
    private static extern int AcedInvoke(IntPtr args, out IntPtr result);  

#endif

    /// <summary>  
    /// 定义lisp  
    /// </summary>  
    /// <param name="arg">lisp语句</param>  
    /// <returns>缓冲结果,返回值</returns>  
    public static ResultBuffer RunLisp(string arg)  
    {  
        AcedEvaluateLisp(arg, out IntPtr rb);  
        if (rb != IntPtr.Zero)  
        {  
            try  
            {  
                var rbb = DisposableWrapper.Create(typeof(ResultBuffer), rb, true) as ResultBuffer;  
                return rbb;  
            }  
            catch  
            {  
                return null;  
            }  
        }  
        return null;  
    }

    /// <summary>  
    /// c#发送lisp,这个同步方式可以在自动运行函数上跑,也是同步的  
    /// </summary>  
    /// <returns></returns>  
    \[CommandMethod("testLisp")\]  
    public void Cmdtest()  
    {  
        string Strlisp = "(setq a 10)";  
        var res = RunLisp(Strlisp); //有lisp的返回值  
    }

最后是提供一些发送命令的函数,这里有些备注,他们可以异步发送lisp,有些会导致自动执行时候发送lisp出错,但是却是异步发送的必需品.

public partial class SendToCad  
{  

#if AC2006 || AC2007 || AC2008 || AC2009 || AC2010 || AC2011 || AC2012
///

/// 发送命令 ///
///
///
[DllImport("acad.exe", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ads_queueexpr")]
private static extern int Ads_queueexpr(string strExpr);//非同步,这个在08/12发送lisp不会出错,但是发送bo命令出错了..

    /// <summary>  
    /// 发送命令,设置CommandFlags.Session可以同步,  
    /// 发送lisp也可以,但是非同步,在自动执行函数上面会非同步  
    /// </summary>  
    /// <param name="str"></param>  
    public static void SendLisp(string str)  
    {  
        try  
        {  
            Ads\_queueexpr(str + "\\n");  
        }  
        catch (Exception ee)  
        {  
            //自执行发送lisp都是在最后的(异步执行)  
            var ed = Application.DocumentManager.MdiActiveDocument.Editor;  
            ed.WriteMessage(Environment.NewLine + "发送命令失败,导致加载失败!");  
            ed.WriteMessage(Environment.NewLine + "" + ee.Message + Environment.NewLine);  
        }  
    }

    /// <summary>  
    /// 发送命令  
    /// </summary>  
    /// <param name="strExpr"></param>  
    /// <returns></returns>  
    \[DllImport("acad.exe", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "?acedPostCommand@@YAHPB\_W@Z")\]  
    private static extern int AcedPostCommand(string strExpr);  

#else
///

/// 发送命令 ///
/// 发送lisp加载命令
///
public static void SendLisp(string str)
{
Document dc = Application.DocumentManager.MdiActiveDocument;
string commands = str + "\n";
try
{
dc.SendStringToExecute(commands, false, false, false);//08所有都flase会有问题,出现报错
}
catch (System.Exception ee)
{
//自执行发送lisp都是在最后的(异步执行)
var ed = dc.Editor;
ed.WriteMessage(ee.Message);
ed.WriteMessage(Environment.NewLine + "发送命令失败,导致加载失败!");
ed.WriteMessage(Environment.NewLine + ee.Message);
}
}
#endif
/// /// 发送命令 ///
/// 命令
///
public static bool SendCommand(string str) //非同步,这里加载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;
}
}

最后的最后是一个发送esc的操作,我也不知道为什么要放这里,可能他存在的价值不高…

    //http://www.tiancao.net/blogview.asp?logID=1871&cateID=3&tdsourcetag=s\_pcqq\_aiomsg  
    /\*  
    我想从我的非模态对话框的按钮中启动一些命令。我已经发现,对于SendStringToExecute,我应该使用‘\\x03’字符而不是^C。  
    我唯一的问题是,在这种情况下,如果没有运行命令,那么我将在命令窗口,如果单击菜单项时没有运行命令,则菜单的^C^C不会导致\* Cancel\*出现。  
    我怎样才能达到同样的目的呢?  
    解:  
    你可以查帐CMDNAMES系统变量,根据当前运行的命令数量,可以在命令字符串的开头添加许多转义字符。  
    这里有一个样本这表明:  
    \*/  
    \[CommandMethod("sendEsc")\]  
    public void SendEsc()  
    {  
        //c#非模态窗体发命令  
        string esc = "";  
        string cmds = CadSystem.Getvar("CMDNAMES");  
        if (cmds.Length > )  
        {  
            int cmdNum = cmds.Split(new char\[\] { '\\'' }).Length;  
            for (int i = ; i < cmdNum; i++)  
                esc += '\\x03';  
        }  
        Document doc = Application.DocumentManager.MdiActiveDocument;  
        doc.SendStringToExecute(esc + "\_.LINE ", true, false, true);  
        //设置cad窗口的焦点激活绘图区  
        //acApp.MainWindow.Focus();  
    }

最最后,写一下com接口的新旧版本写法:

public static class Com  
{  

#if AC2006 || AC2007 || AC2008 || AC2009 || AC2010 || AC2011 || AC2012
public static AcadDocument Adm { get; } = Application.DocumentManager.MdiActiveDocument.AcadDocument as AcadDocument;
#else
public static AcadDocument Adm { get; } = Application.DocumentManager.MdiActiveDocument.GetAcadDocument() as AcadDocument;
#endif

#if !HC2020
public static AcadApplication App { get; } = Application.AcadApplication as AcadApplication;
#else
public static GcadApplication App { get; } = Application.AcadApplication as GcadApplication;
#endif
}