.NET
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Retrieving block attribute using my VB.NET form application

16 REPLIES 16
Reply
Message 1 of 17
Macia.Lopez
6021 Views, 16 Replies

Retrieving block attribute using my VB.NET form application

Hi all,

 

Sorry for my basic type of question in advance.Smiley Indifferent

 

I have already developed my VB.NET Windows Form app (.NET 4.0 in VS2012) which does some Windows operation on roughly hundreds of AutoCAD drawing files(e.g. purely copy/move/rename type of actions).

 

Now I need to tell my app to communicate with AutoCAD 2013 and collect some attributes from a specific existing block in all of these drawings and then replace the collected values with some other values passed to AutoCAD by my Form  application (see diagram below if that helps: )

 

 

img.jpg

 

I had a look to couple of AutoCAD .NET blogs and realised how to make DLL files and leverage NETLOAD tool in AutoCAD afterwards but I am afraid DLL files and NETLOAD are not what I am looking after (correct me if I am wrong).

 

At this stage I am not asking for any coding assistance but rather I would like to clearly know what is the best pathway to achieve my goal described above.

 

Regards

Moji

16 REPLIES 16
Message 2 of 17
santoshr0114
in reply to: Macia.Lopez

Hi,

 

Usually to run a program on hundreds of files, user will not intend to open AutoCAD and run the program. He will expect a Windows Application which does the operation silently in the background without the graphical interface of AutoCAD.

 

But Autodesk recommends to run the program inside AutoCAD.

 

Hope this clears your pathway

Regards
Santosh
Message 3 of 17
Macia.Lopez
in reply to: santoshr0114

Thanks for the reply  santoshr0114,

 

Well yes and no about being clarified.

 

I dont mind perofrming all operations behind the scene (even prefered) but don't know how to achive that.

 

Currently I have splited my goal to two section:

 

  1. WinForm app will do its job on each and every single input file. This app will load a dll file (developed in next section)
  2. A dll application generated by VB.NET is taking care of all drawing modifications in AutoCAD. Save & close the drawing.

 

Have no freaking idea how to combine these two steps though Smiley Sad

 

Suggections welcomed.

 

Cheers

Message 4 of 17
santoshr0114
in reply to: Macia.Lopez

Your Windows Form should load an instance of AutoCAD.

Once the AutoCAD instance is loaded. run your function to perfom the task

 

To do this outside of AutoCAD and by not using Netload you need to used AutoCAD Interop

 

The Below Sample might get you started

 

