Feb 28, 2017

From Macros to Add-Ins

At some point after creating and using macros regularly, you may begin to realize that deploying and sharing them with many users isn’t always easy.  You can share them across the network, or through your SOLIDWORKS PDM, but how do you make sure all users get the same layout for new buttons in the user interface?

Building SOLIDWORKS add-ins can make macro deployment much simpler across your teams, but it does mean making some slight modifications to your existing macros.  The great news is that you don’t have to start from scratch or import your existing code into an add-in.  SOLIDWORKS also provides a comprehensive add-in template through the SOLIDWORKS API SDK.  This can make the whole process pretty simple really.

The first assumption is that you’ll be using a Visual Studio development environment.  I will use Visual Studio 2015 for this example, but older versions will work as well.  There are free Express and Community editions of Visual Studio available for personal and educational use if you don’t have access to a full edition.

This example will cover three essential tasks:  tweaking your existing macros to work from the add-in, creating the add-in itself, referencing your macros, and finally, creating a deployment package (install files).

Tweak your Macro

A quick discussion about macros.  Most SOLIDWORKS macros out there are VBA macros written in Visual Basic and are saved in the SWP file format (SOLIDWORKS Project).  These shouldn’t need any adjustment to reference in an add-in.  However, the less-common, but more interesting VSTA macros (Visual Studio for Applications) will need some preparation work.  These are compiled as DLL files and can be written in either C# or VB.NET languages.  We’ll focus on how to prepare VSTA macros for add-ins first.

.NET Macro Adjustments

To make DLL macros with an external entry point, we need a new class with a public procedure that calls your desired code.  With VSTA macros, that can include arguments that come from the add-in.  In these examples, the SOLIDWORKS application interface is passed to the Run procedure as an argument.

VB

Imports SolidWorks.Interop.sldworks
Imports SolidWorks.Interop.swconst
Public Class App
  Public Sub Run(ByVal swApp As SldWorks)
    'processing code starts here

  End Sub
End Class

C#

using System.Collections.Generic;
using System.Text;
using SolidWorks.Interop.sldworks;
using SolidWorks.Interop.swconst;
namespace MakePart.csproj
{
  public class App
  {
    public void Run(ISldWorks swApp)
    {
        //process code starts here

    }
  }
}

Copy everything you had in your original main procedure to the new Run procedure.  Don’t forget necessary Imports or using statements.  The following are just examples of format, not any real macro code.

VB

Public Sub Run(ByVal swApp As SldWorks)
    Dim MyDialog As New Dialog1
    AddComponentsMod.m_swApp = swApp
    MyDialog.Show()
End Sub

C#

public void Run(ISldWorks swApp)
{
  ModelDoc2 swDoc = null;
  bool boolstatus = false;
  ...
  // Zoom To Fit
  swDoc.ViewZoomtofit2();
}

If you want your macro to still work stand-alone in SOLIDWORKS (and who wouldn’t), you need to call the Run procedure from your original main procedure.  Declare a variable as a new instance of your application class and call the new Run procedure in your main procedure.  This will be the same format you use to call your macro from your add-in later in the example.

VB

Public Sub main()
  Dim myMacro As New App
  myMacro.Run(swApp)
End Sub

C#

public void Main()
{
  App MyMacro = new App();
  MyMacro.Run(swApp);                       
}

The last thing you need to do is make the .NET macro COM-Visible by enabling the setting in Project Properties, Application, Assembly Information.



After making adjustments, build and run your macro as usual to make sure it still works stand-alone.

VBA Macro Information

Now, a quick discussion about VBA macros and how to call them.
For VBA macros, you need to know the macro name, the module name and the procedure, typically “main”.  The following macro is named MakePart.swp, its module is MakePart1 and the procedure name is main (not shown).


Create your Add-In

Visual Studio 2008 through 2015 will work.  This example will use 2015.  There are free express versions available or full versions for purchase.
Install the SOLIDWORKS API SDK – found here on your SOLIDWORKS DVD or in a full media kit download.

