The scenario: a supplier sends fifty SolidWorks parts. The files land in the PDM vault. The PDM admin manually fills in the data cards — material, description, part number, revision. Everything is correct in the vault database. But the files themselves have no custom properties. Any downstream tool that reads properties directly from the file (a drawing, an ERP integration, a batch export macro) sees blanks.
The natural fix — run the built-in Task Scheduler “Add/Update Custom Properties” task — breaks the data cards. The task adds the file properties and then syncs them back to the data card. Because the file properties were blank when the task ran, the sync writes blank back over the values the admin entered.
The correct fix reads the data card values first, writes them to the file, and then lets the normal sync reinforce rather than overwrite. This requires two lines of EdmLib and the real ICustomPropertyManager API.
Why the Built-in Task Breaks the Data Card
SOLIDWORKS PDM links data card variables to file custom properties using the variable’s Override setting. When a variable maps to a custom property of the same name, the card value and the file property stay synchronized — but the direction of sync matters.
On file check-in, PDM reads the file’s custom properties and writes them into the variable database. This is the “file wins” direction: whatever is in the file overrides the card.
When you run the built-in “Add/Update Custom Properties” task on a file that has no custom properties:
- The task writes the property based on its template (often blank or a hardcoded default)
- The file is saved with blank properties
- On check-in, PDM reads those blank properties and overwrites the data card values the admin had set
The admin’s work is lost.
The fix is to read the existing data card values before touching the file, then write those exact values as file properties. When PDM syncs on check-in, it reads back the same values it already had. Nothing is overwritten.
The EdmLib API for Reading Data Card Values
EdmLib is the API for SOLIDWORKS PDM. The relevant interface is IEdmObject5, which is the base interface that file objects inherit from.
The method:
IEdmObject5::GetVar(bsVariableName As String, bsConfiguration As String) As Variant
bsVariableName— the variable name as defined in the PDM administration tool (case-sensitive)bsConfiguration— the configuration name for config-specific variables; pass""for the default (non-configuration-specific) card value- Return value — a
Variantcontaining the stored value; checkIsEmpty()andIsNull()before using
IEdmFile5 inherits from IEdmObject5, so you call GetVar directly on a file object.
To get the file object, you need the vault and a path:
Dim oVault As Object
Set oVault = CreateObject("ConisioLib.EdmVault5")
oVault.LoginAuto "YourVaultName", 0 ' 0 = parent window handle
Dim oFile As Object
Set oFile = oVault.GetFileFromPath("C:\Vault\Parts\PN12345.SLDPRT", oFolder)
GetFileFromPath returns an IEdmFile5 object (typed as Object here for late binding) and also fills the oFolder parameter with the containing IEdmFolder5.
Complete Macro: Data Card Values to File Custom Properties
This macro connects to the PDM vault, reads the active document’s data card values for a configurable list of variables, and writes each non-blank value to the file’s custom properties. It checks out the file if needed, saves, and leaves it checked in.
Option Explicit
' ============================================================
' Configuration — edit these to match your vault and variable names
' ============================================================
Private Const VAULT_NAME As String = "YourVaultName"
Private Const CONFIG_NAME As String = "" ' "" = global card; or a config name
' Variable names in the PDM admin tool (must match exactly, case-sensitive)
Private CARD_VARIABLES() As String
Sub main()
' Set variable list here (VBA can't initialize array in declaration with values)
CARD_VARIABLES = Array( _
"Description", _
"Material", _
"PartNumber", _
"Revision", _
"DrawingNumber")
Dim swApp As SldWorks.SldWorks
Set swApp = Application.SldWorks
Dim swDoc As SldWorks.ModelDoc2
Set swDoc = swApp.ActiveDoc
If swDoc Is Nothing Then
MsgBox "No active document.", vbExclamation
Exit Sub
End If
Dim docPath As String
docPath = swDoc.GetPathName()
If Len(docPath) = 0 Then
MsgBox "Save the document before running this macro.", vbExclamation
Exit Sub
End If
' Connect to the vault
Dim oVault As Object
Set oVault = CreateObject("ConisioLib.EdmVault5")
On Error Resume Next
oVault.LoginAuto VAULT_NAME, 0
On Error GoTo 0
If Not oVault.IsLoggedIn Then
MsgBox "Cannot connect to vault: " & VAULT_NAME, vbExclamation
Exit Sub
End If
' Get the vault file object
Dim oFolder As Object
Dim oFile As Object
Set oFile = oVault.GetFileFromPath(docPath, oFolder)
If oFile Is Nothing Then
MsgBox "File not found in vault:" & vbCrLf & docPath, vbExclamation
Exit Sub
End If
' Read card variables
Dim i As Integer
Dim propCount As Integer
propCount = 0
For i = 0 To UBound(CARD_VARIABLES)
Dim varName As String
varName = CStr(CARD_VARIABLES(i))
Dim varVal As Variant
On Error Resume Next
varVal = oFile.GetVar(varName, CONFIG_NAME)
On Error GoTo 0
If IsEmpty(varVal) Or IsNull(varVal) Then GoTo NextVar
Dim strVal As String
strVal = Trim(CStr(varVal))
If Len(strVal) = 0 Then GoTo NextVar
' Write to file custom property
If WriteCustomProperty(swDoc, varName, strVal) Then
propCount = propCount + 1
End If
NextVar:
Next i
If propCount = 0 Then
MsgBox "No non-blank data card values found for the listed variables.", vbInformation
Exit Sub
End If
' Check out if needed, save, let the caller decide about check-in
Dim isCheckedOut As Boolean
isCheckedOut = IsFileCheckedOutByMe(oFile)
If Not isCheckedOut Then
' Check out the file
Dim oFiles As Object
Set oFiles = CreateObject("ConisioLib.EdmSelectionList5")
oFiles.AddFile oFile.ID, oFolder.ID
oVault.CheckOutFiles oFiles, 0, ""
End If
' Save
Dim saveErrors As Long, saveWarnings As Long
swDoc.Save3 swSaveAsOptions_e.swSaveAsOptions_Silent, saveErrors, saveWarnings
MsgBox propCount & " properties written from data card." & vbCrLf & _
"Check in the file to complete the sync.", vbInformation
End Sub
' Writes one custom property to the document's global configuration.
' Returns True if the property was written successfully.
Private Function WriteCustomProperty( _
swDoc As SldWorks.ModelDoc2, _
propName As String, _
propValue As String) As Boolean
WriteCustomProperty = False
Dim custMgr As SldWorks.CustomPropertyManager
Set custMgr = swDoc.Extension.CustomPropertyManager("")
' Add3: name, type, value, add option
' swCustomPropertyReplaceValue replaces existing; swCustomPropertyOnlyIfNew skips existing
Dim retVal As Long
retVal = custMgr.Add3(propName, _
swCustomInfoType_e.swCustomInfoText, _
propValue, _
swCustomPropertyAddOption_e.swCustomPropertyReplaceValue)
' Add3 returns swCustomInfoAddResult_e
' swCustomInfoAddResult_AddedOrChanged = 0 (success)
WriteCustomProperty = (retVal = swCustomInfoAddResult_e.swCustomInfoAddResult_AddedOrChanged)
End Function
' Returns True if the file is checked out by the current Windows user.
Private Function IsFileCheckedOutByMe(oFile As Object) As Boolean
IsFileCheckedOutByMe = False
On Error Resume Next
Dim lockUserName As String
lockUserName = oFile.LockedByUser.Name
If Err.Number <> 0 Then Exit Function
On Error GoTo 0
Dim currentUser As String
currentUser = Environ("USERNAME")
IsFileCheckedOutByMe = (LCase(lockUserName) = LCase(currentUser))
End Function
What Add3’s Return Value Means
CustomPropertyManager.Add3 returns a value from swCustomInfoAddResult_e:
| Value | Meaning |
|---|---|
swCustomInfoAddResult_AddedOrChanged (0) | Property written successfully |
swCustomInfoAddResult_GenericFail (1) | Write failed (document locked, read-only, etc.) |
swCustomInfoAddResult_NotPresentInConfigurationAndDocument (2) | Configuration-specific add failed |
If Add3 returns swCustomInfoAddResult_GenericFail, the most common cause is that the document is open read-only or is checked in without being checked out first.
Batch Version: Process All Files in a Vault Folder
The single-document version above works when running the macro manually from within a SolidWorks session. For processing a folder of supplier files without opening each one interactively, the vault object can iterate files without going through an active SolidWorks document:
Sub BatchSyncProperties()
' Variable list
CARD_VARIABLES = Array("Description", "Material", "PartNumber", "Revision")
Dim swApp As SldWorks.SldWorks
Set swApp = Application.SldWorks
swApp.Visible = False ' background session
Dim oVault As Object
Set oVault = CreateObject("ConisioLib.EdmVault5")
oVault.LoginAuto VAULT_NAME, 0
If Not oVault.IsLoggedIn Then Exit Sub
' Get the target folder
Dim targetFolderPath As String
targetFolderPath = "@\Incoming\Supplier_A" ' vault-relative path; @ = vault root
Dim oFolder As Object
Set oFolder = oVault.GetFolderFromPath(targetFolderPath)
If oFolder Is Nothing Then
MsgBox "Folder not found: " & targetFolderPath
Exit Sub
End If
' Iterate files in the folder
Dim oFilePos As Object
oFolder.GetFirstFilePosition oFilePos
Dim processed As Integer
processed = 0
Do While Not oFilePos.IsNull
Dim oFile As Object
Set oFile = oFolder.GetNextFile(oFilePos)
Dim fileName As String
fileName = oFile.Name
' Only process SolidWorks files
Dim ext As String
ext = LCase(Right(fileName, 7))
If ext <> ".sldprt" And ext <> ".sldasm" And ext <> ".slddrw" Then GoTo NextFile
' Collect data card values
Dim propPairs() As String
ReDim propPairs(UBound(CARD_VARIABLES))
Dim anyNonBlank As Boolean
anyNonBlank = False
Dim i As Integer
For i = 0 To UBound(CARD_VARIABLES)
Dim varVal As Variant
On Error Resume Next
varVal = oFile.GetVar(CStr(CARD_VARIABLES(i)), CONFIG_NAME)
On Error GoTo 0
If Not (IsEmpty(varVal) Or IsNull(varVal)) Then
Dim sv As String
sv = Trim(CStr(varVal))
If Len(sv) > 0 Then
propPairs(i) = sv
anyNonBlank = True
End If
End If
Next i
If Not anyNonBlank Then GoTo NextFile
' Check out and open
Dim checkOutFiles As Object
Set checkOutFiles = CreateObject("ConisioLib.EdmSelectionList5")
checkOutFiles.AddFile oFile.ID, oFolder.ID
oVault.CheckOutFiles checkOutFiles, 0, ""
Dim localPath As String
localPath = oFile.LocalPath
Dim swErrors As Long, swWarnings As Long
Dim swDoc As SldWorks.ModelDoc2
' Detect document type from extension
Dim docType As Long
ext = LCase(Right(localPath, 7))
If ext = ".sldprt" Then
docType = swDocumentTypes_e.swDocPART
ElseIf ext = ".sldasm" Then
docType = swDocumentTypes_e.swDocASSEMBLY
Else
docType = swDocumentTypes_e.swDocDRAWING
End If
Set swDoc = swApp.OpenDoc6(localPath, docType, _
swOpenDocOptions_e.swOpenDocOptions_Silent, "", _
swErrors, swWarnings)
If swDoc Is Nothing Then
' Failed to open — check back in without changes
Dim undoFiles As Object
Set undoFiles = CreateObject("ConisioLib.EdmSelectionList5")
undoFiles.AddFile oFile.ID, oFolder.ID
oVault.UndoCheckOutFiles undoFiles, 0
GoTo NextFile
End If
' Write custom properties
Dim custMgr As SldWorks.CustomPropertyManager
Set custMgr = swDoc.Extension.CustomPropertyManager("")
For i = 0 To UBound(CARD_VARIABLES)
If Len(propPairs(i)) > 0 Then
custMgr.Add3 CStr(CARD_VARIABLES(i)), _
swCustomInfoType_e.swCustomInfoText, _
propPairs(i), _
swCustomPropertyAddOption_e.swCustomPropertyReplaceValue
End If
Next i
' Save and close
swDoc.Save3 swSaveAsOptions_e.swSaveAsOptions_Silent, swErrors, swWarnings
swApp.CloseDoc localPath
' Check in
Dim checkInFiles As Object
Set checkInFiles = CreateObject("ConisioLib.EdmSelectionList5")
checkInFiles.AddFile oFile.ID, oFolder.ID
oVault.CheckInFiles checkInFiles, 0, "Synced file properties from data card"
processed = processed + 1
NextFile:
Loop
swApp.Visible = True
MsgBox "Batch complete. " & processed & " files updated."
End Sub
The vault-relative path @\Incoming\Supplier_A uses @ as a shorthand for the vault root. GetFolderFromPath resolves it to the correct local path given the vault mapping on this workstation.
Configuration-Specific Properties
Some teams use per-configuration custom properties so that a single part file carries different descriptions for each configuration (Default, Lightweight, Rev-B, etc.). The GetVar call supports this:
' Read variable for a specific configuration
Dim varVal As Variant
varVal = oFile.GetVar("Description", "Rev-B")
' Write to that configuration's custom property manager
Dim custMgr As SldWorks.CustomPropertyManager
Set custMgr = swDoc.Extension.CustomPropertyManager("Rev-B")
custMgr.Add3 "Description", swCustomInfoType_e.swCustomInfoText, _
CStr(varVal), swCustomPropertyAddOption_e.swCustomPropertyReplaceValue
Pass "" for the non-configuration-specific (global) card value, or the exact configuration name for a config-specific variable. If the variable is not linked to any specific configuration in PDM, it will return the same value for all configuration queries.
Why This Doesn’t Break the Data Card
The confusion about this sync direction is common. Here is why the approach above is safe:
After the macro runs, the file contains custom properties that match the data card values the admin entered. When the file is checked in, PDM’s override sync reads the file properties — which now contain the correct values — and writes them to the variable database. The variable database already had those values. The sync is a no-op.
If the admin later updates the data card after file check-in, the change sits in the variable database until the next time the file is checked out, modified, and checked back in. At that point the “file wins” sync would normally overwrite the card with the old file value. To prevent this, the team needs a Dispatch rule that runs on check-in and syncs the other direction — database to file — when needed. That is a separate workflow.
Related PDM Automation Patterns
The SOLIDWORKS PDM batch plot guide covers the IEdmTaskHandler interface for implementing full PDM task extensions in C#. The PDM task outside-vault PDF export guide covers a similar EdmLib-based batch pattern for exporting to paths outside the vault. If you are building multiple PDM automation workflows, the workflow automation decision guide covers when a custom PDM task extension is worth the overhead versus a simpler standalone macro.
Summary
The built-in “Add/Update Custom Properties” task overwrites data card values when the file has no existing custom properties, because PDM’s file-wins sync direction reads blank values from the file and writes them to the card on check-in.
The correct approach:
- Read data card values with
IEdmObject5::GetVar()before opening the file - Write those values to the file’s custom properties with
ICustomPropertyManager.Add3() - Save and check in — the sync now reinforces existing values rather than overwriting them
The full VBA implementation above handles both the single-document interactive case and batch processing across an entire vault folder, with proper checkout state detection and rollback on open failure.