using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using PdmSwPlugin.Common.Constants;
using PdmSwPlugin.Common.Entity;
// SolidWorks 的命名空间们
using SolidWorks.Interop.swdocumentmgr;
namespace PdmSwPlugin.Common.Util
{
///
/// 通过 SolidWorks Document Management API (DM API) 得到文档属性的类
///
public static class GetDocumentPropertiesViaDM
{
///
/// 凌创公司的 DM API 授权码
///
public static string LinktronLicenseKey = @"SuzhouLinktronSystemsCoLtd:swdocmgr_general-11785-02051-00064-33793-08629-34307-00007-05128-58478-32321-57480-30765-00622-59999-16385-62752-47753-50558-31076-40652-29868-22926-54604-41449-54717-42289-44473-51665-47549-58701-53709-46485-03533-12733-37329-14337-29280-51290-50890-25690-25696-964";
///
/// 经常使用的属性名
/// 考虑到 SolidWorks 里的自定义属性名可能大小写比较随意,比较的时候请注意大小写
///
public static string[] PropertyNamesOftenUsed = new string[]
{
// 图号,物料编码
PropertyName.PART_NO,
// 物料型号
PropertyName.PART_MODEL,
// 物料名称
PropertyName.PART_NAME,
// 加工件类型
PropertyName.JGJ_TYPE,
// 材料名称
PropertyName.STUFF_TYPE,
// 表面处理
PropertyName.SURFACE_TYPE,
// 热处理
PropertyName.HEAT_TYPE,
};
///
/// 检索一个 SolidWorks 文档里指定的属性值
///
/// 指定的 SolidWorks 文档
/// 指定的属性的名称,如果为空将返回全部
/// 是否把一些表达式还原为字符串,例如如果图号设置为 $PROP:FileName, 那就还原为文件名
/// 指定的属性和它们的值的集合
public static NameValueCollection RetrieveProperties(string sldWorksPath, IEnumerable properties, bool resolve)
{
string originalExt;
SwDmDocumentType docType = DMSldFileExtentionChecker.CheckDM(sldWorksPath, out originalExt);
if (docType == SwDmDocumentType.swDmDocumentUnknown)
{
return null;
}
SwDMClassFactory swDMClassFactory = new SwDMClassFactory();
SwDMApplication swDMApp = swDMClassFactory.GetApplication(LinktronLicenseKey);
SwDmDocumentOpenError returnValue = 0;
SwDMDocument17 swDoc = (SwDMDocument17)swDMApp.GetDocument(sldWorksPath, docType, true, out returnValue); // 允许只读
bool originallyReadOnly = false; // 文件最开始是只读的吗?
FileAttributes fileAttributes = FileAttributes.Offline; // 文件属性,给任意初始值
if (returnValue == SwDmDocumentOpenError.swDmDocumentOpenErrorFileReadOnly)
{ // 由于打开时允许只读,理论上不会运行到这里
originallyReadOnly = true;
fileAttributes = File.GetAttributes(sldWorksPath); // 保留文件最开始的属性
File.SetAttributes(sldWorksPath, FileAttributes.Normal); // 设置文件属性为正常
swDoc = (SwDMDocument17)swDMApp.GetDocument(sldWorksPath, docType, true, out returnValue); // 重新打开文档
}
if (swDoc == null || returnValue != SwDmDocumentOpenError.swDmDocumentOpenErrorNone)
{
Marshal.ReleaseComObject(swDMApp);
Marshal.ReleaseComObject(swDMClassFactory);
return null;
}
SwDMConfigurationMgr dmConfigMgr = swDoc.ConfigurationManager;
//NameValueCollection result = RetrieveProperties_Internal(sldWorksPath, docType, dmConfigMgr, properties, resolve);
NameValueCollection result = RetrieveProperties_Core(sldWorksPath, swDoc, properties, resolve);
Marshal.ReleaseComObject(swDoc);
Marshal.ReleaseComObject(swDMApp);
Marshal.ReleaseComObject(swDMClassFactory);
if (originallyReadOnly)
{ // 如果文件本来是只读的,在得到自定义属性之后再恢复回去
File.SetAttributes(sldWorksPath, fileAttributes);
}
return result;
}
///
/// 检索一个 SolidWorks 文档里指定的属性值
/// 只从当前活动配置里检索自定义属性
///
/// 指定的 SolidWorks 文档
/// 文档
/// 指定的属性的名称,如果为空将返回全部
/// 是否把一些表达式还原为字符串,例如如果图号设置为 $PROP:FileName, 那就还原为文件名
/// 指定的属性和它们的值的集合
private static NameValueCollection RetrieveProperties_Core(string sldWorksPath, SwDMDocument17 doc, IEnumerable properties, bool resolve)
{
if (doc == null)
return null;
object names;
object types;
object links;
object values;
doc.GetAllCustomPropertyNamesAndValues(out names, out types, out links, out values);
if (names == null || values == null)
return null;
NameValueCollection collection = new NameValueCollection();
List expectedPropertiesInLowerCase = new List();
if (properties != null)
expectedPropertiesInLowerCase.AddRange(properties.Select(x => x.ToLower())); // 期望的属性名称都变成小写
string[] propNames = names as string[];
string[] propValues = values as string[];
if (propNames.Length != propValues.Length)
return null; // 数量不可能不一样
for (int i = 0; i < propNames.Length; i++)
{
if (expectedPropertiesInLowerCase.Count == 0) // 没有设置期望的属性名称,那就是所有的属性都要返回
collection.Add(propNames[i], resolve ? ResolvePropertyValue(sldWorksPath, propValues[i]) : propValues[i]);
else if (expectedPropertiesInLowerCase.Contains(propNames[i].ToLower())) // 只要期望的属性
collection.Add(propNames[i], resolve ? ResolvePropertyValue(sldWorksPath, propValues[i]) : propValues[i]);
}
return collection;
}
///
/// 检索一个 SolidWorks 文档里指定的属性值
/// 只从当前活动配置里检索自定义属性
/// 废弃,因为工程图没有配置,请使用 RetrieveProperties_Core
///
/// 指定的 SolidWorks 文档
/// 文档类型
/// 文档配置管理器
/// 指定的属性的名称,如果为空将返回全部
/// 是否把一些表达式还原为字符串,例如如果图号设置为 $PROP:FileName, 那就还原为文件名
/// 指定的属性和它们的值的集合
private static NameValueCollection RetrieveProperties_Internal(string sldWorksPath, SwDmDocumentType docType, SwDMConfigurationMgr dmConfig, IEnumerable properties, bool resolve)
{
if (dmConfig == null)
return null;
SwDMConfiguration14 activeCfg = null;
if (docType != SwDmDocumentType.swDmDocumentDrawing)
{
string activeConfigName = dmConfig.GetActiveConfigurationName();
string[] configuration = (string[])dmConfig.GetConfigurationNames();
activeCfg = (SwDMConfiguration14)dmConfig.GetConfigurationByName(activeConfigName);
}
else
{ // 工程图不能得到配置名的,因为它就没有配置,所以本方法只能废弃
activeCfg = (SwDMConfiguration14)dmConfig.GetConfigurationByName(string.Empty);
}
if (activeCfg == null)
return null;
string[] allPropNameArr = (string[])activeCfg.GetCustomPropertyNames();
if (allPropNameArr == null)
return null;
NameValueCollection collection = new NameValueCollection();
List expectedPropertiesInLowerCase = new List();
if (properties != null)
expectedPropertiesInLowerCase.AddRange(properties.Select(x => x.ToLower())); // 期望的属性名称都变成小写
foreach (string custPropName in allPropNameArr)
{
SwDmCustomInfoType propType = SwDmCustomInfoType.swDmCustomInfoUnknown;
string propStr = activeCfg.GetCustomProperty2(custPropName, out propType);
if (expectedPropertiesInLowerCase.Count == 0) // 没有设置期望的属性名称,那就是所有的属性都要返回
collection.Add(custPropName, resolve ? ResolvePropertyValue(sldWorksPath, propStr) : propStr);
else if (expectedPropertiesInLowerCase.Contains(custPropName.ToLower())) // 只要期望的属性
collection.Add(custPropName, resolve ? ResolvePropertyValue(sldWorksPath, propStr) : propStr);
}
return collection;
}
///
/// 还原属性值,例如属性值是个表达式 $PROP:FileName,那就还原成不带路径和后缀的文件名
///
/// 指定的 SolidWorks 文档
/// 原始的属性值
/// 还原后的属性值
public static string ResolvePropertyValue(string sldWorksPath, string originalPropertyValue)
{
if (string.IsNullOrEmpty(originalPropertyValue))
return originalPropertyValue;
if (originalPropertyValue.ToLower().StartsWith("$prp:")) // 一般是 $PRP:SW File Name
{
return Path.GetFileNameWithoutExtension(sldWorksPath);
}
else
{
return originalPropertyValue;
}
}
///
/// 没有物料编码时用此字符串表示,原计划用空字符串,但也不好
///
public static string NoDrawNoOfCustomProperty = @"[无物料编码]";
///
/// 得到图纸的图号,也就是得到模型的物料编码 (DrawNo 自定义属性)
///
/// 模型文件
/// 模型的物料编码
public static string GetDrawNo(string modelFileName)
{
NameValueCollection nameValueCollection = RetrieveProperties(modelFileName, PropertyNamesOftenUsed.Take(1), true);
if (nameValueCollection != null && nameValueCollection.Count == 1)
return nameValueCollection[0];
else
return string.Empty;
}
///
/// 得到一个装配体下的所有一级部件,不用递归
///
/// 装配体的路径
/// 配置名,如果为空就取默认的配置
/// 一级子部件
public static List GetComponentsWithoutRecursion(string assemblyPath, string configurationName)
{
if (string.IsNullOrEmpty(assemblyPath))
return null;
if (!File.Exists(assemblyPath))
return null;
string originalExt;
SwDmDocumentType docType = DMSldFileExtentionChecker.CheckDM(assemblyPath, out originalExt);
if (docType != SwDmDocumentType.swDmDocumentAssembly)
{
return null;
}
SwDMClassFactory swDMClassFactory = new SwDMClassFactory();
SwDMApplication swDMApp = swDMClassFactory.GetApplication(LinktronLicenseKey);
SwDmDocumentOpenError returnValue = 0;
SwDMDocument17 swDoc = (SwDMDocument17)swDMApp.GetDocument(assemblyPath, docType, true, out returnValue); // 允许只读
if (swDoc == null || returnValue != SwDmDocumentOpenError.swDmDocumentOpenErrorNone)
{
Marshal.ReleaseComObject(swDMApp);
Marshal.ReleaseComObject(swDMClassFactory);
return null;
}
SwDMConfigurationMgr dmConfigMgr = swDoc.ConfigurationManager;
string[] configurationNames = (string[])dmConfigMgr.GetConfigurationNames();
if (configurationNames == null || configurationNames.Length <= 0)
{ // 装配体至少会有一个配置的
Marshal.ReleaseComObject(dmConfigMgr);
Marshal.ReleaseComObject(swDoc);
Marshal.ReleaseComObject(swDMApp);
Marshal.ReleaseComObject(swDMClassFactory);
return null;
}
string configNameToOpen = null;
if (string.IsNullOrEmpty(configurationName))
{ // 即将打开的配置名
configNameToOpen = dmConfigMgr.GetActiveConfigurationName();
}
else
{
configNameToOpen = configurationName;
}
SwDMConfiguration14 activeCfg = (SwDMConfiguration14)dmConfigMgr.GetConfigurationByName(configNameToOpen);
if (activeCfg == null)
return null;
object[] all = (object[])activeCfg.GetComponents();
List names = new List();
if (all != null)
{
foreach (object o in all)
{
SwDMComponent9 component = o as SwDMComponent9;
names.Add(component.PathName);
}
}
Marshal.ReleaseComObject(activeCfg);
Marshal.ReleaseComObject(dmConfigMgr);
Marshal.ReleaseComObject(swDoc);
Marshal.ReleaseComObject(swDMApp);
Marshal.ReleaseComObject(swDMClassFactory);
return names;
}
///
/// 递归得到一个装配体或零件下的所有部件的信息
///
/// 装配体或零件的路径
/// 配置名,如果为空就取默认的配置
/// 装配体下的所有部件,包括所有子孙,递归得到的
public static List GetComponentsRecursively(string assemblyPath, string configurationName)
{
if (string.IsNullOrEmpty(assemblyPath))
return null;
if (!File.Exists(assemblyPath))
return null;
string originalExt;
SwDmDocumentType docType = DMSldFileExtentionChecker.CheckDM(assemblyPath, out originalExt);
if (docType != SwDmDocumentType.swDmDocumentAssembly && docType != SwDmDocumentType.swDmDocumentPart)
{
return null;
}
SwDMClassFactory swDMClassFactory = new SwDMClassFactory();
SwDMApplication swDMApp = swDMClassFactory.GetApplication(LinktronLicenseKey);
SwDmDocumentOpenError returnValue = 0;
SwDMDocument17 swDoc = (SwDMDocument17)swDMApp.GetDocument(assemblyPath, docType, true, out returnValue); // 允许只读
if (swDoc == null || returnValue != SwDmDocumentOpenError.swDmDocumentOpenErrorNone)
{
Marshal.ReleaseComObject(swDMApp);
Marshal.ReleaseComObject(swDMClassFactory);
return null;
}
SwDMConfigurationMgr dmConfigMgr = swDoc.ConfigurationManager;
string[] configurationNames = (string[])dmConfigMgr.GetConfigurationNames();
if (configurationNames == null || configurationNames.Length <= 0)
{ // 装配体至少会有一个配置的
Marshal.ReleaseComObject(dmConfigMgr);
Marshal.ReleaseComObject(swDoc);
Marshal.ReleaseComObject(swDMApp);
Marshal.ReleaseComObject(swDMClassFactory);
return null;
}
string configNameToOpen = null;
if (string.IsNullOrEmpty(configurationName))
{ // 即将打开的配置名
configNameToOpen = dmConfigMgr.GetActiveConfigurationName();
}
else
{
configNameToOpen = configurationName;
}
SwDMConfiguration14 activeCfg = (SwDMConfiguration14)dmConfigMgr.GetConfigurationByName(configNameToOpen);
if (activeCfg == null)
return null;
List all = new List();
SldComponentInfo_DM root = GetTopDocumentInfo(ref all, assemblyPath, swDoc, activeCfg); // 一般不会返回空的
if (root == null)
throw new InvalidOperationException("GetComponentsRecursively: 不可能的错误,没有遍历到顶级文档的信息");
object[] allComponents = activeCfg.GetComponents();
if (allComponents != null)
{
root.ChildrenCount = allComponents.Length;
foreach (object o in allComponents)
{
SwDMComponent9 subComponent = o as SwDMComponent9;
GetComponentInfoWithChildren(ref all, subComponent, 1, root.Id, swDMApp);
}
}
root.ConfigurationCount = configurationNames.Length;
Marshal.ReleaseComObject(activeCfg);
Marshal.ReleaseComObject(dmConfigMgr);
Marshal.ReleaseComObject(swDoc);
Marshal.ReleaseComObject(swDMApp);
Marshal.ReleaseComObject(swDMClassFactory);
return all;
}
///
/// 检索当前 SolidWorks 配置下的所有属性值
///
/// 当前配置
/// 全路径
/// 指定的属性和它们的值的集合
public static NameValueCollection RetrieveAllProperties(SwDMConfiguration14 currentConfig, string fullPath)
{
if (currentConfig == null)
return null;
NameValueCollection result = RetrievePropertiesUnderConfig(fullPath, currentConfig, null, true);
return result;
}
///
/// 得到一个部件的信息
/// 仅仅 ChildrenCount, ConfigurationCount 没有设置,其它属性都设置了
/// 没有递归,仅仅是本部件,不牵涉任何其它的
///
/// 当前部件的信息,一定不会为空的
/// 当前部件
/// 当前配置
/// 当前部件的层级
/// 父节点的 Id
/// 当前的 SolidWorks DM 应用
/// 成功与否
public static bool GetCurrentComponentInfo(
ref SldComponentInfo_DM componentInfo,
SwDMComponent9 currentComponent,
SwDMConfiguration14 currentConfig,
int level,
int parentId,
SwDMApplication swDMApplication)
{
if (componentInfo == null || currentComponent == null || currentConfig == null || level < 0)
return false;
componentInfo.ParentId = parentId;
componentInfo.Level = level;
componentInfo.FullPath = currentComponent.PathName;
componentInfo.FeatureName = currentComponent.Name2;
componentInfo.FeatureId = currentComponent.GetID();
componentInfo.IsSuppressed = currentComponent.IsSuppressed();
componentInfo.FileName2D = EngineeringDrawingFile.Get2DFileNameIfExisting(componentInfo.FullPath);
componentInfo.Visible = !currentComponent.IsHidden();
if (File.Exists(componentInfo.FullPath))
componentInfo.AllCustomProperties = RetrieveAllProperties(currentConfig, componentInfo.FullPath);
return true;
}
///
/// 得到一个部件及其子部件的信息,递归调用
///
/// 当前所有部件的信息,一定不会为空的,新的部件将加入本列表
/// 当前部件
/// 当前部件的层级
/// 父节点的 Id
/// 当前的 SolidWorks DM 应用
/// 成功与否
private static bool GetComponentInfoWithChildren(
ref List componentsInfo,
SwDMComponent9 currentComponent,
int level,
int parentId,
SwDMApplication swDMApplication)
{
if (componentsInfo == null || currentComponent == null || level <= 0 || swDMApplication == null)
return false;
string fullPath = currentComponent.PathName;
string originalExt;
SwDmDocumentType docType = DMSldFileExtentionChecker.CheckDM(fullPath, out originalExt);
SwDmDocumentOpenError returnValue = SwDmDocumentOpenError.swDmDocumentOpenErrorFail;
SwDMDocument17 swDoc = (SwDMDocument17)swDMApplication.GetDocument(fullPath, docType, true, out returnValue); // 允许只读
if (swDoc == null || returnValue != SwDmDocumentOpenError.swDmDocumentOpenErrorNone)
return true;
SwDMConfigurationMgr swDMConfigurationMgr = swDoc.ConfigurationManager;
string activeConfigName = swDMConfigurationMgr.GetActiveConfigurationName();
SwDMConfiguration14 activeConfig = swDMConfigurationMgr.GetConfigurationByName(activeConfigName) as SwDMConfiguration14;
SldComponentInfo_DM top = SldComponentInfo_DM.GenerateTheNext();
GetCurrentComponentInfo(ref top, currentComponent, activeConfig, level, parentId, swDMApplication);
componentsInfo.Add(top); // 先把根节点放进列表
top.ConfigurationCount = swDoc.ConfigurationManager.GetConfigurationCount();
//if (!File.Exists(top.FullPath))
// return true;
object[] all = (object[])activeConfig.GetComponents();
List names = new List();
if (all != null)
{
top.ChildrenCount = all.Length;
foreach (object o in all)
{
SwDMComponent9 subComponent = o as SwDMComponent9;
GetComponentInfoWithChildren(ref componentsInfo, subComponent, level + 1, top.Id, swDMApplication); // 递归把子孙都给加载进去
}
}
Marshal.ReleaseComObject(activeConfig);
Marshal.ReleaseComObject(swDMConfigurationMgr);
Marshal.ReleaseComObject(swDoc);
return true;
}
///
/// 得到顶级文档的信息,没有递归调用
///
/// 当前所有部件的信息,一定不会为空的,新的部件将加入本列表
/// 当前顶级文档的全路径
/// 当前顶级文档
/// 当前的配置
/// 当前的 SolidWorks DM 应用
/// 顶级部件信息
private static SldComponentInfo_DM GetTopDocumentInfo(
ref List componentsInfo,
string fullPath, SwDMDocument17 currentDocument,
SwDMConfiguration14 currentConfig)
{
if (componentsInfo == null || string.IsNullOrEmpty(fullPath) || currentDocument == null || currentConfig == null)
return null;
SldComponentInfo_DM root = SldComponentInfo_DM.GenerateTheRoot();
root.FullPath = fullPath;
root.FeatureName = currentDocument.Title;
//root.FeatureId = 0;
//root.IsSuppressed = false;
//root.FileName2D = null;
//root.Visible = true;
if (File.Exists(fullPath))
root.AllCustomProperties = RetrievePropertiesUnderConfig(fullPath, currentConfig, null, true);
componentsInfo.Add(root);
return root;
}
///
/// 检索一个 SolidWorks 文档里指定配置下的指定的属性值
///
/// 指定的 SolidWorks 文档
/// 配置
/// 指定的属性的名称,如果为空将返回全部
/// 是否把一些表达式还原为字符串,例如如果图号设置为 $PRP:FileName, 那就还原为文件名
/// 指定的属性和它们的值的集合
public static NameValueCollection RetrievePropertiesUnderConfig(
string sldWorksPath,
SwDMConfiguration14 config,
IEnumerable properties,
bool resolve)
{
if (config == null)
return null;
string[] allPropNameArr = (string[])config.GetCustomPropertyNames();
if (allPropNameArr == null)
return null;
NameValueCollection collection = new NameValueCollection();
List expectedPropertiesInLowerCase = new List();
if (properties != null)
expectedPropertiesInLowerCase.AddRange(properties.Select(x => x.ToLower())); // 期望的属性名称都变成小写
foreach (var v in allPropNameArr)
{
SwDmCustomInfoType propType = SwDmCustomInfoType.swDmCustomInfoUnknown;
string propStr = config.GetCustomProperty2(v, out propType);
if (expectedPropertiesInLowerCase.Count == 0) // 没有设置期望的属性名称,那就是所有的属性都要返回
collection.Add(v, resolve ? ResolvePropertyValue(sldWorksPath, propStr) : propStr);
else if (expectedPropertiesInLowerCase.Contains(v.ToLower())) // 只要期望的属性
collection.Add(v, resolve ? ResolvePropertyValue(sldWorksPath, propStr) : propStr);
}
return collection;
}
}
}