July 04, 2013

ActiveX Access to ACA/AMEP Drawing Settings

Updated 10/26/2019 to add support for versions 2015 through 2020 to the AECAPPVER code.

Since the 2010 releases, the AEC data stored in "dictionaries" has not been accessible through AutoLISP® entities/entity data, as it had been previously, through statements such as:
(cdr (assoc 350 (member '(3 . "AEC_VARS") (entget (namedobjdict)))))

I recently had the time and opportunity to investigate the use of the Visual LISP® functions to access the drawing data to access the data previously available through entity data.

The keys to getting ACA or AMEP data are understanding the Object Model, so that you know which Application holds the data of interest, a willingness to explore the Object Model, so you can find things that are exposed in locations other than what the object model suggests, and knowing that the "undocumented" vla-GetInterfaceObject function can be used to access those Applications and thereby the Object Model elements defined/stored therein.

The Help menu (in recent releases, in the upper right corner of the ACA/AMEP window, to the right of the InfoCenter and to the left of the Minimize/Maximize/Close buttons) has a choice called AutoCAD® Architecture Developer Help (in ACA) or AutoCAD® MEP Developer Help (in AMEP) that will open "local" Help files that explain the Object Models and available objects, methods and properties. AMEP users will need to see the ACA file, which can also be found under the installation directory (C:\Program Files\Autodesk\AutoCAD 2014\Help\adtauto.chm for my installation of the 2014 Building Design Suite). The AMEP file can also be found in the same folder (absauto.chm).

The loading of the Application objects is version-dependent, so the first step on my journey was to write a subroutine to return the proper string indicating the version of the release of ACA/AMEP running. Because many of the routines I am trying to update were written in the ADT 3.3/2004 time frame, I have chosen to include releases back to that time in this function. Unfortunately, I no longer have access to many of those releases to verify the AEC version numbers, so some of the return values in the routine are based on my best guess. If anyone is still running any of the the releases marked "verify", please do verify the values before trying to use the following code. And if you do, and are so inclined, I would appreciate a comment to this post either verifying that I guessed correctly or providing the right values. The subroutine initially tests to see if ACA or AMEP is running, and returns nil if aecarchbase.dbx or aecarchbasenn.dbx is not loaded, where nn is a two-digit number indicating a specific release from 2007 or earlier. If it is not loaded, then the subroutine ends with a warning to the user and returns nil, since there is little point looking for information that does not exist. The calling routine will have to determine how to handle the return of a nil value.
(defun AECAPPVER (            ; No arguments.
                   / ;_ Local variable:  
                     sacdvr   ; String stored in ACADVER system variable.
                 ) ;_ End arguments and local variables.
  (setq sacdvr (getvar "ACADVER"))
  ;; Return appropriate string:
  (cond                       ; cond A.
         (member "aecarchbase.dbx" (arx))
         (member "aecarchbase50.dbx" (arx))
                              ; DBX file name not verified for 2007.
         (member "aecarchbase47.dbx" (arx))
         (member "aecarchbase45.dbx" (arx))
                              ; DBX file name not verified for 2005.
         (member "aecarchbase40.dbx" (arx))
                              ; DBX file name not verified for 2004.
         (member "aecarchbase30.dbx" (arx))
                              ; DBX file name not verified for 3.3.
       ) ;_ End or.
     ) ;_ End not.
      ;; ACA or AMEP is not running.
          "Unsupported version of AutoCAD Architecture/MEP is running."
          "\nACADVER = "
          "\n\nPlease notify the CAD/BIM IT Staff."
         ) ;_ End strcat.
      ) ;_ End alert.
      nil                     ; Return nil.
    ) ;_ End condition A.1.
    ((= "15.06" sacdvr)       ; ADT 3.3.
      ".3.0"                  ; VERIFY THIS RETURN STRING!
    ) ;_ End condition A.2.
    ((= "16.0" sacdvr)        ; ADT 2004.
      ".4.0"                  ; VERIFY THIS RETURN STRING!
    ) ;_ End condition A.3.
    ((= 16.1 (atof sacdvr))   ; ADT 2005.
      ".4.5"                  ; VERIFY THIS RETURN STRING!
    ) ;_ End condition A.4.
    ((= 16.2 (atof sacdvr))   ; ADT 2006.
      ".4.7"                  ; VERIFY THIS RETURN STRING!
    ) ;_ End condition A.5.
    ((= 17.0 (atof sacdvr))   ; ADT 2007.
      ".5.0"                  ; VERIFY THIS RETURN STRING!
    ) ;_ End condition A.6.
    ((= 17.1 (atof sacdvr))   ; ADT 2008.
      ".5.5"                  ; Return string.
    ) ;_ End condition A.7.
    ((= 17.2 (atof sacdvr))   ; ADT 2009.
      ".5.7"                  ; VERIFY THIS RETURN STRING!
    ) ;_ End condition A.8.
    ((= 18.0 (atof sacdvr))   ; ADT 2010.
      ".6.0"                  ; Return string.
    ) ;_ End condition A.9.
    ((= 18.1 (atof sacdvr))   ; ADT 2011.
      ".6.5"                  ; Return string.
    ) ;_ End condition A.10.
    ((= 18.2 (atof sacdvr))   ; ADT 2012.
      ".6.7"                  ; VERIFY THIS RETURN STRING!
    ) ;_ End condition A.11.
    ((= 19.0 (atof sacdvr))   ; ADT 2013.
      ".7.0"                  ; Return string.
    ) ;_ End condition A.12.
    ((= 19.1 (atof sacdvr))   ; ADT 2014.
      ".7.5"                  ; Return string.
    ) ;_ End condition A.13.
    ((= 20.0 (atof sacdvr))   ; ACA 2015.
      ".7.7"                  ; Return string.
    ) ;_ End condition A.14.
    ((= 20.1 (atof sacdvr))   ; ACA 2016.
      ".7.8"                  ; Return string.
    ) ;_ End condition A.14.
    ((= 21.0 (atof sacdvr))   ; ACA 2017.
      ".7.9"                  ; Return string.
    ) ;_ End condition A.15.
    ((= 22.0 (atof sacdvr))   ; ACA 2018.
      ".8.0"                  ; Return string.
    ) ;_ End condition A.15.
    ((= 23.0 (atof sacdvr))   ; ACA 2019.
      ".8.1"                  ; Return string.
    ) ;_ End condition A.16.
    ((= 23.1 (atof sacdvr))   ; ACA 2020.
      ".8.2"                  ; Return string.
    ) ;_ End condition A.17.
    (T                        ; Unsupported version running.
         "Unsupported version of AutoCAD is running."
         "\nACADVER = " sacdvr "."
         "\n\nPlease notify the CAD/BIM IT Staff."
       ) ;_ End strcat.
     ) ;_ End alert.
     nil                       ; Return nil.
    ) ;_ End condition A.18.
  ) ;_ End cond A.

