7 Replies Latest reply on Oct 22, 2018 3:49 PM by Tom Sowers

    How to Import/convert files in Parallel

    Thiago Motta

      Hello,

       

      I'm still pretty new to Solidworks and its API, but I've already managed to write a code to import parasolid files and export them to a different format with a specified configuration, which is working great!

       

      Now I'd like to do the same for lots of file and since I've noticed Solidworks doesn't seem to be multi threaded when loading a file (at least not a parasolid file), my first attempt was to create multiple Solidworks instances, each loading a new file and exporting it. Although this does solve my problem, I'm left with a few warning messages displaying something about another instance of Solidworks already running, which doesn't annoy me much. After some tests, this solution seems to be about 3x faster than a single instance/thread one.

       

      With that solution I can run several instances of Solidworks on the background (up to 9 I think), with no UI at all. But, lets be honest, this is a terribly ugly solution.

       

      Is it possible to solve this issue with a single instance of Solidworks and multiple threads? Perhaps creating several sessions and issuing the 'save as' command on different tasks? I didn't find much reference to multi threading on the forums or how to have multiple sessions on a single instance...

       

      Also, if a single instance solution isn't possible, is there a way to suppress that warning message?

       

      So, just to round up my question: Id like to write a code to import a bunch of parasolid files and convert them to STL, all in parallel and using a single instance of Solidworks. Is that possible?

       

      Thank you all for your help! =)

        • Re: How to Import/convert files in Parallel
          Artem Taturevych

          As you have already noted SOLIDWORKS is single threaded, so the only way to do parallel is to have multiple instances. How do you start your sw instance? If you are running .exe application directly via process (which I assume you are as I do not know of other way of start another instance). You can try /m switch when starting and if you have SW Task Scheduler installed it will run sw in pure background mode. Also, you can actually ignore this message about journal file, it is not blocking the thread and your model still will be converted without any user interaction.

            • Re: How to Import/convert files in Parallel
              Thiago Motta

              Im using the helper methods from How to open second SolidWorks from C# (which is, as you said, being run directly via process) along with the following code to spawn new instances:

               

              SldWorks.SldWorks swApp = GetSolidworks.Solidworks( );
              swApp.UserControl = false;
              swApp.Visible = false;
              swApp.UserControlBackground = true;
              swApp.EnableBackgroundProcessing = true;
              swApp.EnableFileMenu = false;
              

               

              Oh great! The /m switch gets rid of the splashscreen, if only I could get rid of that warning message... even though its harmless, it would be better to just close it then having 9 instances of it laying around. Guess I'll have to resort to Microsoft API to capture the dialog and try to close it. I was also thinking on trying to change the journal file path for each instance, which should solve this also.

            • Re: How to Import/convert files in Parallel
              Tom Sowers

              As far as the journal message this worked for me

              How to Close the journal file pop up window

              Then i just made a folder for each instance that needed a journal file.

                • Re: How to Import/convert files in Parallel
                  Thiago Motta

                  I ended up having all sorts of problem when dealing with solidworks in parallel. Here is my personal experience with it:

                   

                  - It can be messy to start multiple solidworks instances simultaneously. It can mess with the COM interface (generic bugs happens, ("Exception from HRESULT: 0x80010105 (RPC_E_SERVERFAULT)", "COM object that has been separated from its underlying RCW cannot be used.")), the journal, and probably some more. Even starting them with a delay from each other cant keep undesired results from happening. In my case I ended up having to use try-catch and multiple tries to ensure Solidworks was starting correctly and had enough time to properly start.

                  - I'm running on a Intel Core i7-8700k (6 core, 12 threads). The maximum number of Solidworks instances I was able to run simultaneously was 8. More than that and everything starts crashing, so keeping a maximum of 1 instance per core seems to be the best case scenario here.

                  - When running multiple instances, its common to have Solidworks randomly crashing after some operations (I'm mainly loading a file, removing its internal parts, simplifying it and exporting to a different format). So yeah, I had to do multiple retries throughout the code to restart a crashing instance if needed. (Never figured out or could pinpoint the exact crashing reason). Also, I'm figuring that because of the COM interface, if two or more Solidworks instances starts the same method at the same time, then thats enough to crash them.

                  - Journals can be a bit annoying and I ended up using the same approach as Tom Sowers, Getting the RegistryKey and setting it to a local path WITH a random number generator to ensure each instance will always have a unique journal attached to it. Still, the journal warning dialog can STILL appear. No idea why.

                  - Even after hiding Solidworks UI with different approaches, from time to time I'll still get some UI to show and/or some warning dialogs saying "SOLIDWORKS cannot startup because the system desktop application resources have been exceeded" (even though I'm only using at most 40% of my RAM, but 100% of the CPU) . This message doesn't seem to have any consequences.

                  - I never tried to run different versions of Solidworks at the same time, but still I decided to ensure the latest version was used.

                  - Since the user can close the application at any time and with system related methods, I had to develop a method to kill all existent Solidworks instances if needed.

                   

                  I cant say I had the best time possible going through all of that and can't help but leave out with a feeling that there should be an easier method to handle with all of this. I also can't say I'd recommend this approach to many people as I couldn't trace several bugs/crashes that happened. My advice is that if you need to run multiple Solidworks instances, do think a lot ahead of what you're doing as it can be really frustrating at times and it's common to have something simple taking hours to be achieved simply because of the COM interface or because Solidworks doesn't really seem to be thread safe.

                   

                  Attached below is the code I'm using to start Solidworks with everything I've learned so far:

                   

                  using System;
                  using System.Collections.Generic;
                  using System.Diagnostics;
                  using System.IO;
                  using System.Linq;
                  using System.Runtime.InteropServices;
                  using System.Runtime.InteropServices.ComTypes;
                  using System.Text;
                  using System.Threading.Tasks;
                  using Microsoft.Win32;
                  using SldWorks;
                  using SolidWorks.Interop.swdocumentmgr;
                  using SwConst;
                  using Environment = System.Environment;
                  
                  
                  public static class SolidWorksManager
                  {
                      private static int _instanceCounter = 0;
                      private const string _baseJournalFolderName = "JournalFolder-";
                  
                  
                      /// <summary>
                      /// The name of the SolidWorks exe
                      /// </summary>
                      private const string _solidWorksFilename = "SLDWORKS.exe";
                  
                  
                      private static string _solidWorksPath = "";
                  
                  
                      private static Random _rnd = new Random();
                  
                  
                      public static SldWorks.SldWorks Solidworks( int waitTime = 5000 )
                      {
                          System.Diagnostics.Process SolidWorks = null;
                          if ( string.IsNullOrEmpty( _solidWorksPath ) )
                              LocateSolidWorks( "" );
                  
                  
                          SolidWorks = System.Diagnostics.Process.Start( _solidWorksPath, "/m" );
                          //You can try /m switch when starting and if you have SW Task Scheduler installed it will run sw in pure background mode.
                          //https://forum.solidworks.com/create-advanced-comment.jspa?draftID=802298
                  
                  
                          if ( SolidWorks == null )
                              return null;
                  
                  
                          int retryCounter = 0;
                          while ( retryCounter < 10 )
                          {
                              SldWorks.SldWorks swApp = null;
                              System.Threading.Thread.Sleep( waitTime );
                  
                  
                              int ID = SolidWorks.Id;
                  
                  
                              try
                              {
                                  swApp = ( SldWorks.SldWorks )
                                      ROTHelper.GetActiveObjectList( ID.ToString( ) )
                                  .Where(
                                      keyvalue => ( keyvalue.Key.ToLower( ).Contains( "solidworks" ) ) )
                                  .Select( keyValue => keyValue.Value )
                                  .First( );
                              }
                              catch
                              {
                              }
                  
                  
                              if ( swApp == null )
                              {
                                  retryCounter++;
                                  string message = "Failed to open Solidworks. Retrying for the " + retryCounter + "th time.";
                                  if ( Task.CurrentId == null )
                                      Console.WriteLine( message );
                                  else
                                      Logger.WriteLog( message );
                              }
                              else
                              {
                                  return swApp;
                              }
                          }
                          return null;
                      }
                  
                  
                      private static bool _SWExists(string swPath )
                      {
                          return !string.IsNullOrEmpty( swPath ) && File.Exists( swPath ) && swPath.Contains( _solidWorksFilename );
                      }
                  
                  
                      public static void LocateSolidWorks( string swPath )
                      {
                          if ( !_SWExists( swPath ) && !string.IsNullOrEmpty( swPath ) )
                          {
                              string [ ] files = null;
                  
                  
                              try
                              {
                                  if ( Utils.IsDirectory( swPath ) && !swPath.EndsWith( "\\" ) )
                                      swPath += "\\";
                  
                  
                                  files = Directory.GetFiles( Path.GetDirectoryName( swPath ), _solidWorksFilename, SearchOption.AllDirectories );
                              }
                              catch { }
                              
                              if ( files == null || files.Length == 0 )
                              {
                                  Console.WriteLine( "Couldnt find " + _solidWorksFilename + " at " + swPath + ". Searching for it at the system..." );
                                  swPath = "";
                              }
                              else
                              {
                                  for ( int i = 0; i < files.Length; i++ )
                                  {
                                      if ( _SWExists( files [ i ] ) )
                                      {
                                          swPath = files [ i ];
                                          break;
                                      }
                                  }
                              }
                          }
                  
                  
                          if ( _SWExists( swPath ) )
                          {
                              _solidWorksPath = swPath;
                              return;
                          }
                  
                  
                          if( _SWExists( DefaultSolidWorksPath() ) )
                          {
                              _solidWorksPath = DefaultSolidWorksPath();
                              return;
                          }
                  
                  
                  
                  
                          IEnumerable<KeyValuePair<string, string>> SWVersionAndPaths = FindSolidWorksPath( );
                  
                  
                          foreach ( var versionAndPaths in SWVersionAndPaths )
                          {
                              string exePath = versionAndPaths.Value + _solidWorksFilename;
                  
                  
                              if ( _SWExists( exePath ) )
                              {
                                  _solidWorksPath = exePath;
                                  Console.WriteLine( "Found a valid SolidWork instalation at " + _solidWorksPath );
                  
                  
                                  return;
                              }
                          }
                  
                  
                          if ( string.IsNullOrEmpty( _solidWorksPath ) )
                          {
                              throw new Exception( "Couldnt find a valid SolidWorks instance in the system. Please specify a valid path that has " + _solidWorksFilename + " with the \"-w\" option " );
                          }
                      }
                  
                  
                      /// <summary>
                      /// Gets the latest SolidWorks installation
                      /// </summary>
                      /// <returns></returns>
                      private static IEnumerable<KeyValuePair<string, string>> FindSolidWorksPath( )
                      {
                          Microsoft.Win32.RegistryKey LocalMachineRegistryKey32 = Microsoft.Win32.RegistryKey.OpenBaseKey( Microsoft.Win32.RegistryHive.LocalMachine, Microsoft.Win32.RegistryView.Registry32 );
                          Microsoft.Win32.RegistryKey LocalMachineRegistryKey64 = Microsoft.Win32.RegistryKey.OpenBaseKey( Microsoft.Win32.RegistryHive.LocalMachine, Microsoft.Win32.RegistryView.Registry64 );
                  
                  
                          DateTime now = DateTime.Today;
                  
                  
                  
                  
                          for ( int i = now.Year + 2; i >= 2012; i-- )
                          {
                              Microsoft.Win32.RegistryKey SolidWorksSetupRegistryKey;
                  
                  
                              SolidWorksSetupRegistryKey = LocalMachineRegistryKey64.OpenSubKey( @"SOFTWARE\SolidWorks\SolidWorks " + i.ToString( ) + @"\Setup" );
                              if ( SolidWorksSetupRegistryKey != null )
                              {
                                  string Path = ( string )SolidWorksSetupRegistryKey.GetValue( "SolidWorks Folder" );
                                  if ( Path != null )
                                      yield return new KeyValuePair<string, string>( "SolidWorks " + i.ToString( ), Path );
                              }
                  
                  
                              SolidWorksSetupRegistryKey = LocalMachineRegistryKey32.OpenSubKey( @"SOFTWARE\SolidWorks\SolidWorks " + i.ToString( ) + @"\Setup" );
                              if ( SolidWorksSetupRegistryKey != null )
                              {
                                  string Path = ( string )SolidWorksSetupRegistryKey.GetValue( "SolidWorks Folder" );
                                  if ( Path != null )
                                      yield return new KeyValuePair<string, string>( "SolidWorks " + i.ToString( ), Path );
                              }
                          }
                      }
                  
                  
                  
                  
                      /// <summary>
                      /// Exits SolidWorks running in the background
                      /// </summary>
                      /// <param name="swApp"></param>
                      public static void Exit( ref SldWorks.SldWorks swApp )
                      {
                          if ( swApp != null )
                          {
                              swApp.ExitApp( );
                              Marshal.ReleaseComObject( swApp );
                              Marshal.FinalReleaseComObject( swApp );
                              swApp = null;
                          }
                      }
                  
                  
                      public static void ClearJournalFiles( )
                      {
                          foreach ( string d in Directory.GetDirectories( System.Environment.CurrentDirectory, _baseJournalFolderName + "*" ) )
                              Directory.Delete( d, true );
                      }
                  
                  
                      public static void KillAllSolidInstances( )
                      {
                          bool killedAll = false;
                          Process [ ] instances;
                          while ( !killedAll )
                          {
                              instances = Process.GetProcessesByName( "SLDWORKS" );
                              if ( instances.Length == 0 )
                              {
                                  killedAll = true;
                                  break;
                              }
                  
                  
                              foreach ( var process in instances )
                              {
                                  try
                                  {
                                      process.Kill( );
                                  }
                                  catch { };
                  
                  
                              }
                          }
                      }
                  
                  
                      private static void _CreateNewJournal( )
                      {
                          RegistryKey myKey = Registry.CurrentUser.OpenSubKey( "Software\\Solidworks\\SOLIDWORKS 2018\\ExtReferences", true );
                  
                  
                          if ( myKey != null )
                          {
                              string journalFolder = _baseJournalFolderName + ++_instanceCounter + "_" + _rnd.Next(147, 15874238); //O random esta aqui para garantir que ao abrir duas instancias desse executavel, os journals ainda possam ser criados
                              if ( Directory.Exists( journalFolder ) )
                                  Directory.Delete( journalFolder, true );
                              Directory.CreateDirectory( journalFolder );
                              myKey.SetValue( "SolidWorks Journal Folders", journalFolder, RegistryValueKind.String );
                              myKey.Close( );
                          }
                      }
                  
                  
                      /// <summary>
                      /// Cria instancias do SolidWorks com opcoes já predefinidas
                      /// </summary>
                      /// <param name="outputQuality"></param>
                      /// <returns></returns>
                      public static SldWorks.SldWorks CreateSldWorksInstance( swSTLQuality_e outputQuality, bool exportToMultiple = false, bool setToPositiveSpace = false, int timeout = 5000 )
                      {
                          _CreateNewJournal( );
                  
                  
                          SldWorks.SldWorks swApp = SolidWorksManager.Solidworks( timeout );
                          if ( swApp == null )
                              return null;
                  
                  
                          swApp.UserControl = false;
                          swApp.Visible = false;
                          swApp.UserControlBackground = true;
                          swApp.EnableBackgroundProcessing = true;
                          swApp.EnableFileMenu = false;
                  
                  
                          SetOutputPreferences( swApp, outputQuality, exportToMultiple, setToPositiveSpace);
                  
                  
                          return swApp;
                      }
                      /// <summary>
                      /// Seta as configuracoes usadas para exportar um arquivo STL e SLDPRT
                      /// </summary>
                      /// <param name="swApp"></param>
                      /// <param name="outputQuality"></param>
                      public static void SetOutputPreferences( SldWorks.SldWorks swApp, swSTLQuality_e outputQuality, bool exportToMultiple, bool setToPositiveSpace)
                      {
                          swApp.SetUserPreferenceToggle( ( int )swUserPreferenceToggle_e.swSTLBinaryFormat, true ); //Output as - Binary
                          swApp.SetUserPreferenceIntegerValue( ( int )swUserPreferenceIntegerValue_e.swExportStlUnits, ( int )swLengthUnit_e.swMETER ); //Using METERS unit system
                          swApp.SetUserPreferenceIntegerValue( ( int )swUserPreferenceIntegerValue_e.swSTLQuality, ( int )outputQuality ); //Resolution
                          swApp.SetUserPreferenceToggle( ( int )swUserPreferenceToggle_e.swSTLShowInfoOnSave, false ); //Resolution - Show STL info before file saving
                          swApp.SetUserPreferenceToggle( ( int )swUserPreferenceToggle_e.swSTLPreview, false );//Resolution - Preview before saving file
                          swApp.SetUserPreferenceDoubleValue( ( int )swUserPreferenceDoubleValue_e.swViewTransitionHideShowComponent, 0 );
                          swApp.SetUserPreferenceToggle(( int )swUserPreferenceToggle_e.swSTLDontTranslateToPositive, !setToPositiveSpace); //Do not translate STL output data to positive space
                          swApp.SetUserPreferenceToggle(( int )swUserPreferenceToggle_e.swSTLComponentsIntoOneFile, true); //Save all components of an assembly in a single file
                  
                  
                          swApp.SetUserPreferenceIntegerValue( ( int )swUserPreferenceIntegerValue_e.swSaveAssemblyAsPartOptions, ( int )swSaveAsmAsPartOptions_e.swSaveAsmAsPart_ExteriorFaces ); //Save SLDPRT as ExteriorFaces only
                      }
                  
                  
                      /// <summary>
                      /// The default SolidWorks installation Path
                      /// </summary>
                      /// <returns></returns>
                      public static string DefaultSolidWorksPath( )
                      {
                          return @"C:\Program Files\SolidWorks Corp\SolidWorks\SLDWORKS.exe";
                      }
                  
                  
                  
                  
                  }
                  
                  
                  /// <summary>  
                  /// The COM running object table utility class.  
                  /// </summary>  
                  class ROTHelper
                  {
                      #region APIs  
                  
                  
                      [DllImport( "ole32.dll" )]
                      private static extern int CreateBindCtx( int reserved, out IBindCtx ppbc );
                  
                  
                      [DllImport( "ole32.dll", PreserveSig = false )]
                      private static extern void CLSIDFromProgIDEx( [MarshalAs( UnmanagedType.LPWStr )] string progId, out Guid clsid );
                  
                  
                      [DllImport( "ole32.dll", PreserveSig = false )]
                      private static extern void CLSIDFromProgID( [MarshalAs( UnmanagedType.LPWStr )] string progId, out Guid clsid );
                  
                  
                      [DllImport( "ole32.dll" )]
                      private static extern int ProgIDFromCLSID( [In( )]ref Guid clsid, [MarshalAs( UnmanagedType.LPWStr )]out string lplpszProgID );
                  
                  
                      #endregion
                  
                  
                      #region Public Methods  
                  
                  
                      /// <summary>  
                      /// Converts a COM class ID into a prog id.  
                      /// </summary>  
                      /// <param name="progID">The prog id to convert to a class id.</param>  
                      /// <returns>Returns the matching class id or the prog id if it wasn't found.</returns>  
                      public static string ConvertProgIdToClassId( string progID )
                      {
                          Guid testGuid;
                          try
                          {
                              CLSIDFromProgIDEx( progID, out testGuid );
                          }
                          catch
                          {
                              try
                              {
                                  CLSIDFromProgID( progID, out testGuid );
                              }
                              catch
                              {
                                  return progID;
                              }
                          }
                          return testGuid.ToString( ).ToUpper( );
                      }
                  
                  
                      /// <summary>  
                      /// Converts a COM class ID into a prog id.  
                      /// </summary>  
                      /// <param name="classID">The class id to convert to a prog id.</param>  
                      /// <returns>Returns the matching class id or null if it wasn't found.</returns>  
                      public static string ConvertClassIdToProgId( string classID )
                      {
                          Guid testGuid = new Guid( classID.Replace( "!", "" ) );
                          string progId = null;
                          try
                          {
                              ProgIDFromCLSID( ref testGuid, out progId );
                          }
                          catch ( Exception )
                          {
                              return null;
                          }
                          return progId;
                      }
                  
                  
                      /// <summary>  
                      /// Get a snapshot of the running object table (ROT).  
                      /// </summary>  
                      /// <param name="filter">The filter to apply to the list (nullable).</param>  
                      /// <returns>A hashtable of the matching entries in the ROT</returns>  
                      public static Dictionary<string, object> GetActiveObjectList( string filter )
                      {
                          Dictionary<string, object> result = new Dictionary<string, object>( );
                  
                  
                          IntPtr numFetched = new IntPtr( );
                          IRunningObjectTable runningObjectTable;
                          IEnumMoniker monikerEnumerator;
                          IMoniker [ ] monikers = new IMoniker [ 1 ];
                  
                  
                          IBindCtx ctx;
                          CreateBindCtx( 0, out ctx );
                  
                  
                          ctx.GetRunningObjectTable( out runningObjectTable );
                          runningObjectTable.EnumRunning( out monikerEnumerator );
                          monikerEnumerator.Reset( );
                  
                  
                          while ( monikerEnumerator.Next( 1, monikers, numFetched ) == 0 )
                          {
                              string runningObjectName;
                              monikers [ 0 ].GetDisplayName( ctx, null, out runningObjectName );
                  
                  
                              if ( filter == null || filter.Length == 0 || runningObjectName.IndexOf( filter ) != -1 )
                              {
                                  object runningObjectVal;
                                  runningObjectTable.GetObject( monikers [ 0 ], out runningObjectVal );
                                  result [ runningObjectName ] = runningObjectVal;
                              }
                          }
                  
                  
                          return result;
                      }
                  
                  
                      /// <summary>  
                      /// Returns an object from the ROT, given a prog Id.  
                      /// </summary>  
                      /// <param name="progId">The prog id of the object to return.</param>  
                      /// <returns>The requested object, or null if the object is not found.</returns>  
                      public static object GetActiveObject( string progId )
                      {
                          // Convert the prog id into a class id  
                          string classId = ConvertProgIdToClassId( progId );
                  
                  
                          IRunningObjectTable runningObjectTable = null;
                          IEnumMoniker monikerEnumerator = null;
                          IBindCtx ctx = null;
                          try
                          {
                              IntPtr numFetched = new IntPtr( );
                              // Open the running objects table.  
                              CreateBindCtx( 0, out ctx );
                              ctx.GetRunningObjectTable( out runningObjectTable );
                              runningObjectTable.EnumRunning( out monikerEnumerator );
                              monikerEnumerator.Reset( );
                              IMoniker [ ] monikers = new IMoniker [ 1 ];
                  
                  
                              // Iterate through the results  
                              while ( monikerEnumerator.Next( 1, monikers, numFetched ) == 0 )
                              {
                  
                  
                                  string runningObjectName;
                                  monikers [ 0 ].GetDisplayName( ctx, null, out runningObjectName );
                                  if ( runningObjectName.IndexOf( classId ) != -1 )
                                  {
                                      // Return the matching object  
                                      object objReturnObject;
                                      runningObjectTable.GetObject( monikers [ 0 ], out objReturnObject );
                                      return objReturnObject;
                                  }
                              }
                              return null;
                          }
                          finally
                          {
                              // Free resources  
                              if ( runningObjectTable != null )
                                  Marshal.ReleaseComObject( runningObjectTable );
                              if ( monikerEnumerator != null )
                                  Marshal.ReleaseComObject( monikerEnumerator );
                              if ( ctx != null )
                                  Marshal.ReleaseComObject( ctx );
                          }
                      }
                  
                  
                      #endregion
                  }
                  
                    • Re: How to Import/convert files in Parallel
                      Tom Sowers

                      Where you set your running conditions for solidworks, lines 302 to 306 try adding swApp.CommandInProgress = true; and at the end of you API calls set this property to false.  Not sure if this will help but for me it has really made my code run more consistently.  Also what are you using to create multiple threads? I'm using System.Threading.Tasks.Tasks and the Task.Factory to create and manage the threads, has been working fine for me even when my CPU and RAM are near 100%. 

                        • Re: How to Import/convert files in Parallel
                          Thiago Motta

                          I'm about to give the swApp.CommandInProgress a try... Trying to figure out if I should be extremely strict about it or if this can be more loose. For instance, in the following case, my first attempt will be to add CommandInProgress before Import is called and right after it ends. Or should I put it before LoadFile4 and after it?

                           

                          public static bool Import( ref Threading.Job job )
                          {
                              string argString = "r";
                              int Err = 0;
                              var inputFilename = job.InputPath;
                              var jobIDstr = job.JobIDstr;
                              ModelDoc2 swModel = job.SwInstance.ActiveDoc as ModelDoc2;
                              swModel = null;
                          
                          
                              if ( !Utils.CheckFileExistance( job.JobIDstr, inputFilename ) )
                              {
                                  return false;
                              }
                          
                          
                              swModel = ( ModelDoc2 )job.SwInstance.LoadFile4( inputFilename, argString, null, ref Err );
                          
                          
                              // If the SldWorks::LoadFile4 failed, do not continue
                              if ( swModel == null || Err != 0 )
                              {
                                  Logger.PrintErrorOnImport( jobIDstr, inputFilename, Err );
                                  return false;
                              }
                          
                          
                              return true;
                          }
                          

                           

                          Also, this is something that bugs me when using Solidworks.. There are a bunch of different methods for basically the same thing: SaveAs, SaveAs2, SaveAs3, SaveAs4, Extension.SaveAs and so on.. And even the Extension one being the standard recommendation, somehow I've noticed different results when using them...

                           

                          What I have for threading is

                          await Threading.DoWork(workerAction, numWorkers);
                          

                           

                          /// <summary>
                          /// https://stackoverflow.com/questions/14075029/have-a-set-of-tasks-with-only-x-running-at-a-time
                          /// Here is an alternate option that will work much harder to have exactly N tasks running 
                          /// (although the number of threads in the thread pool processing those tasks may be different) 
                          /// and that returns a Task indicating when it finishes, rather than blocking until done.
                          /// </summary>
                          /// <param name="actions"></param>
                          /// <param name="numWorkers"></param>
                          /// <returns></returns>
                          public static Task DoWork( Action action, int numWorkers )
                          {
                              List<Task> tasks = new List<Task>( );
                          
                          
                              for ( int i = 0; i < numWorkers; i++ )
                              {
                                  tasks.Add( Task.Factory.StartNew( ( ) =>
                                  {
                                      action( );
                                  } ) );
                              }
                          
                          
                              return Task.WhenAll( tasks );
                          }
                          

                           

                           

                          Where Inside workerAction I use a Stack<SldWorks.SldWorks> to get the available Solidworks instances inside a lock statement. The calculation part, when the API calls are made, are outside of the lock statement, but whenever I have to do something about the instances itself, they had to be inside locks.

                           

                          Are you starting a new job/thread for each work (and if you have more works then threads, killing the old ones as they get done and starting new ones) or are you reusing the threads?

                            • Re: How to Import/convert files in Parallel
                              Tom Sowers

                              As far as where to put command in progress I would suggest before all you API method calls and after them all, if you check the help doc on it it says it is intended to speed runtimes up and should be called first and last. Actually am spending today using StopWatch to record the run times and determine what is best with this as one parameter I am evaluating.  Currently working with using multiple threads to pass API calls to solidworks.  Logic would say this wouldn't work but practice is indicating otherwise.  The relationship between runtime and number of threads passing commands appears to be parabolic with a vertex (min) somewhere between 1 thread and 11 threads, trying to pin it down and this will likely vary from implementation to implementation.  Will have more info in the days to come.

                               

                              I'm not sure why you're using job.SwInstance etc. Why not just pass the swApp object into the method? this point is irrelevant if this portion is not causing exceptions though, was just something I noticed.

                               

                              You're next point with the multiple methods that do the same thing, this also bugs me.  I've just been doing my best to use methods that aren't listed as obsolete.  It's really annoying when one of the obsoleted functions would result in leaner code than the non obsolete methods.

                               

                              As far as your threading I'll be honest, not exactly sure what you are doing there with the stack etc. I use the method presented here https://forum.solidworks.com/message/258451#comment-258451 to get the SldWorks objects then pass that into methods to do the work within the threads.  I would suggest getting each instance and then holding onto the objects and passing them from method to method in order to not grab the wrong instance.  This practice has been working for me.  The code in that link would give me errors unless I staggered the thread starts with delays that corresponded to the open time of solidworks.  If you have a better method than a delay please let me know as I have been unable to figure something else out and the delay makes me nervous. 

                               

                              Below is what is in my main method to trigger the starts.  OpenDoc contains a method that calls the methods outlined in the above link to return the SldWorks objects.

                              So each thread gets its own SldWorks object which it then passes to another method that performs the API calls

                              Within this MakeAssembly method I make the API calls as normal no Stack stuff required.

                               

                              I hope this helps sorry for the long post.  If you would like we could start a discussion on the topic of parallel processing as there is almost no reference information on this topic.