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; } } }