Import blocks from an external DWG file using .NET
We're going to use a "side database" - a drawing that is loaded in memory, but not into the AutoCAD editor - to import the blocks from another drawing into the one active in the editor.
Here's some C# code. The inline comments describe what is being done along the way. Incidentally, the code could very easily be converted into a RealDWG application that works outside of AutoCAD (we would simply need to change the destDb from the MdiActiveDocument's Database to the HostApplicationServices' WorkingDatabase, and use a different user interface for getting/presenting strings from/to the user).
using System;
using Autodesk.AutoCAD;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using System.Collections.Generic;
namespace BlockImport
{
public class BlockImportClass
{
[CommandMethod("IB")]
public void ImportBlocks()
{
DocumentCollection dm =
Application.DocumentManager;
Editor ed = dm.MdiActiveDocument.Editor;
Database destDb = dm.MdiActiveDocument.Database;
Database sourceDb = new Database(false, true);
PromptResult sourceFileName;
try
{
// Get name of DWG from which to copy blocks
sourceFileName =
ed.GetString("\nEnter the name of the source drawing: ");
// Read the DWG into a side database
sourceDb.ReadDwgFile(sourceFileName.StringResult,
System.IO.FileShare.Read,
true,
"");
// Create a variable to store the list of block identifiers
ObjectIdCollection blockIds = new ObjectIdCollection();
Autodesk.AutoCAD.DatabaseServices.TransactionManager tm =
sourceDb.TransactionManager;
using (Transaction myT = tm.StartTransaction())
{
// Open the block table
BlockTable bt =
(BlockTable)tm.GetObject(sourceDb.BlockTableId,
OpenMode.ForRead,
false);
// Check each block in the block table
foreach (ObjectId btrId in bt)
{
BlockTableRecord btr =
(BlockTableRecord)tm.GetObject(btrId,
OpenMode.ForRead,
false);
// Only add named & non-layout blocks to the copy list
if (!btr.IsAnonymous && !btr.IsLayout)
blockIds.Add(btrId);
btr.Dispose();
}
}
// Copy blocks from source to destination database
IdMapping mapping = new IdMapping();
sourceDb.WblockCloneObjects(blockIds,
destDb.BlockTableId,
mapping,
DuplicateRecordCloning.Replace,
false);
ed.WriteMessage("\nCopied "
+ blockIds.Count.ToString()
+ " block definitions from "
+ sourceFileName.StringResult
+ " to the current drawing.");
}
catch(Autodesk.AutoCAD.Runtime.Exception ex)
{
ed.WriteMessage("\nError during copy: " + ex.Message);
}
sourceDb.Dispose();
}
}
}
And that's all there is to it. More information on the various objects/properties/methods used can be found in the ObjectARX Reference.

