November 2014

Sun Mon Tue Wed Thu Fri Sat
            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30            










« A handy .NET class to help manage long operations in AutoCAD | Main | Moving entities from one AutoCAD layer to another using .NET »

August 08, 2007

Updating a specific attribute inside a folder of AutoCAD drawings using RealDWG from .NET

This post finally takes the code last shown in this previous post, migrating it to use RealDWG to update a folder of DWGs without the need for AutoCAD to be installed on the system. A big thanks to Adam Nagy, a member of DevTech working from our Prague office, who turned around my request to convert the code to work with RealDWG in a matter of hours (if not minutes).

Firstly I need to make it clear that this code will not run without both RealDWG installed (I'm using RealDWG 2007, as the file format didn't change between 2007 and 2008) and a "clear text license key" inserted in the code. You'll see some missing lines (lines 9-15), where it needs to be inserted. Once you've licensed RealDWG you can get this key from Autodesk, allowing you to create RealDWG applications using .NET.

Below is the C# code, with the lines that have been added since the previous entry in red. Firstly, a summary of the changes...

There are clearly lines that are no longer needed - these have just been deleted.

In terms of the additional lines: it's a mixture of code that replaces the use of the AutoCAD editor for user-input, with some additional code needed specifically by RealDWG applications.

Lines 22-122 implement the HostApplicationServices class for our application, which RealDWG will call under certain circumstances, such as when it's trying to find particular support files. The FindFile() function has been implemented to search the Windows fonts folder and the RealDWG install folder for any fonts the system needs to adequately load a DWG. You would need to modify the code to point to the folder your application installs fonts into. Additionally I suspect there's work needed to open files that have fonts missing, mapping alternate fonts in their place: this post assumes that the fonts are all available; in a future post we can certainly look at adding support for alternate font mapping.

These fonts are especially important when dealing with alignment of text and attributes. If RealDWG cannot find the fonts on the system, the DWG will be updated with the new text but the attributes will not be positioned correctly (until they are edited in some way inside the AutoCAD editor). This is quite a common issue when developing with RealDWG, but thankfully one that's fairly easy to solve.

Lines 115-145 replace the use of the AutoCAD editor to prompt the user for the important information. In this case we're just using standard console functions for reading/writing data from/to a command window. This is also the reason for lines 174, 197, 208, 223, 233, 243 & 244 changing.

Line 185 sets the working database: this is very important - especially when working with fonts - and without it your attributes will not align properly.