Install_media\api\SolidWorks API SDK.msi

If the SDK has been installed successfully, when you create a new project and select Visual Basic or C#, you will see the SwVBAddin (or SwCSharpAddin) available as a template.   Give it a good name to begin.




Code Structure of an Add-In


There are several code regions in the primary class SwAddin (same in VB and C#).  The most important are ISwAddin Implementation, UI Methods and UI Callbacks.

The two functions in the ISwAddin Implementation code section run when the add-in is turned on: ConnectToSW and when it is turned off or SOLIDWORKS is closed: DisconnectFromSW.

In this example, the event handlers and property manager pages are not needed, so it is just two procedures to focus on: AddCommandMgr and RemoveCommandMgr.  Nothing needs to be modified in RemoveCommandMgr.

AddCommandMgr is found within the UI Methods code section.  The procedure sets up the user interface by adding menu items, toolbars and a Command Manager tab.  It also initializes Icons and establishes “callback” functions.  This procedure also establishes which document types the command group will display under.  We will come back to this section shortly.

The UI Callbacks section is where you add your processing code.  To reference an existing .NET macro, add the macro as a reference to the project first, then call it.  Add a reference to the macro by selecting Project, Add Reference.  Browse to the compiled .NET macro dll.  After adding the reference, based on our example, call the Run procedure as shown.

VB

Sub MakePartMacro()
  Dim myMacro As New MakePart.vbproj.App
  myMacro.Run(SwApp)
End Sub

C#

public void MakePartMacro()
{
  MakePart.csproj.App myMacro = new MakePart.csproj.App;
  myMacro.Run(SwApp);
}


To reference a VBA macro, first add it to the add-in project by selecting Project, Add Existing Item.  Browse to the macro SWP file.  After adding it to the project, find it in the Project Explorer and set its properties as follows.  Build Action should be Content and Copy to Output Directory should be Copy if newer.


Call the VBA macro from the add-ins runtime folder by using the following.  This assumes the macro name, module name and procedure name explained above.

VB

Sub MakePartVBA()
  Dim myDir As String
  myDir = IO.Path.GetDirectoryName _
    (Assembly.GetExecutingAssembly.Location)
  Dim macroPath As String
  Dim errors As String
  macroPath = myDir & "\MakePart.swp"
  SwApp.RunMacro2(macroPath, "MakePart1", _
    "main", 1, errors)
End Sub

C#

public void MakePartVBA()
{
   string myDir;
   myDir = System.IO.Path.GetDirectoryName
      (Assembly.GetExecutingAssembly().Location);
   string macroPath;
   macroPath = myDir + "\\MakePart.swp";
   int errors;
   SwApp.RunMacro2(macroPath, "MakePart1", "main", 1, out errors);
}

The RunMacro2 method of SldWorks needs the following arguments: 1) the full path to the macro file, 2) the module name, 3) the procedure name, 4) run options, and 5) an output variable for error handling.  The example above assumes you have added the VBA macro to the add-in project in Visual Studio and that it will be copied to the installation location.

Now that you have your callback function for your macro, it is time to connect that function to the buttons in the user interface.  Within the “UI Methods” code region, inside the AddCommandMgr procedure, look for the two lines that create Command Items, or buttons.  The lines will start with the following code in both VB and C#.

cmdIndex0 = cmdGroup.AddCommandItem2(...)

The AddCommandItem2 method creates a command and associates it to a callback function.  It requires nine arguments, most of which relate to button labels, tooltips and icon.  For our example, the sixth argument is the most important.  Even though it is a string, it must be the exact name of the callback function that runs your macro.  For example, if the callback function name were MakePartVBA, the AddCommandItem2 call might look like the following.

cmdIndex0 = cmdGroup.AddCommandItem2("MakePart", -1, "Make a Part", _
    "Make a part from a VBA macro", 0, "MakePartVBA", "", _
    mainItemID1, menuToolbarOption)

Edit the two existing command items to run two different macros.  If you need more than two, you will need to also declare additional variables related to command items listed below.

