Iterating AutoCAD system variables using .NET – Part 2

In the last post we saw some code to exercise to two available system variable enumeration mechanisms in AutoCAD, SystemObjects.Variables and the new SystemVariableEnumerator class.

Today we're going to take a closer look at these two mechanisms, briefly comparing their performance and results. I took some code from this previous post to measure elapsed time, deleting the "runs" database to simplify the code once I'd realised the performance was basically comparable.

Here's the C# code, with the updated ESV and ESV2 commands which now create correspondingly named .txt files in c:\temp, rather than printing the results to the command-line (and with Primary/SecondaryType information ignored, to make the output consistent – and therefore diffable – between the two commands):

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.ApplicationServices.Core;

using Autodesk.AutoCAD.Geometry;

using Autodesk.AutoCAD.Runtime;

using System;

using System.Diagnostics;

using System.IO;

using System.Text;

 

namespace SystemVariableEnumeration

{

  public class Commands

  {

    public void MeasureTime(

      Document doc, Func<int> func, string name

    )

    {

      // Get the name of the running command(s)

      // (might also have queried the CommandMethod attribute

      // via reflection, but that would be a lot more work)

 

      var cmd = (string)Application.GetSystemVariable("CMDNAMES");

 

      // Start a Stopwatch to time the execution

 

      var sw = new Stopwatch();

      sw.Start();

 

      // Run the function, getting back the count of the results

 

      var cnt = func();

 

      // Stop the Stopwatch and print the results to the command-line

 

      sw.Stop();

      doc.Editor.WriteMessage(

        "\n{0} found {1} {2} in {3}.", cmd, cnt, name, sw.Elapsed

      );

    }

 

    [CommandMethod("ESV")]

    public void EnumerateSysVars()

    {

      var doc = Application.DocumentManager.MdiActiveDocument;

      if (doc == null)

        return;

 

      MeasureTime(

        doc,

        () =>

        {

          int numVars = 0;

 

          using (var sw = new StreamWriter("c:\\temp\\esv.txt"))

          {

            // Use the existing SystemObjects iteration mechanism

 

            foreach (var v in SystemObjects.Variables)

            {

              sw.WriteLine(GetVariableInfo(v));

              numVars++;

            }

          }

          return numVars;

        },

        "variables"

    &#
160; );

    }

 

    [CommandMethod("ESV2")]

    public void EnumerateSysVars2()

    {

      var doc = Application.DocumentManager.MdiActiveDocument;

      if (doc == null)

        return;

 

      MeasureTime(

        doc,

        () =>

        {

          int numVars = 0;

 

          using (var sw = new StreamWriter("c:\\temp\\esv2.txt"))

          {

            // Use the new system variable enumerator

 

            var sve = new SystemVariableEnumerator();

 

            while (sve.MoveNext())

            {

              var v = sve.Current;

              if (v != null)

              {

                sw.WriteLine(GetVariableInfo(v));

                numVars++;

              }

            }

          }

 

          return numVars;

        },

        "variables"

      );

    }

 

    // Helper function to get the information for a particular

    // variable

 

    private static string GetVariableInfo(Variable v)

    {

      var t = GetType(v.PrimaryType);

 

      var sb = new StringBuilder();

      sb.AppendFormat(

        "{0} ({1}): {2}", // Skip the additional type info

        v.Name,

        t == null ? "null" : t.Name,

        /*v.PrimaryType, v.SecondaryType,*/ v.TypeFlags

      );

 

      if (v.Range != null)

      {

        sb.AppendFormat(

          " [{0}...{1}]",

          v.Range.LowerBound, v.Range.UpperBound

        );

      }

      return sb.ToString();

    }

 

    // Determine the type of a system variable based on

    // the internal representation

 

    private static System.Type GetType(short v)

    {

      Type ret = null;

 

      switch (v)

      {

        case 1:

        case 5001: // RTREAL real number

          {

            ret = ty
peof
(Double);

            break;

          }

        case 2:

        case 5002: // RTPOINT: 2D point X and Y only

          {

            ret = typeof(Point2d);

            break;

          }

        case 3:

        case 5003: // RTSHORT: short integer

          {

            ret = typeof(Int16);

            break;

          }

        case 4:

        case 5004: // RTANG: angle

          {

            ret = null; // Angle

            break;

          }

        case 5:

        case 5005: // RTSTR: string

          {

            ret = typeof(String);

            break;

          }

        case 6:

        case 5006: // RTENAME: entity name

          {

            ret = null;

            break;

          }

        case 7:

        case 5007: // RTPICKS: pick set

          {

            ret = null;

            break;

          }

        case 8:

        case 5008: // RTORIENT: orientation

          {

            ret = null; // Orientation

            break;

          }

        case 9:

        case 5009: // RT3DPOINT: 3D point - X, Y and Z

          {

            ret = typeof(Point3d);

            break;

          }

        case 10:

        case 5010: // RTLONG: long integer

          {

            ret = typeof(Int32);

            break;

          }

        case 11:

        case 5011: // 2D extents of some kind

          {

            ret = typeof(Point2d);

            break;

          }

      }

  
0;   return ret;

    }

  }

}

