cad.RunCSharp(source,args,doc,options) 将完整 C# 源码按次内存编译,在当前 CAD 文档上下文执行,再返回普通数据。适合一次性查询、计算、批量图元操作。常驻事件、菜单和后台服务仍使用 CompileCSharp 插件。
com.cad.task,没有新 Socket 或管道。dotnet build 或 NETLOAD。当前短脚本宿主仅支持.NET8+ AutoCAD;中望/浩辰使用CompileCSharp DLL或COM/LISP,见兼容与迁移。
以下只读取当前文档并返回数据,不绘图;要求已经打开目标文档。
import com.cad;
var cad=com.cad(true,"AutoCAD.Application");
if(!cad || !cad.Documents.Count) error("请先打开目标图纸");
var doc=cad.ActiveDocument;
cad.ShowForeground();
var source=/***
using System.Text.Json;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
public static class Script {
public static object Execute(Document doc, Database db, Editor ed,
Transaction tr, JsonElement args) {
return new {
answer=args.GetProperty("a").GetInt32()+args.GetProperty("b").GetInt32(),
document=doc.Name
};
}
}
***/;
var result,err=cad.RunCSharp(source,{a=12;b=30},doc);
if(!result || !result.ok) error(err);
print(result.data);
成功时 result.data.answer 为 42。再次修改源码仍使用同一个 Script.Execute,不必生成新的命令名。宿主内部为每次业务程序集生成唯一身份。
技能同样保留增强cad;使用普通子模块时显式传入这个实例,不从doc.Application另取宿主:
import com.cad.script;
var result,err,task=com.cad.script.run(cad,source,{a=12;b=30},doc);
本库不依赖 Autos。source 可以是字符串、buffer 或 .cs 路径;UTF-16LE 文件应有 BOM。不处理 aardio 模板,也不是 .csx/REPL,不保留跨次脚本变量。
必须定义无命名空间的 public static class Script,提供以下同步静态方法:
public static object Execute(Document doc, Database db, Editor ed,
Transaction tr, JsonElement args)
doc/db/ed 属于任务绑定的活动文档;tr 由宿主创建。实际事务可以是 Transaction 的子类,不应比较具体类名。args 是 JSON 参数;省略时为 JSON null。支持对象、数组、字符串、数值、布尔值、Unicode、NUL 等 JSON 数据。JsonElement。返回值在提交事务前编码,外部得到 aardio 的普通表/数组/标量。Document/Database/DBObject/Transaction、委托、Task、反射对象等活对象;图元身份返回 Handle 字符串。数值精度仍受 JSON/调用方数值类型约束。AcCoreMgd/AcDbMgd/AcMgd 及当前 .NET Core/Desktop 框架。首版不提供任意 NuGet/第三方引用管理;需要长期依赖的复杂插件走 CompileCSharp。结果转换不把脚本动态类型交给长期序列化元数据缓存;只将宿主自身类型和已经编码的 JSON 送出。
宿主锁定目标文档,开启事务,调用入口并编码返回结果,最后提交。正常调用的 result.committed 表示宿主事务已提交;编译或入口错误不会执行脚本。
脚本使用传入的 tr.GetObject、tr.AddNewlyCreatedDBObject 等标准 CAD API;不要自行 Commit/Abort/Dispose 传入的事务,不切换或关闭文档,不修改宿主全局状态。新建 DBObject 等本地资源按 AutoCAD API 约定释放。
执行异常或结果编码错误发生在宿主提交前时,未提交事务会释放并回滚事务内数据库修改。实机已验证“创建圆后抛异常”和“创建圆后返回不可编码的 CAD 活对象”均未留下圆。
这不是任意副作用事务:文件/网络/事件/系统变量、其他文档、脚本自行提交的事务不在这项承诺内。不要注册长期事件或启动持久后台线程;需要这些功能时改走常驻插件。提示词和入口检查不是隔离边界。
run/增强 RunCSharp 返回 result,err,task:
null,err。task.path。result.ok 同时表示传输完成和 C# 业务成功。phase 区分 host_load、request、compiler(编译器初始化)、compile、entry、context、execute、result、commit 等阶段。C# 编译诊断包含编号、消息和行列。state=timeout, terminal=false 只是等待快照,代码可能继续运行。不要换任务ID重发;取消也不能中断已开始的 C#。compileMs/executeMs/encodeMs/totalMs 是 CAD 宿主内分段耗时;run 的 elapsed 还包含宿主准备、传输与等待。不能把内存编译毫秒数当作完整往返耗时。需要在等待前持久化路径时使用 create:
import com.cad.script;
var task,err=com.cad.script.create(cad,source,{a=12;b=30},doc);
if(!task) error(err);
print(task.path); // 在此保存恢复路径
var submitted,err=task.submit();
if(!submitted) error(err);
var result,err=task.wait();
print(result,err);
只读原回执,不提交、不准备 SDK:
var original,err=com.cad.script.open(task.path);
if(!original) error(err);
var result,err=original.wait(20000);
print(result,err);
open 得到的 read/wait 会解码 C# 结果;直接用 com.cad.task.open 仍可查看底层传输回执,但其中 data 是宿主的原始 JSON 字符串。submit/cancel/nudge 原样委托既有任务机制,默认 post、nudge=false。源码请求文件提交后不得修改,宿主核对 SHA256。
timeout/startTimeout/method/directory/nudge 等沿用 任务选项,directory 仍是任务目录。hostDirectory 指定宿主构建与缓存目录。默认在当前用户 LocalAppData 的 aardio/com.cad/script-hosts。sdkOptions 传给 process.dotnet,例如 {install=false}。通常无需指定 SDK 路径或版本。NetLoad 共用内部实现,无需安全选项或手动配置目录。CAD 允许时临时调整加载检查并恢复;受限时由 CAD 原生提示或拒绝,出现确认框请由用户处理。宿主成功加载并完成设置恢复后才执行业务脚本,后续直接复用。phase=host_load。内部复用 CompileCSharp 的实际 runtimeconfig/版本判断,在 CAD 内使用与目标运行时匹配的 SDK 所带 Roslyn。例如 SDK10 可以在外部编译 net8 宿主,但不能据此把 SDK10 编译器的运行时依赖任意加载进 .NET8 CAD。缺少匹配 SDK 时可由 process.dotnet 私有安装,不改全局 PATH。
编译器服务另置于一个私有、常驻的 AssemblyLoadContext,薄入口宿主不强引用 Roslyn;跨边界只传框架类型(源码、路径、字节、JSON)。这避免与 CAD 或其他插件所带的 Roslyn 冲突。本机 CAD 目录自带 4.10,而所选 SDK8.0.425 为 4.11;直接放入默认上下文的初版在冷启动失败,隔离后按实际 4.11 完成调用。这里的编译器上下文刻意常驻,和每次业务脚本的 collectible ALC 不同。
宿主缓存键包含宿主/编译器服务源码、CAD 路径/版本/托管 DLL 内容和编译器版本;同一缓存构建用进程互斥保护。只准备而不加载可用 com.cad.script.prepare(app,options),返回 host,err,info。缓存清单只在两个小组件均构建成功、依赖复制完成后发布。
构建目录、Roslyn 依赖和任务记录默认保留,不覆盖/删除被 CAD 加载的 DLL。关闭相关 CAD 后可以清理自己不再需要的缓存和请求记录。
2026-09-24 在本机 AutoCAD 2026 / .NET8 实测了参数、匿名结果、几何回读、异常回滚、保存重开、恢复与连续 100 份源码。旧 Framework 不走短脚本宿主;.NET10 选型路径仍需相应 CAD 实机验收。
每次业务脚本使用 collectible ALC,并在单独的 NoInlining 执行帧中请求卸载。本机 CAD 中,执行过这些脚本的 ALC 在诊断 GC 后仍存活;空程序集仅加载不执行可以回收。独立 .NET 进程里的动态匿名结果测试可以回收。尚未定位 CAD 内存活上下文的具体根引用,不能宣称完整热卸载已经解决。
最终隔离版本冷启动后的 100 个微小脚本样本:托管内存净增加约 8.09 MiB,进程私有内存末值反而比批次前低约 7.37 MiB,存活 ALC 从 8 到 108。私有内存会随 GC/运行时波动,末值下降不代表脚本 ALC 已卸载。这个样本支持继续使用短脚本路线,但不是长期内存上限,也不能用 IL 大小估算真实占用。暖态内存编译中位约 9.5 ms,而包含现有 COM 任务往返的中位约 1.66 s,二者不可混淆。
只在需要时查看统计:
var result,err=com.cad.script.stats(cad,false,doc); // 不触发全局GC
print(result,err);
com.cad.script.stats(cad,true,doc) 是显式 GC 诊断,可能暂停整个 CAD;不要放进日常每次脚本执行。ALC 隔离不保护 CAD 免受任意错误/恶意代码影响,也不能强制停止死循环。