Public Class variables: mainItemID1, mainItemID2, ... , mainItemIDn

AddCommandMgr procedure variables: cmdIndex0, cmdIndex1, ... , cmdIndexn

AddCommandMgr arrays: cmdIDs and TextType (you will need to modify the array size as well as continue to populate the arrays with your additional command items)

Test your Add-In

Now that you’ve made edits and included your macros, try out the new add-in.  You can run in Debug mode in Visual Studio and it will automatically register the add-in with SOLIDWORKS.  Hit Debug, Start Debugging to automatically launch a session of SOLIDWORKS with your new add-in.


For more information on installation and deployment options, check out my book, Automating SOLIDWORKS Using Macros 2017 for an in-depth chapter on the subject.

Feb 16, 2016

Extending SOLIDWORKS Interference Detection

Model c/o Malcom Ma


Have you ever run the SOLIDWORKS Interference Detection tool and thought, "If I could only save this list of interfering components?"

I was talking with a colleague who recently hit this problem square-on.  He wanted to work through hundreds of interference problems resulting from the lackluster design of a printed circuit board.  Here comes the SOLIDWORKS API to the rescue!

The fundamentals come from an API help example here.  Minor modifications were made to store the data being sent to the Debug window and save it to a text file.  Finally, to give the macro some real flair, adding the option to turn the interfering volumes into bodies in a part.

Rather than packing all of the code into the macro's main procedure, I've created new classes named InterferenceChecking and LocalInterferences.  These two classes have all of the routines needed and can be called from a separate application if you don't want to just run it as a macro.

Download the macro source code here (VB.NET)

Implement the new class like this.

Public Sub main()
    Dim model As ModelDoc2 = swApp.ActiveDoc
    Dim intCheck As New InterferenceChecking
    intCheck.CheckInterference(model, swApp)
    intCheck = Nothing

End Sub

The following is all code for the project, not including the calling reference.

Imports SolidWorks.Interop.sldworks
Imports SolidWorks.Interop.swconst
Imports System.Runtime.InteropServices
Imports System
Imports System.Diagnostics
Imports System.Windows.Forms

Public Class InterferenceChecking

    Public Sub CheckInterference(ByVal model As ModelDoc2, _
    ByRef swApp As SldWorks)
        Dim swModelDoc As ModelDoc2 = model
        Dim swAssemblyDoc As AssemblyDoc
        Dim pIntMgr As InterferenceDetectionMgr
        Dim vInts As Object
        Dim i As Long
        Dim j As Long
        Dim interference As IInterference
        Dim vComps As Object = Nothing
        Dim comp As Component2
        Dim vol As Double
        Dim vTrans As Object = Nothing
        Dim ret As Boolean

        Dim repMessage As String = "Interference report for: " _
        & model.GetTitle & vbCrLf

        swAssemblyDoc = swModelDoc
        pIntMgr = swAssemblyDoc.InterferenceDetectionManager

        ' Specify the interference detection settings and options
        pIntMgr.TreatCoincidenceAsInterference = False
        pIntMgr.TreatSubAssembliesAsComponents = True
        pIntMgr.IncludeMultibodyPartInterferences = True
        pIntMgr.MakeInterferingPartsTransparent = False
        pIntMgr.CreateFastenersFolder = True
        pIntMgr.IgnoreHiddenBodies = True
        pIntMgr.ShowIgnoredInterferences = False
        pIntMgr.UseTransform = True

        ' Specify how to display non-interfering components
        pIntMgr.NonInterferingComponentDisplay = _
        swNonInterferingComponentDisplay_e. _
        swNonInterferingComponentDisplay_Wireframe

        ' Run interference detection
        vInts = pIntMgr.GetInterferences
        Debug.Print("Total number of interferences: " _
        & pIntMgr.GetInterferenceCount)
        repMessage = repMessage _
  & "Total number of interferences: " _
        & pIntMgr.GetInterferenceCount & vbCrLf

        ' Get interfering components and transforms
        ret = pIntMgr.GetComponentsAndTransforms(vComps, vTrans)
        Dim bodies(0) As Object

        ' Get interference information
        For i = 0 To UBound(vInts)

            Debug.Print("Interference " & (i + 1))
            repMessage = repMessage & "Interference " & (i + 1) _
