Skip to the content.

问题日志

记录开发过程中发现的真实问题。包括已经修好的,和目前还没修的。

为什么要公开这个:一个只展示成功记录的项目不可信。 下面的每一条都对应一次实际失败,能查到具体现象和修复提交。


已修复

2026-11-03 · 干涉检查「计数有、明细空」

现象 check_interference() 返回 interference_count: 1,但 interferences 是空列表。 调用方知道有干涉,但不知道是哪两个零件相交,也拿不到干涉体积。

根因 IInterferenceDetectionMgr.GetInterferences 在 pywin32 后期绑定下是属性(返回 tuple), 不是方法。代码写成 mgr.GetInterferences() 抛 TypeError: 'tuple' object is not callable, 被外层 except Exception: raw = None 吞掉,于是明细变成空。

为什么难发现 计数是从另一处 GetInterferenceCount 读的,正常返回 1。 所以整个调用「看起来成功了」,只是明细为空——没有报错、没有异常、没有警告。

修复 改用 _value() 统一处理「属性还是方法」的不确定性,并补充从 Volume / InterferenceVolume、Components / GetComponents 多路径提取。

验证 两个 20mm 立方沿 X 偏移 10mm 装配:

{'volume_mm3': 4000.0, 'components': ['a-1', 'b-1']}

理论值 10×20×20 = 4000,偏差 0.00%。

提交 9f3ad42


2026-11-03 · 尺寸过度标注 + 非主视图标注值错误

现象一:数量 一个 20 个特征的支架,自动标注出 22 个尺寸,全部堆在视图同一边互相重叠,图纸不可用。

排查 逐个测标注类型位:

INS_DIMS(8)        →  0 个
MARKED(32768)      → 22 个
NOT_MARKED(524288) →  0 个
所有组合            → 22 个

说明不是类型选错——SolidWorks 自己摆放时不做避让,也没有对应开关。

修复一 改成受控标注:自己选极值顶点建尺寸,位置由我们决定; 内部特征尺寸只取最大的几个跨度(≥总长 15%),并做跨视图去重。 结果 22 → 9 个。

现象二:值错了 右视图标出 114,实际深度是 120。

根因 用模型坐标 X/Y 找极值——这只对主视图成立。 右视图的屏幕水平方向是模型 Z,上视图的竖直方向也是模型 Z。 选错了顶点对,SolidWorks 算出的是错的值。

修复二 按 IView.GetOrientationName() 选择投影轴: 前视用 X/Y,上视用 X/Z,右视用 Z/Y。

验证

工程图视图1(主视)  H=160  V=140  I=80  I=40
工程图视图2(俯视)  H=160  V=120  I=28
工程图视图3(右视)  H=120  V=140

深度 120 是对的——立柱比底板多探出 20mm。

提交 163a06e


2026-11-03 · PyPI 首次上传被拒:写了不存在的 classifier

现象 上传返回 HTTP 400 Bad Request,Actions 页面上看不出原因。

根因 pyproject.toml 里写了:

"Topic :: Scientific/Engineering :: Computer Aided Design"

这个 classifier 在 PyPI 上不存在。官方 320 个 Topic 里最接近的是 Topic :: Scientific/Engineering 和 Topic :: Multimedia :: Graphics :: 3D Modeling。

错误信息藏在 HTML 响应体里,要点进日志逐行看才能发现。

修复 换成 13 条逐条验证过的 classifier,并给发布 workflow 加了强制检查: 上传前下载官方清单,任何 classifier 不在清单里就拒绝上传。

提交 52a0b57


2026-11-03 · swforge --help 在非中文 Windows 上崩溃

现象 CI 报 UnicodeEncodeError: 'charmap' codec can't encode characters。 一个陌生人装完之后跑的第一条命令就崩。

根因 标准输出编码是 cp1252 时,打印中文帮助文本抛异常。

修复 _make_output_safe() 用 errors='replace' 兜底; JSON 输出在单字节编码下转义成 \uXXXX,保证机器还能解析。

提交 2d7eb8d


2026-11-03 · 自检工具自己把文档名额占满

