Author Topic: ODBX or ReadDwg?  (Read 5799 times)

0 Members and 1 Guest are viewing this topic.

BillZndl

  • Guest
ODBX or ReadDwg?
« on: February 06, 2013, 08:37:27 AM »
AutoCAD 2012, win7, VS2010 4.0-net

I'm thinking of converting an exising Autolisp routine that loops through a list of .dwg filenames, opening them in odbx,
and checking them to see if they contain a certain text string (see code below).
Code: [Select]
;;------------------;
;;10/13/05
;;Thanks to - T.Willey & Jeff Mishler
;;
(defun Chk4String (Doc String / flag)
  (vl-catch-all-apply
    '(lambda ()
       (vlax-for i (vla-get-Layouts Doc)
          (vlax-for Obj (vla-get-Block i)
      (if (and
           (or (= (vla-get-ObjectName Obj) "AcDbText")
               (= (vla-get-ObjectName Obj) "AcDbMText")
              )
               (vl-string-search String (vla-get-TextString Obj))
            )
           (progn
           (setq flag T)
           (exit)
              )
             )
            )
           )
          )
         )
            flag
  )
;;------------------;

My knowledge of .net programming is still small and I'm trying to determine what would work best:
Should I use dbx code similar to what is found HERE
or could I obtain desired results as efficiently using the ReadDwg method?
I'll be running this in the AutoCAD MDIActive document.
Thoughts or comments appreciated.

Just need a shove in the right direction.




n.yuan

  • Bull Frog
  • Posts: 348
Re: ODBX or ReadDwg?
« Reply #1 on: February 06, 2013, 10:09:48 AM »
I do not see why you need to use ObjectDBX now that you have already been in managed API project. I believe both ObjectDBX and opening side database the calling its ReadDwg are the same thing in terms of the ObjectARX being wrapped behind the scene, the former being exposed as COM and the latter being exposed as .NET. If you are doing managed code and the API provides all you need, why add an extra dependency (to the COM), which is not platform independent (32bit/64bit)?

BillZndl

  • Guest
Re: ODBX or ReadDwg?
« Reply #2 on: February 06, 2013, 10:41:41 AM »
I do not see why you need to use ObjectDBX now that you have already been in managed API project. I believe both ObjectDBX and opening side database the calling its ReadDwg are the same thing in terms of the ObjectARX being wrapped behind the scene, the former being exposed as COM and the latter being exposed as .NET. If you are doing managed code and the API provides all you need, why add an extra dependency (to the COM), which is not platform independent (32bit/64bit)?

Thanks Norman!
That's the answer I could only hope for.
Yes, it will be an ordinary netloadable .dll so like you say, I'll just read the .dwg and go from there.  :-)



TheMaster

  • Guest
Re: ODBX or ReadDwg?
« Reply #3 on: February 06, 2013, 07:29:23 PM »
AutoCAD 2012, win7, VS2010 4.0-net

I'm thinking of converting an exising Autolisp routine that loops through a list of .dwg filenames, opening them in odbx,
and checking them to see if they contain a certain text string (see code below).
Code: [Select]
;;------------------;
;;10/13/05
;;Thanks to - T.Willey & Jeff Mishler
;;
(defun Chk4String (Doc String / flag)
  (vl-catch-all-apply
    '(lambda ()
       (vlax-for i (vla-get-Layouts Doc)
          (vlax-for Obj (vla-get-Block i)
      (if (and
           (or (= (vla-get-ObjectName Obj) "AcDbText")
               (= (vla-get-ObjectName Obj) "AcDbMText")
              )
               (vl-string-search String (vla-get-TextString Obj))
            )
           (progn
           (setq flag T)
           (exit)
              )
             )
            )
           )
          )
         )
            flag
  )
;;------------------;

My knowledge of .net programming is still small and I'm trying to determine what would work best:
Should I use dbx code similar to what is found HERE
or could I obtain desired results as efficiently using the ReadDwg method?
I'll be running this in the AutoCAD MDIActive document.
Thoughts or comments appreciated.

