This was a nice little solution I saw provided recently by Viru Aithal from DevTech India. It’s a simple command to open a drawing and generate preview icons for each of the blocks it contains. It forces an icon to be generated – when it doesn’t already exist – via Viru’s old trick of using InvokeMember() to call IAcadDocument::SendCommand() via COM (which is synchronous, where Document.SendStringToExecute() is asynchronous and only executes once the command has completed).
Viru’s trick allows him to call SendCommand() without creating a dependency on the AutoCAD Type Library (something developers often prefer to avoid, as it simplifies supporting multiple AutoCAD product versions). A similar effect can presumably be achieved using Type Embedding, but I like Viru’s approach for just the odd function call.
Here’s the C# code:
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using System.IO;
namespace BlockPreviews
{
publicclassCommands
{
[CommandMethod("GBP", CommandFlags.Session)]
staticpublicvoid GenerateBlockPreviews()
{
Editor ed =
Application.DocumentManager.MdiActiveDocument.Editor;
PromptFileNameResult res =
ed.GetFileNameForOpen(
"Select file for which to generate previews"
);
if (res.Status != PromptStatus.OK)
return;
Document doc = null;
try
{
doc =
Application.DocumentManager.Open(res.StringResult, false);
}
catch
{
ed.WriteMessage("\nUnable to read drawing.");
return;
}
Database db = doc.Database;
string path = Path.GetDirectoryName(res.StringResult),
name = Path.GetFileName(res.StringResult),
iconPath = path + "\\" + name + " icons";
int numIcons = 0;
Transaction tr =
doc.TransactionManager.StartTransaction();
using (tr)
{
BlockTable table =
(BlockTable)tr.GetObject(
db.BlockTableId, OpenMode.ForRead
);
foreach (ObjectId blkId in table)
{
BlockTableRecord blk =
(BlockTableRecord)tr.GetObject(
blkId, OpenMode.ForRead
);
// Ignore layouts and anonymous blocks
if (blk.IsLayout || blk.IsAnonymous)
continue;
// Attempt to generate an icon, where one doesn't exist
if (blk.PreviewIcon == null)
{
object ActiveDocument = doc.AcadDocument;
object[] data = { "_.BLOCKICON " + blk.Name + "\n" };
ActiveDocument.GetType().InvokeMember(
"SendCommand",
System.Reflection.BindingFlags.InvokeMethod,
null, ActiveDocument, data
);
}
// Hopefully we now have an icon
if (blk.PreviewIcon != null)
{
// Create the output directory, if it isn't yet there
if (!Directory.Exists(iconPath))
Directory.CreateDirectory(iconPath);
// Save the icon to our out directory
blk.PreviewIcon.Save(
iconPath + "\\" + blk.Name + ".bmp"
);
// Increment our icon counter
numIcons++;
}
}
tr.Commit();
}
doc.CloseAndDiscard();
ed.WriteMessage(
"\n{0} block icons saved to \"{1}\".", numIcons, iconPath
);
}
}
}
When we run the GBP command use it to select a number of AutoCAD’s sample files containing blocks, we see it generates a number of icons for each:
Command: GBP
13 block icons saved to "C:\Program Files\Autodesk\AutoCAD 2012 -
English\Sample\Dynamic Blocks\Annotation - Imperial.dwg icons".
Command: GBP
10 block icons saved to "C:\Program Files\Autodesk\AutoCAD 2012 -
English\Sample\Dynamic Blocks\Architectural - Imperial.dwg icons".
Command: GBP
6 block icons saved to "C:\Program Files\Autodesk\AutoCAD 2012 -
English\Sample\Dynamic Blocks\Electrical - Imperial.dwg icons".
Command: GBP
11 block icons saved to "C:\Program Files\Autodesk\AutoCAD 2012 -
English\Sample\Dynamic Blocks\Civil - Imperial.dwg icons".
While the code executes, you’ll see the BLOCKICON command displaying a dialog for each block it processes, but once completed the drawing is closed (without being saved to disk).
Here’s what we see in Windows Explorer:
Update:
See this post for an improved technique for achieving this.