现象 swforge selftest 前 5 项通过,后 4 项连环失败,报 SW 文档名额不足:需要 1,当前 8/8。

根因 「资源守卫」这一项故意建了 3 个文档来测试,但没关掉。 后面每一项都要建文档,全被 8 个上限挡住。 自检工具自己制造了它要检测的问题。

修复 每项检查跑完调用 _close_created() 关掉自己建的文档; runner 在每项之间再扫一遍。

教训 测试工具本身也需要测试。第一次跑出 9/9 是通过的, 但那是环境干净时;在真实场景(前一项留下垃圾)下就暴露了。

提交 9f3ad42


2026-11-04 · BOM 实机验证发现的三个问题

问题 1:GetComponents 被当成无参方法调用

session._value() 的判断是「有 _oleobj_ 就不调用」,但通过 getattr 拿到的 绑定方法没有 _oleobj_,于是它被无参调用,COM 抛 -2147352561 非选择性的参数(DISP_E_PARAMNOTOPTIONAL)。

IAssemblyDoc.GetComponents(Boolean ToplevelOnly) 必须显式传参。 修复后 BOM 能正确读到 4 个组件。

问题 2:_value 把 COM 对象当函数调用

比问题 1 更隐蔽:IComponent2.GetModelDoc2 在后期绑定下直接返回文档对象 (CDispatch),而 COM 对象本身是 callable 的。 我的 _value 看到 callable 就 v(),等于把整个文档当函数执行, 返回 None —— 不报错,静默失败。

结果就是材料永远读不到(显示「未指定」),但流程看起来一切正常。

修复:判断顺序改成「带 _oleobj_ 的当成对象原样返回,其余 callable 才调用」。

问题 3:组件自身的材料方法读不到材料

实测在 SolidWorks 2025 SP5.0 上:

IComponent2.GetMaterialUserName()        -> ''(空)
IComponent2.GetMaterialIdName()          -> ''(空)
ModelDoc2.GetMaterialPropertyName2()     -> 'Q235'   ← 只有这个能读到
ModelDoc2.MaterialIdName                 -> 'swforge_gb_materials|Q235|9015'

而且 GetMaterialPropertyName2 的第二个参数是字符串出参, 必须传 VARIANT(VT_BSTR | VT_BYREF),直接传普通字符串报「类型不匹配」。

修复后 BOM 材料列正确显示 Q235,missing_material 为空。

验证结果

装配:底板 ×1 + 垫片 ×3
种类 2 项,共计 4 件
1. 垫片  x3  [Q235]
2. 底板  x1  [Q235]
缺材料:[]

未修复 / 已记录

BOM 表插入成功但位置不可控

现象 insert_bom_table() 返回 {'ok': True, 'rows': 3, 'columns': 3}, 图纸上确实有 1 个表格注解(GetTableAnnotationCount = 1), 但导出 PDF 后表格不在页面上。

use_anchor=False 时给 (0.02, 0.02),表格落在图框外; 改成 use_anchor=True(用模板定义的 BOM 锚点)后,仍然不可见。

排查到的限制 TableAnnotation 在 pywin32 后期绑定下:

GetPosition    -> AttributeError(取不到)
SetPosition    -> 无效果
TextHeight     -> AttributeError

也就是说位置既读不到也改不了,无法靠代码调整。

当前状态

下一步

影响 swforge bom 命令(读明细 + 导出 CSV)不受影响,可以正常使用。 只有「自动插表到工程图」这一项还没达到可用状态。

2026-11-04 · 配合在复杂零件上自动选面不可靠

现象 演示脚本里用「支架(含 Ø16 通孔)+ 销(Ø15.6)」做配合,结果:

coincident(0,1)                  ok=True   位置 [0.0, 200.0]  没动
concentric(0,1, tol=0.05)        ok=False  位置 [0.0, 200.0]  明确报错
concentric(0,1, tol=0.5)         ok=True   位置 [0.0, 0.0]    落错位置

孔在 x=80,但同心配合把销放到了 x=0,于是销压在板边缘, 随后干涉检查报出 2 处假干涉(3822.69 + 1911.345 mm³)。

