4 Replies Latest reply on Oct 19, 2018 5:12 PM by Thiago Motta

    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
                  }