August 2014

Sun Mon Tue Wed Thu Fri Sat
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31            








« Now it's even easier to play with the Project Draw API | Main | Using the P/Invoke Interop Assistant to help call ObjectARX from .NET »

August 01, 2008

Implementing a CAD Standards plugin for AutoCAD using .NET

This question came in recently from a developer:

I've noticed a lot of talk about cad standards in my circles - any chance of a post on creating a CAD Standards plugin for use in the AutoCAD CAD Standards checker that companies could take and run with to produce their custom checks? For example, is the titleblock to company standard and inserted in the right space, are the xrefs inserted at 0,0 etc.

I thought this was an interesting one to handle: back when I was based in San Rafael I (along with members of my team) worked closely with the CAD Standards feature team (during the AutoCAD 2004 timeframe, if memory serves me correctly), to develop custom plugin samples showing how to use the CAD Standards API from C++ and Visual Basic. The core requirement was to develop plugins that checked actual geometry in the drawing - something the standard plugins did not, as they focused on symbol table records such as layers and linetypes. The concept we came up with, at the time, was contrived but fun: to compare circles inside a drawing with those stored in the associated CAD Standards template (.DWS) file, and suggest changing the colours of the "incorrect" circles to those of the most similar size in the DWS file.

While this concept is, of course, something that no-one in their right mind would ever want to implement in a real software package, the principles shown are generic enough in nature and (in my opinion, at least) quite relevant to anyone wanting to check geometry using a CAD Standards plugin - something in which the developer asking the above question is clearly interested.

The CAD Standards API is a COM API, so while we implemented the original plugin sample in C++, the main objective at the time was to create a sample in VB6. This meant some changes were needed to the original API implementation, as although it was a COM API, the original API specification relied on datatypes that were not usable from VB6 clients (there was at least one pure COM interface in the API set, and not Automation-compatible, if I recall correctly). That's also why the COM interface we need to implement for a CAD Standards plugin is IAcStPlugin2 rather than IAcStPlugin.

The original VB6 sample, along with an early port to VB.NET (in this case VB7, the first .NET implementation of Visual Basic) are available as part of this DevNote (accessible to ADN members). The DevNote is, admittedly, out-of-date... we're in the process of during a mass refresh of KB content on the ADN site, so this was a very timely request (and allows me to contribute in some way to the content migration effort :-). There's quite a lot to this sample, so here is the source along with the supporting files I created when writing this post.

Here's the C# code:

//

//  AutoCAD CAD Standards API Sample

//

//  CircleStandard.cs : CAD Standards Plugin Sample for C#

//

// This sample adds a custom plugin to the CAD Standards

// Drawing Checker.

//

// The sample plugin tests for a match between the color of a

// circle in the current drawing, and any of the colors of

// circles contained in the specified standards (.DWS) files.

// All the colors of the standard circles are considered as

// fix candidates of the circle being checked. The recommended

// fix object will be the standard circle having the nearest

// radius to the circle being checked.


using AcStMgr;

using Autodesk.AutoCAD.Interop.Common;

using MSXML2;

using System;

using System.Collections.Generic;

using System.Runtime.InteropServices;


namespace CircleStandard

{

  [ProgId("CircleStandard.CircleStandard")]

  public class CircleStandard : IAcStPlugin2

  {

    // Declare variables


    private ContextList m_contexts =

      new ContextList();

    private AcStManager m_mgr;

    private CircleStandard m_plugin;

    private AcadDatabase m_checkDb;

    private AcadDatabase m_dwsDb;

    private AcStError m_err;

    private object m_fixArray;

    private CircleCache[] m_cirCacheArray;

    private int m_recFixIndex;

    private int m_curIndex;

    private int m_fixCnt;

    private string m_propName;


    // Initialize


    // Initializes the plugin


    public void Initialize(AcStManager mgr)

    {

      // This is the only member function in which

      // the interface is passed an IAcStManager interface.


      // Store pointer to Manager object


      m_mgr = mgr;

      m_plugin = this;

    }


