Kean Walmsley


  • About the Author
    Kean on Google+

July 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 31    








« Counting the RegAppIds for a specific AutoCAD drawing and its external references using .NET | Main | Transforming a list of RegAppIds from XML to HTML using XSLT »

January 28, 2011

Persisting RegAppId data from an external AutoCAD drawing to XML using .NET

This post takes the code from the last post and extends it to serialize the collected RegAppId data to an XML file, as per Step 3 below:

  1. Implement a command to collect RegAppId information for the active document
  2. Extend this command to work on a drawing not loaded in the editor
  3. Save our RegAppId information to some persistent location (XML)
  4. Create a modified version of ScriptPro 2.0 (one of our Plugins of the Month) to call our command without opening the drawing

I’ve chosen once again to leverage the very handy XmlSerializer class to handle serialization to and from XML: all we have to do is get our data in a data structure with appropriate metadata and the rest is taken care of by .NET. More or less, anyway. :-)

Here’s the updated code, with the new/modified lines in red (and here’s the updated source file):

    1 using Autodesk.AutoCAD.ApplicationServices;

    2 using Autodesk.AutoCAD.DatabaseServices;

    3 using Autodesk.AutoCAD.EditorInput;

    4 using Autodesk.AutoCAD.Runtime;

    5 using System.Collections.Generic;

    6 using System.Text;

    7 using System.Xml;

    8 using System.Xml.Serialization;

    9 using System.IO;

   10 using System;

   11 

   12 namespace XrefRegApps

   13 {

   14   [XmlRoot("RegAppData")]

   15   public class RegAppData

   16   {

   17     // The name of our drawing

   18 

   19     private string _name;

   20 

   21     [XmlAttribute("Name")]

   22     public string Name

   23     {

   24       get { return _name; }

   25       set { _name = value; }

   26     }

   27 

   28     // The location of our drawing

   29 

   30     private string _dwg;

   31 

   32     [XmlAttribute("File")]

   33     public string File

   34     {

   35       get { return _dwg; }

   36       set { _dwg = value; }

   37     }

   38 

   39     // The list of our drawing's Registered Application IDs

   40 

   41     private List<string> _regAppIds = new List<string>();

   42 

   43     [XmlAttribute("RegAppIds")]

   44     public List<string> RegAppIds

   45     {

   46       get { return _regAppIds; }

   47       set { _regAppIds = value; }

   48     }

   49 

   50     public void AddRegAppId(string id)

   51     {

   52       if (!_regAppIds.Contains(id))

   53         _regAppIds.Add(id);

   54     }

   55   }

   56 

   57   public class Commands

   58   {

   59     private const string regAppDataXml = "RegAppData.xml";

   60 

   61     private void SerializeRegAppData(

   62       Dictionary<string, RegAppData> dict

   63     )

   64     {

   65       if (dict.Count > 0)

   66       {

   67         // No direct way to serialize a dictionary to XML,

   68         // so we'll create a list from it and serialize that

   69 

   70         List<RegAppData> regData =

   71           new List<RegAppData>(dict.Count);

   72         foreach (KeyValuePair<string, RegAppData> kv in dict)

   73         {

   74           regData.Add(kv.Value);

   75         }

   76 

   77         // Serialize our list to the specified XML file

   78 

   79         XmlSerializer xs =

   80           new XmlSerializer(typeof(List<RegAppData>));

   81         XmlTextWriter xw =

   82           new XmlTextWriter(regAppDataXml, Encoding.UTF8);

   83         xs.Serialize(xw, regData);

   84         xw.Close();

   85       }

   86     }

   87 

   88     // Read and return the previous Registered Application data

   89     // from our stored XML file

   90 

   91     private Dictionary<string, RegAppData>

   92       DeserializeRegAppData()

   93     {

   94       if (File.Exists(regAppDataXml))

   95       {

   96         XmlSerializer xs =

   97           new XmlSerializer(typeof(List<RegAppData>));

   98         XmlTextReader xr = new XmlTextReader(regAppDataXml);

   99         if (xs.CanDeserialize(xr))

  100         {

  101           // No direct way to serialize a dictionary to XML,

  102           // so we'll read a list and populate a dictionary

  103           // from it

  104 

  105           List<RegAppData> regData =

  106             (List<RegAppData>)xs.Deserialize(xr);

  107           xr.Close();

  108           Dictionary<string, RegAppData> dict =

  109             new Dictionary<string, RegAppData>();

  110           foreach (RegAppData rad in regData)

  111           {

  112             dict.Add(rad.File, rad);

  113           }

  114           return dict;

  115         }

  116       }

  117       return new Dictionary<string, RegAppData>();

  118     }

  119 

  120     [CommandMethod("XRA")]

  121     public void ExternalReferenceRegisteredApps()

  122     {

  123       Document doc =

  124           Application.DocumentManager.MdiActiveDocument;

  125       Database db = doc.Database;

  126       Editor ed = doc.Editor;

  127 

  128       // Prompt the user for the path to a DWG to test

  129 

  130       PromptStringOptions pso =

  131         new PromptStringOptions(

  132           "\nEnter path to root drawing file: "

  133         );

  134       pso.AllowSpaces = true;

  135       PromptResult pr = ed.GetString(pso);

  136       if (pr.Status != PromptStatus.OK)

  137         return;

  138 

  139       if (!File.Exists(pr.StringResult))

  140       {

  141         ed.WriteMessage("\nFile does not exist.");

  142         return;

  143       }

  144 

  145       try

  146       {

  147         // We start by reading any existing RegApp data

  148         // from XML

  149 

  150         Dictionary<string, RegAppData> regData =

  151           DeserializeRegAppData();

  152 

  153         // Load the DWG into a side database

  154 

  155         Database mainDb = new Database(false, true);

  156         using (mainDb)

  157         {

  158           mainDb.ReadDwgFile(

  159             pr.StringResult,

  160             FileOpenMode.OpenForReadAndAllShare,

  161             true,

  162             null

  163           );

  164 

  165           // We need an additional step to resolve Xrefs

  166 

  167           mainDb.ResolveXrefs(false, false);

  168 

  169           // Get the XrefGraph for the specified database

  170 

  171           XrefGraph xg = mainDb.GetHostDwgXrefGraph(true);

  172 

  173           // Loop through the nodes in the graph

  174 

  175           for (int i = 0; i < xg.NumNodes; i++)

  176           {

  177             // Get each node and check its status

  178 

  179             XrefGraphNode xgn = xg.GetXrefNode(i);

  180 

  181             switch (xgn.XrefStatus)

  182             {

  183               // If Un*, print a message

  184 

  185               case XrefStatus.Unresolved:

  186                 ed.WriteMessage(

  187                   "\nUnresolved xref \"{0}\"", xgn.Name

  188                 );

  189                 break;

  190               case XrefStatus.Unloaded:

  191                 ed.WriteMessage(

  192                   "\nUnloaded xref \"{0}\"", xgn.Name

  193                 );

  194                 break;

  195               case XrefStatus.Unreferenced:

  196                 ed.WriteMessage(

  197                   "\nUnreferenced xref \"{0}\"", xgn.Name

  198                 );

  199                 break;

  200               case XrefStatus.Resolved:

  201                 {

  202                   // If Resolved, get the RegAppTable

  203 

  204                   Database xdb = xgn.Database;

  205                   if (xdb != null)

  206                   {

  207                     Transaction tr =

  208                     xdb.TransactionManager.StartTransaction();

  209                     using (tr)

  210                     {

  211                       RegAppTable rat =

  212                         (RegAppTable)tr.GetObject(

  213                           xdb.RegAppTableId,

  214                           OpenMode.ForRead

  215                         );

  216 

  217                       // We'll store our RegApp data in an

  218                       // object

  219 

  220                       RegAppData dwgData = new RegAppData();

  221                       dwgData.Name =

  222                         (i == 0 ?

  223                           Path.GetFileNameWithoutExtension(

  224                             xgn.Name

  225                           ) :

  226                           xgn.Name

  227                         );

  228                       dwgData.File = xdb.Filename;

  229 

  230                       // Collect the contained names of the

  231                       // RegAppTableRecords (the RegAppIds)

  232                       // in a list

  233 

  234                       foreach (ObjectId id in rat)

  235                       {

  236                         RegAppTableRecord ratr =

  237                           (RegAppTableRecord)tr.GetObject(

  238                             id,

  239                             OpenMode.ForRead

  240                           );

  241                         dwgData.AddRegAppId(ratr.Name);

  242                       }

  243 

  244                       // Store the object in our dictionary,

  245                       // replacing any existing item

  246                       // (keyed off the entire DWG path, not

  247                       // just the name)

  248 

  249                       if (regData.ContainsKey(dwgData.File))

  250                         regData.Remove(dwgData.File);

  251                       regData.Add(dwgData.File, dwgData);

  252 

  253                       // Print the drawing information

  254                       // and the RegAppId count

  255 

  256                       ed.WriteMessage(

  257                         "\nDrawing \"{0}\" (\"{1}\") with " +

  258                         "{2} RegAppIds",

  259                         dwgData.Name,

  260                         dwgData.File,

  261                         dwgData.RegAppIds.Count

  262                       );

  263 

  264                       // Even if only reading, commit the

  265                       // transaction (it's cheaper than

  266                       // aborting)

  267 

  268                       tr.Commit();

  269                     }

  270                   }

  271                   break;

  272                 }

  273             }

  274           }

  275           // Save our data to XML

  276 

  277           SerializeRegAppData(regData);

  278         }

  279       }

  280       catch (System.Exception ex)

  281       {

  282         ed.WriteMessage(

  283           "\nProblem reading/processing \"{0}\": {1}",

  284           pr.StringResult, ex.Message

  285         );

  286       }

  287     }

  288   }

  289 }

