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:

  1. The task writes the property based on its template (often blank or a hardcoded default)
  2. The file is saved with blank properties
  3. 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 Variant containing the stored value; check IsEmpty() and IsNull() 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:

ValueMeaning
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.

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:

  1. Read data card values with IEdmObject5::GetVar() before opening the file
  2. Write those values to the file’s custom properties with ICustomPropertyManager.Add3()
  3. 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.