# 连接、文档管理与错误处理

## 1. 连接模式

```aardio
import com.sldWorks;

// 参数 @2 为 false 时只附加已运行的实例。
var sw,err = com.sldWorks(,false);
if(!sw){ print(err); return; }
print(sw.GetAppInfo());
```

- `com.sldWorks()`：先连接已运行的通用实例，失败后尝试创建。
- `com.sldWorks(false)`：只连接已运行的通用实例，允许版本回退。
- `com.sldWorks(true,2025)`：指定版本，失败时保留向其他已安装版本回退的旧行为。
- `com.sldWorks(false,2025)`：仅连接但不创建新实例、不跨版本回退，并核对返回实例的实际主版本。
- `com.sldWorks(ISldWorksObject)`： 封装已有的原生 `ISldWorks` COM 对象，为它添加本库的方法。

> 构造参数 @2 支持年份或表示主版本的整数，不接受完整修订字符串。
> 2025 对应内部主版本 33；`33.4.0` 是运行实例的修订号，而不是构造函数的版本参数。
> 对于普通的桌面应用或 AI 自动化，一般没必要限定版本号。建议忽略一台电脑跑多个版本 SOLIDWORKS 的边界情况，步步设防把简单的事复杂化无实际意义。

进程权限应一致。同版本多实例可能共享 COM 注册入口，本库不承诺选中其中哪一个；必要时先通过 `GetProcessID()` 和文档路径核对。不要误以为重复构造必然得到独立实例。

不要在 aardio 线程间直接传递 SOLIDWORKS COM 对象。每个执行线程内重新连接、按路径重新获取对象；跨线程只传参数和纯数据反馈。已关闭文档、重建后失效的面/边引用也必须重新获取。

## 2. 新建文档：先验证模板

`NewDoc(docType,template,paperSize,width,height)`：

| 参数 | 默认值 / 含义 |
| --- | --- |
| `docType` | 1；零件 1、装配体 2、工程图 3 |
| `template` | 当前配置的默认模板；也可显式传入路径 |
| `paperSize` | 0；工程图纸张枚举 |
| `width,height` | 0；自定义图纸大小时使用，单位为米 |

支持 `.prtdot`、`.asmdot`、`.drwdot`。库检查模板存在且扩展名与文档类型一致，再调用 `NewDocument`。文件存在不代表模板一定有效，最终仍检查 SOLIDWORKS 的返回值。

```aardio
import com.sldWorks;
var sw,err = com.sldWorks(false);
if(!sw){ print(err); return; }

// 示例位置，请根据实际安装修改；不是库内硬编码的回退路径。
var template = "%ProgramData%\SOLIDWORKS\SOLIDWORKS 2025\templates\gb_part.prtdot";
var doc,newError = sw.NewDoc(1,template);
if(!doc){ print(newError); return; }
print(sw.GetDocInfo(doc));
```

`GetDefaultTemplate(1)` 只读当前设置。如果路径指向旧版本、移动后的目录或已删除文件，会返回 `null,说明`，不会自动改注册表、首选项或弹模板选择框。显式给 `NewDoc` 传模板也不会修改默认模板配置。

## 3. 查找与激活

`FindDoc()` 取活动文档；也可传文档对象、完整路径或准确标题。标题忽略大小写比较，但**不是模式匹配**。有歧义时失败，完整路径查找失败时不会退而选择同名文档。

```aardio
import com.sldWorks;
var sw,err = com.sldWorks(false);
if(!sw){ print(err); return; }

var doc,findError = sw.FindDoc();
if(!doc){ print(findError); return; }
var activated,status,message = sw.ActivateDoc(doc);
if(!activated){ print(status,message); return; }
if(status) print("激活附带状态，需要检查：",status);
```

默认 `ActivateDoc(nameOrDoc,false,1)` 不依据用户的“激活时重建”首选项，也不弹出询问框。如果文档需要重建，可能返回有效文档和 `swDocNeedsRebuildWarning`；请按任务需要调用 `RebuildDoc`。

未保存的文档使用 `GetTitle()`，不能先把该标题当文件路径转换。保存改名后应重新读取标题。

## 4. 打开与状态码

```aardio
import com.sldWorks;
var sw,err = com.sldWorks(false);
if(!sw){ print(err); return; }

var doc,errors,warnings,message = sw.OpenDoc("/models/example.SLDPRT");
if(!doc){ print("打开失败",errors,warnings,message); return; }
if(errors || warnings){
    print("已返回文档，但载入不完整或有警告：",errors,warnings);
    return; // 先诊断，不盲目继续修改
}
print(sw.GetDocInfo(doc));
```

`OpenDoc(path,docType,options,config)` 默认推断原生扩展名，`options=1`（静默），`config=""`。只读可用 `options=1|2`。原生错误/警告为枚举位掩码，可按需要用按位与检查，不能只按单个相等值判断。