    // GetObjectFilter


    // Plugin populates the provided array with class names

    // of objects that it can check


    public object GetObjectFilter()

    {

      // In this case we're only interested in circles


      string[] filtArray = new string[1];

      filtArray[0] = "AcDbCircle";

      return filtArray;

    }


    // SetupForAudit


    // Sets the context for a plugin to check a drawing


    public void SetupForAudit(

      AcadDatabase db,

      string pathName,

      object objNameArray,

      object objPathArray,

      object objDbArray)

    {

      // This method defines the context in which a plug-in

      // will operate, specifically the drawing to check and

      // the DWS files that should be used to check the drawing.

      // Here we cache our DWS standards definitions and make

      // an initial cache of circles in the DWG to be checked.


      // NOTE: AcadDatabase objects contained in objDbArray

      // are ***not*** guaranteed to be valid after this call.

      // They should not be cached!!!


      if (db != null)

      {

        // Cache a pointer to the database


        m_checkDb = db;


        // pDb is the DWG to be checked

        // Store list of circles in drawing in m_ObjIDArray


        if (m_checkDb != null)

        {

          // Cache list of all circles in the current drawing


          foreach (AcadObject obj in

            m_mgr.get_ModelSpaceProxy(m_checkDb))

          {

            if (obj.ObjectName == "AcDbCircle")

            {

              m_contexts.Add(obj.ObjectID, true);

            }

          }

        }


        object[] dbArray = (object[])objDbArray;

        string[] nameArray = (string[])objNameArray;

        string[] pathArray = (string[])objPathArray;


        int i = 0;


        // Iterate over the DWSes and cache properties (color

        // and radius) of standard circles


        for (int iDWS = 0; iDWS < dbArray.Length; iDWS++)

        {

          // Get the DWS database


          m_dwsDb = (AcadDatabase)dbArray[iDWS];

          foreach (AcadCircle stdCircle in

            m_mgr.get_ModelSpaceProxy(m_dwsDb))

          {

            CircleCache cirCache = new CircleCache();


            // CircleCache is utility object for storing

            // properties


            // Cache properties (color and radius) of all

            // circles in the DWS database


            cirCache.color = stdCircle.color;

            cirCache.radius = stdCircle.Radius;

            cirCache.standardFileName = nameArray[iDWS];


            // pFix contains fix information to be passed back

            // to the manager later


            AcStFix fix = new AcStFix();

            fix.Description = "Color fix";

            fix.StandardFileName =

              cirCache.standardFileName;

            fix.FixObjectName =

              "Color: " +

              StripAcPrefix(stdCircle.color.ToString());


            if (fix.PropertyCount == 0)

            {

              fix.PropertyValuePut(

                "Color",

                stdCircle.color

              );

            }

            cirCache.pFix = fix;


            Array.Resize<CircleCache>(

              ref m_cirCacheArray,

              i+1

            );

            m_cirCacheArray[i++] = cirCache;

          }

        }

      }

    }


    // SetContext


    // Sets the objects to examine when iterating over errors


    public void SetContext(object objIdArray, bool useDb)

    {

      // If useDb is set to "true" (default), or if

      // objIdArray is blank, we use the database (we get

      // all ids for the current drawing). Otherwise, we

      // set supplied list of objIdArrays as our list.


      m_contexts.SetContext(useDb, objIdArray);

    }


    // Start


    // Initializes the error iterator mechanism


    public void Start(AcStError err)

    {

      // If pStartError is set to an error object, we should

      // only start checking from that error, not from the

      // beginning. Mostly we will just go the Next item at

      // this point...


      if (err != null)

      {

        long badId;

        badId = err.BadObjectId;


        // Find the index for BadObjectId in m_objIDArray


        for (

          m_curIndex = 0;

          m_curIndex < m_contexts.Count;

          m_curIndex++

        )

        {

          if (m_contexts[m_curIndex] == badId)

          {

            m_curIndex = (m_curIndex - 1);

            Next();

          }

        }

      }

      else

      {

        // No AcStError object was passed in. Start checking

        // from the very begining


        m_curIndex = -1;

        Next();

      }

    }


