# 材料、自定义属性与配置

这些接口读写的是模型的“非几何”数据，但会直接影响质量属性、BOM 与图纸标题栏。它们都返回普通表，可以直接序列化为 JSON。

## 1. 材料

材料名称随 SOLIDWORKS 安装语言本地化：英文库中的 `1060 Alloy` 在中文安装里是 `1060 合金`。不要凭记忆硬编码名称，先从材料库读取：

```aardio
import com.sldWorks;
var sw,err = com.sldWorks(false);
if(!sw){ print(err); return; }

// 默认解析 "solidworks materials.sldmat"；也可传入 sw.GetMaterialDatabases() 中的文件名或完整路径。
var materials,loadError = sw.GetMaterials();
if(!materials){ print(loadError); return; }
for i,item in materials {
    if(string.find(item.name,"^1060")) print(item.classification,item.name,item.density,"kg/m^3");
}
```

`GetMaterials(database)` 直接解析 `.sldmat`（UTF-16 XML），每项含 `name,classification,density,database`。它不需要打开任何文档。

设置材料后用回读和质量变化验证：

```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 before = sw.GetMaterial(doc);              // {name,database,density,configuration}
var material,materialError,current = sw.SetMaterial(doc,"1060 合金","solidworks materials.sldmat");
if(!material){ print(materialError,current); return; } // 名称未被应用时返回当前材料信息
var mass = sw.GetMassProperties(doc);
print(before,material,mass.mass,mass.volume*material.density);
```

- `SetMaterial(doc,name,database,configuration)`：`database` 默认 `""`（默认材料库），`configuration` 默认活动配置；`name=""` 移除材料。
- SOLIDWORKS 对未知名称不报错，只是不应用；本库通过 `GetMaterialPropertyName2` 回读并在不一致时返回 `null,说明,当前材料`。
- `GetDocInfo(doc).material` 也会带出零件材料摘要。未指定材料时 `density` 是文档默认密度（通常 1000 kg/m³），此时质量数值没有工程意义。

## 2. 自定义属性

```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 written,writeError,code = sw.SetCustomProperty(doc,"Description","AA plate with hole");
if(!written){ print(writeError,code); return; }
sw.SetCustomProperty(doc,"Weight",1.5,"","double");
for i,item in sw.GetCustomProperties(doc) {
    print(item.name,item.typeName,item.value,"=>",item.resolvedValue);
}
sw.DeleteCustomProperty(doc,"Weight");
```

- `SetCustomProperty` 调用 `Add3(..., swCustomPropertyReplaceValue)`：不存在则新增，存在则替换值；返回值 0 以外的状态码会转为 `null,说明,状态码`。
- 类型可用 `text`（默认）、`number`、`double`、`yesno`、`date` 或 `swCustomInfoType_e` 数值。数值参数会转为字符串写入。
- `value` 是原始表达式，`resolvedValue` 是解析结果。GB 模板自带的 `"SW-Mass@零件.SLDPRT"` 之类的表达式会在 `resolvedValue` 中给出实际质量。
- 属性名区分文档级和配置级：同名属性可能同时存在于两个层级，读取时按需分别传 `""` 与配置名。

## 3. 配置：增删、切换与按配置压缩特征

`GetDocInfo(doc)` 只给出 `configuration`（活动配置）与 `configurations`（配置名）。需要纯数据明细和写操作时用配置助手：

```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; }

// 明细：index,name,active,comment,description,alternateName,typeName,isDerived,parent,childCount
var configs = sw.GetConfigurations(doc);
var base = configs[1].name;

// 新增配置。默认与 UI 一致：新配置立即成为活动配置；activate=false 保持当前配置。
var heavy,addError = sw.AddConfiguration(doc,"AA_Heavy",{comment="加厚版";activate=false});
if(!heavy){ print(addError); return; }
var derived = sw.AddConfiguration(doc,"AA_Heavy_Painted",{parent="AA_Heavy";activate=false}); // 派生配置

// 只改 AA_Heavy 的一个尺寸（不必先激活），再激活并用质量属性核对
var dims = sw.GetDimensions(doc,"AA_Boss");
var depth; for i,d in dims { if(d.name=="D1") depth = d; }
if(depth) sw.SetDimension(doc,depth.fullName,0.02,"AA_Heavy");
var activated,state = sw.ActivateConfiguration(doc,"AA_Heavy"); // state: activated / alreadyActive
print(state,sw.GetMassProperties(doc).volume);

// 按配置压缩特征：省略配置=当前配置，"*"=全部，或配置名/数组；返回逐配置回读结果
var suppression,suppressError = sw.SetFeatureSuppression(doc,"AA_H1",true,"AA_Heavy");
print(suppression ? suppression.configurations : suppressError);
print(sw.GetFeatureSuppression(doc,"AA_H1").configurations); // 每个配置的 suppressed 状态
sw.SetFeatureSuppression(doc,"AA_H1",false,"*");

// 删除：活动配置、唯一配置、不存在的配置都直接拒绝；删除父配置会连带派生配置
sw.ActivateConfiguration(doc,base);
var deleted,deletedNames = sw.DeleteConfiguration(doc,"AA_Heavy");
print(deleted,deletedNames); // true,["AA_Heavy","AA_Heavy_Painted"]
```

