cad.net 仿猫老师的vlisp小助手
说明
本功能就是直接点击cad图元获取vla函数,快速知道图元对应的方法和属性(存在可用的).
小贱贱他复刻了一个高版本用的vlisp小助手,
因为他调用了高版本函数,没有Acad08版,我很郁闷,
然后就在他的基础上造了一个net全版本通用的...
至于猫老师曾经实现过的,通过帮助文件实现参数获取,
我懒得做了,要新建数据库做索引才行,还要对数据库整理...
动图演示
乱码问题
Acad08测试时候:
以下操作不加T表示仅看vlisp支持的属性,
加T表示加看vlisp支持的方法,而加T时候会有乱码信息.
(if (setq ent (entsel))(progn (vlax-dump-object (vlax-ename->vla-object(car ent))T)))
它会将";支持的方法:" 会随机变成 ";?С值姆椒?"
直接使用Acad08获取都有,所以这无法通过改变字符串编码修改,
这可能仅发生在Acad08或者net35,
怀疑是Win10系统中文和Acad08编译环境不同导致.
因为我用Acad21测试并没有这样的问题.
处理方法详情见代码SplitMethod内
如果是用Lisp制作小助手,恐怕每次获取都是不一样的码值(lisp的解释器有bug),
无法用eq/equal对比,因为图层乱码是导致我学c#原因之一...
这可能就是不死猫老师vlisp小助手会出现有的能用,有的不能用的问题.
对话框模式和发送命令
代码同时支持调用winform的模态窗口和非模态窗口两种方式,可自己修改为自己喜欢的.
首先要说明:
先要发送复制命令历史命令,再从剪贴板获取历史.
1: 当发送命令时,此时要同步处理剪贴板,那么需要同步命令,
但是非模态不能发送同步命令,
否则出现Acap.DocumentManager.IsApplicationContext == true
命令上下文的报错.
2: 异步命令会导致先获取剪贴板,之后才发送了复制命令历史.
而cad异步命令又没有执行完成后的回调.
在我一筹莫展的时候,edata给了我个建议:
写一个自己的同步命令,在命令内发送复制命令历史命令,
再在剪贴板获取命令,并且修改窗体信息.
然后点击图元时候发送异步命令,调用这个自写的同步命令,
这样执行顺序就是正常的,窗体也能得到信息.
简直妙到不行,有一种用魔法打败魔法的感觉.
代码
调用命令
using Autodesk.AutoCAD.Runtime;
namespace JoinBox.CommandLisp.Vlisp
{
public class VlispCmd
{
public VlispForm vlisp = null;
[CommandMethod(nameof(Lisp))]
public void Lisp()
{
if (vlisp == null || vlisp.IsDisposed)
{
vlisp = new VlispForm();
}
#if true //两种模式我都调整好了
vlisp.Show();
Win32API.WinApi.SetFocus(vlisp.Handle);
#else
vlisp.ShowDialog();
#endif
}
/* 此命令并不是由用户调用,而是点选的时候自动调用.
因为在非模态窗口发送同步命令会失败.
所以制作了一个同步命令来
进行获取命令历史,加入剪贴板.
*/
[CommandMethod(nameof(Cmd_Copyhist), CommandFlags.NoHistory)]
public void Cmd_Copyhist()
{
VlispTool.SendCmd();
if (vlisp == null) return;
VlispTool.ClipboardData(cmdLines =>
{
// 发送的最后一行是发送的命令,移除掉
cmdLines.Remove(nameof(Cmd_Copyhist));
vlisp.SplitMethod(cmdLines);
});
}
}
}
剪贴板
using Autodesk.AutoCAD.DatabaseServices;
using System.Diagnostics;
using System.Windows.Forms;
using System;
using System.Collections.Generic;
namespace JoinBox.CommandLisp.Vlisp
{
public static class VlispTool
{
/// <summary>
/// 发送命令获取剪贴板历史
/// </summary>
public static void SendCmd()
{
const string cps = "_.copyhist";
var rb = new ResultBuffer
{
new TypedValue((int)EnumBufCode.Text, cps)
};
int cnum = CSendSynchronization.AcedCmd(rb);
if (cnum == 0)
{
Debug.WriteLine($"{cps}发送命令失败!");
}
}
/// <summary>
/// 获取剪贴板数据
/// </summary>
/// <param name="action"></param>
public static void ClipboardData(Action<List<string>> action)
{
// 此处还需要保存原来的剪贴板,再进行获取,再还原.
// 剪贴板内容
var iData = Clipboard.GetDataObject();
if (!iData.GetDataPresent(DataFormats.Text)) return;
string clipboardText = (string)iData.GetData(DataFormats.Text);
if (string.IsNullOrEmpty(clipboardText.Trim())) return;
// 将剪贴板的东西赋值给窗体
var cmdLines = clipboardText.Split(new[] { '\r', '\n' },
StringSplitOptions.RemoveEmptyEntries)
.ToList();
action(cmdLines);
}
}
}
发送命令
/// <summary>
/// 非模态对话框切换焦点回cad
/// </summary>
/// <param name="hWnd"></param>
/// <returns></returns>
[DllImport("user32.dll", EntryPoint = "SetFocus")]
public static extern int SetFocus(IntPtr hWnd);
public partial class CSendSynchronization
{
//发送同步命令
//由于cad2016里AcedCmd改成了AcedCmdS,Accore.dll也是2013版本后才开始改的。
//调用AutoCAD命令,ARX原型:int acedCmdS(const struct resbuf * rbp);
#if NET35 || NET40
[DllImport("acad.exe", EntryPoint = "acedCmd", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
#else
[DllImport("accore.dll", EntryPoint = "acedCmdS", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
#endif
private static extern int AcedCmd(IntPtr rbp);
/// <summary>
/// 调用C++的acedCmdS函数
/// </summary>
/// <param name="args">命令参数列表</param>
/// <returns>返回命令执行的状态</returns>
public static int AcedCmd(ResultBuffer args)
{
if (!Acap.DocumentManager.IsApplicationContext)
return AcedCmd(args.UnmanagedObject);
return 0;
}
}
窗体
窗体前台
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Acap = Autodesk.AutoCAD.ApplicationServices.Application;
using static JoinBox.CommandLisp.LSend;
using JoinBox.CommandLisp.Vlisp;
namespace JoinBox
{
public partial class VlispForm : Form
{
// 最高命令行数,用于清空历史记录,再获取历史时候就很干净
string _clear = new string('\n', 2048);
const string _entsel = "(Vlax-Ename->Vla-Object (Car (Entsel)))";
const string _dump = "(if (setq ent (entsel))(progn (vlax-dump-object (vlax-ename->vla-object(car ent))T)))";
public VlispForm()
{
InitializeComponent();
// 载入COM接口
RunLisp("(vl-load-com)");
}
/// <summary>
/// 点击图元按钮
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Button3_Click(object sender, EventArgs e)
{
ModalAction(ed =>
{
ed.WriteMessage(_clear); //清空命令历史
ResultBuffer dump = null;
dump = RunLisp(_dump);
if (dump == null || !dump.AutoDelete)
return;
ed.WriteMessage("\n");
SendCmdGetClipboard();
});
}
/// <summary>
/// 判断模态和非模态进入不同的处理
/// </summary>
/// <param name="action"></param>
private void ModalAction(Action<Editor> action)
{
var doc = Acap.DocumentManager.MdiActiveDocument;
if (doc is null) return;
var ed = doc.Editor;
if (this.Modal)
{
// 模态要启用用户交互
using ed.StartUserInteraction(this);
action.Invoke(ed);
return;
}
// 非模态设置焦点,并锁定文档才能执行修改数据库
Win32API.WinApi.SetFocus(Acap.MainWindow.Handle);
using (doc.LockDocument()) {
action.Invoke(ed);
}
Win32API.WinApi.SetFocus(this.Handle);
}
/// <summary>
/// 发送命令及获取剪贴板
/// </summary>
private void SendCmdGetClipboard()
{
// 仿照Acad2016函数让Acad2008用
// _cmdLines = Autodesk.AutoCAD.Internal.Utils.GetLastCommandLines(500, false);
if (this.Modal)
{
// 同步命令
VlispTool.SendCmd();
VlispTool.ClipboardData(cmdLines =>
{
SplitMethod(cmdLines);
});
return;
}
// 异步命令
string cmd = nameof(VlispCmd.Cmd_Copyhist) + "\n";
var doc = Acap.DocumentManager.MdiActiveDocument;
doc.SendStringToExecute(cmd, false, true, true);
}
/// <summary>
/// 切割字符串加入对话框
/// </summary>
/// <param name="cmdLines"></param>
public void SplitMethod(List<string> cmdLines)
{
// 由于只有方法和属性,它们就存在互斥关系.
// 网友建议用";"和其后一个空格来判断分类行,这样即使乱码也不怕.
int a = cmdLines.FindIndex(s => s.Contains(";")
&& s.Length > s.IndexOf(';')+1
&& s[s.IndexOf(';')+1] != ' '); //返回行号
if (a < 0) return;
ListBox lb = MethodList; // 加入函数:_dump有T参数
if (cmdLines[a] == ";特性值:") {
lb = AttributesList; // 加入属性:_dump没有T参数
}
this.BeginInvoke(new Action(() => {
lb.Items.Clear();
for (int i = a + 1; i < cmdLines.Count; i++) {
lb.Items.Add(cmdLines[i].Replace("; ", ""));
}
lb.TopIndex = lb.Items.Count - 1;
}));
}
/// <summary>
/// 属性值单击显示 Vlax-Get
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void AttributesList_SelectedIndexChanged(object sender, EventArgs e)
{
this.BeginInvoke(new Action(() =>
{
var text = AttributesList.Text.Split(" ".ToCharArray()).GetValue(0);
CodeText.Text = $"(Vlax-Get {_entsel} '{text} )";
}));
}
/// <summary>
/// 属性值双击显示 Vlax-Put
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void AttributesList_MouseDoubleClick(object sender, MouseEventArgs e)
{
this.BeginInvoke(new Action(() =>
{
var text = AttributesList.Text.Split(" ".ToCharArray()).GetValue(0);
CodeText.Text = $"(Vlax-Put {_entsel} '{text} 参数)";
}));
}
/// <summary>
/// 方法选择动作
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void MethodList_SelectedIndexChanged(object sender, EventArgs e)
{
this.BeginInvoke(new Action(() =>
{
var text = MethodList.Text.Split(' ');
var t1 = text.GetValue(0).ToString();
var t2 = text.GetValue(1).ToString().Split((new char[] { '(', ')' }));
int num = 1;
if (t2.GetValue(1).ToString() != "")
{
num = Convert.ToInt16(t2.GetValue(1));
}
StringBuilder canshu = new();
for (int i = 0; i < num; i++)
{
canshu.Append("参数 "); // ????这是什么?我当时为什么这样写?
}
CodeText.Text = $"(Vlax-Invoke-Method {_entsel} '{t1} {canshu})";
}));
}
/// <summary>
/// 数据复制到粘贴板
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Button2_Click(object sender, EventArgs e)
{
Clipboard.SetDataObject(CodeText.Text);
}
/// <summary>
/// 自动拷贝
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void CodeText_TextChanged(object sender, EventArgs e)
{
Clipboard.SetDataObject(CodeText.Text);
}
/// <summary>
/// 变量赋值
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void TextBox2_Leave(object sender, EventArgs e)
{
this.BeginInvoke(new Action(() =>
{
CodeText.Text = $"(setq {VariableText.Text} {CodeText.Text})";
}));
}
/// <summary>
/// 加载按钮
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Button1_Click(object sender, EventArgs e)
{
ModalAction(ed =>
{
ed.WriteMessage("\n\n");
ed.WriteMessage(CodeText.Text + "\n");
ResultBuffer aa = RunLisp(CodeText.Text);
var bb = aa.AsArray();
ed.WriteMessage(bb[0].Value.ToString() + "\n");
});
}
}
public static class LResultBufferTool
{
public static object GetValue(this ResultBuffer res)
{
var arr = res.AsArray();
object ret = arr[0].Value;
return ret;
}
}
}
窗体设计
namespace JoinBox
{
partial class VlispForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.AttributesList = new System.Windows.Forms.ListBox();
this.MethodList = new System.Windows.Forms.ListBox();
this.label3 = new System.Windows.Forms.Label();
this.CodeText = new System.Windows.Forms.TextBox();
this.button1 = new System.Windows.Forms.Button();
this.button3 = new System.Windows.Forms.Button();
this.label4 = new System.Windows.Forms.Label();
this.FeaturesText = new System.Windows.Forms.TextBox();
this.label5 = new System.Windows.Forms.Label();
this.MethodText = new System.Windows.Forms.TextBox();
this.label6 = new System.Windows.Forms.Label();
this.VariableText = new System.Windows.Forms.TextBox();
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.groupBox2 = new System.Windows.Forms.GroupBox();
this.groupBox1.SuspendLayout();
this.groupBox2.SuspendLayout();
this.SuspendLayout();
//
// AttributesList
//
this.AttributesList.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.AttributesList.FormattingEnabled = true;
this.AttributesList.ItemHeight = 12;
this.AttributesList.Location = new System.Drawing.Point(6, 20);
this.AttributesList.Name = "AttributesList";
this.AttributesList.Size = new System.Drawing.Size(546, 424);
this.AttributesList.TabIndex = 0;
this.AttributesList.SelectedIndexChanged += new System.EventHandler(this.AttributesList_SelectedIndexChanged);
this.AttributesList.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.AttributesList_MouseDoubleClick);
//
// MethodList
//
this.MethodList.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.MethodList.FormattingEnabled = true;
this.MethodList.ItemHeight = 12;
this.MethodList.Location = new System.Drawing.Point(6, 20);
this.MethodList.Name = "MethodList";
this.MethodList.Size = new System.Drawing.Size(256, 424);
this.MethodList.TabIndex = 2;
this.MethodList.SelectedIndexChanged += new System.EventHandler(this.MethodList_SelectedIndexChanged);
//
// label3
//
this.label3.AutoSize = true;
this.label3.Enabled = false;
this.label3.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.label3.Location = new System.Drawing.Point(8, 530);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(29, 12);
this.label3.TabIndex = 4;
this.label3.Text = "代码";
//
// CodeText
//
this.CodeText.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.CodeText.Location = new System.Drawing.Point(41, 527);
this.CodeText.Name = "CodeText";
this.CodeText.Size = new System.Drawing.Size(800, 21);
this.CodeText.TabIndex = 5;
this.CodeText.TextChanged += new System.EventHandler(this.CodeText_TextChanged);
//
// button1
//
this.button1.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.button1.Location = new System.Drawing.Point(416, 552);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(117, 27);
this.button1.TabIndex = 6;
this.button1.Text = "加载";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.Button1_Click);
//
// button3
//
this.button3.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.button3.Location = new System.Drawing.Point(293, 552);
this.button3.Name = "button3";
this.button3.Size = new System.Drawing.Size(117, 27);
this.button3.TabIndex = 6;
this.button3.Text = "选择图元";
this.button3.UseVisualStyleBackColor = true;
this.button3.Click += new System.EventHandler(this.Button3_Click);
//
// label4
//
this.label4.AutoSize = true;
this.label4.Enabled = false;
this.label4.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.label4.Location = new System.Drawing.Point(8, 475);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(29, 12);
this.label4.TabIndex = 7;
this.label4.Text = "功能";
//
// FeaturesText
//
this.FeaturesText.Enabled = false;
this.FeaturesText.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.FeaturesText.Location = new System.Drawing.Point(41, 471);
this.FeaturesText.Name = "FeaturesText";
this.FeaturesText.Size = new System.Drawing.Size(800, 21);
this.FeaturesText.TabIndex = 5;
this.FeaturesText.Text = "判断是在发生命令之前(先选取再执行)或之后选取对象。";
//
// label5
//
this.label5.AutoSize = true;
this.label5.Enabled = false;
this.label5.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.label5.Location = new System.Drawing.Point(8, 503);
this.label5.Name = "label5";
this.label5.Size = new System.Drawing.Size(29, 12);
this.label5.TabIndex = 4;
this.label5.Text = "方法";
//
// MethodText
//
this.MethodText.Enabled = false;
this.MethodText.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.MethodText.Location = new System.Drawing.Point(41, 499);
this.MethodText.Name = "MethodText";
this.MethodText.Size = new System.Drawing.Size(800, 21);
this.MethodText.TabIndex = 5;
//
// label6
//
this.label6.AutoSize = true;
this.label6.Enabled = false;
this.label6.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.label6.Location = new System.Drawing.Point(8, 559);
this.label6.Name = "label6";
this.label6.Size = new System.Drawing.Size(29, 12);
this.label6.TabIndex = 4;
this.label6.Text = "变量";
//
// VariableText
//
this.VariableText.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.VariableText.Location = new System.Drawing.Point(41, 555);
this.VariableText.Name = "VariableText";
this.VariableText.Size = new System.Drawing.Size(246, 21);
this.VariableText.TabIndex = 5;
this.VariableText.Leave += new System.EventHandler(this.TextBox2_Leave);
//
// groupBox1
//
this.groupBox1.Controls.Add(this.AttributesList);
this.groupBox1.Location = new System.Drawing.Point(6, 5);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(561, 456);
this.groupBox1.TabIndex = 8;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "属性";
//
// groupBox2
//
this.groupBox2.Controls.Add(this.MethodList);
this.groupBox2.Location = new System.Drawing.Point(573, 5);
this.groupBox2.Name = "groupBox2";
this.groupBox2.Size = new System.Drawing.Size(268, 456);
this.groupBox2.TabIndex = 9;
this.groupBox2.TabStop = false;
this.groupBox2.Text = "方法";
//
// VlispLook
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
this.ClientSize = new System.Drawing.Size(849, 583);
this.Controls.Add(this.groupBox2);
this.Controls.Add(this.groupBox1);
this.Controls.Add(this.label4);
this.Controls.Add(this.button3);
this.Controls.Add(this.button1);
this.Controls.Add(this.FeaturesText);
this.Controls.Add(this.VariableText);
this.Controls.Add(this.MethodText);
this.Controls.Add(this.CodeText);
this.Controls.Add(this.label5);
this.Controls.Add(this.label6);
this.Controls.Add(this.label3);
this.Name = "VlispLook";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Vlisp图元属性查看器";
this.groupBox1.ResumeLayout(false);
this.groupBox2.ResumeLayout(false);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.ListBox AttributesList;
private System.Windows.Forms.ListBox MethodList;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.TextBox CodeText;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Button button3;
private System.Windows.Forms.Label label4;
private System.Windows.Forms.TextBox FeaturesText;
private System.Windows.Forms.Label label5;
private System.Windows.Forms.TextBox MethodText;
private System.Windows.Forms.Label label6;
private System.Windows.Forms.TextBox VariableText;
private System.Windows.Forms.GroupBox groupBox1;
private System.Windows.Forms.GroupBox groupBox2;
}
}
后记
反射图元
实际上我们需求是反射图元下面所拥有的东西,那么,我们为什么要靠net调用arx反射?
我们net可是有API的啊!所以只需要反射图元就好了...
Arx制作
由于Arx可以通过重写命令栏的方式拦截命令文本数据,所以不需要像c#一样在那绕圈圈,
便可以导出命令历史到窗体上面,不过实现了功能就好啦
(完)