    // Next


    // Finds the next error in the current context


    public void Next()

    {

      m_err = null;

      if (m_contexts.Count > 0)

      {

        // Drawing contains AcDbCircle objects


        AcadCircle circle;

        bool foundErr;


        if (m_cirCacheArray.Length > 0)

        {

          // If we've not reached end of list, we first

          // increment current list index


          if (m_curIndex < m_contexts.Count - 1)

          {

            m_curIndex++;

            foundErr = false;

            while (m_curIndex < m_contexts.Count)

            {

              // Don't iterate beyond end of list

              // Retrieve object using its ObjectId


              try

              {

                circle =

                  (AcadCircle)m_checkDb.ObjectIdToObject(

                    (int)m_contexts[m_curIndex]

                  );


                // Try to find a circle with the same color from

                // the cached standard circle (Iterate over cached

                // standards)


                for (

                  int iCache = 0;

                  iCache < m_cirCacheArray.Length;

                  iCache++

                )

                {

                  if (circle.color.CompareTo(

                        m_cirCacheArray[iCache].color

                      ) != 0)

                  {

                    // If it doesn't match, we've found a potential

                    // error


                    foundErr = true;

                  }

                  else

                  {

                    // If it matches any one standard, then we can

                    // stop checking


                    foundErr = false;

                    break;

                  }

                }

                // Check for color differences


                if (foundErr)

                {

                  // We found an error so create a local error

                  // object


                  AcStError err = new AcStError();

                  err.Description = "Color is non-standard";

                  err.BadObjectId = circle.ObjectID;

                  err.BadObjectName =

                    StripAcPrefix(

                      circle.color.ToString()

                    );                 

                  err.Plugin = m_plugin;

                  err.ErrorTypeName = "Color ";

                  err.ResultStatus =

                    AcStResultStatus.acStResFlagsNone;


                  if (err.PropertyCount == 0)

                  {

                    err.PropertyValuePut(

                      "Color",

                      circle.color

                    );

                  }

                  m_err = err;

                  foundErr = false;

                  break;

                }

              }

              catch

              {

              }             

              m_curIndex = (m_curIndex + 1);

            }

          }

        }

      }

    }


    // Done


    // Returns true if there are no more errors


    public bool Done()

    {

      return (m_err == null);

    }


    // GetError -- Returns the current error


    public AcStError GetError()

    {

      return m_err;

    }


    // GetAllFixes


    // Returns an array of IAcStFix objects for the given

    // error (note: The caller is responsible for releasing

    // the objects in this array)


    public void GetAllFixes(

      AcStError err,

      ref object fixArray,

      ref int recommendedFixIndex

    )

    {

      if (err != null)

      {

        IAcStFix[] arr =

          new IAcStFix[m_cirCacheArray.Length];

        ACAD_COLOR vErrorVal;

        recommendedFixIndex = -1;

        m_fixCnt = 0;


        // If we have a cache of fixes, then use that


        if (m_cirCacheArray.Length > 0)

        {

          for (int i = 0; i < m_cirCacheArray.Length; i++)

          {

            vErrorVal =

              (ACAD_COLOR)err.PropertyValueGet("Color");

            if (vErrorVal.CompareTo(

                  m_cirCacheArray[i].color

                ) != 0)

            {

              // If color property of fix matches error, then

              // add to list of fixes.


              arr[i] = m_cirCacheArray[i].pFix;

            }

          }

          fixArray = arr;

          m_fixArray = fixArray;


          // Find the recommendedFixIndex

          // (we call this function to retrieve the index -

          // we don't need the returned fix object here)


          GetRecommendedFix(err);

          recommendedFixIndex = m_recFixIndex;

        }


        // Did we find a recommended fix along the way?


        if (recommendedFixIndex == -1)

        {

          // No recomended fix, so set the proper flag on the

          // error object


          err.ResultStatus =

            AcStResultStatus.acStResNoRecommendedFix;

        }

      }

    }