When we first run the XRA command with our Civil sample file, we see the contents of the XML file (which seems to be written to the current directory – we could easily hardcode another location, of course) are:

  <?xml version="1.0" encoding="utf-8" ?>
- <ArrayOfRegAppData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <RegAppData Name="Master Site Plan" File="C:\Program Files\Autodesk\AutoCAD 2011\Sample\Sheet Sets\Civil\Master Site Plan.dwg" RegAppIds="ACAD RAK DCA_FIGURE_XENT ADE ALIGNMENT_DB_APP SDI_PROFILE SDSK_POINT SVPLINE ADCADD_ZZ SDSK_PMN CIVIL_DRAFT CIVIL_LINE_TABLE CIVIL_CURVE_TABLE CIVIL_SPIRAL_TABLE SDI_XSECTIONS ACAD_PSEXT ACAD_MLEADERVER DCO15" />
  <RegAppData Name="PB-BASE" File="C:\Program Files\Autodesk\AutoCAD 2011\Sample\Sheet Sets\Civil\PB-BASE.dwg" RegAppIds="ACAD RAK DCA_FIGURE_XENT ALIGNMENT_DB_APP SDI_PROFILE ACAD_PSEXT ACAD_MLEADERVER" />
  <RegAppData Name="PB-EX41" File="C:\Program Files\Autodesk\AutoCAD 2011\Sample\Sheet Sets\Civil\PB-EX41.dwg" RegAppIds="ACAD SDSK_POINT SVPLINE ACAD_PSEXT DCO15 ACAD_MLEADERVER" />
  <RegAppData Name="PB-EX61" File="C:\Program Files\Autodesk\AutoCAD 2011\Sample\Sheet Sets\Civil\PB-EX61.dwg" RegAppIds="ACAD SVPLINE ACAD_PSEXT DCO15 ACAD_MLEADERVER" />
  </ArrayOfRegAppData>