返回有效文档和非零错误码是可能的，例如装配体缺少引用文件。不要丢弃这些诊断，也不要把有效对象当作“完全成功”。STEP、IGES 等中性格式不由文件后缀假装成 SLDPRT；应根据对应格式使用原生 `LoadFile4` 和导入数据接口。

## 5. 保存、导出、关闭

```aardio
import com.sldWorks;
var sw,err = com.sldWorks(false);
if(!sw){ print(err); return; }
var doc,findError = sw.FindDoc();
if(!doc){ print(findError); return; }

var outputDir = io.createDir("/output");
if(!outputDir){ print("不能创建输出目录"); return; }
var saved,errors,warnings,message = sw.SaveAs(io.joinpath(outputDir,"example.SLDPRT"),,,,doc);
if(!saved){ print("保存失败",errors,warnings,message); return; }
if(warnings) print("保存警告",warnings);

// STEP 导出也是 SaveAs，根据扩展名决定转换格式。
var exported,exportError,exportWarning = sw.SaveAs(io.joinpath(outputDir,"example.step"),,,,doc);
print(exported,exportError,exportWarning);
```

`SaveAs(path,version,options,exportData,doc,advancedOptions)`：

- 默认 `version=0`、`options=1`，对象空参自动构造 `Nothing`。
- `doc` 省略取活动文档；会激活目标并清空选择，避免只导出部分面或实体。
- 不自动创建保存目录；可能覆盖目标文件。生产脚本应使用独立输出目录、复制模型或预先检查目标是否存在。
- `exportData` 可传 PDF 等导出对象，`advancedOptions` 可传原生高级保存选项。
- `SaveDoc(doc,options)` 用原路径保存。新文档没有路径时直接失败，不弹另存为框。

`CloseDoc(doc,discardChanges)` 覆盖了原生按标题关闭的接口：`doc` 用法同 `FindDoc`（`""` 表示活动文档）；**默认拒绝丢弃未保存修改**，必须显式传 `discardChanges=true`；同标题歧义时拒绝执行；已保存文档按完整路径唯一定位。成功返回 `true,状态`：`closed` 已关闭；`hidden`/`referenced` 表示文档仍被其他打开文档引用，SOLIDWORKS 只释放了窗口、模型数据仍在内存（正常行为，装配体关闭后自动释放）。原生行为可直接调用 `QuitDoc(name)`。不要为了清理自己的任务而调用 `CloseAllDocuments(true)`。

自行启动的后台实例在关闭最后一个文档时可能退出。要保留会话，可显式设置 `sw.UserControl=true`。本库不自动调用 `ExitApp()`。

## 5.1 会话卫生：同名冲突、快照清理与响应探测

SOLIDWORKS 按**文件名（含扩展名）**索引已打开文档：UI 会拒绝同时打开两个 `block.SLDPRT`（不同目录），但 `SaveAs3` 等 API 会放行。实测在一次自动化中反复把新文档另存为同名文件、又从不关闭旧文档，累计近 30 个文档后，SOLIDWORKS 主线程进入数小时的无响应状态，所有 COM 调用（包括重新连接）都无限等待。因此：

- `SaveAs`、`OpenDoc` 现在会先检查“已打开另一个同名不同路径的文档”，命中直接返回失败说明（`SaveAs` 为 `false,-1,0,说明`，`OpenDoc` 为 `false,65536,0,说明`，65536 即原生 `swFileWithSameTitleAlreadyOpen`），不调用 SOLIDWORKS。
- 自动化输出的文件名应带运行标记（时间戳或随机数），不要用固定名字覆盖。
- 任务开始时用 `GetDocumentList()` 记录快照，结束时用 `CloseNewDocs(snapshot[,discardChanges])` 关闭自己打开的全部文档，包括 `AddComponent`、工程图视图隐式加载的引用文档。`CloseDocs(docs)` 按 工程图 → 装配体 → 零件 顺序批量关闭。
- `AddComponent` 默认以 `visible=false` 隐藏加载组件文件（`OpenDoc(path,,,,false)` 内部调用 `DocumentVisible`，用后自动恢复），不再弹出组件窗口。