    // GetRecommendedFix


    // Retrieves a fix object that describes the

    // recommended fix


    public AcStFix GetRecommendedFix(AcStError err)

    {

      AcStFix recFix = new AcStFix();


      if (m_cirCacheArray.Length == 0)

      {

        err.ResultStatus =

          AcStResultStatus.acStResNoRecommendedFix;

      }

      else

      {

        // Get the objectId for this error


        long tmpObjID = err.BadObjectId;


        // Retrieve the object to fix from the DWG


        AcadCircle tmpCircle =

          (AcadCircle)m_checkDb.ObjectIdToObject(

            (int)tmpObjID

          );

        double radiusToBeChecked = tmpCircle.Radius;


        CircleCache cirCache = m_cirCacheArray[0];

        double diff =

          Math.Abs(radiusToBeChecked - cirCache.radius);

        m_recFixIndex = 0;


        // Attempt to get a fix color from the cached

        // m_CircleCacheArray


        // Rule: the color of the standard circle with the

        // nearest radius as the one to be fixed


        for (int i = 0; i < m_cirCacheArray.Length; i++)

        {

          if (diff >

              Math.Abs(

                radiusToBeChecked - m_cirCacheArray[i].radius

              )

            )

          {

            cirCache = m_cirCacheArray[i];

            diff =

              Math.Abs(radiusToBeChecked - cirCache.radius);

            m_recFixIndex = i;

          }

        }


        // Populate properties of the recommended fix object


        recFix.Description = "Color fix";

        recFix.StandardFileName =

          m_cirCacheArray[m_recFixIndex].

          standardFileName;

        recFix.FixObjectName = "Color";

        if (recFix.PropertyCount == 0)

        {

          recFix.PropertyValuePut(

            "Color",

            m_cirCacheArray[m_recFixIndex].color

          );

        }

      }

      return recFix;

    }


    // GetPropertyDiffs


    // Populates the provided arrays with the names of

    // properties that are present in the provided

    // error and fix objects (used to populate the fix

    // dialog with 'property name, current value, fix value')


    public void GetPropertyDiffs(

      AcStError err,

      AcStFix fix,

      ref object objPropNames,

      ref object objErrorValues,

      ref object objFixValues,

      ref object objFixableStatuses)

    {

      if (err != null)

      {

        string[] propNames = new string[0];

        string propName = "";

        string[] errorValues = new string[0];

        object objErrorVal = new object();

        string[] fixValues = new string[0];

        object objFixVal = new object();

        bool[] fixableStatuses = new bool[0];


        // Iterate error properties


        for (int i = 0; i < err.PropertyCount; i++)

        {

          err.PropertyGetAt(i, ref propName, ref objErrorVal);

          m_propName = propName;


          // Retrieve corresponding Fix property value


          try

          {

            fix.PropertyValueGet(propName, ref objFixVal);


            ACAD_COLOR errVal = (ACAD_COLOR)objErrorVal;

            ACAD_COLOR fixVal = (ACAD_COLOR)objFixVal;


            // Fix object has the same prop, so see if they match


            if (errVal.CompareTo(fixVal) != 0)

            {

              // Store error and fix properties in array ready to

              // pass back to caller


              Array.Resize<string>(ref propNames, i+1);

              propNames[i] = propName;

              Array.Resize<string>(ref errorValues, i+1);

              errorValues[i] = StripAcPrefix(errVal.ToString());

              Array.Resize<string>(ref fixValues, i+1);

              fixValues[i] = StripAcPrefix(fixVal.ToString());

              Array.Resize<bool>(ref fixableStatuses, i+1);

              fixableStatuses[i] = true;

            }

          }

          catch

          {

          }

        }


        // Initialize the arrays supplied by caller


        objPropNames = propNames;

        objErrorValues = errorValues;

        objFixValues = fixValues;

        objFixableStatuses = fixableStatuses;

        m_fixCnt++;

      }

    }


