Picking a block from a drawing and copying into another

Picking a block from a drawing and copying into another

charley.rocha
Contributor Contributor
5,641 Views
15 Replies
Message 1 of 16

Picking a block from a drawing and copying into another

charley.rocha
Contributor
Contributor

Hello everyone,

 

I'm stuck with something that I think should be quite simple: I have a drawing with multiple blocks in it. Based on some conditions (this part os O.K to me) the macro should pick one of the blocks, copy it and paste into another drawing.

 

I'm not experienced in Autocad VBA, but I'm used to Excel VBA, and thus, although I don't know to achieve want I want to do, it seems to be simple. However, looking around in this forum I found some very complicated solutions to similar questions and I thought: there must be a simpler way.

 

Can anyone help me out with this?

 

The seemingly straight-forward method Drawing.CopyObjects doesn't work for me. It throws the error: CopyObjects failed, with code -2145320837 (8021007b)

 

 

 

Sub CopyingBlock()

Dim myBlock As AcadBlockReference
Dim oEnt As AcadEntity

Dim newPoint(0 To 2) As Double
newPoint(0) = 0
newPoint(1) = 0
newPoint(2) = 0

Dim blockName As String
blockName = "Teste"

For Each oEnt In ThisDrawing.ModelSpace

    If TypeOf oEnt Is AcadBlockReference Then
        
        Set myBlock = oEnt
        
        If myBlock.Name = blockName Then
            
            Dim awCopy As AcadDocument
            Set awCopy = ThisDrawing
            Dim awPaste As AcadDocument
            Set awPaste = Application.Documents.Open("D:\Users\charley_r\Desktop\TesteFernandinhoPaste.dwg")
            awCopy.CopyObjects myBlock, awPaste.ModelSpace

            
        End If
        
    End If
    
Next oEnt


End Sub

 

 

0 Likes
Accepted solutions (1)
5,642 Views
15 Replies
Replies (15)
Message 2 of 16

norman.yuan
Mentor
Mentor

That is because you passed WRONG parameter type to the first argument of CopyObject() method: the argument expect an ARRAY of object, but you passes a SINGLE object of AcadBlockReference

 

The code should look like something below (whether you may have multiple BlockReferences to be copied or not):

 

Dim ent As AcadEntity

Dim blk As AcadBlockReference

Dim sourceObjs() As object

Dim i As Integer

 

Dim copyDwg As AcadDocument

Dim pasteDwg As AcadDocument

 

Set copyDwg=ThisDrawing

For Each ent In copyDwg.ModelSpace

  If TypeOf ent Is AcadBlockReference Then

    Set blk = ent

    '' Use EffectiveName would be safer, in case the block is dynamic one

    If UCase(blk.EffectiveName)="TESTE" Then 

      Redim Preserve sourceObjs(i)

      Set sourceObjs(i)=blk

      i = i + 1

    End If

  End If

Next

'' Only do copying if at least a target block reference is found

If UBound(sourceObjs)>=0 Then

  '' Go to the other drawing/ or Open it

  Set pasteDwg = ....

  copyDwg.CopyObject sourceObjs, pasteDwg.ModelSpace

End If

 

 

 

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 3 of 16

charley.rocha
Contributor
Contributor

Thanks a lot for your response, but I tried your code out and got the very same error: "CopyObjects method failed".

 

In fact, if the problem was passing an object instead of an array of objects, I would expect a Type Mismatch error (I may be wrong here, just wondering).

 

Any guess on what could be the problem now?

 

Thanks again.

0 Likes
Message 4 of 16

norman.yuan
Mentor
Mentor
Accepted solution

Well, you DEFINITELY need to pass CopyObjects() an array of object to be copied.

 

The cause of issue (after passing an array of objects!) very likely is because of opening the paste drawing. Since AutoCAD2015 an breaking change was introduced: switching active drawing (in your case, open the drawing for pasting) would cancel current command. This change would also cancel code execution in most, if not all, cases. So, you need to have the destination drawing per-opened before executing your copying code. Following code works OK with my Acad2018/2020, where I have 2 drawing opened, and one has a block "Teste", whoch is to be copied to another:

 

Option Explicit