Imports Autodesk.AutoCAD.Interop
    Public Function GetAcadApp() As AcadApplication

        Dim progID As String = My.Settings.strAcadProgID
        Dim acApp As AcadApplication = Nothing
        Try
            acApp = DirectCast(Marshal.GetActiveObject(progID), AcadApplication)
        Catch ex As Exception
            Try
                Dim acType As Type = Type.GetTypeFromProgID(progID)
                acApp = DirectCast(Activator.CreateInstance(acType, True), AcadApplication)
            Catch ex2 As Exception
                MessageBox.Show("Cannot create object of type """ & progID & """")

            End Try
        End Try

        If acApp IsNot Nothing Then

        End If

        Return acApp
    End Function

 

 

 

Regards
Santosh
Message 5 of 17
Macia.Lopez
in reply to: santoshr0114

There was a compilation error with your code:

 

'strAcadProgID' is not a member of '[...].My.Settings'. 

 

  

However, after replacing it by this:

 

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim AppComObj As AcadDocument = Nothing
        Dim ProgId As String = "AutoCAD.Application.19"
        Try
            AppComObj = GetObject(, ProgId)
        Catch ex As System.Exception
            Try
                ' Create a new instance of AutoCAD
                AppComObj = CreateObject(ProgId)
            Catch ex2 As Exception
                MsgBox(ex2.Message)
                Exit Sub
            End Try
        End Try
        AppComObj.Visible = True
    End Sub

I got no compilation error but sadly after triggering the code, this message pops up:

 

2013-05-08_102016.jpg

 

To be more precise, this is how my project reference setup looks like:

 

22.jpg

 

and I have imported followings:

Imports System.IO
Imports Autodesk.AutoCAD.Interop
Imports Autodesk.AutoCAD.Interop.Common

 

Message 6 of 17
santoshr0114
in reply to: Macia.Lopez

That was just a sample code to get you started. You have to change the code as per your needs.

Anyways the below code works for me.

 

public void OpenAutoCAD()
        {
            const string progID = "AutoCAD.Application";           
            try
            {
                acApp = (AcadApplication)Marshal.GetActiveObject(progID);
            }
            catch
            {
                try
                {
                    Type acType = Type.GetTypeFromProgID(progID);
                    acApp = (AcadApplication)Activator.CreateInstance(acType, true);
                }
                catch
                {
                    MessageBox.Show("Cannot create object of type \"" + progID + "\"");
                }
            }
            if (acApp != null)
            {
                try
                {
                    // By the time this is reached AutoCAD is fully
                    // functional and can be interacted with through code                                       

                    acApp.Visible = false;
                }
                catch (Exception ex)
                {
                    MessageBox.Show(this, "Problem executing component: " + ex.Message);
                }
            }
        }

 convert the same code to vb.net

 

Remove the references of AcCoreMgd, AcDbMgd, AcMgd. 

 

And you are creating a Windows Application(.exe) not a class library(.dll) right?

 

Regards
Santosh
Message 7 of 17
Macia.Lopez
in reply to: santoshr0114

Thank you so much. Simply added following line to it and it works like a charm:

        Dim acApp As AcadApplication = Nothing

 Regarding your question, I am creating an EXE file at the end of the day. But as I mentioned in previous posts, this EXE application should be able to leverage NETLOAD command in AutoCAD and load my dll library.

 

So, now, can you please tell me how can I inject an OPEN functionality in my 'try...catch' block. i.e. I want to have s/th like this in my try..catch block:

 

OpenDWG("c:\test.dwg")

 

In other words I need to call function to open a dwg document in AutoCAD session which has been just launched invisible.

 

The last step would be calling "NETLOAD" command for my opened *.dwg file. 

 

 

Cheers

Message 8 of 17
santoshr0114
in reply to: Macia.Lopez

Why do you need to call the netload command when all the fucnionality can be acieved without usig Netload.

 

to open just use AcadDocument doc = AcadApp.Documents.Open(DWGFILEPATH);

and then activate using doc.acivate();

 

then once the document / drawing is active you can perform your task to getting or updating the attributes.

 

Regards
Santosh
Message 9 of 17
santoshr0114
in reply to: Macia.Lopez

Hi There,

 

Accept this as solution if it has clarified your query

 

 

Regards
Santosh
Message 10 of 17
Macia.Lopez
in reply to: santoshr0114

Dealing with drawing via COM sounds more pleasant to me but I dont know how to convert my class library codes to be acceptable for COM. I had some codes in my DLL file to purge and also to update some attributes.

 

Here is the code which I was using to Purge (in DLL file):

 

Private Sub PurgeBlocks()
            Dim doc As Document = Application.DocumentManager.MdiActiveDocument
            Dim ed As Editor = doc.Editor
            Dim db As Database = doc.Database
            Using trans As Transaction = db.TransactionManager.StartTransaction()
                Dim bt As BlockTable = TryCast(trans.GetObject(db.BlockTableId, OpenMode.ForWrite), BlockTable)
                For Each oid As ObjectId In bt
                    Dim btr As BlockTableRecord = TryCast(trans.GetObject(oid, OpenMode.ForWrite), BlockTableRecord)
                    If btr.GetBlockReferenceIds(False, False).Count = 0 AndAlso Not btr.IsLayout Then
                        btr.[Erase]()
                    End If
                Next
                trans.Commit()
            End Using
        End Sub

 And these ones to update some attributes(in DLL file):

 

Private Sub UpdateAttributesInDatabase(db As Database, blockName As String, attbName As String, attbValue As String)
            Dim doc As Document = Application.DocumentManager.MdiActiveDocument
            Dim ed As Editor = doc.Editor
            ' Get the IDs of the spaces we want to process and simply call a function to process each
            Dim ModelSpaceId As ObjectId
            Dim PaperSpaceId As ObjectId
            Dim Transactn As Transaction = db.TransactionManager.StartTransaction()
            Using Transactn
                Dim bt As BlockTable = DirectCast(Transactn.GetObject(db.BlockTableId, OpenMode.ForRead), BlockTable)
                ModelSpaceId = bt(BlockTableRecord.ModelSpace)
                PaperSpaceId = bt(BlockTableRecord.PaperSpace)
                Transactn.Commit()  
            End Using
            UpdateAttributesInBlock(ModelSpaceId, blockName, attbName, attbValue)
            UpdateAttributesInBlock(PaperSpaceId, blockName, attbName, attbValue)
            ed.Regen()
        End Sub
'============================================================
        Private Sub UpdateAttributesInBlock(btRecordId As ObjectId, blockName As String, attbName As String, attbValue As String)
            Dim doc As Document = Application.DocumentManager.MdiActiveDocument
            Dim db As Database = doc.Database
            Dim ed As Editor = doc.Editor
            Dim tr As Transaction = doc.TransactionManager.StartTransaction()
            Using tr
                Dim btRecord As BlockTableRecord = DirectCast(tr.GetObject(btRecordId, OpenMode.ForRead), BlockTableRecord)
                For Each entId As ObjectId In btRecord
                    Dim ent As Entity = TryCast(tr.GetObject(entId, OpenMode.ForRead), Entity)
                    If ent IsNot Nothing Then
                        Dim br As BlockReference = TryCast(ent, BlockReference)
                        If br IsNot Nothing Then
                            Dim bd As BlockTableRecord = DirectCast(tr.GetObject(br.BlockTableRecord, OpenMode.ForRead), BlockTableRecord)  'to see whether it's a block with the name we're after
                            If bd.Name.ToUpper() = blockName Then               ' Check each of the attributes...
                                For Each attReferenceId As ObjectId In br.AttributeCollection
                                    Dim obj As DBObject = tr.GetObject(attReferenceId, OpenMode.ForRead)
                                    Dim attReference As AttributeReference = TryCast(obj, AttributeReference)
                                    If attReference IsNot Nothing Then                    'to see whether it has the tag we're after
                                        If attReference.Tag.ToUpper() = attbName Then     ' If so, update the value and increment the counter
                                            attReference.UpgradeOpen()
                                            attReference.TextString = attbValue
                                            attReference.DowngradeOpen()
                                        End If
                                    End If
                                Next
                            End If
                            UpdateAttributesInBlock(br.BlockTableRecord, blockName, attbName, attbValue)    ' Recurse for nested blocks
                        End If
                    End If
                Next
                tr.Commit()
            End Using
        End Sub

 Any idea for getting their equivalent for COM mode? I dont mind using any other code as long as it provides my the following functionalies:

  • Purge
  • UpdateAttribute(TagName As String, NewValue As String)

 

Alternatively, if I could not find any solution for conversion, how can I load a DLL code using "NETLOAD" command?

Message 11 of 17
santoshr0114
in reply to: Macia.Lopez

Explore more COM objects. like AcadDocument, AcadBlockReference, AcadEntity, AcadBlockReference.

Using these you can update atributes using com

 

I am still not sure why do you need "netload"

Regards
Santosh
Message 12 of 17
Macia.Lopez
in reply to: santoshr0114

 

I will advise soon. Thanks for that.  I need more sample codes for COM. That's all Smiley Indifferent


@santoshr0114 wrote:

Explore more COM objects. like AcadDocument, AcadBlockReference, AcadEntity, AcadBlockReference.

Using these you can update atributes using com

 

 

 

That was a thought for doomsday. Not serious though -  until I could turn my head around COM solutions.


@santoshr0114 wrote:

I am still not sure why do you need "netload"




Message 13 of 17
santoshr0114
in reply to: Macia.Lopez

You cannot find samples using COM easily. Search on CAD forums

You have to explore more on yourself rather than finding samples. Searching will eat your time.

 

If i come acoss any i can help you.

 

And its good to be prepared for doomsday.

 

Regards
Santosh
Message 14 of 17


@santoshr0114 wrote:

Why do you need to call the netload command when all the fucnionality can be acieved without usig Netload.

 

to open just use AcadDocument doc = AcadApp.Documents.Open(DWGFILEPATH);

and then activate using doc.acivate();

 

then once the document / drawing is active you can perform your task to getting or updating the attributes.

 


Sorry, you're not really offering very good advice here.

 

Interprocess COM is extremely slow, and that is why loading the part of the application that does the heavy-lifting into AutoCAD's process is the preferred solution.

 

The OP can achieve that in one of two ways:

 

1.  NETLOAD the in-process component into AutoCAD via NETLOAD, executed using SendCommand() and invoke a command registered by the in-process component using SendCommand(). This is not the preferred solution because it is not synchronous, and therefore, the caller will not be able to wait until the process has completed.

 

2.  Expose the in-process component as a COM object, that would be accessed from the out-of-process component using the AcadApplication's GetInterfaceObject() method. This is the preferred solution, because it can be made fully-synchronous, allowing the caller to wait until the in-process component has completed its task on each file before moving on to the next file.  

 

When using option 2, the in-process COM object must be derived from System.ServiceModel.ServicedComponent to ensure that it runs in the STA threading context.

 

 

Message 15 of 17

Thanks for the contribution DiningPhilosopher. Can you tell me more about your "option 2"?  The  current provided description sounds far beyond my knowledge on AutoCAD VB.NET programming. I am trying to improve though.

 

Therefore, a primitive approach i.e. communication with AutoCAD via COM looks more achievable option to me. Moreover, the speed is not super critical for my application. At this stage, the main purpose is to deliver the given task in a logical way.

 

Cheers

Message 16 of 17

Implementing a COM object in .NET that can be loaded into AutoCAD's process is fairly-advanced, and I don't have any VB.NET examples of that (or even a C# example that is 'bare-bones').

 

If speed is not critical, and you can do the job synchronously from another process then it may be the best approach given your ability, but it will be painfully-slow.

 

 

Message 17 of 17

I agree to your opinion DiningPhilosopher. But i haven't tried Exposing the in-process component as a COM object and i am not aware of the possibilites.

 

And i already suggested using In-Process communication in my first post as reply.

 

As the user will will be processing bunch of drawings at a time, so suggested using COM component. And yes the processing time is very slow compared to In-Process communication

Regards
Santosh

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk DevCon in Munich May 28-29th


Autodesk Design & Make Report

”Boost