When we run the code we see that the ESV command – the implementation using SystemObjects.Variables – returns the results more quickly but finds only 274 (or 275 with AutoCAD 2015 SP1 installed, as we have the new CURSORBADGE sysvar), whereas the ESV2 command finds 912 (or 913 with SP1) in a timespan that's basically consistent with the fact there are more variables found. So performance isn't an issue.

It is interesting (and somewhat reassuring) that the new enumerator finds so many more sysvars than the former mechanism. On the one hand the old collection allows you to modify a system variable's value via the returned object…

[CommandMethod("TOGCB")]

public void ToggleCursorBadge()

{

  var doc = Application.DocumentManager.MdiActiveDocument;

  if (doc == null)

    return;

  var ed = doc.Editor;

 

  const string curbadge = "CURSORBADGE";

 

  // Get our CURSORBADGE system variable object

 

  var cb = SystemObjects.Variables[curbadge];

 

  // Report its initial value

 

  ed.WriteMessage(

    "\nInitial value of {0} is {1}.", curbadge, cb.Value

  );

 

  // Set the new value, toggling between 1 & 2

  // (with too many casts for my liking, but hey)

 

  cb.Value = (short)((short)cb.Value == 1 ? 2 : 1);

 

  // And report the new value to make sure it worked

 

  ed.WriteMessage(

    "\nNew value of {0} is {1}.", curbadge, cb.Value

  );

}

… but on the other hand this is really only for a subset of system variables (and I haven't yet worked out which subset… I do know the new enumerator skips anonymous sysvars, and yet it still finds more than 3x the number).

In case you want to dig into the sysvars returned by each, here are the results for the ESV and the ESV2 commands. I did my own diff – and the results are returned in a consistent order, in that the ones found by ESV are at the end of the ones found by ESV2 – but I haven't yet spent much time working out which ones are excluded from the old mechanism and why.

If anyone does look into this, please do post a comment: I'll also post an update if I find out something further from my side.

2 responses to “Iterating AutoCAD system variables using .NET – Part 2”

  1. You can use the following to get the type of the system variable...

    object vObject = Application.GetSystemVariable(v.Name);
    string type = vObject.GetType().ToString();

    I came up with the following, mind you I was only looking at variables that weren't read only.

    System.Int16
    System.String
    System.Double
    System.Int32
    Autodesk.AutoCAD.Geometry.Point2d
    Autodesk.AutoCAD.Geometry.Point3d

    Since GetSystemVariable returns object you need to know the type to cast to.

    1. I couldn't help my self, had to add more code...

      var doc = Application.DocumentManager.MdiActiveDocument;
      if (doc == null)
      return;

      using (var sw = new StreamWriter("c:\\temp\\esv2.txt"))
      {

      List<string> fullListOfTypes = new List<string>();
      // Use the new system variable enumerator
      var sve = new SystemVariableEnumerator();
      while (sve.MoveNext())
      {
      var v = sve.Current;
      if (v != null)
      {
      if (!v.IsReadOnly)
      {
      if (!v.Name.Equals(string.Empty))
      {
      try
      {
      object vObject = Application.GetSystemVariable(v.Name);
      fullListOfTypes.Add(vObject.GetType().ToString());

      string str = string.Empty;
      if (vObject is System.Int16)
      {
      Int16 value = (Int16)vObject;
      str = value.ToString();
      }
      else if (vObject is System.String)
      {
      String value = (String)vObject;
      str = value.ToString();
      }
      else if (vObject is System.Double)
      {
      Double value = (Double)vObject;
      str = value.ToString();
      }
      else if (vObject is System.Int32)
      {
      Int32 value = (Int32)vObject;
      str = value.ToString();
      }
      else if (vObject is Autodesk.AutoCAD.Geometry.Point2d)
      {
      Point2d value = (Point2d)vObject;
      str = value.ToString();
      }
      else if (vObject is Autodesk.AutoCAD.Geometry.Point3d)
      {
      Point3d value = (Point3d)vObject;
      str = value.ToString();
      }
      else
      {

      }

      sw.WriteLine(GetVariableInfo(v) + "\t" + vObject.GetType().ToString() + "\t" + str);
      }
      catch (System.Exception ex)
      {
      sw.WriteLine(v.Name + "\tError");
      sw.WriteLine(ex.Message);
      }
      }
      }
      }
      }

      List<string> distinctListOfTypes = fullListOfTypes.Distinct().ToList();

      foreach (string item in distinctListOfTypes)
      {
      sw.WriteLine(item);
      }

      }

Leave a Reply to Brian Pruitt Cancel reply

Your email address will not be published. Required fields are marked *