Public Sub CopyTest()

    Dim ent As AcadEntity
    Dim blk As AcadBlockReference
    Dim sourceObjs() As Object
    Dim i As Integer
    
    Dim copyDwg As AcadDocument
    Dim pasteDwg As AcadDocument
    
    Dim dwg As AcadDocument
    For Each dwg In Documents
        Select Case UCase(dwg.Name)
            Case "DRAWING1.DWG"
            Set copyDwg = dwg
            Case "DRAWING3.DWG"
            Set pasteDwg = dwg
        End Select
    Next
    
    If copyDwg Is Nothing Then
        MsgBox "Source drawing is not open!"
        Exit Sub
    End If

    If pasteDwg Is Nothing Then
        MsgBox "Destination drawing is not open!"
        Exit Sub
    End If
    
    For Each ent In copyDwg.ModelSpace
        If TypeOf ent Is AcadBlockReference Then
            Set blk = ent
            If UCase(blk.EffectiveName) = "TESTE" Then
                ReDim Preserve sourceObjs(i)
                Set sourceObjs(i) = blk
                i = i + 1
            End If
        End If
    Next

    If VarType(sourceObjs) <> vbEmpty And UBound(sourceObjs) >= 0 Then
        copyDwg.CopyObjects sourceObjs, pasteDwg.ModelSpace
    End If

End Sub

Notice that I have a bit code to make sure both source/destination drawings are currently open in AutoCAD. Because I use drawing name to identify them, so, which one is active drawing does not matter when the code execution begins.

 

If you do want to only open the destination drawing when the source objects are found in source drawing, you may try open the destination drawing in memory only as ObjectDBX document, so the code execution would not be break by active drawing switch. 

 

Norman Yuan

Drive CAD With Code

EESignature

Message 5 of 16

charley.rocha
Contributor
Contributor

My deepest thanks mate! That code worked perfectly.

 

However, I wonder if opening the drawing in a separate method ou something could be a workaround for this.

 

You know, I'm just starting an automation project and the code I posted is the very beginning of things. When I'm finished, the user would click a button in an Excel spreadsheet, which will contain all the parameters and logic needed to determine which block to copy/paste.

 

I'll make a test telling Excel to open the drawings, and then calling the Autocad macro you provided to see if it works. In the mean time, feel free to share any thoughts on this, since I don't want the user to have the source and destination drawings open in front of him by the time the macro starts.

 

Anyways, you were of big help and your code is surely a breakthrough for my project. Thanks.

0 Likes
Message 6 of 16

norman.yuan
Mentor
Mentor

Well, if you start the process from external app (Excel VBA), you can certainly have both source and destination drawing open in AutoCAD first. I'd imagine the steps would be:

 

1. User runs Excel macro, which get an existing runing AutoCAD session, or create one (using GetObject()/CreateObjeect());

2. VBA code in Excel opens source drawing, and destination drawing(s) in AutoCAD;

3. VBA Code in Excel load the AutoCAD VBA project and then run the AutoCAD VBA macro to actually do the copying and saving the change to destination drawing(s);

 

Technically, you can do the copying all with VBA code in Excel, so that you have only one place to manage the code. However, that would slow down the execution significantly; I'd prefer the drawing processing work (copying) run inside AutoCAD as AutoCAD VBA macro.

 

I am also not fond of having to run 2 apps (Excel and AutoCAD) for such a simple work. I'd prefer do all in AutoCAD VBA. That is, you just need to read data from Excel sheet (if the data is stored as CSV file, the VBA code to read it would be a lot simpler, and running Excel app to reach the data is completely not needed).

 

 

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 7 of 16

charley.rocha
Contributor
Contributor

Yeah, at this point my Autocad macro does the exact three steps you pointed out 😃

 

I get your concern about using two different applications, but in my case the user must have Excel open to input the required information to determine which block to copy from a template drawing to the final one, hence the button on the Excel spreadsheet. In my opinion, it would make little sense to have the user open Excel, input the data, close Excel, open Autocad and then run the macro...

 

Nevertheless, I appreciate your help so far and hope to be able to count on you again in future questions 😃

 

Best regards from Brazil.

0 Likes
Message 8 of 16

charley.rocha
Contributor
Contributor

@norman.yuan 

 

