How to Insert a Block From an External DWG in AutoLISP

A step-by-step tutorial to guide your AutoLISP code: insert a block from an external DWG file into AutoCAD without ever opening the source file on screen

Allan Ransau
Allan RansauFounder of RANSAU SYSTEME — Engineer & Developer
26 July 20266 min read
Cover image for article How to Insert a Block From an External DWG in AutoLISP

Development

Automation

#autolisp

#autolisp-tutorial

#objectdbx

#autocad

#dwg

#vla-copyobjects

#vlax-release-object

#cad-automation

#single-line-diagram

You want to insert a block stored in an external DWG file — a shared block library, say — without opening that file in AutoCAD. The OPEN command can't do it: it attaches the file to the visible editor, flickers the screen, and pollutes your session with a drawing you only wanted a single block from. And calling vla-insertblock directly on the external file is no better: it inserts the entire drawing as a nested block, which you then have to explode and clean up.

The right solution is called ObjectDBX. In this tutorial, you will write, step by step, an AutoLISP function that:

  • opens the external DWG in the background, without ever touching the editor;
  • copies only the target block's definition into the active drawing;
  • inserts the block at the requested point, with guaranteed COM cleanup.

By the end, you'll have a test command ready to run on your own files.

Prerequisites

Before you start, make sure you have:

  • Full AutoCAD — ObjectDBX does not exist on AutoCAD LT;
  • a "library" DWG containing at least one named block, placed in a folder on AutoCAD's search path (OPTIONS > Files tab);
  • a text editor to create a .lsp file (the built-in VLIDE is enough).

Create an empty file named insert-dbx.lsp: this is where you'll paste each function from the tutorial. COM functions (vla-..., vlax-...) require ActiveX to be loaded, so add this line at the top of the file:

lisp
(vl-load-com)

Step 1: Create the ObjectDBX Instance

ObjectDBX instantiates a completely invisible side database, into which a DWG can be loaded without displaying it. This is the headless opening that OPEN simply can't do.

Add the following function to insert-dbx.lsp:

