Link SOLIDWORKS custom properties from text file
This VBA macro allows to link external comma separated file into the configuration specific or file specific custom properties of SOLIDWORKS file.
CSV file consists of 2 columns (property name and property value) without a header.
If value of the cell contains special symbol " then the cell must have "" at the start and ant the end of the cell value.
Company,Xarial Pty Limited
Material,"""SW-Material"""
Mass,"""SW-Mass"""
You can use Excel to modify these values and export to CSV file with comma delimiter, special symbol will be formatted correctly automatically.
Commas and new line symbols in the property names or values are not supported
Set the value of the CLEAR_PROPERTIES constant to True or False to configure if existing properties need to be deleted before updating.
Set ALL_COMPONENTS to True to process all components of the assembly
Const CLEAR_PROPERTIES As Boolean = False
Const ALL_COMPONENTS As Boolean = True
Type RefCompModel
RefModel As SldWorks.ModelDoc2
RefConf As String
End Type
#Const ARGS = True 'True to use arguments from Toolbar+ or Batch+ instead of the constant
Const CLEAR_PROPERTIES As Boolean = False
Const ALL_COMPONENTS As Boolean = False
Sub main()
Dim swApp As SldWorks.SldWorks
Set swApp = Application.SldWorks
Dim swModel As SldWorks.ModelDoc2
Dim csvFilePath As String
Dim confSpecific As Boolean
If GetParameters(swApp, swModel, csvFilePath, confSpecific) Then
If Not swModel Is Nothing Then
Dim vTable As Variant
vTable = GetArrayFromCsv(csvFilePath)
Dim swRefConf As SldWorks.Configuration
Set swRefConf = swModel.ConfigurationManager.ActiveConfiguration
WritePropertiesFromTable swModel, vTable, IIf(CBool(confSpecific), swRefConf.Name, ""), CLEAR_PROPERTIES
If ALL_COMPONENTS Then
Dim refCompModels() As RefCompModel
refCompModels = CollectUniqueComponents(swRefConf, confSpecific)
If (Not refCompModels) <> -1 Then
Dim i As Integer
For i = 0 To UBound(refCompModels)
WritePropertiesFromTable refCompModels(i).RefModel, vTable, refCompModels(i).RefConf, CBool(clearPrps)
Next
End If
End If
'WritePropertiesFromFile swModel, csvFilePath, IIf(CBool(confSpecific), swModel.ConfigurationManager.ActiveConfiguration, Nothing)
Else
Err.Raise vbError, "", "Please open model"
End If
End If
End Sub
Function GetParameters(app As SldWorks.SldWorks, ByRef model As SldWorks.ModelDoc2, ByRef csvFilePath As String, ByRef confSpecific As Boolean) As Boolean
Dim confSpecArgsParsed As Boolean
#If ARGS Then
try_:
On Error GoTo catch_
Dim macroRunner As Object
Set macroRunner = CreateObject("CadPlus.MacroRunner.Sw")
Dim param As Object
Set param = macroRunner.PopParameter(app)
Dim vArgs As Variant
vArgs = param.Get("Args")
Set model = param.Get("Model")
If Not IsEmpty(vArgs) Then
csvFilePath = CStr(vArgs(0))
End If
If UBound(vArgs) > 0 Then
confSpecific = CBool(vArgs(1))
confSpecArgsParsed = True
End If
GoTo finally_
catch_:
finally_:
#End If
If Trim(csvFilePath) = "" Then
csvFilePath = app.GetOpenFileName("Custom Properties Template File", "", "CSV Files (*.csv)|*.csv|Text Files (*.txt)|*.txt|All Files (*.*)|*.*|", 0, "", "")
End If
If model Is Nothing Then
Set model = app.ActiveDoc
End If
If csvFilePath <> "" Then
If Not confSpecArgsParsed Then
confSpecific = app.SendMsgToUser2("Link to configuration specific properties (Yes) or File Specific (No)?", swMessageBoxIcon_e.swMbQuestion, swMessageBoxBtn_e.swMbYesNo) = swMessageBoxResult_e.swMbHitYes
End If
GetParameters = True
Else
GetParameters = False
End If
End Function
Function GetArrayFromCsv(filePath As String) As Variant
Dim fileNo As Integer
fileNo = FreeFile
Dim rows As Collection
Set rows = New Collection
Open filePath For Input As #fileNo
Do While Not EOF(fileNo)
Dim tableRow As String
Line Input #fileNo, tableRow
Dim vCells As Variant
vCells = Split(tableRow, ",")
rows.Add vCells
Loop
Close #fileNo
Dim tableData() As String
Dim rowCount As Integer
Dim columnCount As Integer
rowCount = rows.Count
columnCount = UBound(rows(1)) + 1
Dim rowIndex As Integer
Dim columnIndex As Integer
ReDim tableData(rowCount - 1, columnCount - 1)
For rowIndex = 1 To rowCount
Dim vRow As Variant
vRow = rows.Item(rowIndex)
For columnIndex = 1 To columnCount
Dim cellVal As String
cellVal = vRow(columnIndex - 1)
If Left(cellVal, 2) = """""" And Right(cellVal, 2) = """""" Then
cellVal = Mid(cellVal, 3, Len(cellVal) - 4)
End If
tableData(rowIndex - 1, columnIndex - 1) = cellVal
Next
Next
GetArrayFromCsv = tableData
End Function
Sub WritePropertiesFromTable(model As SldWorks.ModelDoc2, table As Variant, confName As String, clearPrps As Boolean)
Dim i As Integer
Dim swCustPrpMgr As SldWorks.CustomPropertyManager
Set swCustPrpMgr = model.Extension.CustomPropertyManager(confName)
If clearPrps Then
ClearProperties swCustPrpMgr
End If
For i = 0 To UBound(table, 1)
Dim prpName As String
prpName = CStr(table(i, 0))
Dim prpVal As String
prpVal = CStr(table(i, 1))
If swCustPrpMgr.Add3(prpName, swCustomInfoType_e.swCustomInfoText, prpVal, swCustomPropertyAddOption_e.swCustomPropertyReplaceValue) <> swCustomInfoAddResult_e.swCustomInfoAddResult_AddedOrChanged Then
Err.Raise vbError, "", "Failed to add property '" & prpName & "'"
End If
Next
End Sub
Sub ClearProperties(custPrpMgr As SldWorks.CustomPropertyManager)
Dim vPrpNames As Variant
vPrpNames = custPrpMgr.GetNames
If Not IsEmpty(vPrpNames) Then
Dim i As Integer
For i = 0 To UBound(vPrpNames)
custPrpMgr.Delete2 CStr(vPrpNames(i))
Next
End If
End Sub
Function CollectUniqueComponents(assmConf As SldWorks.Configuration, confSpecific As Boolean) As RefCompModel()
Dim swRootComp As SldWorks.Component2
Set swRootComp = assmConf.GetRootComponent3(False)
Dim refCompModels() As RefCompModel
ProcessComponents swRootComp.GetChildren(), confSpecific, refCompModels
CollectUniqueComponents = refCompModels
End Function
Sub ProcessComponents(vComps As Variant, confSpecific As Boolean, refCompModels() As RefCompModel)
If Not IsEmpty(vComps) Then
Dim i As Integer
For i = 0 To UBound(vComps)
Dim swComp As SldWorks.Component2
Set swComp = vComps(i)
Dim swRefModel As SldWorks.ModelDoc2
Set swRefModel = swComp.GetModelDoc2
If Not swRefModel Is Nothing Then
Dim refConfName As String
refConfName = IIf(confSpecific, swComp.ReferencedConfiguration, "")
If Not Contains(refCompModels, swRefModel, refConfName) Then
If (Not refCompModels) = -1 Then
ReDim refCompModels(0)
Else
ReDim Preserve refCompModels(UBound(refCompModels) + 1)
End If
Set refCompModels(UBound(refCompModels)).RefModel = swRefModel
refCompModels(UBound(refCompModels)).RefConf = refConfName
End If
ProcessComponents swComp.GetChildren(), confSpecific, refCompModels
End If
Next
End If
End Sub
Function Contains(refCompModels() As RefCompModel, model As SldWorks.ModelDoc2, conf As String) As Boolean
Contains = False
If (Not refCompModels) <> -1 Then
Dim i As Integer
For i = 0 To UBound(refCompModels)
If refCompModels(i).RefModel Is model And LCase(refCompModels(i).RefConf) = LCase(conf) Then
Contains = True
Exit Function
End If
Next
End If
End Function
In order to dynamically link external text file and update properties on every rebuild, use the following macro.
Set the value of the UPDATE_ON_CSV_FILE_CHANGE_ONLY constant to True or False to configure if properties need to reload only if properties text file is changed or always when macro.
Const UPDATE_ON_CSV_FILE_CHANGE_ONLY As Boolean = False
Macro will ask for the following input parameters upon insertion of the macro feature:
- Should the properties be configuration specific or file specific
- Should the properties be cleared upon update
- Should the reference components of the assembly be included into the scope of the properties
Properties will be automatically updated upon rebuild of the macro feature.
Type RefCompModel
RefModel As SldWorks.ModelDoc2
RefConf As String
End Type
Const BASE_NAME As String = "LinkedCustomProperties"
Const EMBED As Boolean = False
Const UPDATE_ON_CSV_FILE_CHANGE_ONLY As Boolean = True
Const PARAM_CLEAR_PROPERTIES As String = "ClearProperties"
Const PARAM_PROCESS_COMPONENTS As String = "ProcessComponents"
Const PARAM_CSV_PATH As String = "CsvPath"
Const PARAM_CONF_SPEC_NAME As String = "ConfigurationSpecific"
Const PARAM_CSV_TIME_STAMP As String = "CsvFileTimeStamp"
Function GetParameters(app As SldWorks.SldWorks, model As SldWorks.ModelDoc2, ByRef csvFilePath As String, ByRef confSpecific As Boolean, ByRef clearPrps As Boolean, ByRef processComps As Boolean) As Boolean
csvFilePath = app.GetOpenFileName("Custom Properties Template File", "", "CSV Files (*.csv)|*.csv|Text Files (*.txt)|*.txt|All Files (*.*)|*.*|", 0, "", "")
If csvFilePath <> "" Then
csvFilePath = GetRelativeFilePath(model, csvFilePath)
confSpecific = app.SendMsgToUser2("Link to configuration specific properties (Yes) or File Specific (No)?", swMessageBoxIcon_e.swMbQuestion, swMessageBoxBtn_e.swMbYesNo) = swMessageBoxResult_e.swMbHitYes
clearPrps = app.SendMsgToUser2("Clear existing properties?", swMessageBoxIcon_e.swMbQuestion, swMessageBoxBtn_e.swMbYesNo) = swMessageBoxResult_e.swMbHitYes
If TypeOf model Is SldWorks.AssemblyDoc Then
processComps = app.SendMsgToUser2("Process children components of the assembly?", swMessageBoxIcon_e.swMbQuestion, swMessageBoxBtn_e.swMbYesNo) = swMessageBoxResult_e.swMbHitYes
Else
processComps = False
End If
GetParameters = True
Else
GetParameters = False
End If
End Function
Sub main()
Dim swApp As SldWorks.SldWorks
Set swApp = Application.SldWorks
Dim swModel As SldWorks.ModelDoc2
Set swModel = swApp.ActiveDoc
If Not swModel Is Nothing Then
Dim csvFilePath As String
Dim confSpecific As Boolean
Dim clearPrps As Boolean
Dim processComps As Boolean
If GetParameters(swApp, swModel, csvFilePath, confSpecific, clearPrps, processComps) Then
Dim curMacroPath As String
curMacroPath = swApp.GetCurrentMacroPathName
Dim vMethods(8) As String
Dim moduleName As String
GetMacroEntryPoint swApp, curMacroPath, moduleName, ""
vMethods(0) = curMacroPath: vMethods(1) = moduleName: vMethods(2) = "swmRebuild"
vMethods(3) = curMacroPath: vMethods(4) = moduleName: vMethods(5) = "swmEditDefinition"
vMethods(6) = curMacroPath: vMethods(7) = moduleName: vMethods(8) = "swmSecurity"
Dim vParamNames(4) As String
vParamNames(0) = PARAM_CSV_PATH
vParamNames(1) = PARAM_CONF_SPEC_NAME
vParamNames(2) = PARAM_CLEAR_PROPERTIES
vParamNames(3) = PARAM_PROCESS_COMPONENTS
vParamNames(4) = PARAM_CSV_TIME_STAMP
Dim vParamTypes(4) As Long
vParamTypes(0) = swMacroFeatureParamType_e.swMacroFeatureParamTypeString
vParamTypes(1) = swMacroFeatureParamType_e.swMacroFeatureParamTypeInteger
vParamTypes(2) = swMacroFeatureParamType_e.swMacroFeatureParamTypeInteger
vParamTypes(3) = swMacroFeatureParamType_e.swMacroFeatureParamTypeInteger
vParamTypes(4) = swMacroFeatureParamType_e.swMacroFeatureParamTypeString
Dim vParamValues(4) As String
vParamValues(0) = csvFilePath
vParamValues(1) = CLng(confSpecific)
vParamValues(2) = CLng(clearPrps)
vParamValues(3) = CLng(processComps)
vParamValues(4) = ""
Dim opts As swMacroFeatureOptions_e
opts = swMacroFeatureOptions_e.swMacroFeatureAlwaysAtEnd
If EMBED Then
opts = opts + swMacroFeatureOptions_e.swMacroFeatureEmbedMacroFile
End If
Dim swFeat As SldWorks.Feature
Set swFeat = swModel.FeatureManager.InsertMacroFeature3(BASE_NAME, "", vMethods, _
vParamNames, vParamTypes, vParamValues, Empty, Empty, Empty, _
Empty, opts)
If swFeat Is Nothing Then
MsgBox "Failed to create linked properties feature"
End If
End If
Else
MsgBox "Please open model"
End If
End Sub
Sub GetMacroEntryPoint(app As SldWorks.SldWorks, macroPath As String, ByRef moduleName As String, ByRef procName As String)
Dim vMethods As Variant
vMethods = app.GetMacroMethods(macroPath, swMacroMethods_e.swMethodsWithoutArguments)
Dim i As Integer
If Not IsEmpty(vMethods) Then
For i = 0 To UBound(vMethods)
Dim vData As Variant
vData = Split(vMethods(i), ".")
If i = 0 Or LCase(vData(1)) = "main" Then
moduleName = vData(0)
procName = vData(1)
End If
Next
End If
End Sub
Function GetArrayFromCsv(filePath As String) As Variant
Dim fileNo As Integer
fileNo = FreeFile
Dim rows As Collection
Set rows = New Collection
Open filePath For Input As #fileNo
Do While Not EOF(fileNo)
Dim tableRow As String
Line Input #fileNo, tableRow
Dim vCells As Variant
vCells = Split(tableRow, ",")
rows.Add vCells
Loop
Close #fileNo
Dim tableData() As String
Dim rowCount As Integer
Dim columnCount As Integer
rowCount = rows.Count
columnCount = UBound(rows(1)) + 1
Dim rowIndex As Integer
Dim columnIndex As Integer
ReDim tableData(rowCount - 1, columnCount - 1)
For rowIndex = 1 To rowCount
Dim vRow As Variant
vRow = rows.Item(rowIndex)
For columnIndex = 1 To columnCount
Dim cellVal As String
cellVal = vRow(columnIndex - 1)
If Left(cellVal, 2) = """""" And Right(cellVal, 2) = """""" Then
cellVal = Mid(cellVal, 3, Len(cellVal) - 4)
End If
tableData(rowIndex - 1, columnIndex - 1) = cellVal
Next
Next
GetArrayFromCsv = tableData
End Function
Function GetRelativeFilePath(model As SldWorks.ModelDoc2, filePath As String) As String
GetRelativeFilePath = filePath
Dim modelDir As String
modelDir = model.GetPathName
If modelDir <> "" Then
modelDir = Left(modelDir, InStrRev(modelDir, "\"))
If Len(filePath) > Len(modelDir) Then
If LCase(modelDir) = LCase(Left(filePath, Len(modelDir))) Then
GetRelativeFilePath = Right(filePath, Len(filePath) - Len(modelDir) + 1)
End If
End If
End If
End Function
Function swmRebuild(varApp As Variant, varDoc As Variant, varFeat As Variant) As Variant
try_:
Dim swModel As SldWorks.ModelDoc2
Dim swFeat As SldWorks.Feature
Set swModel = varDoc
Set swFeat = varFeat
UpdateProperties swModel, swFeat
catch_:
swmRebuild = Err.Description
finally_:
End Function
Sub UpdateProperties(model As SldWorks.ModelDoc2, feat As SldWorks.Feature)
Dim swMacroFeat As SldWorks.MacroFeatureData
Set swMacroFeat = feat.GetDefinition()
Dim csvFilePath As String
Dim confSpecific As Long
Dim clearPrps As Long
Dim processComps As Long
Dim csvFileTimeStamp As String
Dim curCsvFileTimeStamp As String
swMacroFeat.GetIntegerByName PARAM_CONF_SPEC_NAME, confSpecific
swMacroFeat.GetIntegerByName PARAM_CLEAR_PROPERTIES, clearPrps
swMacroFeat.GetIntegerByName PARAM_PROCESS_COMPONENTS, processComps
swMacroFeat.GetStringByName PARAM_CSV_TIME_STAMP, csvFileTimeStamp
csvFilePath = GetCsvFileFullPath(swMacroFeat, model)
curCsvFileTimeStamp = FileDateTime(csvFilePath)
If Not UPDATE_ON_CSV_FILE_CHANGE_ONLY Or curCsvFileTimeStamp <> csvFileTimeStamp Then
If Dir(csvFilePath) = "" Then
Err.Raise "Linked CSV file is missing: " & csvFilePath
End If
Dim vTable As Variant
vTable = GetArrayFromCsv(csvFilePath)
If UBound(vTable, 2) <> 1 Then
Err.Raise vbError, "", "There must be only 2 columns in the CSV file"
End If
Dim swRefConf As SldWorks.Configuration
Set swRefConf = swMacroFeat.CurrentConfiguration
WritePropertiesFromTable model, vTable, IIf(CBool(confSpecific), swRefConf.Name, ""), CBool(clearPrps)
If CBool(processComps) Then
Dim refCompModels() As RefCompModel
refCompModels = CollectUniqueComponents(swRefConf, CBool(confSpecific))
If (Not refCompModels) <> -1 Then
Dim i As Integer
For i = 0 To UBound(refCompModels)
WritePropertiesFromTable refCompModels(i).RefModel, vTable, refCompModels(i).RefConf, CBool(clearPrps)
Next
End If
End If
swMacroFeat.SetStringByName PARAM_CSV_TIME_STAMP, curCsvFileTimeStamp
End If
End Sub
Function CollectUniqueComponents(assmConf As SldWorks.Configuration, confSpecific As Boolean) As RefCompModel()
Dim swRootComp As SldWorks.Component2
Set swRootComp = assmConf.GetRootComponent3(False)
Dim refCompModels() As RefCompModel
ProcessComponents swRootComp.GetChildren(), confSpecific, refCompModels
CollectUniqueComponents = refCompModels
End Function
Sub ProcessComponents(vComps As Variant, confSpecific As Boolean, refCompModels() As RefCompModel)
If Not IsEmpty(vComps) Then
Dim i As Integer
For i = 0 To UBound(vComps)
Dim swComp As SldWorks.Component2
Set swComp = vComps(i)
Dim swRefModel As SldWorks.ModelDoc2
Set swRefModel = swComp.GetModelDoc2
If Not swRefModel Is Nothing Then
Dim refConfName As String
refConfName = IIf(confSpecific, swComp.ReferencedConfiguration, "")
If Not Contains(refCompModels, swRefModel, refConfName) Then
If (Not refCompModels) = -1 Then
ReDim refCompModels(0)
Else
ReDim Preserve refCompModels(UBound(refCompModels) + 1)
End If
Set refCompModels(UBound(refCompModels)).RefModel = swRefModel
refCompModels(UBound(refCompModels)).RefConf = refConfName
End If
ProcessComponents swComp.GetChildren(), confSpecific, refCompModels
End If
Next
End If
End Sub
Function Contains(refCompModels() As RefCompModel, model As SldWorks.ModelDoc2, conf As String) As Boolean
Contains = False
If (Not refCompModels) <> -1 Then
Dim i As Integer
For i = 0 To UBound(refCompModels)
If refCompModels(i).RefModel Is model And LCase(refCompModels(i).RefConf) = LCase(conf) Then
Contains = True
Exit Function
End If
Next
End If
End Function
Function GetCsvFileFullPath(macroFeatDef As SldWorks.MacroFeatureData, model As SldWorks.ModelDoc2) As String
Dim csvFilePath As String
macroFeatDef.GetStringByName PARAM_CSV_PATH, csvFilePath
If Left(csvFilePath, 1) = "\" And Mid(csvFilePath, 2, 1) <> "\" Then 'if relative but not UNC
modelDir = model.GetPathName
If modelDir <> "" Then
modelDir = Left(modelDir, InStrRev(modelDir, "\") - 1)
csvFilePath = modelDir & csvFilePath
End If
End If
GetCsvFileFullPath = csvFilePath
End Function
Sub WritePropertiesFromTable(model As SldWorks.ModelDoc2, table As Variant, confName As String, clearPrps As Boolean)
Dim i As Integer
Dim swCustPrpMgr As SldWorks.CustomPropertyManager
Set swCustPrpMgr = model.Extension.CustomPropertyManager(confName)
If clearPrps Then
ClearProperties swCustPrpMgr
End If
For i = 0 To UBound(table, 1)
Dim prpName As String
prpName = CStr(table(i, 0))
Dim prpVal As String
prpVal = CStr(table(i, 1))
If swCustPrpMgr.Add3(prpName, swCustomInfoType_e.swCustomInfoText, prpVal, swCustomPropertyAddOption_e.swCustomPropertyReplaceValue) <> swCustomInfoAddResult_e.swCustomInfoAddResult_AddedOrChanged Then
Err.Raise vbError, "", "Failed to add property '" & prpName & "'"
End If
Next
End Sub
Sub ClearProperties(custPrpMgr As SldWorks.CustomPropertyManager)
Dim vPrpNames As Variant
vPrpNames = custPrpMgr.GetNames
If Not IsEmpty(vPrpNames) Then
Dim i As Integer
For i = 0 To UBound(vPrpNames)
custPrpMgr.Delete2 CStr(vPrpNames(i))
Next
End If
End Sub
Function swmEditDefinition(varApp As Variant, varDoc As Variant, varFeat As Variant) As Variant
Dim swApp As SldWorks.SldWorks
Set swApp = varApp
Dim swModel As SldWorks.ModelDoc2
Set swModel = varDoc
Dim csvFilePath As String
Dim confSpecific As Boolean
Dim clearPrps As Boolean
Dim processComps As Boolean
If GetParameters(swApp, swModel, csvFilePath, confSpecific, clearPrps, processComps) Then
Dim swFeat As SldWorks.Feature
Set swFeat = varFeat
Dim swMacroFeat As SldWorks.MacroFeatureData
Set swMacroFeat = swFeat.GetDefinition()
swMacroFeat.AccessSelections swModel, Nothing
swMacroFeat.SetStringByName PARAM_CSV_PATH, csvFilePath
swMacroFeat.SetIntegerByName PARAM_CONF_SPEC_NAME, CLng(confSpecific)
swMacroFeat.SetIntegerByName PARAM_CLEAR_PROPERTIES, CLng(clearPrps)
swMacroFeat.SetIntegerByName PARAM_PROCESS_COMPONENTS, CLng(processComps)
swFeat.ModifyDefinition swMacroFeat, swModel, Nothing
End If
swmEditDefinition = True
End Function
Function swmSecurity(varApp As Variant, varDoc As Variant, varFeat As Variant) As Variant
swmSecurity = SwConst.swMacroFeatureSecurityOptions_e.swMacroFeatureSecurityByDefault
End Function