Skip to main content

SOLIDWORKS macro feature to link and auto update general table to Excel

Linked table macro feature in the feature tree{ width=350 }

This macro allows to create General Table in part, assembly and drawing and link it to external Excel or text/csv file using SOLIDWORKS API. This macro implemented as embedded macro feature which means that table will be automatically updated once the model is rebuilt.

  • Run the macro
  • Specify the full path to excel (.xls or .xlsx) or comma separated text file (.csv or .txt) in the first prompt dialog
  • Optionally specify the name of the spreadsheet to read data from. If empty string is specified first spreadsheet will be used

Macro inserts the table and macro feature in the feature tree with the data from external file. Modify the file or general table and rebuild the model - table is updated.

Notes and limitations

  • Only simple CSV files are supported (i.e. simple comma separated values, new line symbols or commas in the values are not supported)
  • Excel is not required when CSV file is used
  • Using CSV files has significant performance benefits as it is not required to start Excel and load document to get the data. Use this option where applicable
  • Excel is displayed invisible and session may be cached for better performance benefits
  • If CSV or Excel files are saved relative to the model - relative path will be maintained. It means that the SOLIDWORKS file can be moved together with Excel/CSV and link won't be broken
  • If General Table is selected when inserting new feature - this table will be used instead of creating new one
  • Currently it is not possible to change the path to external Excel file. Delete the macro feature instead and reinsert it by selecting the general table (see previous point)
  • Macro feature is embedded into the model which means that the table will be updated on any other workstations even if this macro is not available.
Const BASE_NAME As String = "LinkedTable"

Const PARAM_EXCEL_PATH As String = "ExcelPath"
Const PARAM_SHEET_NAME As String = "SheetName"

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 excelFilePath As String
Dim excelSheetName As String

excelFilePath = InputBox("Specify the full path to the excel or text/csv file")
excelSheetName = InputBox("Specify the sheet name for excel file (specify empty string for first sheet)")

If excelFilePath = "" Then
Exit Sub
End If

excelFilePath = UpdateRelativePath(swModel, excelFilePath)

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(1) As String
vParamNames(0) = PARAM_EXCEL_PATH
vParamNames(1) = PARAM_SHEET_NAME

Dim vParamTypes(1) As Long
vParamTypes(0) = swMacroFeatureParamType_e.swMacroFeatureParamTypeString
vParamTypes(1) = swMacroFeatureParamType_e.swMacroFeatureParamTypeString

Dim vParamValues(1) As String

vParamValues(0) = excelFilePath
vParamValues(1) = excelSheetName

Dim swTable As SldWorks.TableAnnotation
Set swTable = TryGetSelectedTable(swModel)
If swTable Is Nothing Then
Dim emptyTable(2, 2) As String
Set swTable = CreateTableFromArray(swModel, emptyTable)
End If

swTable.GetAnnotation().Select3 False, Nothing

Dim swFeat As SldWorks.Feature
Set swFeat = swModel.FeatureManager.InsertMacroFeature3(BASE_NAME, "", vMethods, _
vParamNames, vParamTypes, vParamValues, Empty, Empty, Empty, _
Empty, swMacroFeatureOptions_e.swMacroFeatureEmbedMacroFile + swMacroFeatureOptions_e.swMacroFeatureAlwaysAtEnd)

If swFeat Is Nothing Then
MsgBox "Failed to create macro runner"
End If

Else
MsgBox "Please open model"
End If

End Sub

Function UpdateRelativePath(model As SldWorks.ModelDoc2, path As String) As String

Dim modelPath As String
modelPath = model.GetPathName

UpdateRelativePath = path