lisp
;; Creates a headless ObjectDBX instance.
;; Returns: [vla] DBX object, or nil on failure
(defun RNS:dbx-create (/ acad dbx ver candidates id)
  (setq acad (vl-catch-all-apply 'vlax-get-acad-object nil))
  (if (vl-catch-all-error-p acad)
    (progn
      (princ "\n[ERROR] RNS:dbx-create: vlax-get-acad-object failed.")
      (exit))
  )
  ;; Major ACADVER version, e.g. "24", "25", "26"
  (setq ver (itoa (atoi (getvar "ACADVER"))))
  (setq candidates
    (list "ObjectDBX.AxDbDocument"
          (strcat "ObjectDBX.AxDbDocument." ver)
          "ObjectDBX.AxDbDocument.26"
          "ObjectDBX.AxDbDocument.25"
          "ObjectDBX.AxDbDocument.24"
          "ObjectDBX.AxDbDocument.23"
          "ObjectDBX.AxDbDocument.22")
    dbx nil
  )
  (while (and candidates (null dbx))
    (setq id         (car candidates)
          candidates (cdr candidates)
          dbx        (vl-catch-all-apply 'vla-getinterfaceobject (list acad id)))
    (if (vl-catch-all-error-p dbx) (setq dbx nil))
  )
  (if (null dbx)
    (princ "\n[ERROR] RNS:dbx-create: Could not create ObjectDBX instance.")
    dbx)
)

Two things to understand in this code:

  1. The ObjectDBX.AxDbDocument ProgID depends on the installed AutoCAD version (.26, .25...). The function derives the right suffix from (getvar "ACADVER"), then walks a list of candidates: that way it works on any machine without manual configuration.
  2. Every COM call is wrapped in vl-catch-all-apply. Without it, an ObjectDBX failure would break the script with no usable message.

Check it: load the file with APPLOAD, then type (RNS:dbx-create) on the command line. AutoCAD should answer with something like #<VLA-OBJECT IAxDbDocument ...>.

Step 2: Load the External DWG Into the Instance

The DBX instance is empty for now. The next function loads your library file into it — this is the invisible opening itself.

Add it to your file:

lisp
;; Loads an external DWG into an existing DBX instance.
;; dbx: [vla] object created by RNS:dbx-create — blk-file: [str] resolved path
(defun RNS:dbx-open (dbx blk-file / result)
  (setq result (vl-catch-all-apply 'vla-open (list dbx blk-file)))
  (if (vl-catch-all-error-p result)
    (progn
      (princ (strcat "\n[ERROR] RNS:dbx-open : Could not open \"" blk-file "\""))
      nil)
    dbx)
)

Check it: reload the file, then test with your own DWG:

lisp
(setq dbx (RNS:dbx-create))
(RNS:dbx-open dbx (findfile "my-library.dwg"))
(vlax-release-object dbx)

If the path is valid, the second call returns the DBX object instead of nil — and nothing opens on screen.

Note: the last line of the test is not optional. dbx is a COM object created outside AutoCAD's document lifecycle: it will never be released automatically. Left unreleased, it leaks memory and can lock the source file. Build the reflex now — it shapes Step 4.

Step 3: Find and Copy the Block Definition

The DWG is loaded in the background; there's no need to pull anything out of it besides the target block. That's the whole point compared to vla-insertblock, which drags in the entire file.

Add these two functions:

lisp
;; Finds a named block definition in an open DBX database.
(defun RNS:dbx-find-block (dbx block-name / src-blocks src-blk-def)
  (setq src-blocks  (vla-get-blocks dbx))
  (setq src-blk-def (vl-catch-all-apply 'vla-item (list src-blocks block-name)))
  (if (vl-catch-all-error-p src-blk-def)
    (progn
      (princ (strcat "\n[ERROR] RNS:dbx-find-block : Block \"" block-name "\" not found"))
      nil)
    src-blk-def)
)
 
;; Copies a single block definition from DBX to the active drawing.
(defun RNS:dbx-copy-block (dbx src-blk-def / active-doc dest-blocks obj-array result)
  (setq active-doc (vla-get-activedocument (vlax-get-acad-object))
        dest-blocks (vla-get-blocks active-doc))
  (setq obj-array (vlax-make-safearray vlax-vbObject '(0 . 0)))
  (vlax-safearray-put-element obj-array 0 src-blk-def)
  (setq result (vl-catch-all-apply 'vla-copyobjects (list dbx obj-array dest-blocks)))
  (if (vl-catch-all-error-p result)
    (progn
      (princ "\n[ERROR] RNS:dbx-copy-block : Failed to copy block definition")
      nil)
    T)
)

RNS:dbx-find-block looks the definition up by name in the side database's Blocks collection. RNS:dbx-copy-block then builds a single-element COM array (a safearray) holding the block it found, and calls vla-copyobjects to duplicate it into the active drawing. No nested instance, no explode/delete pass.

Step 4: Assemble the Insertion Function

All that's left is to chain the three building blocks together, with two safeguards: don't reopen the DWG if the block already exists locally, and release the COM object no matter what happens.

Add the main function:

lisp
(defun RNS:insert-block-with-name
    (space block-filename block-name ins-point
     / blk-file dbx-obj src-blk-def pt result local-blocks local-exists copied)
  (setq blk-file (findfile block-filename))
  (setq local-blocks (vla-get-blocks (vla-get-activedocument (vlax-get-acad-object))))
 
  ;; Optimization: if the definition already exists locally, skip ObjectDBX
  (setq local-exists
    (not (vl-catch-all-error-p
           (vl-catch-all-apply 'vla-item (list local-blocks (strcase block-name))))))
 
  (if (not local-exists)
    ;; ObjectDBX pipeline — dbx-obj is released in the guaranteed cleanup
    ;; below, whether the previous steps succeed or fail
    (if (setq dbx-obj (RNS:dbx-create))
      (progn
        (if (RNS:dbx-open dbx-obj blk-file)
          (if (setq src-blk-def (RNS:dbx-find-block dbx-obj block-name))
            (setq copied (RNS:dbx-copy-block dbx-obj src-blk-def))
          )
        )
        (vlax-release-object dbx-obj) ;; guaranteed cleanup
      )
    )
  )
 
  ;; Insertion: if the block was already local, or if the DBX copy succeeded
  (if (or local-exists copied)
    (progn
      (setq pt (vlax-3d-point ins-point))
      (setq result (vl-catch-all-apply 'vla-insertblock (list space pt block-name 1.0 1.0 1.0 0.0)))
      (if (vl-catch-all-error-p result)
        (princ (strcat "\n[ERROR] Failed to insert \"" block-name "\""))
        result
      )
    )
    (princ (strcat "\n[ERROR] Aborted, block \"" block-name "\" could not be loaded"))
  )
)

(Lightweight version for readability — full input validation available in the source library.)

Follow the flow:

  1. local-exists queries the active drawing's Blocks collection first. If the block is already there, Steps 1 to 3 are skipped entirely — opening an external DWG has a cost, so pay it only once.
  2. Otherwise, the ObjectDBX pipeline runs: create, open, find, copy. The (vlax-release-object dbx-obj) sits outside any conditional block: it runs whether the copy succeeded or failed.
  3. Finally, vla-insertblock places the now-local definition at the requested point.

Step 5: Test It

Add a test command at the end of the file, adapting the DWG name and block name to your own library:

lisp
(defun RNS:test-insert (/ doc space)
  (setq doc   (vla-get-activedocument (vlax-get-acad-object))
        space (vla-get-modelspace doc))
  (RNS:insert-block-with-name space "my-library.dwg" "MY-BLOCK" '(0.0 0.0 0.0))
  (princ)
)

Check it: reload the file with APPLOAD, then type RNS:test-insert. The block appears at the drawing origin, without a single window opening. Run the command a second time: the insertion is instantaneous, because the definition is now local — that's the local-exists check at work. You can now insert a block from an external DWG in AutoLISP hundreds of times inside a loop, without ever reopening the source file.

Going Further

Beyond ObjectDBX, the real takeaway is this: anything repetitive in AutoCAD can almost always be automated. Inserting blocks, filling in attributes, generating complete diagrams — a well-built LISP routine turns hours of manual manipulation into a single, reliable, repeatable command.

That's exactly what we do at RANSAU SYSTEME. We industrialized this very pipeline for ITEC Engineering on a shared single-line diagram block library: discover the case study.

Have a question about this tutorial? A repetitive drafting task you'd like to automate? Get in touch: we can help you design and develop your LISP routines and CAD automation tools, from a one-off script to a complete business application.

Was this article helpful?

Share this article with your network

In brief

This AutoLISP tutorial shows in five steps how to insert a block from an external DWG without ever opening it in AutoCAD: create a headless ObjectDBX instance with vla-getinterfaceobject, load the file in the background with vla-open, copy only the target block's definition with vla-copyobjects, assemble everything into a function with a local cache check and unconditional COM release (vlax-release-object), then validate with a RNS:test-insert test command. Each step provides the complete code and a verification checkpoint.

Key takeaways

  • A five-step tutorial with complete code: headless ObjectDBX instance, background DWG loading, targeted copy of the block definition, insertion function with local cache, RNS:test-insert test command.
  • ObjectDBX creates an invisible side database to load an external DWG without ever opening it in the AutoCAD editor — something the OPEN command simply can't do.
  • vla-copyobjects copies a single block definition into the active drawing; vlax-release-object must be called unconditionally to avoid COM leaks.