
简介《CAD多重插入引用-炸开方法(加密解密)》是一份面向CAD绘图人员、图纸接收方及需要处理加密图纸的技术人员的小型文档聚焦多重插入引用造成的无法编辑、无法分解问题整理可操作的解密与炸开思路。包内共1个doc文档压缩包约18KB篇幅精简适合作为随查随用的方法备忘。文档围绕多种处理路径展开既介绍借助AutoLISP脚本与CAD加密插件将多重插入块转换为普通块的操作方式也给出用LISP代码实现转换的思路并延伸到AutoLISP编程、ENTSEL与ENTGET取块信息、DEFUN定义函数等知识点其中exm与lockb分别对应多重插入块与普通块的双向转换便于读者理解加密与解密的实现逻辑。内容还帮助读者按块类型选择合适路径理解脚本加载、命令调用到块转换的基本流程。目前已有457人学习适合需要快速排查加密图纸、补充LISP处理思路的初中级CAD用户参考。1. 为什么 EXPLODE 按下去没反应多重插入引用的本质接手外部图纸做翻模经常遇到这种场面块能选中、能移动、能缩放属性面板里块名写得明明白白可右键“分解”是灰的命令行敲 EXPLODE 回车只回一句“无法分解”。换机器、换版本、重启软件都一个样很多人第一反应是文件坏了其实这是被做成了多重插入引用也就是 CAD 里最省事的一种图纸锁。MINSERT 在 DXF 里的 0 组码照样写 INSERT真正的区别是子类标记从 AcDbBlockReference 换成了 AcDbMInsertBlock再挂上行数、列数、行距、列距四个组码。CAD 内核的分解命令碰到这个标记直接拒绝执行于是它成了不需要任何第三方组件就能实现的“加密”。流传很广的那几个 AutoLISP 小脚本exm、lockb 这类干的就是把标记换回去、顺手把四个阵列组码清零让实体退化成普通块参照再炸。这套办法适合经常接外部图纸、要做翻模、算量、二次深化的人。它解决的是“标记”这一层图纸里要是还叠了代理对象、外部参照或者嵌套块脚本一样白给后面几章会把边界一条条摊开。2. 先看清敌人MINSERT 在 DXF 组码里长什么样动手改之前得先会看。AutoLISP 改块本质上是在改实体的关联表你对着一张表瞎 subst改错一个组码块就可能原地消失或者跑到坐标原点去。所以先把普通块和多插入块的表结构差异摸清楚后面每一行代码才知道在动什么。2.1 INSERT 与 MINSERT 的组码差异用一个对照表把关键组码列出来左边是普通块参照右边是被锁过的块。注意 0 组码完全一样光看实体类型是分不出来的。组码含义普通 INSERTMINSERT0实体类型INSERTINSERT无法区分100子类标记AcDbBlockReferenceAcDbMInsertBlock2块名有有10/20/30插入点有有41/42/43X/Y/Z 缩放有有50旋转角有有70列数缺省或 0常见 1 或更大71行数缺省或 0常见 1 或更大44列间距无有可能为 045行间距无有可能为 0最容易翻车的地方在于判定依据。很多人以为 70、71 大于 1 才是多重插入实际上加密工具普遍把 70 和 71 都设成 1屏幕上看起来就是一个孤零零的块缩放、旋转、阵列全都看不出来但分解照样被拒。判定只能认 100 子类标记这是血泪经验别信数值。另一个细节是 44 和 45 允许为 0。间距为 0 的时候多个副本完全重叠在一起你看到的是一个块实际上图里躺着好几个炸开之后会“凭空”多出东西这种图纸最容易让人怀疑人生。2.2 用 ENTGET 把一块的体检报告打出来先把表完整读出来看。下面这段不依赖任何插件粘进命令行或者存成 lsp 都能跑。;; 打印选中对象的完整 DXF 关联表 (defun c:dxdump ( / e ) (setvar cmdecho 0) (setq e (car (entsel \n选一个块: ))) (if e (progn (foreach x (entget e) (print x) ; 逐条打印 (组码 . 值) ) (princ \n---- 表尾 ----) ) (princ \n没选中对象) ) (setvar cmdecho 1) (princ) )entsel返回的是一个只含一个元素的表外面必须套car才能拿到实体名这一点和ssget取首元素是同一个套路。entget拿到的是完整关联表foreach挨个print命令行里就能看到(100 . AcDbMInsertBlock)这一行有没有。这里有个很隐蔽的坑entget返回的表里100子类标记会出现不止一次普通块通常是(100 . AcDbEntity)加(100 . AcDbBlockReference)两条。如果你用(assoc 100 e)去判断它只会返回表里第一条也就是AcDbEntity于是所有块都被判成“不是多重插入”。正确写法是用member做整表查找;; 判定当前对象是不是多重插入引用 (defun c:what ( / e d typ ) (setvar cmdecho 0) (setq e (car (entsel \n选对象: ))) (if e (progn (setq d (entget e)) (setq typ (cdr (assoc 0 d))) (cond (( typ INSERT) (if (member (100 . AcDbMInsertBlock) d) (princ \n判定结果: 多重插入引用 MINSERT需要还原) (princ \n判定结果: 普通块参照可以直接 EXPLODE) ) ) (( typ ACAD_PROXY_ENTITY) (princ \n判定结果: 代理实体改组码没用) ) (T (princ (strcat \n判定结果: 其他类型 typ))) ) ) ) (setvar cmdecho 1) (princ) )member判断的是整条点对在表里存不存在不受出现次序影响这是判定的标准写法。顺带把ACAD_PROXY_ENTITY也拦下来因为这类实体是原厂自定义对象留下的壳组码里根本没有块参照那一套subst 改完 entmake 会直接返回 nil。2.3 加密、代理对象、外部参照三条路要分清选中对象之后先看 0 组码这一步能省掉大量无用功。0 组码是 INSERT 且带 AcDbMInsertBlock属于本章讨论的范围改标记就能解决。0 组码是 INSERT 但不带这个标记那它本来就是普通块炸不开的原因通常是块被设成了“不可分解”进块编辑器把“允许分解”勾上就行和加密没关系。0 组码是 ACAD_PROXY_ENTITY 或者块名里带一串星号加字母的匿名块说明图纸里塞了自定义对象这类东西必须要有对应的应用程序才能还原成基本图元脚本层面基本无解。还有一种情况是 0 组码为 INSERT、块名带路径“特性”面板里显示为外部参照这种也不在多重插入的范畴得先绑定再处理。我一般会养成一个习惯拿到不认识的图纸先跑一遍判定函数把可疑对象挨个点一遍心里有数了再决定用哪种手段。这一步花三十秒能避免后面半小时的瞎折腾。3. 把 MINSERT 打回普通块exm 脚本逐行拆解网上流传的版本里全角括号、中文引号、被论坛排版吃掉的换行到处都是直接复制多半报 malformed list。下面这份是把原始脚本整理成可运行状态后的版本逻辑没有改动只补了判空和失败保护因为原版在没选中对象时会直接报错退出。3.1 脚本全文与加载方式;; exm.lsp —— 把多重插入引用还原成普通块参照 ;; 加载: APPLOAD 或直接拖入窗口, 命令行输入 EXM ;; 反向操作见 4.1 的 lockb (defun c:exm ( / stm e stlx r70 c71 d44 d45 ) (setvar cmdecho 0) (setq stm (car (entsel \n点取多重块 回车结束: ))) (while stm (setq e (entget stm)) (setq stlx (cdr (assoc 0 e))) (if ( stlx INSERT) (progn ;; 先取到四个阵列组码的原始点对 (setq r70 (assoc 70 e)) (setq c71 (assoc 71 e)) (setq d44 (assoc 44 e)) (setq d45 (assoc 45 e)) ;; subst 会返回新表, 必须接住, 不能只调用不赋值 (if d44 (setq e (subst (cons 44 0) d44 e))) (if d45 (setq e (subst (cons 45 0) d45 e))) (if r70 (setq e (subst (cons 70 0) r70 e))) (if c71 (setq e (subst (cons 71 0) c71 e))) ;; 核心一步: 换子类标记 (setq e (subst (list 100 AcDbBlockReference) (list 100 AcDbMInsertBlock) e)) ;; 先建新的, 成功了再删旧的 (if (entmake e) (entdel stm) (princ \nentmake 失败, 原实体已保留) ) ) (princ \n选中的不是块参照, 跳过) ) (setq stm (car (entsel \n点取下一多重块 回车结束: ))) ) (setvar cmdecho 1) (princ) )加载方式有两种命令行敲APPLOAD打开加载对话框选中文件或者直接把 lsp 文件拖进绘图区。拖拽失败是高频问题新版 CAD 有安全加载限制只信任特定目录文件放在桌面或者下载文件夹里会被静默拦掉命令行什么都不提示。稳妥做法是把 lsp 放进一个固定目录再把这个目录加到受信任位置列表里一次配置长期有效。加载成功之后命令行敲EXM回车按提示点块。原版脚本里entsel前面没有判空用户一按回车或者点空处就抛“参数类型错误”把整段 while 打断。上面这份加了if回车能正常退出循环。3.2 subst 替换的五个组码各自管什么subst的语义是“在表里找到第一个与旧项相等的元素换成新项返回整张新表”它不改原表。所以每一步都必须写成(setq e (subst ...))只写(subst ...)而不接收返回值等于白干命令行不报错块一动不动这是新手最常犯的错。五个替换里100是决定性的。把AcDbMInsertBlock换成AcDbBlockReference实体在 CAD 眼里就从阵列块退化成了普通块参照分解命令立刻就认了。这一步做完即使 70、71 还是 1EXPLODE 也能执行。70和71是列数和行数。清零的作用是把可能存在的多行多列信息抹掉让实体彻底变成一个单一行列的块。如果这两个值本来就是 1替换成 0 在语义上也合法DXF 里 0 和 1 都表示单行单列。44和45是列间距和行间距。这两个组码在普通块参照里根本不存在留着它们会让 entmake 断言失败所以必须一起处理。注意它们是“取不到就跳过”而不是无条件替换因为subst的旧项参数如果是 nil行为不可预期可能把表里其他 nil 项一把换掉。这里还有一个隐含风险subst只替换第一个匹配项。如果表里出现两个(70 . 1)只会改掉一个。正常 entget 结果不会重复但经过其他脚本改写过的实体就不好说。写法上更保险的是用vl-remove先删再加只是那样就绑定了 Visual LISP 扩展纯 AutoLISP 环境下跑不了。折中办法就是现在这样先assoc拿到原始点对再整体替换只要原表没被污染就没问题。3.3 entmake 与 entdel 的顺序就是后悔药entmake吃一张完整的关联表在图里新建一个实体成功返回新实体名失败返回 nil。entdel删除实体。这两个命令的先后顺序决定了出错时你还有没有后悔药。写成先entdel再entmake中间任何一步失败块就彻底没了撤销一次未必救得回来尤其是批量跑的时候。写成先entmake再entdel失败时原实体还在最多是多了一个改了一半的副本删掉就行。所以顺序不能反这是原则问题。entmake失败最常见的原因是组码顺序不对或者子类标记与组码不匹配。AutoCAD 对 INSERT 的子类标记顺序有要求先是AcDbEntity再是AcDbBlockReference或AcDbMInsertBlock后面的块名、插入点、缩放、旋转都跟在子类标记之后。用subst原地换标记不会打乱顺序所以上面这份脚本是安全的如果你自己往表里append新组码就得注意追加位置。还有一个容易忽略的点entget返回的表第一项是(-1 . 实体名)中间还有(5 . 句柄)、(330 . 所有者)。entmake对这几项是忽略的会自己重新分配句柄和所有者所以原样丢进去不会出问题。但如果你手工拼表把这些组码塞到不该在的位置entmake一样会返回 nil。判断成功与否不能只看命令行有没有报错最直接的验证是转换之后立刻LIST一下或者用手动 EXPLODE 试一次。命令行没有输出不代表成功entmake失败时很安静只返回 nil不打印任何东西。4. 反向加锁与批量处理lockb、ssget 全图扫一遍解密和加密是同一套组码的两头。理解了 exm 在干什么反过来把普通块锁成“炸不开”的状态只需要改回去这也是很多加密插件最省事的实现方式——它们根本不需要真的做阵列只要把子类标记换掉图纸就从“可编辑”变成“看起来能选、实际动不了”。4.1 lockb把普通块重新锁成 MINSERT;; lockb.lsp —— 把普通块参照锁成多重插入引用 ;; 加载后命令行输入 LOCKB, 点选块即可 (defun c:lockb ( / stm e stlx r70 c71 ) (setvar cmdecho 0) (setq stm (car (entsel \n点取要锁的块 回车结束: ))) (while stm (setq e (entget stm)) (setq stlx (cdr (assoc 0 e))) (if ( stlx INSERT) (progn (setq r70 (assoc 70 e)) (setq c71 (assoc 71 e)) ;; 行列为 0 的普通块, 补上 70/71 两个组码 (if r70 (setq e (subst (cons 70 1) r70 e)) (setq e (append e (list (cons 70 1)))) ) (if c71 (setq e (subst (cons 71 1) c71 e)) (setq e (append e (list (cons 71 1)))) ) ;; 子类标记换过去, 分解命令从此拒绝执行 (setq e (subst (list 100 AcDbMInsertBlock) (list 100 AcDbBlockReference) e)) (if (entmake e) (entdel stm) (princ \nentmake 失败, 原实体已保留) ) ) (princ \n选中的不是块参照, 跳过) ) (setq stm (car (entsel \n点取下一块 回车结束: ))) ) (setvar cmdecho 1) (princ) )这段的关键在于“补组码”而不是“换组码”。普通块的表里通常没有 70 和 71assoc返回 nilsubst就无从下手必须用append往表尾加。表尾追加对 INSERT 来说位置是对的因为 70、71 本来就该排在子类标记之后的组码区。想锁成多行多列把(cons 70 1)改成(cons 70 3)这类数值再补上 44、45 两个间距组码。间距值要按图纸单位给单位是毫米的图给 500、300 这种量级单位是米的图给 0.5、0.3给错了阵列会飞出去老远看起来像块丢了。间距不要设 0虽然合法但副本完全重叠接收方打开会觉得图里有鬼。锁完自己验一下选中块命令行敲LIST如果类型显示为块参照但 EXPLODE 被拒说明锁成功了。这个反向操作我一般只在需要给别人发阶段性成果、又不想对方乱改的时候用锁之前务必另存一份别把自己也锁在外面。4.2 批量改造用 ssget 一次扫全图一张总图里躺几十个多重插入块一个个点太慢。把逻辑套进ssget循环一次扫完还能顺便输出处理数量。;; exmall.lsp —— 全图批量还原多重插入引用 (defun c:exmall ( / ss i e n r70 c71 d44 d45 ) (setvar cmdecho 0) ;; 只选模型空间的 INSERT, 需要连布局一起处理就去掉 410 过滤 (setq ss (ssget _X ((0 . INSERT) (410 . Model)))) (setq i 0 n 0) (if ss (repeat (sslength ss) (setq e (entget (ssname ss i))) (if (member (100 . AcDbMInsertBlock) e) (progn (setq r70 (assoc 70 e) c71 (assoc 71 e)) (setq d44 (assoc 44 e) d45 (assoc 45 e)) (if d44 (setq e (subst (cons 44 0) d44 e))) (if d45 (setq e (subst (cons 45 0) d45 e))) (if r70 (setq e (subst (cons 70 0) r70 e))) (if c71 (setq e (subst (cons 71 0) c71 e))) (setq e (subst (list 100 AcDbBlockReference) (list 100 AcDbMInsertBlock) e)) (if (entmake e) (progn (entdel (ssname ss i)) (setq n (1 n))) ) ) ) (setq i (1 i)) ) ) (princ (strcat \n扫描完成, 实际还原 (itoa n) 个)) (setvar cmdecho 1) (princ) )ssget的_X表示全图选择不带用户交互后面的过滤表限定实体类型为 INSERT。(410 . Model)用来只处理模型空间布局里的块默认不在结果里如果图纸的图框和内容都画在布局里把这一项去掉但去掉之后可能会选到视口之类的对象数量会变多。循环里先entget再entdel是安全写法。entdel只是把实体标记为删除同一条命令内实体仍然存在选择集里记录的名字也还有效所以边遍历边删不会踩到野指针。但如果你在循环里又调了ssget重建选择集索引就会错乱这类脚本里千万不要在循环体内重新选。锁定图层上的块是这个脚本的盲区。ssget能选中锁定图层上的实体entdel对它们会失败现象是命令跑完提示还原了 N 个实际图上完全是原样。跑之前先LAYER看一眼有没有锁图层或者干脆先全选解锁。同理冻结图层上的实体默认不会被选中图纸里如果关掉了一层再跑漏掉的东西下次打开才会冒出来。4.3 备用路线第三方命令、块编辑器与它们各自的边界手里没有编辑器、或者脚本加载被公司策略拦住的时候还有其他路可走但每条都有边界先知道边界再选比撞墙之后回头强。第一条是第三方插件提供的命令常见形态是在命令行敲一个带符号的命令名回车后选中块即可还原。这类命令的底层逻辑和上面的 lsp 一致都是改子类标记区别在于封装得更严实、处理了更多边界情况。限制也明显命令名依赖具体插件不同插件不一样换了环境就得重新确认插件本身可能带其他副作用比如顺手改了图层或者加了自定义对象处理完的图纸发给别人可能提示缺少应用程序。第二条是块编辑器。普通块双击就进块编辑器但多重插入块进不去双击会被直接忽略或者提示无法编辑。所以这条路只对“已经还原过、但块本身还带不可分解属性”的块有效先用脚本拆掉阵列标记再进块编辑器把“允许分解”勾上两者不是替代关系是接力关系。很多人卡住就是因为指望块编辑器一步到位。第三条是导出 DXF 再处理。把图纸另存为 ASCII 格式的 DXF理论上可以用文本工具搜索子类标记做替换再读回图纸。听起来很美好实际上组码 70、71、44、45 和标记必须同步修改任何一处漏改都会让 DXF 读入时报格式错误而且大文件用文本编辑器打开就是灾难。这条路我只在实在没有 CAD 环境的应急场景下用过处理完必须逐块核对性价比很低不作为首选。5. 避坑清单六类翻车现场与排查顺序脚本本身只有十几行翻车基本都发生在环境、图纸状态和输入数据上。下面六类是我这些年实际踩过的按出现频率排每条都按现象、原因、解决三步走。第一类拖进去没反应命令行敲 EXM 提示未知命令。现象是文件明明加载了命令就是认不出来。原因有两个一是命令名和文件名不是一回事文件名是 wjjm里面defun定义的可能是另一个名字网上流传的版本里就有输入 wjmm 和输入 wjjm 两种说法照着文件名敲多半落空二是安全加载把文件拦了拖拽动作完成但程序根本没执行。解决方法是打开 lsp 文件搜defun c:后面跟的那串字符那才是真正的命令名同时把文件所在目录加进受信任位置重新APPLOAD一次加载对话框里能看到“已成功加载”的字样才算数。第二类跑完脚本块凭空消失。现象是命令行没报错图上的块没了UNDO一次也回不来。原因是原版脚本先删后建或者subst时旧项取了 nil 把表改坏了entmake返回 nil 而entdel已经执行。解决办法是换成本文这份先建后删的写法并且在entmake失败时打印提示。如果已经丢了别急着存盘先UNDO回退回退不了就关掉不保存重新打开多数情况下图纸文件本身没被改动。第三类块炸开了但阵列副本只剩一个。现象是原本看着是一个块炸开之后只出来一份图元朋友那边打开却是一排。原因是原来的 70、71 大于 1脚本把行列数清零其他副本的定位信息一起被抹掉了。解决办法是炸之前先看 70、71 的数值大于 1 的先按行距列距把副本复制出来再转换或者干脆用EXPLODE之外的方式先把块整体复制若干份每份偏移一个间距再逐份还原。间距为 0 的情况最麻烦副本完全重叠还原之后会突然多出一堆重叠图元得用OVERKILL清一遍。第四类块在锁定图层或者嵌套在别的块里脚本选不中、改不动。现象是批量脚本提示还原了 N 个但图上明显还有没处理的。原因是锁定图层上的实体能被选中但删不掉冻结图层上的实体干脆不被选中嵌套块则是外层块里还装着内层块外层还原之后里面那层依然是多重插入。解决办法是跑之前先解锁、解冻所有图层跑完再恢复嵌套的情况就在同一张图上把脚本连跑两到三遍每跑一遍都会往内层剥一层直到提示还原数量为 0。第五类图纸里是代理实体脚本怎么改都没用。现象是entget出来的 0 组码不是 INSERT改完entmake一直返回 nil。原因是图纸用了原厂自定义对象技术实体只是别人家程序留下的空壳图元数据根本不在 DXF 里。解决方法是找原始设计方要能识别的版本或者让对方导出成基本图元实在拿不到只能把这块当图片参照用别在它身上浪费时间。教育版图纸打印出来的戳记也是同理脚本层面无解。第六类脚本加载报 malformed list 或者参数类型错误。现象是APPLOAD时直接弹错误框指针停在一个奇怪的括号位置。原因是网上抄来的代码里混了全角括号、中文引号、智能引号或者被论坛排版吃掉了换行。解决办法是把文件用纯文本编辑器打开把所有中文标点替换成半角括号配对检查一遍AutoLISP 对括号极其敏感少一个右括号报的错可能在几十行之外别按报错行号去找。保存时编码选 ANSI 或者带 BOM 的 UTF-8纯 UTF-8 无 BOM 在某些版本里会把中文注释读成乱码进而报错。排查顺序建议固定成先判定对象类型再看图层状态再看组码表最后才怀疑脚本本身。绝大多数“脚本没用”的结论都是前两步没做。6. 松开之后怎么验收三个检查动作和一条固定流程转换完不能靠肉眼扫一遍就完事。图上几十上百个块漏一个后面算量、出图、交付全是连锁反应。我固定用三段式验收每段都有明确的通过标准。第一段是数量核对。跑一个统计脚本数出当前图里还剩多少个带多重插入标记的块;; chkm.lsp —— 统计图中剩余的多重插入引用数量 (defun c:chkm ( / ss i e cnt ) (setq ss (ssget _X ((0 . INSERT))) i 0 cnt 0) (if ss (repeat (sslength ss) (setq e (entget (ssname ss i))) (if (member (100 . AcDbMInsertBlock) e) (setq cnt (1 cnt)) ) (setq i (1 i)) ) ) (princ (strcat \n剩余多重插入引用: (itoa cnt) 个)) (princ) )返回 0 才算通过。如果有残留先别急着重跑用 2.3 那个判定函数挨个点看清楚是锁定图层、嵌套块还是代理对象对症处理比重跑一遍有效。第二段是抽样分解。随机挑三到五个转换过的块手动EXPLODE一次看命令行有没有报错、炸出来的图元是不是完整。特别注意炸开后的图元数量对不对一个原本带三个属性文字的块炸开后应该能数出这些文字如果只剩线条说明属性在entmake重建的时候丢了得用ATTEDIT或者重新插入带属性的块补回来。第三段是坐标复核。挑块插入点附近的几条线用本文还有配套的精品资源点击获取