Just need a shove in the right direction.

ObjectDBX (e.g., AxDbDocument) is mostly a wrapper around AcDbDatabase and some other functionality. The major issue is not which API you use, it is how many files you will try to open in a single AutoCAD session. There's a limit because no matter what you do, there will be incremental memory consumption with each processed file, that is not completely released until you exit the process. The details were explained by someone from ADN (forget who), but essentially, AutoCAD must allocate memory in chunks of a certain size, regardless of whether it actually needs that much memory or not, and hence, it can't always free it because some of it may be still be needed until the process is terminated.

So, if you use an in-process solution, you will have to terminate and restart AutoCAD after having processed a certain number of files, depending on their size. Of course, that makes an out-of-process solution easier because it can simply kill and restart AutoCAD as needed without having to kill itself in the process.  The best of both worlds, is to use an out-of-process controller along with an in-process worker, where the out-of-process controller only controls the process of opening files, and closing and restarting AutoCAD while the heavy-lifting is done by the in-process component.

That arrangement also makes it easer to control multiple AcCoreConsole sessions as well.

BillZndl

  • Guest
Re: ODBX or ReadDwg?
« Reply #4 on: February 07, 2013, 08:29:43 AM »
Thanks Tony.

Is there any way i can tell when I'm nearing the edge of running out of memory?
Most of my dwg files are ~200kb and I run maybe 2000 files at a time.
I guess the big white box that tells me "AutoCAD cannot continue" would be one indicator.   :lol:

TheMaster

  • Guest
Re: ODBX or ReadDwg?
« Reply #5 on: February 07, 2013, 10:43:23 AM »
Thanks Tony.

Is there any way i can tell when I'm nearing the edge of running out of memory?
Most of my dwg files are ~200kb and I run maybe 2000 files at a time.
I guess the big white box that tells me "AutoCAD cannot continue" would be one indicator.   :lol:

The System.Diagonstics.Process class has members that provide information about memory use, which might be helpful, but if you ask me, it would make sense to not try to test the limits.

I would just set a reasonable limit on the number of files/session, and not bother  checking memory use and/or if virtual memory faults are sloiwing things to a crawl.


BillZndl

  • Guest
Re: ODBX or ReadDwg?
« Reply #6 on: February 07, 2013, 11:22:13 AM »
Thanks Tony.

Is there any way i can tell when I'm nearing the edge of running out of memory?
Most of my dwg files are ~200kb and I run maybe 2000 files at a time.
I guess the big white box that tells me "AutoCAD cannot continue" would be one indicator.   :lol:

The System.Diagonstics.Process class has members that provide information about memory use, which might be helpful, but if you ask me, it would make sense to not try to test the limits.

I would just set a reasonable limit on the number of files/session, and not bother  checking memory use and/or if virtual memory faults are sloiwing things to a crawl.

Okay, makes sense. Thanks!

I'll post this here in case anyone else is interested and wants to play with it.
It's just a bare bones, internal net loadable "command" that will match the input text to any text it finds in the list of drawings.
I'll have to work on it further to make it fit out needs here but I'm on to other things for a while.
We use it to find all drawings that contain a part number that needs to be changed.
Code: [Select]
using System.IO;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Internal;
using Autodesk.AutoCAD.Runtime;
using AcadApp = Autodesk.AutoCAD.ApplicationServices.Application;

namespace FindTextInDWG
{
    public class FindTXT
    {
        [CommandMethod("WhereUsed", CommandFlags.Session)]