    // StripAcPrefix


    // Helper function to make color names prettier


    private string StripAcPrefix(string p)

    {

      if (p.StartsWith("ac"))

        return p.Substring(2);

      else

        return p;

    }


    // FixError


    // Takes an error and a fix object and attempts

    // to fix the error


    public void FixError(

      AcStError err,

      AcStFix fix,

      out string failureReason)

    {

      failureReason = "";

      if (err != null)

      {

        long badObjID = err.BadObjectId;


        // Retrieve object to fix from DWG


        AcadCircle badObj =

          (AcadCircle)m_checkDb.ObjectIdToObject(

            (int)badObjID

          );

        if (fix == null)

        {

          // If the fix object is null then attempt to get

          // the recommended fix


          AcStFix tmpFix =

            GetRecommendedFix(err);


          if (tmpFix == null)

          {

            // Set the error's result status to failed and

            // noRecommendedFix


            err.ResultStatus =

              AcStResultStatus.acStResNoRecommendedFix;

          }

          else

          {

            fix = tmpFix;

          }

        }


        if (fix != null)

        {

          // Fix the bad circle


          object sFixVal = new object();

          fix.PropertyValueGet(m_propName, ref sFixVal);

          ACAD_COLOR fixVal = (ACAD_COLOR)sFixVal;

          try

          {

            badObj.color = fixVal;

            err.ResultStatus =

              AcStResultStatus.acStResFixed;

          }

          catch

          {

            err.ResultStatus =

              AcStResultStatus.acStResFixFailed;

          }

        }

      }

    }


    // Clear


    // Clears the plugin state and releases any cached

    // objects


    public void Clear()

    {

      // Called just before a plugin is released.

      // Use this function to tidy up after yourself


      m_plugin = null;

      m_curIndex = -1;

      m_recFixIndex = -1;

      m_fixCnt = 0;

      m_propName = "";

      m_mgr = null;

      m_dwsDb = null;

      m_checkDb = null;


      if (m_err != null)

      {

          m_err.Reset();

          m_err = null;

      }

      if (m_cirCacheArray != null)

      {

        for (int i = 0; i < m_cirCacheArray.Length; i++)

        {

          if (m_cirCacheArray[i].pFix != null)

          {

            m_cirCacheArray[i].pFix.Reset();

            m_cirCacheArray[i].pFix = null;

          }

        }

      }


      m_contexts.Clear();

    }


    // CheckSysvar


    // Checks a system variable


    public void CheckSysvar(

      string sysvarName,

      bool getAllFixes,

      ref bool passFail)

    {

    }


    // StampDatabase


    // Returns whether the plugin uses information

    // from the database for checking


    public void StampDatabase(

      AcadDatabase db,

      ref bool stampIt

    )

    {

      // If the DWS contains circles, we stamp it by

      // returning stampIt as true, otherwise, returning

      // stampIt as false


      stampIt = false;

      foreach (

        AcadObject obj in

        m_mgr.get_ModelSpaceProxy(db)

      )

      {

        if (obj.ObjectName == "AcDbCircle")

        {

          stampIt = true;

          break;

        }

      }

    }


    // UpdateStatus


    // Updates the result status of the provided error


    public void UpdateStatus(AcStError err)

    {

    }


    // WritePluginInfo


    // Takes an AcStPluginInfoSection node and creates a

    // new AcStPluginInfo node below it (note: used by the

    // Batch Standards Checker to get information about the

    // plugin)


    public void WritePluginInfo(object objSectionNode)