& vbCrLf
            interference = vInts(i)
            ReDim Preserve bodies(i)
            bodies(i) = interference.GetInterferenceBody()
            Debug.Print( _
"  Number of components in this interference: " _
            & interference.GetComponentCount)
            repMessage = repMessage _
            & "  Number of components in this interference: " _
            & interference.GetComponentCount & vbCrLf
            vComps = interference.Components
            For j = 0 To UBound(vComps)

                comp = vComps(j)
                Dim cName As String = comp.ComponentReference
                If cName = "" Then cName = comp.Name2
                Debug.Print("   " & cName)
                repMessage = repMessage & "   " & cName & vbCrLf
            Next j
            vol = interference.Volume
            Dim volMessage As String = _
            "  Interference volume: " _
            & Format((vol * 1000000000), "#.###") & " mm^3"
            Debug.Print(volMessage)
            repMessage = repMessage & volMessage & vbCrLf
        Next i

        ' Stop interference detection
        pIntMgr.Done()

        If MsgBox("Save report to file?", MsgBoxStyle.YesNo _
        + MsgBoxStyle.Question) = MsgBoxResult.Yes Then
            'write out the report
            WriteReport(repMessage, model.GetTitle & ".txt")
        End If

        If MsgBox("Create intereference volume part?" & vbCrLf _
        & "*Complex interference can take time.", _
        MsgBoxStyle.YesNo + MsgBoxStyle.Question) _
  = MsgBoxResult.Yes Then
            'Create a part with the bodies array
            Dim intf As New LocalInterferences(bodies)
            intf.CreateInterferingBodiesPart(swApp)
            intf = Nothing
        End If

    End Sub

    Private Sub WriteReport(ByVal repMessage As String, _
    Optional ByVal defaultName As String = "")
        Dim sfd As New SaveFileDialog
        sfd.DefaultExt = "txt"
        sfd.Filter = "Text file(*.txt)|*.txt"
        sfd.FileName = defaultName
        sfd.OverwritePrompt = True
        Dim diaRes As DialogResult = sfd.ShowDialog()
        If diaRes = DialogResult.OK Then
            Dim filePath As String = sfd.FileName
            My.Computer.FileSystem.WriteAllText(sfd.FileName, _
            repMessage, False)
            Process.Start(sfd.FileName)
        End If

    End Sub
End Class


Class LocalInterferences

  Dim intBodies As Object

  Friend Sub CreateInterferingBodiesPart( _
  ByRef swApp As SldWorks)
    If intBodies Is Nothing Then
      Exit Sub
    End If
    Dim part As PartDoc
    part = swApp.NewPart()
    For i As Integer = 0 To UBound(intBodies)
      Dim feat As Feature
      feat = part.CreateFeatureFromBody3(intBodies(i), _
      False, swCreateFeatureBodyOpts_e.swCreateFeatureBodyCheck _
      + swCreateFeatureBodyOpts_e.swCreateFeatureBodySimplify)
    Next i
  End Sub

  Public Sub New(ByVal bodies As Object)
    intBodies = bodies
  End Sub
End Class

Jan 26, 2016

SOLIDWORKS Document Manager API Toolkit


The SOLIDWORKS Document Manager is a powerful tool used for reporting content and manipulating SOLIDWORKS parts, assemblies and drawings.  I’ve used it to export custom properties from thousands of SOLIDWORKS files in seconds!  It can be used to read bill of materials and other tables from drawings as well as update references when files are renamed.

But, just like any other project, it can take time to get started and get familiar with the calls needed.  To make that all easier, I’ve put together a Visual Studio 2010 project in VB.NET I’m calling the Document Manager Toolkit.

I’ll be presenting the toolkit along with a general discussion of the SOLIDWORKS Document Manager at SOLIDWORKS World 2016 in Dallas, TX next week.  Hope to see you there!