        public void FindTextInDrawing()
        {
            Document document = AcadApp.DocumentManager.MdiActiveDocument;           
            Editor editor = document.Editor;           

            PromptStringOptions options = new PromptStringOptions("\nEnter part number < string >");
            options.AllowSpaces = false;           

            PromptResult result = editor.GetString(options);

            if (result.Status == PromptStatus.OK && result.StringResult.Length > 0)
            {
                using (document.LockDocument())
                {
                    string filename = "Your text file list of drawings full path.txt"; //list of filenames.

                    if (File.Exists(filename))
                    {
                        try
                        {
                            string[] lines = System.IO.File.ReadAllLines(@"Your text file list of drawings full path.txt"); //array of filename strings.
                           
                            using (Transaction transaction = document.TransactionManager.StartTransaction())
                            {
                                foreach (string str in lines)
                                {
                                    Database database2 = new Database(false, false);
                                    database2.ReadDwgFile(str, System.IO.FileShare.ReadWrite, true, "");
                                   
                                    BlockTable table = (BlockTable)transaction.GetObject(database2.BlockTableId, OpenMode.ForRead);
                                    BlockTableRecord btr = (BlockTableRecord)transaction.GetObject(table[BlockTableRecord.ModelSpace], OpenMode.ForRead);

                                    foreach (ObjectId oid in btr)
                                    {
                                        Entity ent = (Entity)transaction.GetObject(oid, OpenMode.ForRead);

                                        if (ent != null && ent.GetType().Name.Equals("DBText"))
                                        {
                                            DBText dbt = (DBText)transaction.GetObject(ent.ObjectId, OpenMode.ForRead);

                                            if (dbt.TextString.Contains(result.StringResult))
                                            {
                                                editor.WriteMessage("\n dbt : " + dbt.TextString + " matches result: " + result.StringResult + " in " + str);
                                                //do other work, list file names etc. here.
                                            }
                                        }
                                    }   

                                    database2.Dispose();

                                }  //first foreach

                                transaction.Commit();

                            }      //using                           
                        }          //try block

                        catch (System.Exception exception)
                        {
                            editor.WriteMessage("\nError: " + exception);
                        }
                    }
                }                                   //doc lock.
            }                                       //if string input ok.
                        Utils.PostCommandPrompt();
        }                                           //find text string method end           
    }
}


http://forums.autodesk.com/t5/NET/ReadDwgFile-eFileSharingViolation/td-p/2076678
« Last Edit: February 07, 2013, 03:57:16 PM by BillZndl »

BillZndl

  • Guest
Re: ODBX or ReadDwg?
« Reply #7 on: March 07, 2013, 10:50:03 AM »
where the out-of-process controller only controls the process of opening files, and closing and restarting AutoCAD while the heavy-lifting is done by the in-process component.

I think I followed the idea correctly,
by writing an .exe that can be double clicked from a shortcut on the desktop
which opens a new session of AutoCAD and then fires the command method of the .dll (netload the .dll on AutoCAD startup).
Then the in process app does the work and closes AutoCAD.

I still have some cleanup here when I get time but I can use it in a pinch.

Thanks for all the help.  :kewl:
Code: [Select]
using System;
using System.Windows.Forms;
using Autodesk.AutoCAD.Interop;
using Autodesk.AutoCAD.Interop.Common;

namespace StartAcadApp
{
    public class StartAcadApp
    {
        //if AutoCAD 2012 used.
        const string progID = "AutoCAD.Application.18.2";

  // This C# code is compiled as a Windows application.

        public static void Main()
        {           

        AcadApplication acApp = null;

        Type acType = Type.GetTypeFromProgID(progID);

        acApp = (AcadApplication)Activator.CreateInstance(acType, true);
               
         if (acApp == null)
          {
            MessageBox.Show("Cannot create object of type \"" + progID + "\"");
          }       

         if (acApp != null)
          {
            acApp.Visible = true;
            acApp.WindowState = AcWindowState.acMax;

            acApp.ActiveDocument.SendCommand("WhereUsed\n");
          }
       }
    }
}


Code: [Select]
using System.IO;
using System.Text;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Internal;
using Autodesk.AutoCAD.Runtime;
using AcadApp = Autodesk.AutoCAD.ApplicationServices.Application;

namespace FindTextInDWG
{
    public class FindTEXT
    {
        [CommandMethod("WhereUsed", CommandFlags.Session)]