```aardio
import com.sldWorks;

// 连接前先探测：主线程不泵消息时任何 COM 调用都会无限等待，探测本身不经 COM。
var responsive,probe = com.sldWorks.isResponsive(2000);
if(!responsive){
    print(probe.running ? "SOLIDWORKS 无响应，请检查是否有模态对话框或长时间命令" : "SOLIDWORKS 未运行",probe);
    return;
}
var sw,err = com.sldWorks(false);
if(!sw){ print(err); return; }

var snapshot = sw.GetDocumentList(); // 任务开始前已打开的文档，不属于本任务
var runTag = tostring(time(),"%Y%m%d-%H%M%S");
var outputDir = io.createDir("/output/run-" + runTag);

var doc,newError = sw.NewDoc(1,"%ProgramData%\SOLIDWORKS\SOLIDWORKS 2025\templates\gb_part.prtdot");
if(!doc){ print(newError); return; }
// ... 建模 ...
var saved,errors,warnings,message = sw.SaveAs(io.joinpath(outputDir,"part-" + runTag + ".SLDPRT"),,,,doc);
if(!saved){ print(errors,message); }

// 结束：只关闭本任务打开的文档；未保存的修改必须显式允许丢弃
var cleanup = sw.CloseNewDocs(snapshot,true);
print(cleanup.count,cleanup.failed);
print(sw.IsResponsive(1000));
```

`com.sldWorks.probe(timeout,processId)` 返回 `{running,responsive,processes=[{pid,responsive,hungThreads,mainWindow={title,hung},dialogs=[{title,text}],probed}]}`：用 `SendMessageTimeout(WM_NULL)` 逐线程探测消息响应、读取 `IsHungAppWindow`，并列出可见的 `#32770` 对话框及其文本。已连接后可用 `sw.IsResponsive(timeout)`（使用连接时缓存的 `sw.ProcessId`）。探测发现无响应时不要继续发 COM 调用，也不要在代码里强杀进程；把 `dialogs` 与主窗口标题交给人工处理。

> 这台验证机显卡老旧、三维显示很慢，但上述无响应事故的根因是同名文档堆积，不是显示性能；在正常工作站上同样会发生。

## 6. 返回值约定

为兼容旧程序，已有文件操作保留首返回值的布尔/对象习惯：

| 方法 | 成功 / 原生调用结果 | 本库参数或前置检查失败 |
| --- | --- | --- |
| 构造、`FindDoc`、`NewDoc`、`GetDefaultTemplate` | 对象或路径 | `null,说明` |
| `OpenDoc` | `文档或false,错误码,警告码` | `false,-1,0,说明`；同名冲突为 `false,65536,0,说明` |
| `SaveAs`、`SaveDoc` | `true或false,错误码,警告码` | `false,-1,0,说明` |
| `CloseDoc` | `true,closed/hidden/referenced` | `null,说明` |
| `CloseDocs`、`CloseNewDocs` | `{closed,failed,count}` | `null,说明` |
| `GetDocumentList` | 纯数据数组 | — |
| `IsResponsive`、`com.sldWorks.isResponsive` | `布尔值,探测详情` | — |
| `ActivateDoc` | `文档或null,状态码` | `null,-1,说明` |
| `GetDocInfo`、`CaptureView`、`RebuildDoc` | 纯数据或 true | `null,说明` |
| `GetFeatures` | `数组,truncated` | `null,说明` |
| `GetMassProperties` | 纯数据 | `null,说明,原生状态码（若有）` |

`-1` 是本库的前置检查标识，不是 SOLIDWORKS 原生文件错误码。COM 断开、调用不受支持的成员等异常仍可能抛出；库不会吞掉所有异常，也不自动重试可能已经产生副作用的写操作。

## 7. 原生参数：Nothing 与输出引用

```aardio
import com.sldWorks;

var noObject = com.nothing(); // VT_DISPATCH + NULL，等价 VBA Nothing
var errors = com.int(0,true);         // int 输出引用
var isWarning = com.Variant(false,11/*_VT_BOOL*/,true); // bool 输出引用
print(noObject.vt);                   // 9
```

某些 SOLIDWORKS 原生方法要求空对象而不接受普通 `null`。例如 `SelectByID2` 的 `callout`、`SaveAs3` 的 `exportData` 和 `advancedOptions`。应使用经过验证的 `nothing()`， `com.Variant(null,9)` 则会忽略第 2 个参数并返国 _VT_NULL 类型。

原生 `GetErrorCode2` 的输出参数是 **BOOL**，不是 int。没有运行时类型信息时，不能用普通变量代替明确构造的输出引用。

## 官方接口依据

- [ActivateDoc3](https://help.solidworks.com/2025/english/api/sldworksapi/SolidWorks.Interop.sldworks~SolidWorks.Interop.sldworks.ISldWorks~ActivateDoc3.html)
- [SaveAs3](https://help.solidworks.com/2025/english/api/sldworksapi/SolidWorks.Interop.sldworks~SolidWorks.Interop.sldworks.IModelDocExtension~SaveAs3.html)
- [CloseDoc](https://help.solidworks.com/2025/english/api/sldworksapi/SolidWorks.Interop.sldworks~SolidWorks.Interop.sldworks.ISldWorks~CloseDoc.html)

[返回首页](index.html)