“What’s in the toolkit?” you ask?  Download the project here for a sample implementation and read up on the general functionality below.

Document Manager Toolkit Download
Document Manager Presentation PDF (SOLIDWORKS World 2016)

MyDocMan Class


The MyDocMan class is the basis for the toolkit.  It takes care of license validation and a plethora of common operations shared below.  The functions and procedures are documented in the project to make it easier to use in your own tools.

Instantiating the class will connect to the SOLIDWORKS Document Manager and is built to read your Document Manager license from a text file named “DocMgrLicense.txt” from C:\.  Edit the GetDocManApp procedure to insert and compile your license, redirect the license file location, or point to license information saved in My.Settings.

Common Operations



GetDocument

Summary:
From a full file path, return an opened SwDMDocument.

Parameters:
FilePath: full path to the desired SOLIDWORKS file
forReadOnly: True to open the file read-only, False to open with write access.

Return Values:
Returns an SwDMDocument interface if successful.

GetAllProperties

Summary:
Function to read all file and configuration specific custom properties from a document.

Parameters:
dmDoc: Document Manager document

Return Values:
A tab-delimited string containing all configuration names, properties and values. An empty string in the configuration column indicates a file property.

Remarks:
Each line in the string will terminate in a carriage return character.

GetDelimitedTableText

Summary:
Get all text from a named table with the desired delimiter.

Parameters:
tableName: Name of the table in the SOLIDWORKS file
dmDoc: the SwDMDocument10 interface containing the table
delimiter: the desired column delimiter

Return Values:
A string representing the text of the entire table, including headers. Return characters are applied between rows.

GetBOMTableNames

Summary:
Gets all table names from a document.

Parameters:
dmDoc: The desired document containing tables.

Return Values:
An array of strings in an Object.

Remarks:
Use this function before calling GetDelimitedTableText to retrieve a valid BOM table name.

GetMissingReferences

Summary:
Recursive routine to report all missing file references.

Parameters:
dmDoc: Parent document of reported children, typically an assembly or drawing.

Remarks:
Reports back a string of missing reference files separated by a return character.

File Operations


BrowseForFile
GetFilesFromPath

BrowseForFile

Summary:
Browse for a SOLIDWORKS file using the Windows OpenFileDialog

Return Values:
Returns the full path to the selected file.

GetFilesFromPath

Summary:
Get all file paths from a user-selected directory.

Parameters:
extensionFilter: a file extension to filter. "*.sld*" would retrieve all SOLIDWORKS files.
includeSubFolders: Set to True to get files from all sub folders

Return Values:
Returns an array of full file paths.

Remarks:
Temp files, ~$Part1.sldprt, should be filtered from your results before processing.

Dec 28, 2015

Common VBA Procedures for Batch Export

Try as Microsoft may, it doesn’t look like VBA is going away anytime soon!  And really, it’s a great place to start developing macros for SOLIDWORKS.  The macros are single-file, self-contained, and easy to develop.  Not to mention that there are thousands of existing VBA macros out there and lots of sample code online. 

The downside?  VBA doesn’t have as many pre-built functions for common file operations available to C# and VB.net. 

In an effort to make your macro development easier, I’ve put together a handful of common routines and functions you can add to your SOLIDWORKS VBA macros.  If there are others you’d like to see, let me know!

Batch processing of files is one of those places where you need to manipulate file information.  Rather than packing all of the processing into one routine, I have broken it out into common functions so you can save them into a code module to use in all of your macros.

Macro Code

Start by adding the following procedures and functions to your VBA macro.

Option Explicit
Dim swApp As SldWorks.SldWorks

'batch process using Dir - one folder level only
Sub BatchConvert(folderPath As String, _
    fileNameFilter As String, _
    convertExtension As String, _
    Optional outputFolderPath As String)
   
    Dim file As String
    'use Dir function to get all files matching the filter
    'in the specific folder
    file = Dir(folderPath & fileNameFilter)
    Do While Not file = ""
        Dim filePath As String
        filePath = folderPath & file
        Dim newFilePath As String
        newFilePath = ConvertSWFile(filePath, convertExtension, _
            outputFolderPath)
        'get the next file
        file = Dir
    Loop
