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; //返回点对表
}
}
}
读取和赋值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函数专篇
(完)