Sorry for bothering you with this matter again, but I've been working with this project as I told you previously, and now I'm starting to write the real code. Almost everything is working fine: I'm able to open a template drawing, copy the correct block and place it on the final drawing. After that I can even move the block to its correct location. However, I just realized that I need to explode the blocks after pasting them. I tried

 

destObjs(0).Move blk.InsertionPoint, newPoint
destObjs(0).Explode

 

with no success. The blocks remain as blocks.

 

Do you have any idea of what could possibly be going on here?

0 Likes
Message 9 of 16

grobnik
Collaborator
Collaborator

@charley.rocha 

Hi, could you try to apply to your code what generically below indicated:

Set explodedObjects = blockRefObj.Explode

 

I'm not sure, because I never tried this functionality

Let us know.

0 Likes
Message 10 of 16

charley.rocha
Contributor
Contributor

@grobnik Thanks for answering.

 

I tried to apply your suggestion but I'm not sure what type of variable should the explodedObjs be. I tried Variant, but got a Type Mismatch error. Tried an array of objects too, but got a "Can't assign to array" error.

0 Likes
Message 11 of 16

grobnik
Collaborator
Collaborator

@charley.rocha 

Hi, probably should be a simple "object" because you are exploding a block, so should be no more a block, and in this case cannot be declared as "block" or "block reference". 

Otherwise try to not declare (be careful if you settled before in your code Option Explicit).

As I wrote I'm not sure about the code, but should work because it's a simply block explode functionality, the main issue it's how to point to block to explode.

I'm sorry for poor support.

 

Bye

0 Likes
Message 12 of 16

Ed__Jobe
Mentor
Mentor

Here's the Help topic for the Explode method. As shown, the return object needs to be a variant. If you tried Variant, It should be working. Show your code so we can see what went wrong.

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 13 of 16

norman.yuan
Mentor
Mentor

@charley.rocha wrote:

@norman.yuan 

 

Sorry for bothering you with this matter again, but I've been working with this project as I told you previously, and now I'm starting to write the real code. Almost everything is working fine: I'm able to open a template drawing, copy the correct block and place it on the final drawing. After that I can even move the block to its correct location. However, I just realized that I need to explode the blocks after pasting them. I tried

 

destObjs(0).Move blk.InsertionPoint, newPoint
destObjs(0).Explode

 

with no success. The blocks remain as blocks.

 

Do you have any idea of what could possibly be going on here?


Your code (destObjs(0).Explode) should work, and I bet it must have worked and you just did not noticed:

 

AcadBlockReference.Explode() method works a bit differently from command "EXPLODE" - besides generate all the entities in the block definition in the same space as the block reference by "exploding it", the original block reference REMAINS UNCHANGED. It is up to you as programmer to decide you want to delete or not. The command "EXPLODE" combines the 2 steps in one (i.e. the block reference is removed after exploding API call).

 

You can verify it after AcadBlockReference.Explode() call without deleing the original block rerference: in AutoCAD editor, you should find all the block component entities and the block reference itself.

Norman Yuan

Drive CAD With Code

EESignature

Message 14 of 16

charley.rocha
Contributor
Contributor

@norman.yuan @Ed__Jobe @grobnik 

 

Looks like there was some bug in my autocad. After one more frustrated run of my macro, I decided to manually delete the blocks from the final drawing, and then purge them. Saved the document and tried again. As @norman.yuan predicted, all the blocks were duplicated on top of their exploded counterparts. Then I just added

 

 

destObjs(0).delete

 

 

and all I got were the exploded entities.

 

Thank your all for your good will. 

0 Likes
Message 15 of 16

Ed__Jobe
Mentor
Mentor

Yes, you would only need to set explodedObjs if you planned to do something with them after exploding.

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

0 Likes
Message 16 of 16

charley.rocha
Contributor
Contributor

Hi again folks,

 

In case you feel like lending me a hand again, I did manage to copy all the blocks and assembly my desired drawing. Right now I'm at ~95% of what I want to achieve and I'm struggling with this, that I created a new topic for because it's another issue:

 

https://forums.autodesk.com/t5/visual-basic-customization/rotate-a-line-and-update-angular-dimension...

 

Thanks

0 Likes