End Sub

'batch process using FileSystemObject - all sub folders included
'must add a reference to Microsoft Scripting Runtime type library
Sub BatchConvert2(folderPath As String, _
    fileNameFilter As String, _
    convertExtension As String, _
    Optional doSubFolders As Boolean = False, _
    Optional outputFolderPath As String)
   
    Dim file As String
    Dim fso As New FileSystemObject
    Dim fld As Scripting.Folder
    Set fld = fso.GetFolder(folderPath)
    Dim f As Scripting.file
    For Each f In fld.files()
        'wildcards not allowed in fileNameFilter when using
        'FileSystemObject methods
        If InStr(1, f.Name, fileNameFilter, _
        vbTextCompare) <> 0 Then
            Dim newFilePath As String
            newFilePath = ConvertSWFile(f.Path, _
                convertExtension, outputFolderPath)
        End If
    Next

    'recursive routine if doSubFolders is True
    If doSubFolders Then
        Dim subFolder As Scripting.Folder
        For Each subFolder In fld.subFolders
            Call BatchConvert2(subFolder.Path, fileNameFilter, _
            convertExtension, doSubFolders, outputFolderPath)
        Next
    End If
End Sub

'open the file in SOLIDWORKS and convert by changing 
'the file extension. new file will be created in the 
'same folder as the parent file
'or, pass an outputFolderPath string to send files to 
'a specific folder
Function ConvertSWFile(filePath As String, _
    convertExtension As String, _
    Optional outputFolderPath As String) As String
   
    Dim model As ModelDoc2
    Dim fileType As Long
    fileType = GetSWFileType(filePath)
    Dim errors As Long, warnings As Long
   
    Set model = swApp.OpenDoc6(filePath, _
        fileType, swOpenDocOptions_Silent, _
        "", errors, warnings)
   
    If model Is Nothing Then
        ConvertSWFile = ""
        Exit Function
    End If
   
    Dim newFilePath As String

    newFilePath = ChangeExtension(filePath, convertExtension)
    If outputFolderPath <> "" Then
        newFilePath = ChangeFolderPath(newFilePath, _
        outputFolderPath)
    End If
   
    Call model.Extension.SaveAs(newFilePath, _
        swSaveAsCurrentVersion, swSaveAsOptions_Silent, _
        Nothing, errors, warnings)
   
    Call swApp.CloseDoc(filePath)
   
    ConvertSWFile = newFilePath
End Function


'return the file type from a full file path
'helpful for opening various SOLIDWORKS files
Function GetSWFileType(filePath As String) As Long
    Dim ext As String
    Dim fileType As Long
    ext = Right(filePath, Len(filePath) - InStrRev(filePath, _
        ".") + 1)
    Select Case UCase(ext)
        Case ".SLDPRT"
            fileType = swDocPART
        Case ".SLDASM"
            fileType = swDocASSEMBLY
        Case ".SLDDRW"
            fileType = swDocDRAWING
        Case Else
            fileType = swDocNONE
    End Select
    GetSWFileType = fileType
End Function

'============ File path manipulation ==================

'change the extension of a file from a given file path
Function ChangeExtension(filePath As String, _
newExtension As String) As String
    Dim filePathNoExt As String
    filePathNoExt = Left(filePath, InStrRev(filePath, ".") - 1)
    ChangeExtension = filePathNoExt & newExtension
End Function