        public void FindTextInDrawing()
        {
            Document document = AcadApp.DocumentManager.MdiActiveDocument;           
            Editor editor = document.Editor;
            int counter = 0;

            PromptStringOptions options = new PromptStringOptions("Enter part number: < string > ");
            options.AllowSpaces = false;           
           
            PromptResult result = editor.GetString(options);     
           
            if (result.Status == PromptStatus.OK && result.StringResult.Length > 0)
            {
                editor.WriteMessage("Please wait while files are being searched.");         

                using (document.LockDocument())
                {
                    string filename = "G:\\AutoCAD2012Support\\LispText\\MyFileList.txt"; //list of filenames.

                    if (File.Exists(filename))
                    {
                        try
                        {                           

                            string[] lines = System.IO.File.ReadAllLines(@"G:\\AutoCAD2012Support\\LispText\\MyFileList.txt"); //array of filename strings.
 
                            StringBuilder sb = new StringBuilder();                           

                            using (Transaction transaction = document.TransactionManager.StartTransaction())
                            {
                               
                                foreach (string str in lines)
                                {                                                                       
                                    Database database2 = new Database(false, false);
                                    bool flag = false;

                                    database2.ReadDwgFile(str, System.IO.FileShare.ReadWrite, false, "");
                                   
                                    BlockTable table = (BlockTable)transaction.GetObject(database2.BlockTableId, OpenMode.ForRead);
                                    BlockTableRecord btr = (BlockTableRecord)transaction.GetObject(table[BlockTableRecord.ModelSpace], OpenMode.ForRead);

                                    foreach (ObjectId oid in btr)
                                    {
                                        Entity ent = (Entity)transaction.GetObject(oid, OpenMode.ForRead);

                                        if (ent != null && ent.GetType().Name.Equals("DBText"))
                                        {
                                            DBText dbt = (DBText)transaction.GetObject(ent.ObjectId, OpenMode.ForRead);

                                            if (dbt.TextString.Contains(result.StringResult))
                                            {                                               
                                                flag = true;
                                                break;                                               
                                            }
                                        }
                                    }   

                                    database2.Dispose();

                                    if (flag)
                                    {
                                        sb.AppendLine(str);
                                        counter = counter + 1;
                                    }

                                }  //first foreach
                               
                                    transaction.Commit();                               

                            }    //using

                                    File.WriteAllText("G:\\AutoCAD2012Support\\LispText\\WhereUsed.txt", sb.ToString());                               
                           
                        }          //try block

                        catch (System.Exception exception)
                        {
                            editor.WriteMessage("\nError: " + exception);
                        }
                    }
                }                                   //doc lock.
                            editor.WriteMessage("\nFound < " + counter + " > files that contain: " + result.StringResult);
            }                                       //if string input ok.
                            Utils.PostCommandPrompt();
                            AcadApp.Quit();
        }                                           //FindTextInDrawing method end           
    }
}

TheMaster

  • Guest
Re: ODBX or ReadDwg?
« Reply #8 on: March 09, 2013, 03:15:32 AM »
Quote
Code: [Select]