The protocols of the UpdateAttributesInDatabase() and UpdateAttributesInBlock() functions have also been updated to include the static keyword, although I didn't mark those lines in red as they should probably have been static before. :-)

    1 using Autodesk.AutoCAD.DatabaseServices;

    2 using Autodesk.AutoCAD.Runtime;

    3 using System.Reflection;

    4 using System.IO;

    5 using System;

    6

    7 [assembly: SecuredApplication(

    8 @"THIS IS AN OBJECTDBX (TM) VERSION 2007 CLIENT LICENSE FOR THE EXCLUSIVE USE OF Kean Walmsley. YOUR USE OF OBJECTDBX(TM) IS GOVERNED BY THE SOFTWARE LICENSE INCLUDED IN THE PRODUCT. USE OF THIS SOFTWARE IN VIOLATION OF THE SOFTWARE LICENSE IS A VIOLATION OF U.S. AND/OR INTERNATIONAL COPYRIGHT LAWS AND TREATIES AND YOU MAY BE SUBJECT TO CRIMINAL PENALTIES FOR SUCH USE.",

   16

   17 namespace AttributeUpdater

   18 {

   19   class Program

   20   {

   21     #region RealDWG

   22     class MyHost : HostApplicationServices

   23     {

   24       private string SearchPath(string fileName)

   25       {

   26         // check if the file is already with full path

   27         if (System.IO.File.Exists(fileName))

   28           return fileName;

   29

   30         // check application folder

   31         string applicationPath =

   32           Path.GetDirectoryName(

   33             Assembly.GetExecutingAssembly().Location

   34           );

   35         if (File.Exists(applicationPath + "\\" + fileName))

   36           return applicationPath + "\\" + fileName;

   37

   38         // search folders in %PATH%

   39         string []paths =

   40           Environment.GetEnvironmentVariable(

   41             "Path").Split(new char[]{';'}

   42           );

   43         foreach (string path in paths)

   44         {

   45           // some folders end with \\, some don't

   46           string validatedPath

   47             = Path.GetFullPath(path + "\\" + fileName);

   48           if (File.Exists(validatedPath))

   49             return validatedPath;

   50         }

   51

   52         // check the Fonts folders

   53         string systemFonts =

   54           Environment.GetEnvironmentVariable(

   55             "SystemRoot"

   56           ) + "\\Fonts\\";

   57         if (File.Exists(systemFonts + fileName))

   58           return systemFonts + fileName;

   59

   60         string rdwgFonts =

   61           "C:\\Program Files\\Autodesk RealDWG 2007\\Fonts\\";

   62         if (File.Exists(rdwgFonts + fileName))

   63           return rdwgFonts + fileName;

   64

   65         return "";

   66       }

   67

   68       public override string FindFile(

   69         string fileName,

   70         Database database,

   71         FindFileHint hint

   72       )

   73       {

   74         // add extension if needed

   75         if (!fileName.Contains("."))

   76         {

   77           string extension;

   78           switch (hint)

   79           {

   80             case FindFileHint.CompiledShapeFile:

   81               extension = ".shx";

   82               break;

   83             case FindFileHint.TrueTypeFontFile:

   84               extension = ".ttf";

   85               break;

   86             case FindFileHint.PatternFile:

   87               extension = ".pat";

   88               break;

   89             case FindFileHint.ArxApplication:

   90               extension = ".dbx";

   91               break;

   92             case FindFileHint.FontMapFile:

   93               extension = ".fmp";

   94               break;

   95             case FindFileHint.XRefDrawing:

   96               extension = ".dwg";

   97               break;

   98             // Fall through. These could have

   99             // various extensions

  100             case FindFileHint.FontFile:         

  101             case FindFileHint.EmbeddedImageFile:

  102             default:

  103               extension = "";

  104               break;

  105           }

  106

  107           fileName += extension;

  108         }

  109

  110         return SearchPath(fileName);

  111       }

  112     }

  113     #endregion

  114

  115     static void Main(string[] args)

  116     {

  117       // RealDWG specific

  118       RuntimeSystem.Initialize(new MyHost(), 1033);

  119

  120       // Have the user choose the block and attribute

  121       // names, and the new attribute value

  122

  123       System.Console.Write(

  124         "\nEnter folder containing DWGs to process: "

  125       );

  126       string pathName =

  127         System.Console.ReadLine().ToUpper();

  128

  129       System.Console.Write(

  130         "\nEnter name of block to search for: "

  131       );

  132       string blockName =

  133         System.Console.ReadLine().ToUpper();

  134

  135       System.Console.Write(

  136         "\nEnter tag of attribute to update: "

  137       );

  138       string attbName =

  139         System.Console.ReadLine().ToUpper();

  140

  141       System.Console.Write(

  142         "\nEnter new value for attribute: "

  143       );

  144       string attbValue =

  145         System.Console.ReadLine().ToUpper();

  146

  147       string[] fileNames =

  148         Directory.GetFiles(pathName,"*.dwg");

  149

  150       // We'll use some counters to keep track

  151       // of how the processing is going

  152

  153       int processed = 0, saved = 0, problem = 0;

  154

  155       foreach (string fileName in fileNames)

  156       {

  157         if (fileName.EndsWith(

  158               ".dwg",

  159               StringComparison.CurrentCultureIgnoreCase

  160             )

  161         )

  162         {

  163           string outputName =

  164             fileName.Substring(

  165               0,

  166               fileName.Length - 4) +

  167             "_updated.dwg";

  168

  169           Database db = new Database(false, true);

  170           using (db)

  171           {

  172             try

  173             {

  174               System.Console.WriteLine(

  175                 "\n\nProcessing file: " + fileName

  176               );

  177

  178               db.ReadDwgFile(

  179                 fileName,

  180                 FileShare.ReadWrite,

  181                 false,

  182                 ""

  183               );

  184

  185               MyHost.WorkingDatabase = db;

  186

  187               int attributesChanged =

  188                 UpdateAttributesInDatabase(

  189                   db,

  190                   blockName,

  191                   attbName,

  192                   attbValue

  193                 );

  194

  195               // Display the results

  196

  197               System.Console.WriteLine(

  198                 "\nUpdated {0} instance{1} of " +

  199                 "attribute {2}.",

  200                 attributesChanged,

  201                 attributesChanged == 1 ? "" : "s",

  202                 attbName

  203               );

  204

  205               // Only save if we changed something

  206               if (attributesChanged > 0)

  207               {

  208                 System.Console.WriteLine(

  209                   "\nSaving to file: {0}", outputName

  210                 );

  211

  212                 db.SaveAs(

  213                   outputName,

  214                   DwgVersion.Current

  215                 );

  216

  217                 saved++;

  218               }

  219               processed++;

  220             }

  221             catch (System.Exception ex)

  222             {

  223               System.Console.WriteLine(

  224                 "\nProblem processing file: {0} - \"{1}\"",

  225                 fileName,

  226                 ex.Message

  227               );

  228               problem++;

  229             }

  230           }

  231         }

  232       }

  233       System.Console.WriteLine(

  234         "\n\nSuccessfully processed {0} files, of which {1} had " +

  235         "attributes to update and an additional {2} had errors " +

  236         "during reading/processing." +

  237         "\n[Press ENTER to close window]",

  238         processed,

  239         saved,

  240         problem

  241       );

  242

  243       // Delay the closing of the command prompt

  244       System.Console.Read();

  245     }

  246

  247     private static int UpdateAttributesInDatabase(

  248           Database db,

  249           string blockName,

  250           string attbName,

  251           string attbValue

  252         )

  253     {

  254       // Get the IDs of the spaces we want to process

  255       // and simply call a function to process each

  256

  257       ObjectId msId, psId;

  258

  259       Transaction tr =

  260         db.TransactionManager.StartTransaction();

  261       using (tr)

  262       {

  263         BlockTable bt =

  264           (BlockTable)tr.GetObject(

  265             db.BlockTableId,

  266             OpenMode.ForRead

  267           );

  268         msId =

  269           bt[BlockTableRecord.ModelSpace];

  270         psId =

  271           bt[BlockTableRecord.PaperSpace];

  272

  273         // Not needed, but quicker than aborting

  274         tr.Commit();

  275       }

  276       int msCount =

  277         UpdateAttributesInBlock(

  278           db,

  279           msId,

  280           blockName,

  281           attbName,

  282           attbValue

  283         );

  284       int psCount =

  285         UpdateAttributesInBlock(

  286           db,

  287           psId,

  288           blockName,

  289           attbName,

  290           attbValue

  291         );

  292       return msCount + psCount;

  293     }

  294

  295     private static int UpdateAttributesInBlock(

  296       Database db,

  297       ObjectId btrId,

  298       string blockName,

  299       string attbName,

  300       string attbValue

  301     )

  302     {

  303       // Will return the number of attributes modified

  304

  305       int changedCount = 0;

  306

  307       Transaction tr =

  308         db.TransactionManager.StartTransaction();

  309       using (tr)

  310       {

  311         BlockTableRecord btr =

  312           (BlockTableRecord)tr.GetObject(

  313             btrId,

  314             OpenMode.ForRead

  315           );

  316

  317         // Test each entity in the container...

  318

  319         foreach (ObjectId entId in btr)

  320         {

  321           Entity ent =

  322             tr.GetObject(entId, OpenMode.ForRead)

  323             as Entity;

  324

  325           if (ent != null)

  326           {

  327             BlockReference br = ent as BlockReference;

  328             if (br != null)

  329             {

  330               BlockTableRecord bd =

  331                 (BlockTableRecord)tr.GetObject(

  332                   br.BlockTableRecord,

  333                   OpenMode.ForRead

  334               );

  335

  336               // ... to see whether it's a block with

  337               // the name we're after

  338

  339               if (bd.Name.ToUpper() == blockName)

  340               {

  341                 // Check each of the attributes...

  342

  343                 foreach (

  344                   ObjectId arId in br.AttributeCollection

  345                 )

  346                 {

  347                   DBObject obj =

  348                     tr.GetObject(

  349                       arId,

  350                       OpenMode.ForRead

  351                     );

  352

  353                   AttributeReference ar =

  354                     obj as AttributeReference;

  355                   if (ar != null)

  356                   {

  357                     // ... to see whether it has

  358                     // the tag we're after

  359

  360                     if (ar.Tag.ToUpper() == attbName)

  361                     {

  362                       // If so, update the value

  363                       // and increment the counter

  364

  365                       ar.UpgradeOpen();

  366                       ar.TextString = attbValue;

  367                       ar.DowngradeOpen();

  368                       changedCount++;

  369                     }

  370                   }

  371                 }

  372               }

  373

  374               // Recurse for nested blocks

  375               changedCount +=

  376                 UpdateAttributesInBlock(

  377                   db,

  378                   br.BlockTableRecord,

  379                   blockName,

  380                   attbName,

  381                   attbValue

  382                 );

  383             }

  384           }

  385         }

  386         tr.Commit();

  387       }

  388       return changedCount;

  389     }

  390   }

  391 }

Here's the source file for download. Once again please note that this requires RealDWG and a clear text license key to be inserted in order to work.

When you run the executable this code builds, you should see something like this in a command prompt window:

Realdwg_attributes

For information on licensing RealDWG please visit the RealDWG Developer Center.

TrackBack

TrackBack URL for this entry:
http://www.typepad.com/services/trackback/6a00d83452464869e200e3982488fd8833

Listed below are links to weblogs that reference Updating a specific attribute inside a folder of AutoCAD drawings using RealDWG from .NET :

blog comments powered by Disqus

Feed/Share

10 Random Posts