实测要点（SOLIDWORKS 2025）：

- `ShowConfiguration2` 在目标已激活或不存在时都返回 `false`；`ActivateConfiguration` 先排除这两种情况，再以 `ActiveConfiguration.Name` 回读为准，并默认 `EditRebuild3`。质量属性、`GetFaces` 等只反映活动配置。
- `AddConfiguration2` 的 `swConfigOption_DontActivate`（128）可保持当前配置；同名配置返回 null——助手在调用前就拒绝。派生配置随父配置一起删除，`DeleteConfiguration` 的第二个返回值列出实际删除的名字。
- **特征对象绑定取得它时的活动配置**：切换配置后，旧 `IFeature` 的 `IsSuppressed()` 仍返回旧配置的状态，`SetSuppression2(swThisConfiguration)` 也作用于旧配置。助手总是重新 `FeatureByName` 并把“当前配置”展开为明确的配置名（`swSpecifyConfiguration`）。
- `IsSuppressed2(swSpecifyConfiguration,数组)` 的返回顺序是**文档配置顺序**而不是传入顺序；助手逐个配置查询，不要自己按传入顺序对号入座。
- 压缩父特征会连带子特征；解除压缩可能需要 `unsuppressDependents=true`。回读与请求不一致时返回 `null,说明,回读结果`。

尺寸按配置修改见 [模型反馈与图像](feedback.html) 中的 `SetDimension`：省略配置参数只改当前配置，`"*"` 改所有配置，也可以传配置名数组。`before/after` 在目标配置中回读，作用域含多个配置时为数组。

## 4. 配置表：系列零件的一次读写

“一个模型、多个配置、每个配置几个不同的尺寸/压缩/属性/材料”是最常见的系列零件做法。逐个调用 `AddConfiguration` + `SetDimension` + `SetFeatureSuppression` + `SetCustomProperty` + `SetMaterial` 很啰嗦，配置表助手把它压成一张表：

```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; }

// 读：每个配置一行；尺寸列名用 "D1@特征名"（不带文档名），列出的 differences 就是各配置之间不同的列
var sheet,sheetError = sw.GetConfigurationTable(doc);
if(!sheet){ print(sheetError); return; }
print(sheet.configurations,sheet.differences);
for i,row in sheet.rows { print(row.name,row.dimensions,row.features,row.properties,row.material.name); }

// 写：不存在的配置自动创建（不激活），四类单元格都是可选的；值一律 SI（米/弧度）
var base = sheet.configurations[1];
var applied,applyError,detail = sw.ApplyConfigurationTable(doc,[
	{name=base;dimensions={["D1@AA_Boss"]=0.01};features={AA_H1=false};properties={PartNo="PL-10"};material="1060 合金"},
	{name="AA_Thick";comment="20 mm";dimensions={["D1@AA_Boss"]=0.02};properties={PartNo="PL-20"};material="1060 合金"},
	{name="AA_Solid";comment="30 mm 无孔";dimensions={["D1@AA_Boss"]=0.03};features={AA_H1=true};properties={PartNo="PL-30"};material="AISI 304"},
]);
if(!applied){ print(applyError); print(detail ? detail.failed); return; }
print(applied.created); // ["AA_Thick","AA_Solid"]；applied.verified 是按写过的列回读的配置表

// 每配置质量表：mass=true 会逐个激活配置（每个约 1 秒）并在结束后恢复原活动配置
sheet = sw.GetConfigurationTable(doc,{dimensions="D1@AA_Boss";features="AA_H1";properties="PartNo";mass=true});
for i,row in sheet.rows { print(row.name,row.mass.mass,row.material.density); }
```

`GetConfigurationTable(doc,options)` 的列选择：`dimensions`/`features`/`properties` 省略表示全部（尺寸受 `dimensionLimit=200` 限制，特征自动跳过文件夹、基准面、原点等不可压缩项），`false` 跳过，或给名字/名字数组；`configurations` 可指定子集与行顺序；未知名字进入 `warnings` 而不是报错。

