Accessing DWG file from Excel Without Opening Editor

Accessing DWG file from Excel Without Opening Editor

michellem
Advocate Advocate
1,507 Views
9 Replies
Message 1 of 10

Accessing DWG file from Excel Without Opening Editor

michellem
Advocate
Advocate

Hello Everyone;

 

I am trying to access a dwg file from excel using VBA without loading the drawing into the Autocad editor. So far I have found that I can do so using GetInterfaceObject("ObjectDBX.AxDBDocument.22"); however if Autocad is not running I get a server not running/available error.

 

I then fixed this by using a call to CreateObject("AutoCAD.Application.22) which starts Autocad in the background. Unfortunately, this takes a significant amount of time and I noticed - by looking at the documents property - that Autocad also opened a default drawing.

 

So, I am wondering if there is a way to start just Autocad's ActiveX/COM server without the rest of Autocad?

 

I have a fully licensed version of Autocad & Revit.

 

Sincerely;

Michelle

 

Here is my code from Excel VBA:

Sub TEST()
  
  'Set acadApp = GetObject(, "AutoCAD.Application")
  'y = acadApp.Version
  
  Set myWS = CreateObject("WScript.Shell")
  On Error Resume Next
  y = myWS.RegRead("HKCR\AutoCAD.Application\CurVer\")
  If Err.Number <> 0 Then
    Exit Sub
  End If
  
  y = Split(y, ".", , vbTextCompare)
  y = y(UBound(y))
  
  Debug.Print "START"
  On Error Resume Next
    Set acadApp = GetObject(, "AutoCAD.Application." & y)
    If Err.Number <> 0 Then
      Flag = True
      Debug.Print "  WARNING: Needed to start AutoCad in background."
      Debug.Print "  Will close it at end of function."
      Err.Clear
      Set acadApp = CreateObject("AutoCAD.Application." & y)
      If Err.Number <> 0 Then
        Debug.Print "  ERROR: Could not get AutoCad Object."
      Else
        acadApp.ActiveDocument.Close False
      End If
    End If
  On Error GoTo 0
  
  Debug.Print "OBJECT DXB"
  Debug.Print ""
  Set acadDb = acadApp.GetInterfaceObject("ObjectDBX.AxDBDocument." & y)

  acadDb.Open ("E:\\Revit Local Files\\dwg to revit test.dwg")
  If Len(acadDb.Name) = 0 Then Exit Sub
  

  Set x = acadDb.ModelSpace
  Dim Out As Range
  Set Out = ActiveSheet.Range("a2")
  Out = Out.Resize(x.Count, 5)
  
  
  'acaddb.ModelSpace.Item(inx).GetBoundingBox (Min, Max)
  For inx = 0 To x.Count - 1
    Set Item = x.Item(inx)
    Debug.Print inx & ": " & Item.ObjectName
    Call Item.GetBoundingBox(Min, Max)
    Debug.Print "  min: " & Min(0) & ", " & Min(1) & " -- Max: "; Max(0) & ", " & Max(1)
    Out.Item(inx + 1, 1) = inx
    Out.Item(inx + 1, 2) = Min(0)
    Out.Item(inx + 1, 3) = Min(1)
    Out.Item(inx + 1, 4) = Max(0)
    Out.Item(inx + 1, 5) = Max(1)
    
  Next
  Set acadDb = Nothing
  
  
  If Flag Then
    acadApp.Quit
    Set acadApp = Nothing
    Debug.Print "DONE"
    Debug.Print ""
  End If
  Exit Sub
 

End Sub

 

0 Likes
1,508 Views
9 Replies
Replies (9)
Message 2 of 10

Ed__Jobe
Mentor
Mentor
Accepted solution

ObjectDBX is provided in an arx file loaded into AutoCAD and cannot run outside of AutoCAD. So, AutoCAD IS the ActiveX server. Therefore, the answer is No. However, if you are doing batch processing and you don't need the editor environment, then you can open an instance of AutoCAD in the background and work with files using ObjectDBX. Finally closing the application object when you are done. This improves speed, as you only need to open AutoCAD once.

 

A better way would be to use acCoreConsole.exe. Back in 2013, I think it was, Autodesk split the guts of AutoCAD (acCoreConsole.exe) from the GUI. This command line app works faster than Object dbx. It can run using lisp script or .NET.

 

Also, your code should be split into separate functions so that each one only performs one task, e.g. a function that returns an AcadApplication object and a function that returns an AxDbDocument object. This is especially important due to the use of "On Error Resume Next". A lot of new users assume that execution simply advances to the Next line of code. However, the "On Error Resume Next" statement remains in effect until you use "On Error GoTo 0". If not done, the Resume Next part will hide unexpected errors and you'll wonder why it's failing because you don't get any error messages. By encapsulating "On Error Resume Next" into a function, the error handling goes out of scope when the function does. See the examples below.

 

 

Public Funtion GetAcadApp () As AcadApplication
   On Error Resume Next
   Set GetAcadApp = GetObject(, "AutoCAD.Application")
   If GetAcadApp = Nothing Then
      Set GetAcadApp = CreateObject(, "AutoCAD.Application")
End Function

Public Funtion GetAxDbDoc (app As AcadApplication) As AxDbDocument
   Set GetAcadApp = app.GetInterfaceObject(, "ObjectDBX.AxDBDocument")
   'You could also include logic to open a file.
End Function

 

Ed


Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.
How to post your code.

EESignature

Message 3 of 10

michellem
Advocate
Advocate

Hello Ed;

After working on this awhile, I thought I would add an update for those who are interested.

I noted your comment about having cleaner code but I was not too worried about it at the time of the posting as VBA was just a way station to where I needed to go - that is an IronPython module for use with Revit. As I am much more familiar with VBA than Python, I thought I would get the basic idea working first and then port it into Python. The porting was much more of a slog than I anticipated.

I looked at AcCoreConsole but its emphasis on scripting did not seem to be what I needed. I do not need to change the Autocad file but rather extract info from it for use in Revit. Also, I could not seem to get a COM server from it.

I did find a slightly faster approach to accessing the Autocad COM server than using the CreateObject approach. In this approach, I ran a shell command that started Autocad with the following command line switches: /Automation /InPlaceServer. (This combination was found by searching the registry.) I then tried to get the running instance of AutoCad repeatedly - try, wait 2 seconds, try again.

The CreateObject approach took a min of 10 seconds to get the COM server whereas the Autocad With Switch method took a max of 6 seconds. Still an eternity from the user's perspective but better. From the behind the scenes perspective, the only difference I noticed was that CreateObject approach created a new dwg file whereas the other did not. Both approaches loaded the program, complete with GUI, into a hidden window.

Now onto the data processing part of the project.

Sincerely;
Michelle

PS: Send me a note if anyone would like to see the Python code.

0 Likes
Message 4 of 10

Ed__Jobe
Mentor
Mentor
Accepted solution

From your title, you are trying to get data from acad to xl. But you mention the ultimate goal is Revit. Can I assume that you are only using xl as middleware? i.e. you're not really doing anything with the data in xl, just using it to import it into Revit? If so, VBA is not the best choice. I haven't used Revit, but I know that it has a .NET api. You can use NET in Revit to reference the acad api. Then you can start an instance of acCoreConsole.exe and get the data you need directly. You don't need to use COM either.

Ed


Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.
How to post your code.

EESignature

Message 5 of 10

michellem
Advocate
Advocate

Oh no...It all about the programming language. I have some past experience with VBA and I knew my app needed to be external to Autocad. So I started with what I knew.

 

On the Revit side, as far as I can tell, you have the following choices for programming:
1) You could use the built-in Sharp IDE to create compiled macros or addins, using a variety of languages - VB.net, C#, Ruby and Python. At first glance, it would seem VB.net would be closest to VBA but there are significant differences.

 

2) You could use Dynamo with custom created nodes - nodes that are written in Python.

 