If modelPath <> "" Then
Dim modelDir As String
modelDir = Left(modelPath, InStrRev(modelPath, "\"))

If LCase(path) Like LCase(modelDir) & "*" Then
UpdateRelativePath = Right(path, Len(path) - Len(modelDir) + 1)
End If

End If

End Function

Function GetFullPath(model As SldWorks.ModelDoc2, path As String)

GetFullPath = path

Dim isRelative As Boolean
isRelative = Left(path, 1) = "\"

If isRelative Then

Dim modelPath As String
Dim modelDir As String

modelPath = model.GetPathName

modelDir = Left(modelPath, InStrRev(modelPath, "\") - 1)

GetFullPath = modelDir & path

End If

End Function

Function TryGetSelectedTable(model As SldWorks.ModelDoc2) As SldWorks.TableAnnotation

On Error Resume Next

Dim swTable As SldWorks.TableAnnotation
Set swTable = model.SelectionManager.GetSelectedObject6(1, -1)

If swTable Is Nothing Then

Dim swTableFeat As SldWorks.GeneralTableFeature
Set swTableFeat = swModel.SelectionManager.GetSelectedObject6(1, -1)

If Not swTableFeat Is Nothing Then
Set swTable = swTableFeat.GetTableAnnotations()(0)
End If

End If

Set TryGetSelectedTable = swTable

End Function

Function CreateTableFromArray(model As SldWorks.ModelDoc2, vTableData As Variant) As SldWorks.TableAnnotation

Dim swTable As SldWorks.TableAnnotation
Set swTable = model.Extension.InsertGeneralTableAnnotation(True, 0, 0, swBOMConfigurationAnchorType_e.swBOMConfigurationAnchor_BottomLeft, "", UBound(vTableData, 1), UBound(vTableData, 2))

FillTable swTable, vTableData

Set CreateTableFromArray = swTable

End Function

Sub FillTable(table As SldWorks.TableAnnotation, vTableData As Variant)

Dim rowIndex As Integer
Dim columnIndex As Integer

Dim rowsCount As Integer
Dim colsCount As Integer
Dim i As Integer

rowsCount = UBound(vTableData, 1)
colsCount = UBound(vTableData, 2)

If table.columnCount > colsCount Then
For i = colsCount To table.columnCount - 1
table.DeleteColumn2 table.columnCount - 1, True
Next
ElseIf table.columnCount < colsCount Then
For i = table.columnCount To colsCount - 1
table.InsertColumn2 swTableItemInsertPosition_e.swTableItemInsertPosition_Last, -1, "", swInsertTableColumnWidthStyle_e.swInsertColumn_DefaultWidth
Next
End If

If table.rowCount > rowsCount Then
For i = rowsCount To table.rowCount - 1
table.DeleteRow2 table.rowCount - 1, True
Next
ElseIf table.rowCount < rowsCount Then
For i = table.rowCount To rowsCount - 1
table.InsertRow swTableItemInsertPosition_e.swTableItemInsertPosition_Last, -1
Next
End If

For rowIndex = 0 To UBound(vTableData, 1)
For columnIndex = 0 To UBound(vTableData, 2)
table.Text(rowIndex, columnIndex) = vTableData(rowIndex, columnIndex)
Next
Next

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 GetArrayFromExcel(filePath As String, sheetName As String) As Variant

Dim xlApp As Object

Dim tableData() As String

Set xlApp = GetObject("", "Excel.Application")

Dim xlWorkbook As Object
Dim xlWorksheet As Object

Dim closeWorkbook As Boolean
closeWorkbook = Not IsWorkbookOpen(xlApp, filePath)

Set xlWorkbook = xlApp.Workbooks.Open(filePath)

If sheetName <> "" Then
Set xlWorksheet = xlWorkbook.Sheets(sheetName)
Else
Set xlWorksheet = xlWorkbook.Sheets(1)
End If

Dim rowIndex As Integer
Dim columnIndex As Integer

ReDim tableData(xlWorksheet.UsedRange.rows.Count, xlWorksheet.UsedRange.Columns.Count)

For rowIndex = 1 To xlWorksheet.UsedRange.rows.Count
For columnIndex = 1 To xlWorksheet.UsedRange.Columns.Count
Dim cellVal As String
cellVal = xlWorksheet.Cells(rowIndex, columnIndex).Value
tableData(rowIndex - 1, columnIndex - 1) = cellVal
Next
Next

GetArrayFromExcel = tableData

If closeWorkbook Then
xlWorkbook.Close SaveChanges:=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, columnCount)

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)
tableData(rowIndex - 1, columnIndex - 1) = cellVal
Next
Next

GetArrayFromCsv = tableData

End Function

Function IsWorkbookOpen(xlApp As Object, filePath As String) As Boolean

Dim i As Integer

For i = 1 To xlApp.Workbooks.Count
If LCase(xlApp.Workbooks(i).FullName) = LCase(filePath) Then
IsWorkbookOpen = True
Exit Function
End If
Next

IsWorkbookOpen = False

End Function

Function swmRebuild(varApp As Variant, varDoc As Variant, varFeat As Variant) As Variant

Dim swApp As SldWorks.SldWorks
Dim swModel As SldWorks.ModelDoc2
Dim swFeat As SldWorks.Feature

Set swApp = varApp
Set swModel = varDoc
Set swFeat = varFeat

Dim swMacroFeat As SldWorks.MacroFeatureData
Set swMacroFeat = swFeat.GetDefinition()

Dim excelFileName As String
Dim excelSheetName As String

swMacroFeat.GetStringByName PARAM_EXCEL_PATH, excelFileName
swMacroFeat.GetStringByName PARAM_SHEET_NAME, excelSheetName

Dim vObjects As Variant
swMacroFeat.GetSelections3 vObjects, Empty, Empty, Empty, Empty

Dim swTable As SldWorks.TableAnnotation
Set swTable = vObjects(0)

If swTable Is Nothing Then
swmRebuild = "Linked general table is missing"
Exit Function
End If

excelFileName = GetFullPath(swModel, excelFileName)

If Dir(excelFileName) = "" Then
swmRebuild = "Linked Excel file is missing: " & excelFileName
Exit Function
End If

Dim vTable As Variant

Dim fileExt As String
fileExt = Right(excelFileName, Len(excelFileName) - InStrRev(excelFileName, "."))

Dim isExcel As Boolean
isExcel = LCase(fileExt) = "xls" Or LCase(fileExt) = "xlsx"

If isExcel Then
vTable = GetArrayFromExcel(excelFileName, excelSheetName)
Else
vTable = GetArrayFromCsv(excelFileName)
End If

FillTable swTable, vTable

End Function

Function swmEditDefinition(varApp As Variant, varDoc As Variant, varFeat As Variant) As Variant
swmEditDefinition = True
End Function

Function swmSecurity(varApp As Variant, varDoc As Variant, varFeat As Variant) As Variant
swmSecurity = SwConst.swMacroFeatureSecurityOptions_e.swMacroFeatureSecurityByDefault
End Function