根因 MateBuilder.concentric(a, b) 只接受组件索引,内部自动挑选圆柱面。 在零件有多个圆柱面/圆弧面时,挑中的不一定是目标那一个。 同类项目有相同问题(andrewbartels1 #199:fillet/chamfer 测试打到了错误的面)。

为什么 tol=0.05 反而报错 销 Ø15.6 配孔 Ø16,半径差 0.2mm 超出默认公差 0.05mm。 这时 API 返回 ok=False —— 这个行为是诚实的,比静默失败好。 放宽到 0.5 才「成功」,但选错了面。

当前处理

计划 concentric 应支持显式指定面索引,而不是靠自动挑选。 需要先用 probe.py 的反射能力枚举组件的圆柱面及其半径, 再按目标半径筛选。这是 v1.2 的工作。


2026-11-04 · 出图后没有缩放到适合,只显示半个图框

现象 produce() 生成工程图后,A3 幅面在窗口里只显示一部分, 录屏和截图时第一眼看到的是半个图框。

根因 创建视图后没调用 ViewZoomtofit2()。

修复 在 produce() 保存前、以及 add_dimensions() 标注后各调用一次 md.ViewZoomtofit2() —— 标注会改变视图范围,所以两处都要。


2026-11-04 · SolidWorks 由 COM 拉起时没有可见窗口

现象 用 Dispatch('SldWorks.Application') 启动的 SolidWorks, Visible 属性是 False,MainWindowHandle 是 0 —— 没有窗口。

影响 录屏录不到任何画面;截图也是空的。

修复 显式设置 sw.Visible = True,再用 FindWindowW + ShowWindow + SetForegroundWindow 把它切到前台。演示脚本的 focus_sw() 就是做这件事。

顺带发现 Start-Process "E:\SW2025\SLDWORKS.exe" 路径是错的,正确路径是 E:\SW2025\SOLIDWORKS\SLDWORKS.exe(中间有子目录)。

2026-11-04 · BOM 表「插了看不见」—— 两个原因都不是库的 bug

现象 insert_bom_table() 返回 {ok: True, rows: 3, columns: 3}, 但导出的 PDF 上看不到表。

排查 表其实一直插对了,在标题栏正上方(GB 明细表的标准位置)。看不见的原因是:

  1. 插入后没重新缩放。视图还停在模型上,表在视野之外。
  2. 模板回退。装配图模板 gb_assembly.drwdot 不存在时, 代码回退到目录里第一个 .drwdot,实际是 gb_a0.drwdot。 A0 是 841×1189mm,表缩到 2 倍比例后小得看不见。

修复

顺带修的问题 BOM 里显示的是组件实例名(垫片-3),合并计数后变成「垫片-3 ×3」。 改用零件文件名,显示「垫片 ×3」。

结论 ITableAnnotation 上没有 GetPosition / SetPosition; 位置由 Anchored + AnchorType 控制,而 AnchorType 在 pywin32 下 赋值不生效(读了还是原值)。所以想控制位置只能靠锚点 + 正确幅面的模板, 不能靠坐标。这一点已写进 insert_bom_table 的 docstring。


2026-11-04 · 剖视图 / 局部放大:两次静默失败

现象一:新视图位置和源视图重叠时返回 None

sec = md.CreateSectionViewAt4(cx + 0.20, cy, 0, "A", 0, None)   # None
sec = md.CreateSectionViewAt4(cx + 0.20, cy - 0.10, 0, "A", 0, None)  # 成功

不给 Y 偏移时,新视图和源视图位置重叠,SolidWorks 直接返回 None, 不抛异常、不报错。

现象二:清掉选择后返回 None 画完剖切线后如果调 ClearSelection2(True),再生成剖视图也是 None。 辅助几何(剖切线、放大圆圈)必须保持选中状态。

修复

实测 3 个视图 → 6 个,生成 剖面视图 A-A、剖面视图 B-B、局部视图 I。


2026-11-04 · CreateMassProperty 在 Python 里不可用

现象

ext = md.Extension
mp = ext.CreateMassProperty()
# com_error: (-2147352573, '找不到成员。')

原因 IModelDocExtension.CreateMassProperty 在 pywin32 后期绑定下取不到。 modeling.py 之所以能用,是因为它走的是编译后的 C#(早期绑定)。

解决 Python 侧改走 IBody2.GetMassProperties(1.0) —— 传密度 1.0, 返回数组第 4 项(下标 3)是体积,单位 m³:

for b in md.GetBodies2(0, True):
    mp = b.GetMassProperties(1.0)
    total += float(mp[3]) * 1e9        # m³ -> mm³

已封装为 read_volume()。


2026-11-04 · 多客户端并发:实测通过

测试方式 两个独立 Python 进程,同时操作同一个 SolidWorks 实例。

结果

进程 A: connect / build / open_own / reclaim / readback   全通过
进程 B: connect / build / open_own / reclaim / readback   全通过
A 被切到 并发_B -> 抢回 并发_A
B 被切到 并发_A -> 抢回 并发_B

关键证据是 readback:抢回后读到的体积是 24000.0 mm³, 与各自建的零件一致 —— 证明读到的是自己的文档,不是对方的。

之前的状态 guard.py 的并发逻辑只做过同进程内模拟(selftest 第 10 项)。 现在补上了真正的双进程验证。


2026-11-04 · 发版检查脚本:第一步就抓到自己

写 release_check.py 的测试时,test_source_change_with_version_bump_passes 失败了。原因是我的实现把「源码改了 + 版本号已升」也判成「漏发」。

这个状态其实很正常:改了代码、升了版本号、还没打新标签。 判错会让每次开发中途都报红。

改成:

测试写完就抓到了自己的逻辑错误,这正是写测试的理由。

其他未修复项

Dispatch 会启动第二个 SolidWorks 实例

现象 机器上出现 2 个 SLDWORKS.EXE 进程,之后所有 COM 调用报 MK_E_UNAVAILABLE (0x800401E3),连接时好时坏。

原因 项目规定「只用 Dispatch,禁 GetActiveObject」(SolidWorks 不注册 ROT)。 Dispatch('SldWorks.Application') 找不到已注册实例时会新开一个, 于是有了两个实例,互相抢 ROT 条目。

当前的缓解 清缓存后重连;selftest 遇到 rot_failed 会自动重试一次。

还没解决 没有主动检测「已有多个实例」并给出提示。 计划:swforge selftest 开工前检查进程数,多于一个就警告并说明原因。


session 缓存在实例重启后失效

现象 SolidWorks 实例被杀掉重启后,同一个 Python 进程里的 COM 指针全部失效, 后续调用报 rot_failed。

当前缓解 selftest 里对 rot_failed / Operation unavailable 做一次重连重试。

还没解决 session.py 没有「连接已失效就自动重建」的统一机制, 每个调用方要自己处理。计划在 v1.2 里做。


干涉检查的明细字段依赖多路径猜测

现状 体积从 Volume 或 InterferenceVolume 里试,零件名从 Components 或 GetComponents 里试。目前两条路径都命中了,但这种「试出来」的写法不够稳。

计划 用 probe.py 的反射机制确认 IInterference 到底暴露哪些成员, 把猜测换成确定。


多客户端并发未处理

现状 两个 agent 同时连一个 SolidWorks 时,任一方的 ActivateDoc 都会把 对方的活动文档切走。同类项目有这个 issue(hjbaard #10)。

计划 v1.1 实现「活动文档守卫」:操作前记录目标文档,操作后回读确认, 发现被别人切走就重新激活并重试。


环境相关(非代码问题)

中文写入 .bat 会导致 cmd 解析失败

现象 批处理文件里任何位置出现中文(即使放在引号内),cmd 都会把命令行截断, 报 'ODING' 不是内部或外部命令 这类看不懂的错误。

原因 cmd.exe 逐字节读取 .bat,用当前代码页解释。UTF-8 的中文字节会被误解析, 甚至破坏行结构。chcp 65001 >nul 放在第一行也救不了——文件本身已经被读坏了。

做法 .bat 只写 ASCII,中文全部由 Python 输出(配合 chcp 65001 + PYTHONIOENCODING=utf-8,实测 ✓ ✗ mm³ → 【】 全部正常显示)。