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 > Filestab); - a text editor to create a
.lspfile (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:
(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:
;; 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:
- The
ObjectDBX.AxDbDocumentProgID 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. - 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:
;; 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:
(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.
dbxis 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:
;; 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:
(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:
local-existsqueries the active drawing'sBlockscollection 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.- 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. - Finally,
vla-insertblockplaces 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:
(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.