3) Or using Python, there is the Revit Python Shell addon which gives you the ability to do interactive coding. Type in a line of code and immediately see the results.

 

4) Also, using Python, there is PyRevit which comes with alot of user tools and allows users to easily add items to the Revit Ribbon by simply putting scripts into certain directories. No compiling necessary.

Since I have to learn a new language, it looked like Python was the way to go.

The part in your comment I am most intrigued by is "Then you can start an instance of acCoreConsole.exe and get the data you need directly." How exactly? I can not access the Revit API unless I am running in the context of Revit; therefore, I need to access the Acad API from Revit. 


During my research on this, I came across this bit:

https://www.keanw.com/2007/12/launching-autoc.html 

Most of AutoCAD’s current APIs are designed to be used “in-process” – this includes LISP, ObjectARX and AutoCAD’s .NET API. Given the availability of .NET Remoting, people often hope or expect AutoCAD to be drivable via .NET “out-of-process”, but this is not how the managed API was designed: it is really a wrapper on top of ObjectARX, whose performance is based on direct access to objects via pointers, and this simply does not extend to work across process boundaries. One of the great features of COM Automation is that it was designed to work either out-of-process (from an external EXE) or in-process (from VBA or by calling GetInterfaceObject() to load a VB6 ActiveX DLL). This is still the best way to drive AutoCAD from an external executable.

 