if we run the XRA command again, this time specifying a file from the Architectural sample:

Command: XRA

Enter path to root drawing file: C:\Program Files\Autodesk\AutoCAD 2011\Sample\Sheet Sets\Architectural\A-01.dwg

Drawing "A-01" ("C:\Program Files\Autodesk\AutoCAD 2011\Sample\Sheet Sets\Architectural\A-01.dwg") with 21 RegAppIds

Drawing "Grid Plan" ("C:\Program Files\Autodesk\AutoCAD 2011\Sample\Sheet Sets\Architectural\Res\Grid Plan.dwg") with 7 RegAppIds

Drawing "Wall Base" ("C:\Program Files\Autodesk\AutoCAD 2011\Sample\Sheet Sets\Architectural\Res\Wall Base.dwg") with 11 RegAppIds

We will see the XML now contains additional entries:

  <?xml version="1.0" encoding="utf-8" ?>
- <ArrayOfRegAppData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <RegAppData Name="Master Site Plan" File="C:\Program Files\Autodesk\AutoCAD 2011\Sample\Sheet Sets\Civil\Master Site Plan.dwg" RegAppIds="ACAD RAK DCA_FIGURE_XENT ADE ALIGNMENT_DB_APP SDI_PROFILE SDSK_POINT SVPLINE ADCADD_ZZ SDSK_PMN CIVIL_DRAFT CIVIL_LINE_TABLE CIVIL_CURVE_TABLE CIVIL_SPIRAL_TABLE SDI_XSECTIONS ACAD_PSEXT ACAD_MLEADERVER DCO15" />
  <RegAppData Name="PB-BASE" File="C:\Program Files\Autodesk\AutoCAD 2011\Sample\Sheet Sets\Civil\PB-BASE.dwg" RegAppIds="ACAD RAK DCA_FIGURE_XENT ALIGNMENT_DB_APP SDI_PROFILE ACAD_PSEXT ACAD_MLEADERVER" />
  <RegAppData Name="PB-EX41" File="C:\Program Files\Autodesk\AutoCAD 2011\Sample\Sheet Sets\Civil\PB-EX41.dwg" RegAppIds="ACAD SDSK_POINT SVPLINE ACAD_PSEXT DCO15 ACAD_MLEADERVER" />
  <RegAppData Name="PB-EX61" File="C:\Program Files\Autodesk\AutoCAD 2011\Sample\Sheet Sets\Civil\PB-EX61.dwg" RegAppIds="ACAD SVPLINE ACAD_PSEXT DCO15 ACAD_MLEADERVER" />
  <RegAppData Name="A-01" File="C:\Program Files\Autodesk\AutoCAD 2011\Sample\Sheet Sets\Architectural\A-01.dwg" RegAppIds="ACAD CONTENTBLOCKDESCRIPTION CONTENTBLOCKICON CONTENTTABDATA CONTENTCURRENTTAB ACAD_PSEXT PE_URL RAK AcAecLayerStandard ACAD_DSTYLE_DIMTEXT_FILL ACAD_EXEMPT_FROM_CAD_STANDARDS AcDbBlockRepETag AcDbDynamicBlockTrueName AcDbDynamicBlockGUID AcDbBlockRepBTag ACAD_DSTYLE_DIMEXT_LENGTH ACAD_DSTYLE_DIMEXT_ENABLED ACAD_DSTYLE_DIM_LINETYPE ACAD_DSTYLE_DIM_EXT1_LINETYPE ACAD_DSTYLE_DIM_EXT2_LINETYPE ACAD_MLEADERVER" />
  <RegAppData Name="Grid Plan" File="C:\Program Files\Autodesk\AutoCAD 2011\Sample\Sheet Sets\Architectural\Res\Grid Plan.dwg" RegAppIds="ACAD CONTENTBLOCKDESCRIPTION CONTENTBLOCKICON CONTENTTABDATA CONTENTCURRENTTAB ACAD_PSEXT ACAD_DSTYLE_DIMTEXT_FILL" />
  <RegAppData Name="Wall Base" File="C:\Program Files\Autodesk\AutoCAD 2011\Sample\Sheet Sets\Architectural\Res\Wall Base.dwg" RegAppIds="ACAD CONTENTBLOCKDESCRIPTION CONTENTBLOCKICON CONTENTTABDATA CONTENTCURRENTTAB RAK AcAecLayerStandard ACAD_PSEXT PE_URL ACAD_DSTYLE_DIMTEXT_FILL ACAD_MLEADERVER" />
  </ArrayOfRegAppData>

This is really a reasonably simple way of storing our data: it should be trivial to add additional data to the XML, such as the version of the DWG, an entry holding the number of entries in the RegAppId list (although we do have the list itself).

In the next post we may move on to modifying ScriptPro 2.0, but we may take a quick detour and put together an XML stylesheet to visualize the data – let’s see next week.

blog comments powered by Disqus

10 Random Posts