Kean Walmsley

July 2009

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 31  

Twitter Updates

    follow me on Twitter



    « 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 :

    Comments

    Hello kean,

    Could you please let me know how I can run this program or load this program in Auto cad 2006 or 2008. If I put this program into Note pad then what file extension i have to use. what command I have to use to load into Auto cad.
    Regards
    wakibd@gmail.com

    The whole point of RealDWG (and this post), is to tun outside AutoCAD. You should take a loot at this equivalent AutoCAD post and probably some getting started material.

    Kean

    Thanks kean for your very quick reply

    Hi Kean,

    why we didnt use selection sets to update the block attributes in the above example?

    Can we use selection sets in RealDWG?

    Thanks,
    Praful

    SelectionSets do not exist in RealDWG.

    Kean

    Verify your Comment

    Previewing your Comment

    This is only a preview. Your comment has not yet been posted.

    Working...
    Your comment could not be posted. Error type:
    Your comment has been posted. Post another comment

    The letters and numbers you entered did not match the image. Please try again.

    As a final step before posting your comment, enter the letters and numbers you see in the image below. This prevents automated programs from posting comments.

    Having trouble reading this image? View an alternate.

    Working...

    Post a comment

    Feed & Share

    Search