    {

      IXMLDOMNode section =

        (IXMLDOMNode)objSectionNode;

      IXMLDOMElement xml =

        section.ownerDocument.createElement(

          "AcStPluginInfo"

        );

      IXMLDOMElement info =

        (IXMLDOMElement)section.appendChild(xml);


      info.setAttribute("PluginName", Name);

      info.setAttribute("Version", Version);

      info.setAttribute(

        "Description",

        Description

      );

      info.setAttribute("Author", Author);

      info.setAttribute("HRef", HRef);

      info.setAttribute("DWSName", "");

      info.setAttribute("Status", "1");

    }


    // Author


    // Returns the name of the plugin's author


    public string Author

    {

      get { return "Kean Walmsley, Autodesk, Inc."; }

    }


    // Description


    // Returns a description of what the plugin checks


    public string Description

    {

      get

      {

        return

          "Checks that circles in a drawing have a color " +

          "that matches those of a similar radius in an " +

          "associated standards file.";

      }

    }


    // HRef


    // Returns a URL where the plugin can be obtained


    public string HRef

    {

      get

      {

        return

          "http://blogs.autodesk.com/through-the-interface";

      }

    }


    // Icon


    // Returns the HICON property Icon


    public int Icon

    {

      get { return 1; }

    }


    // Name


    // Returns the name of the plugin


    public string Name

    {

      get { return "Circle color checker"; }

    }


    // Version


    // Returns the version of the plugin


    public string Version

    {

      get { return "2.0"; }

    }


    // CircleCache


    // Caches "standard" circle properties (Color, Radius)

    // from .DWS files, as well as pointers to the circle's

    // relevant AcStFix object


    private class CircleCache

    {

      public double radius;

      public ACAD_COLOR color;

      public string standardFileName;

      public AcStFix pFix;

    }


    // ContextList


    // Manages list of objects to check - either all in

    // database, or just those recently added or modified


    private class ContextList

    {

      // List of objects to use when not in database context


      List<long> m_altIdArray =

        new List<long>();


      List<long> m_dbIdArray =

        new List<long>();


      // All objects in database


      private bool m_useDb;


      // Return item from correct context list


      public long this[int index]

      {

        get

        {

          if (m_useDb)

            return m_dbIdArray[index];

          else

            return m_altIdArray[index];

        }

      }


      // Number of items in current list


      public int Count

      {

        get

        {

          if (m_useDb)

            return m_dbIdArray.Count;

          else

            return m_altIdArray.Count;

        }

      }


      // Flag to determine which conext list to return element

      // from

      // Select all database or just modified items for checking

      // (but also add any new ids to database array


      public void SetContext(bool useDb, object objContextArray)

      {

        if (!useDb && objContextArray != null)

        {

          m_useDb = false;

          int[] idArray = (int[])objContextArray;

          for (int i = 0; i < idArray.Length; i++)

          {

            long val = (long)idArray[i];

            m_altIdArray.Add(val);


            // Have to keep database list up to date


            m_dbIdArray.Add(val);

          }

        }

        else

        {

          // Clear


          m_useDb = true;

          m_altIdArray.Clear();

        }

      }


      public void Add(long id, bool useDb)

      {

        if (useDb)

          m_dbIdArray.Add(id);

        else

          m_altIdArray.Add(id);

      }


      // Clear both lists


      public void Clear()

      {

        m_altIdArray.Clear();

        m_dbIdArray.Clear();

      }

    }

  }

}