Michelle

0 Likes
Message 6 of 10

Ed__Jobe
Mentor
Mentor
Accepted solution

Yes, I was inaccurate partially, You do need COM. But acad NET api does have a COM interop api. I was thinking that since Revit doesn't have VBA, but does support NET, then you could skip xl. I haven't tested this but I would build a Revit project that references Autodesk.AutoCAD.Interop.dll. Originally, I thought that you could run that using acCorConsole, but I just remembered that your code was getting the bounding box. That option is only available in the editor context. When you run code in the console, or even using ObjectDbx, all you can retrieve is object properties, like StartPoint and EndPoint. The bounding box is calculated by the GetBoundingBox method and is not available without the editor environment. To speed up performance, what you could do is keep an instance of the AutoCAD app open and then only open docs instead of the the app and then the doc. The GetAcadApp function I gave you earlier doesn't open acad each time if it's already open.

Ed


Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.
How to post your code.

EESignature

Message 7 of 10

Ed__Jobe
Mentor
Mentor
Accepted solution

Oh, and I noticed from your first post that you didn't want a blank dwg to be created at startup. Just set the STARTUP variable to 3.

Ed


Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.
How to post your code.

EESignature

Message 8 of 10

michellem
Advocate
Advocate

Hmmm...I have gotten IronPython (which is .Net aware) running inside Revit's process, to access the Autocad COM server. I wonder if I am accessing the Interop.dll indirectly thru the Python libraries?

 

Also, on the BoundingBox part, my original VBA code is accessing the bounding box function thru AxDBDocument not the AcadApp.ActiveDoc...BoundingBox function. You would think that if Autocad required the GUI to determine the BoundingBox, that AxDBDocument (open DWG directly) would not enable the BoundingBox function.

Set acadDb = acadApp.GetInterfaceObject("ObjectDBX.AxDBDocument." & y)
Set x = acadDb.ModelSpace
Set Item = x.Item(inx)
Call Item.GetBoundingBox(Min, Max)

 
In my Python code, I have defined a class that as part of its initialization connects to the Autocad COM server. It also has an OpenDwg function.

 

The code outline would be:

 

  • Ask user for files to process.
  • Initialize Class - connect to COM server. Start ACAD if required.
  • Loop
    • Open DWG (opening a new file automatically closes currently open file.)
    • Process file (This is my next area to work on.)
  • Next
  • Free Class - stop COM server if my code started it, otherwise leave Acad running, break connection to COM server. Clean up as required

Michelle

Message 9 of 10

michellem
Advocate
Advocate

Oh, instead of BoundingBox maybe you were thinking of the equivalent of the Lisp function SSGET?

0 Likes
Message 10 of 10

Ed__Jobe
Mentor
Mentor
Accepted solution

Well, I guess I could be wrong...again. I thought I remembered reading that about bounding box or maybe it was extents recently. Don't get old. Anyway, it doesn't hurt to try it.

Ed


Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.
How to post your code.

EESignature