Subscribe via RSS
With this update/override any existing blocks in the active dwg? If not how would you do so?
Posted by: Barry Ralphs | August 19, 2006 at 01:11 AM
Hi Barry,
Yes - the key is in the 4th argument to WblockCloneObject(), which is DuplicateRecordCloning.Replace. If you don't want to overwrite/update existing blocks you can use DuplicateRecordCloning.Ignore.
I'll post another entry next week to look into the code in a bit more detail - it was getting late on Friday night and I wanted to get the code posted before the weekend. There are a few details that are worth looking at a little closer.
Kean
Posted by: Kean | August 19, 2006 at 09:44 AM
Kean,
I have found your blog to be very helpful. I was hoping to meet you at AU2006 but didn't have time to find everyone. Don't even know if you were there.
Do you know if there is a way to copy a block that has sub blocks and create new blocks and sub blocks without the original blocks xdata? I'm trying to create a "dumb" copy of the original block without all the xdata to use in various other views.
Posted by: Stuart Shaw | December 14, 2006 at 09:47 PM
Hi Stuart,
I wasn't able to make it to this year's AU, unfortunately. Perhaps next year.
The way to go should be to go through the "mapping" object populated by WblockCloneObjects(), and post-process the various new blocks to dumb them down (removing their xdata).
Regards,
Kean
Posted by: Kean | December 15, 2006 at 10:10 AM
Hi,
I'm using this code in my application but I need to import also dynamic properties. How can I do it?
Thanks
Posted by: Stefano Benedetti | November 09, 2007 at 02:52 PM
Do you mean dynamic blocks? Please explain exactly what doesn't work in the code, and I'll take a look. I don't remember whether I tested this with dynamic blocks (I posted it quite some time ago).
Kean
Posted by: Kean | November 09, 2007 at 04:24 PM
Hi Kean. Yes I mean dynamic block.
I have a dwg (Dest.dwg) with a static block called PB.
I want to import a block called PB from an external file (Library.dwg).
When I run "IB" command block in Dest.dwg the PB block is modified but it doesn't have rotation, visibility and other dynamic actions.
Thanks
Posted by: Stefano Benedetti | November 09, 2007 at 04:45 PM
Hi Stefano,
I tested the IB command with one of the standard Dynamic Block libraries (in "C:\Program Files\Autodesk\AutoCAD 2008\Sample\Dynamic Blocks\Annotation - Metric.dwg" on my system), and it seemed to work fine. I could insert the imported Dynamic Blocks and they appeared to behave in a dynamic fashion. Can you test against the standard ones, and try to narrow down the specific problem?
Regards,
Kean
Posted by: Kean | November 12, 2007 at 11:05 AM
I post this comment to notify readers that Kean and I understand that the problem with dynamic block resides in AutoCAD 2006. In AutoCAD 2008 it's possibile to redefine a block also with dynamic property. Thanks Kean!
Posted by: Stefano Benedetti | November 14, 2007 at 07:57 PM
Kean,
Thanks for this sample code. I've tried several different similar methods in VB.Net but it always ended with a fatal error. If you don't mind I would like to explain how I entend to use the code to see if you have a better suggestion. I have hundreds of Annotation blocks that I use for both imperial and metric drawings. I'm sure you're aware that if the block being inserted is an annotative block the insunits are ignored. I need to insert the blocks and scale them by both the annotative scale and the insunits. I would also like to put these blocks on toolpalletes. I've written the code to scale the blocks with both parameters and it works great. The only problem is I wrote in VB.Net. My skills are pretty limited with C#. My plan was to code the command macro on the toolpallete to set the UserS1 variable to the path of the source drawing and UserS2 to the name of the block. Then I would call the command to import and scale the block. I was able to tweak your code to read these variables and import the block. Now all I need to do is convert my VB.Net code to C# so I can insert the block. Could you possible give me a similar example of your C# code in VB? Or if you have any other suggestions I would really like to hear them.
Thanks,
Steve
Posted by: Steve Blackwell | December 14, 2007 at 06:22 PM
Steve,
I'm sorry for the delay in responding. When converting between C# and VB, I usually use the tool I linked to by this post:
http://through-the-interface.typepad.com/through_the_interface/2006/08/some_cool_copyp.html
You can also use the online conversions tools directly that this AddIn connects to:
http://www.carlosag.net/Tools/CodeTranslator
http://www.kamalpatel.net/ConvertCSharp2VB.aspx
Also, apparently SharpDevelop is a good tool for code conversion.
Regards,
Kean
Posted by: Kean | January 23, 2008 at 02:03 PM
Is there a similar way for creating Block Definitions 'on the fly'? I need to go from a polyline entity to a block definition...
Posted by: Bert | August 08, 2008 at 01:42 PM
Yes, you can certainly add a new BlockTableRecord to the BlockTable and use its ID as the destination for the WblockCloneObjects() call (passing in the ObjectIds of the objects to put in the block).
Kean
Posted by: Kean | August 08, 2008 at 02:42 PM
Am I mistaken or is this the .NET equivalent to the following line of VBA code:
ThisDrawing.ModelSpace.InsertBlock(newCoord, "c:\blockName.dwg", xyzScale, xyzScale, xyzScale, 0)
I'm confused by the amount of code needed to do what was done in VBA with 1 line. Am I missing something?
Posted by: Brandon | September 18, 2008 at 07:35 PM
Well, yes and no... InsertBlock() loads the DWG into a BlockTableRecord and inserts it into the space upon which it's called. The above code imports blocks from a DWG file so they can be inserted at the user's leisure.
The COM API (to which VBA provides access) is higher-level than .NET - it's focus is automating tasks, while .NET (and ObjectARX, upon which it is very closely based) are more powerful APIs, yet sometimes require more code to do certain tasks.
It's ultimately a balance between control and succinctness: with lower-level code you get much greater control but it often comes with a coding overhead.
Kean
Posted by: Kean | September 19, 2008 at 05:57 PM
Hi Kean,
We have had advances in our project thanks to your blog. But now, we have found another problem. We have been able to import blocks and layer from a DWG to another, but we haven't been able to import the entities. Our project consist in import completly dwg into another dwg. Do you know if that is possible?
With the objectARX can add new entities in a blockTableRecord, but can we do that from another dwg?
Thanks,
Omar
Posted by: Omar Martínez | April 09, 2009 at 03:12 PM
Hi Omar,
WblockCloneObjects() and Insert() should get you there.
Please submit follow-up questions via ADN, if you're a member, or the Autodesk discussion groups, if not.
Regards,
Kean
Posted by: Kean Walmsley | April 09, 2009 at 04:08 PM