Here are some additional steps to turn the above code into a buildable project and then into a working plugin:

  1. Create a Windows Class Library project (in this case for C#)
  2. Copy and paste the code into a .cs file in the project (whether the default Class1.cs file or a new one)
  3. Add a project reference to the appropriate ObjectDBX Type Library (in this case "AutoCAD/ObjectDBX Common 17.0 Type Library")
  4. Add a project reference to the Microsoft XML Type Library (in my case I used "Microsoft XML, v6.0")
  5. Add a project reference to AcStMgr.dll. This is actually harder than just adding the reference via the IDE by browsing to the Type Library on the ObjectARX SDK. The IDE shows this error:

Type library import error

To get around this, we need to use the TLBIMP tool. Launch a "Visual Studio Command Prompt" (in my case by using Start -> All Programs -> Microsoft Visual Studio 2005 -> Visual Studio Tools -> Visual Studio 2005 Command Prompt), from which you should browse to the appropriate ObjectARX SDK include folder (in my case "c:\Program Files\Autodesk\ObjectARX 2009\inc-win32"), and enter "tlbimp acstmgr.tlb" into the command window:

Manual type library import

This creates an interop library (AcStMgr.dll) that can be added as a project reference instead of the original Type Library. When you add it, keep "CopyLocal = True", which will allow the later assembly registration step to work properly. The other assembly references can safely be changed to "CopyLocal = False", if you wish.

At this point the project should now build. Yay! :-)

For the plugin to be loadable inside AutoCAD, a few more steps are needed, however.

  1. Open the project's AssemblyInfo.cs file (under Properties in the project browser window), and set the ComVisible assembly attribute to true:

// Setting ComVisible to false makes the types in this assembly not visible

// to COM components.  If you need to access a type in this assembly from

// COM, set the ComVisible attribute to true on that type.

[assembly: ComVisible(true)]

  1. From our command prompt, navigate to the location of the built assembly, and run "regasm /codebase CircleStandard.dll" (where CircleStandard.dll is the name of the assembly, whatever you've chosen to call it). Remember to use the /codebase flag - without this the COM information stored in the Registry will not include a reference to our assembly (only to the .NET Framework DLL that takes loads/hosts .NET assemblies that are also COM servers).

Register assembly for COM

  1. Create a .reg file with the following contents, and merge it into the Registry:

Windows Registry Editor Version 5.00


[HKEY_LOCAL_MACHINE\SOFTWARE\Autodesk\Drawing Check\Plugins2\CircleStandard.CircleStandard]

@="CircleStandard"

Once you've done that, you should now be able to launch AutoCAD and run the STANDARDS command, and check that our plugin exists:

Configure Standards dialog

By the way - all the above steps are needed (at least as far as I'm aware) for the plugin to show up in the list. If you happen to see the "Error obtaining plugin description" message on the command-prompt, the chances are you've missed a step. In these cases I use a SysInternals (now Microsoft) tool called Process Monitor to diagnose the problem. This tool is the combines RegMon and FileMon - two fantastic tools I used to use very regularly, back in the day. It will help you determine whether AutoCAD is failing to find the information it needs from the Registry or from the file system.

Now let's see how we can use this sample.

Let's start by setting up a DWS file...

Inside a blank drawing, create a number of circles, each of different radii and colours. I simply used the colour index corresponding to the radius, but you can use whatever you like:

Creating our standards file

Save this as a DWS file (I called mine CircleColors.dws), which you can then add to a DWG containing circles you've created:

Adding the standards file

Then via the STANDARDS or CHECKSTANDARDS command you can start checking - and fixing - your circles' colours:

Checking our circle colors

Ending up with a nice set of colourful circles:

Check complete

You can also change the standards settings to display violation alerts and to automatically fix issues:

Standards settings dialog

You should now see the alert message when new circles are created that don't have the correct colour:

Automatic standards checking

Automatic fixing certainly streamlines the fixing of standards violations - changing the colour to the one recommended by the plugin - but it won't stop the user from having to click through a couple of dialogs.

Well, that's it for today... hopefully you will find this sample a useful basis for implementing more specific requirements for geometry-oriented standards-checking.

Update:

See this more recent blog post for an updated project and set of instructions for AutoCAD 2015.

TrackBack

TrackBack URL for this entry:
http://www.typepad.com/services/trackback/6a00d83452464869e200e553e070a38834

Listed below are links to weblogs that reference Implementing a CAD Standards plugin for AutoCAD using .NET:

blog comments powered by Disqus

Feed/Share

10 Random Posts