Entity ent = (Entity)transaction.GetObject(oid, OpenMode.ForRead);
if (ent != null && ent.GetType().Name.Equals("DBText"))
{
     DBText dbt = (DBText)transaction.GetObject(ent.ObjectId, OpenMode.ForRead);
     
     ..........

You might want to take a closer look at the above part of your code

     



BlackBox

  • King Gator
  • Posts: 3770
Re: ODBX or ReadDwg?
« Reply #9 on: March 09, 2013, 09:26:22 PM »
Quote
Code: [Select]

Entity ent = (Entity)transaction.GetObject(oid, OpenMode.ForRead);
if (ent != null && ent.GetType().Name.Equals("DBText"))
{
     DBText dbt = (DBText)transaction.GetObject(ent.ObjectId, OpenMode.ForRead);
     
     ..........

You might want to take a closer look at the above part of your code

   

I thought I just read recently the differences between direct casting, and the use of the as operator, where in a situation such as this it's preferable to use the latter, for logical test thereafter in order to avoid throwing an exception, no?

Code - C#: [Select]
  1. Entity ent = transaction.GetObject(oid, OpenMode.ForRead) as Entity;
  2. if (ent != null)
  3. {
  4.      DBText dbt = transaction.GetObject(ent.ObjectId, OpenMode.ForRead) as DBText;
  5.  
"How we think determines what we do, and what we do determines what we get."

Jeff H

  • Needs a day job
  • Posts: 6144
Re: ODBX or ReadDwg?
« Reply #10 on: March 10, 2013, 07:21:50 PM »
The only reason to use the as operator is if you are not sure of the runtime type. The as operator tries a direct cast then and returns null if fails or returns the casted type, which is more efficient than the is operator which compares the value against null and throws away the casted type. You can be hiding the fact the value your testing against is null.


Here is a little example to show the difference
Code - C#: [Select]
  1.     class Program
  2.     {
  3.         static void Main(string[] args)
  4.         {
  5.             string s = null;
  6.             object o = s;
  7.  
  8.  
  9.             if (s is string)
  10.             {
  11.                 Console.WriteLine("yes is");
  12.             }
  13.             else
  14.             {
  15.                 Console.WriteLine("no is");
  16.             }
  17.            
  18.             string s1 = o as string;
  19.  
  20.  
  21.             if (s1 != null)
  22.             {
  23.                 Console.WriteLine("yes as");
  24.             }
  25.             else
  26.             {
  27.                 Console.WriteLine("no as");
  28.             }
  29.  
  30.  
  31.  
  32.  
  33.             string s2 = (string)o;
  34.             if(o == null)
  35.             {
  36.                 Console.WriteLine("is a string and null");
  37.             }
  38.             else
  39.             {
  40.                 Console.WriteLine("is a string and not null");
  41.             }
  42.  
  43.  
  44.  
  45.  
  46.  
  47.  
  48.             Console.Read();
  49.         }
  50.     }



I would want error thrown if enumerating a BlockTableRecord and it not return a type that derives from an entity.
In both examples below you already have the object opened and would probably be better to do something like.

Code - C#: [Select]
  1.  
  2.             Entity ent = (Entity)transaction.GetObject(oid, OpenMode.ForRead);
  3.             DBText dbt = ent as DBText;
  4.             if (dbt != null)
  5.             {
  6.  
  7.  
  8.             }
  9.  


Or even better just check the ObjectId's ObjectClass property.


Tony has some code posted for working with attributes that has a good example of using the as operator.

TheMaster

  • Guest
Re: ODBX or ReadDwg?
« Reply #11 on: March 11, 2013, 02:47:49 AM »
Quote
Code: [Select]

Entity ent = (Entity)transaction.GetObject(oid, OpenMode.ForRead);
if (ent != null && ent.GetType().Name.Equals("DBText"))
{
     DBText dbt = (DBText)transaction.GetObject(ent.ObjectId, OpenMode.ForRead);
     
     ..........

You might want to take a closer look at the above part of your code

   

I thought I just read recently the differences between direct casting, and the use of the as operator, where in a situation such as this it's preferable to use the latter, for logical test thereafter in order to avoid throwing an exception, no?

Code - C#: [Select]
  1. Entity ent = transaction.GetObject(oid, OpenMode.ForRead) as Entity;
  2. if (ent != null)
  3. {
  4.      DBText dbt = transaction.GetObject(ent.ObjectId, OpenMode.ForRead) as DBText;
  5.  

The 'as' operator is usually used when you will use the resulting object, and/or when the test is being done to determine the type of the object, but that wasn't what my comment was about.

Look at the call to GetObject(), which is being passed the ObjectId of same object that was returned by the previous call. He's opening the same object twice, needlessly.

BillZndl

  • Guest
Re: ODBX or ReadDwg?
« Reply #12 on: March 11, 2013, 08:17:08 AM »
Quote
Code: [Select]

Entity ent = (Entity)transaction.GetObject(oid, OpenMode.ForRead);
if (ent != null && ent.GetType().Name.Equals("DBText"))
{
     DBText dbt = (DBText)transaction.GetObject(ent.ObjectId, OpenMode.ForRead);   
     ..........

You might want to take a closer look at the above part of your code


how did I not see that? 
Not only was I opening the objectId twice,
I was opening many of them unnecessarily(to check for type).
After some research, I found the ObjectClass.DXFName is available for that task.

Code: [Select]
foreach (ObjectId oid in btr)
   {
      if (oid.ObjectClass.DxfName == "TEXT")
        {
           DBText dbt = (DBText)transaction.GetObject(oid, OpenMode.ForRead);

             if (dbt.TextString.Contains(result.StringResult))
               {                                               
                    flag = true;
                    break;                                               
                }
        }
 }

Thanks!

« Last Edit: March 11, 2013, 04:04:34 PM by BillZndl »

MexicanCustard

  • Swamp Rat
  • Posts: 705
Re: ODBX or ReadDwg?
« Reply #13 on: March 12, 2013, 08:13:19 AM »
Quote
Code: [Select]
foreach (ObjectId oid in btr)
   {
      if (oid.ObjectClass.DxfName == "TEXT")
        {
           DBText dbt = (DBText)transaction.GetObject(oid, OpenMode.ForRead);

             if (dbt.TextString.Contains(result.StringResult))
               {                                               
                    flag = true;
                    break;                                               
                }
        }
 }

Thanks!

What the guys above were trying to show you is that if your going to cast the object anyway then use the "as" keyword and skip testing the ObjectId.

Code: [Select]
foreach (ObjectId oid in btr)
   {
      var dbt = transaction.GetObject(oid, OpenMode.ForRead) as DBText;
      if (dbt == null) continue;

             if (dbt.TextString.Contains(result.StringResult))
               {                                               
                    flag = true;
                    break;                                               
                }
 }
Revit 2019, AMEP 2019 64bit Win 10

BillZndl

  • Guest
Re: ODBX or ReadDwg?
« Reply #14 on: March 12, 2013, 09:18:38 AM »
What the guys above were trying to show you is that if your going to cast the object anyway then use the "as" keyword and skip testing the ObjectId.
Code: [Select]
foreach (ObjectId oid in btr)
   {
      var dbt = transaction.GetObject(oid, OpenMode.ForRead) as DBText;
      if (dbt == null) continue;

             if (dbt.TextString.Contains(result.StringResult))
               {                                               
                    flag = true;
                    break;                                               
                }
 }

Okay, thanks, but....
It seems to make sense, to only cast the objects you were going to use,
so while I'm only using the text objects, is there a reason cast all the objectId's?

Just wondering as I would like to learn.
In the case posted below, I can us the "as" operator to cast the Text objects once they are identified as such.
I know that checking the DXFName property takes 1/10th of the time of casting an object,
so wouldn't this be a better way? Or is the extra "if" statement merely a kludge?
Can you help me out here?  :)
Code: [Select]
foreach (ObjectId oid in btr)
                                    {

                                      if (oid.ObjectClass.DxfName == "TEXT")
                                        {
                                          DBText dbt = transaction.GetObject(oid, OpenMode.ForRead) as DBText;

                                            if (dbt.TextString.Contains(result.StringResult))
                                            {                                               
                                                sb.AppendLine(str);
                                                counter = counter + 1;
                                                break;                                               
                                            }
                                        }
                                    }

Jeff H

  • Needs a day job
  • Posts: 6144
Re: ODBX or ReadDwg?
« Reply #15 on: March 12, 2013, 10:49:06 AM »
I would think its calling GetObject() that is time consuming.

Unless your operating at the DBObject level every time you use GetObject() you have to make a cast.
Using the ObjectClass property just avoids calls to GetObject().

As in last example you posted I would do a hard cast because I would want an error thrown if the ObjectClass implementation was messed up and returning incorrect values.

BillZndl

  • Guest
Re: ODBX or ReadDwg?
« Reply #16 on: March 13, 2013, 06:47:06 AM »
I would think its calling GetObject() that is time consuming.

That's what I thought.

Quote
As in last example you posted I would do a hard cast because I would want an error thrown if the ObjectClass implementation was messed up and returning incorrect values.

Okay, good, thanks!