`ApplyConfigurationTable(doc,rows,options)`：全部行先做类型校验（缺 `name`、重名、非数值尺寸、非布尔压缩）再碰 SOLIDWORKS；`createMissing=false` 时缺失的配置记为失败行；`baseConfiguration` 指定新配置从哪个配置复制（**新配置总是复制当前活动配置的尺寸/材料/压缩状态**，所以助手会先激活它）；结束时默认切回原活动配置，`activate="配置名"` 切到指定配置，`activate=false` 保持；`verify=true`（默认）把写过的列回读到 `verified`。任何行有错误时返回 `null,说明,结果`，`结果.failed` 列出行与逐格错误。

实测要点（SOLIDWORKS 2025）：

- **尺寸仍“应用到所有配置”时**（`IDimension::IsAppliedToAllConfigurations`，任何没按配置改过的尺寸都是这样），`GetSystemValue3(swAllConfiguration)` 只有首个槽位有效，其余全是 0；`swSpecifyConfiguration` 读非首配置也返回 0，但几何是正确的。助手改用 `swThisConfiguration` 的值填充全部配置。写 `SetSystemValue3(v,swAllConfiguration)` 会让尺寸回到这种状态。
- 尺寸已按配置区分后，`GetSystemValue3(swAllConfiguration)` 按**文档配置顺序**返回，`swSpecifyConfiguration` 按**传入顺序**返回；而 `IFeature::IsSuppressed2` 的数组返回始终是文档配置顺序。两个 API 语义相反，助手内部分别处理。
- 非活动配置的材料、自定义属性写入都生效且互不串扰；但 `GetUserPreferenceDoubleValue(swMaterialPropertyDensity)` 只反映活动配置，所以非活动行没有 `density`，`mass=true` 时在激活各配置期间补齐。
- 尺寸对象切换配置后仍可用（与特征对象不同）；`Parameter("D1@特征名")` 短名可用且不区分大小写。
- `IConfiguration::GetParameters` 的输出参数是 SAFEARRAY，进程外 COM 下 aardio 无法传递，配置表因此自己定义列，而不依赖 SOLIDWORKS 的参数列表。

## 官方接口

- [SetMaterialPropertyName2](https://help.solidworks.com/2025/english/api/sldworksapi/SolidWorks.Interop.sldworks~SolidWorks.Interop.sldworks.IPartDoc~SetMaterialPropertyName2.html)
- [ICustomPropertyManager::Add3](https://help.solidworks.com/2025/english/api/sldworksapi/SolidWorks.Interop.sldworks~SolidWorks.Interop.sldworks.ICustomPropertyManager~Add3.html)
- [ICustomPropertyManager::Get6](https://help.solidworks.com/2025/english/api/sldworksapi/SolidWorks.Interop.sldworks~SolidWorks.Interop.sldworks.ICustomPropertyManager~Get6.html)
- [IConfigurationManager::AddConfiguration2](https://help.solidworks.com/2025/english/api/sldworksapi/SolidWorks.Interop.sldworks~SolidWorks.Interop.sldworks.IConfigurationManager~AddConfiguration2.html)
- [IModelDoc2::ShowConfiguration2](https://help.solidworks.com/2025/english/api/sldworksapi/SolidWorks.Interop.sldworks~SolidWorks.Interop.sldworks.IModelDoc2~ShowConfiguration2.html)
- [IModelDoc2::DeleteConfiguration2](https://help.solidworks.com/2025/english/api/sldworksapi/SolidWorks.Interop.sldworks~SolidWorks.Interop.sldworks.IModelDoc2~DeleteConfiguration2.html)
- [IFeature::SetSuppression2](https://help.solidworks.com/2025/english/api/sldworksapi/SolidWorks.Interop.sldworks~SolidWorks.Interop.sldworks.IFeature~SetSuppression2.html)
- [IDimension::GetSystemValue3](https://help.solidworks.com/2025/english/api/sldworksapi/SolidWorks.Interop.sldworks~SolidWorks.Interop.sldworks.IDimension~GetSystemValue3.html)
- [IDimension::IsAppliedToAllConfigurations](https://help.solidworks.com/2025/english/api/sldworksapi/SolidWorks.Interop.sldworks~SolidWorks.Interop.sldworks.IDimension~IsAppliedToAllConfigurations.html)
- [IFeature::IsSuppressed2](https://help.solidworks.com/2025/english/api/sldworksapi/SolidWorks.Interop.sldworks~SolidWorks.Interop.sldworks.IFeature~IsSuppressed2.html)

[返回首页](index.html)