'change the folder path of the file
Function ChangeFolderPath(filePath As String, _
newFolderPath As String) As String
    Dim fileName As String
    fileName = Right(filePath, _
        Len(filePath) - InStrRev(filePath, "\"))
    ChangeFolderPath = newFolderPath & fileName
End Function

'verify the path ends with \
Function VerifyFolderPath(folderPath As String) As String
    If InStrRev(folderPath, "\") <> Len(folderPath) Then
        VerifyFolderPath = folderPath & "\"
    Else
        VerifyFolderPath = folderPath
    End If
End Function

'verify folder exists
Function VerifyFileFolderExists(fullPath As String) As Boolean
    Dim res As String
    res = Dir(fullPath, vbDirectory)
    'result will be some value if the folder exists
    If res = vbNullString Or fullPath = "" Then
        VerifyFileFolderExists = False
    Else
        VerifyFileFolderExists = True
    End If
End Function

'============ Other utilities ===================

'open any file in its associated application
Sub OpenFile(ByVal filePath As String)
   Dim WshShell As Object
   Set WshShell = CreateObject("WScript.Shell")
   'append quotes in case of spaces in file path
   WshShell.Run Chr(34) & filePath & Chr(34)
End Sub


'show a folder browser dialog and return the selected folder path
'or empty string if cancelled
Function BrowseFolder(message As String) As String
    Dim folderBrowser As Object
    Dim options As Integer
    options = 40 'UI with New Folder button and right-click menus
    'options = 0 'UI without New Folder button 
    'and no right-click menus
    Set folderBrowser = CreateObject("Shell.Application"). _
        BrowseForFolder(0, message, options)
   
    On Error Resume Next
    BrowseFolder = folderBrowser.Self.Path
    On Error GoTo 0
End Function

Usage

The following main procedure makes use of most of these procedures and functions to publish SOLIDWORKS files to PDF in a selected directory.

Sub main()

    Set swApp = Application.SldWorks
   
    Dim folderPath As String
    'browse for folder
    folderPath = BrowseFolder("Select folder to process...")
    folderPath = VerifyFolderPath(folderPath)
    If Not VerifyFileFolderExists(folderPath) Then Exit Sub

    Dim convertExtension As String
    convertExtension = ".PDF"
   
    'if using Dir function to get files using wildcards
    Dim fileNameFilter As String
    fileNameFilter = _
    InputBox("Enter the file name filter (*.sldprt, *.sld*):")
   
    Dim outputFolderPath As String
    ''optionally, set an output folder path
    'outputFolderPath = _
    'BrowseFolder("Select destination folder...")
    'outputFolderPath = VerifyFolderPath(outputFolderPath)
    Call BatchConvert(folderPath, fileNameFilter, _
        convertExtension, outputFolderPath)
   
    ''or if using FileSystemObject to process all sub folders...
    'fileNameFilter = _
    'InputBox("Enter the file name filter " _
    '& "(sldprt or 1234.slddrw):")
    'Call BatchConvert2(folderPath, fileNameFilter, _
    '    convertExtension, True)
   
    MsgBox "Finished!"
End Sub

Descriptions

BatchConvert uses the Dir function to get all files in a given folder and allows wildcards in the file name filter.  This method doesn’t support processing of sub-folders.  

BatchConvert2 allows processing of sub-folders, but doesn’t allow wildcards because of its use of the FileSystemObject.  Either routine must be passed the desired conversion extension, including the period (.PDF in this example).  Optionally, you can pass an output folder path as a final argument.  If an output folder is specified, all published files go to that folder.  If no output folder path is passed, the files are created in the same location as their source files.

ConvertSWFile is called for each file path.  This does all the heavy lifting of opening the file in SOLIDWORKS, changing the file extension and optional output folder path, saving the new output file and closing the source file.  It returns the newly created file name if you need to log the results.

File Path Manipulation

There are a few functions to help with common operations like changing file extensions, changing folder paths as well as verifying a folder path string is complete and verifying if a file or folder exists.

Other Utilities

The last section includes OpenFile, a routine to open any file in its associated application.  This is triggering the Windows shell, so it is just like double-clicking on a file in Windows Explorer.  It does not return an interface to the opened file and does not wait until the operation is complete. 

BrowseFolder is a function that launches a folder browser and returns the user-selected folder path or an empty string if cancelled. 


I hope you find these helpful in your next macro project!  Let me know if there are others you’d like to see or share.