With the subroutine above loaded, you can access the AecArchBaseApplication with this code
  (strcat "AecX.AecArchBaseApplication" (aecappver))

I will post more of my findings as I develop them.

Backstory Behind This Post:
Many years back, I did a lot of customization in AutoCAD® using AutoLISP, and was particularly pleased when the Visual LISP was introduced, as I made extensive use of the Visual LISP interactive development environment (VLIDE). While perhaps somewhat less slick that the equivalent setup for Visual Basic, it none the less made writing, testing and debugging a LISP routine or project much easier than using Notepad or a DOS text editor. I never really worked with the Visual LISP commands that were added to the AutoLISP arsenal at that time, however, and the time I had for customization dwindled with increasing project responsibilities and with attempting to master Architectural Desktop/AutoCAD Architecture.

I did, however, manage to find time to write a few customizations that accessed ACA settings (such as the drawing scale - before AutoCAD added that feature, Annotation Plot Size, current Layer Key Style, etc.) via DXF access to the dictionaries where that data was stored. There was little to no documentation on the structure of those dictionaries, but by working with what there was and trial and error, I was able to find the locations of various settings. My routines only read these values, and then acted accordingly; changing the values through ENTMOD was not supported and actively discouraged by Autodesk staffers. These routines stopped working in the 2010 release, because the dictionary contents were no longer exposed to DXF "hacking".

My on-the-job responsibilities shifted last year, as I moved out of the Project Architect roll and into the CAD/BIM Manager roll. I had reason to get back into customization, as I was looking for a way to expedite the processing of AutoCAD files exported from Revit, which tend to have a lot of entity-level settings (in particular, color) and which then tend not to work well for the disciplines that remain in CAD, as they are used to externally referencing the background drawings and controlling the appearance and plotting of the elements therein via layer settings. While working on that task, I had reason to become more familiar with the capabilities in the Visual LISP commands, and was reminded that I had a number of routines that no longer worked in versions more recent than 2009. I am using the rehabilitation of these routines as a pretext to become more familiar with the Visual LISP commands and the ACA Object Model, as I suspect that these skills will continue to have some relevance, at least until the day that our work is all Revit and no AutoCAD.

No comments: