您的位置:首页 > 其它

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

2007-08-16 11:35 495 查看

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:





For information on licensing RealDWG please visit the RealDWG Developer Center.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: