I have written this code that adds a block to the block table but does not insert it. In a test project it seems to work flawlessly:
<CommandMethod("test")> Public Sub test() Dim blkname As String = "PS01-L2x2x0.25" Dim blockpath As String = "C:\" If blockpath = "" Then MsgBox("Path to blocks is not valid.") Return Else Dim fullpath As String = blockpath & blkname & ".dwg" Dim curdwg As Document = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument Dim ed As Editor = curdwg.Editor If File.Exists(fullpath) Then Dim dwg As Database = ed.Document.Database Dim blkdwg As New Database blkdwg.ReadDwgFile(fullpath, FileOpenMode.OpenForReadAndAllShare, True, vbNullString) dwg.Insert(blkname, blkdwg, False) blkdwg.Dispose() Else MsgBox(fullpath & vbCr & "Does not exist") Return End If End If End Sub
This simular code is being called from inside a transaction in my primary program, but gives me a eNoDatabase error
Public Shared Sub insb(ByVal blkname As String) Dim curdwg As Document = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument Dim ed As Editor = curdwg.Editor Dim dwg As Database = ed.Document.Database Dim blockpath As String = GetSetting("AMG", "SupportIT", "BlockPath") If blockpath = "" Then MsgBox("Path to blocks is not valid.") Return Else Dim fullpath As String = blockpath & blkname & ".dwg" If File.Exists(fullpath) Then Try Using doclock As DocumentLock = ed.Document.LockDocument Dim blkdwg As New Database blkdwg.ReadDwgFile(fullpath, FileOpenMode.OpenForReadAndAllShare, True, vbNullString) 'Error on line below! dwg.Insert(blkname, blkdwg, False) blkdwg.Dispose() End Using Catch ex As Exception MsgBox(ex.Message) End Try Else MsgBox(fullpath & vbCr & "Does not exist") Return End If End If End Sub End Class
I'm not sure why exactly it is throwing this error, however I have narrowed it down to the statement "dwg.Insert(blkname, blkdwg, False)"
Any help would be greatly appreciated.
Thank you,
Jason Self
Hi,
first: I have not tried your code, just read it. I saw 2 "maybe"-problems:
1) in normal cases the locking of a document should be done before you start/commit/dispose a transaction. In you second code-snippet you start a document-locking during a transaction is active (at least you wrote there is one active).
However I never tried it in your way (first create transaction and then lock the document), but it's worth to try this to change!
2) when you create a new databaseobject and use it for reading a DWG-file, you have to end the read-connection/stop the access to the file. If you don't you will get an when you use it the next time (read the DWG). So after reading the DWG and before disposing the object you should add:
blkdwg.CloseInput(True)
So I wonder if you get the problem with the first insert after you restarted AutoCAD, my feeling is, the problem occurs during the second call to this function.
Hoping that at least one of the above ideas helps,
- alfred -
I don't have the .NET Ref Guide in front of me right now, but the problem could be this line:
>>Dim blkdwg As New Database
In ObjectARX (which the .NET API closely follows), the Database constructor tales two arguments:
AcDbDatabase::AcDbDatabase(bool buildDefaultDrawing = true, bool noDocument = false); in ObjectARX
When creating a database to read a DWG file into (i.e. when using ReadDwgFile(), you must always set buildDefaultDrawing to false. Otherwise, you may experience unexpected (and possibly intermittent) errors.
So try changing your line of code to:
Dim blkdwg As New Database(False, False)
to see if it fixes the problem.
Thank you both for your replies. First, I have to admit that docklock was an accident, I added the statement while I was trying to resolve the issue and never removed it again...gone now. Thanks and Good eye on that one. Unfortunately dwg.closeinput(True) didn't have any effect on the reslut...it still returned eNoDatabase.
Adding (False, False) per your suggestion, Stephen, seems to have progressed the situation...instead of an FE with a eNoDatabase error it now drops through to my Catch statemtent with a eNoInputFiler. Any Ideas....Below is all of the code in the offending class...The blockjig stuff isn't mine, it's....borrowed...from an example I pulled off ot the web and works great if the block already exists in the drawing....my code is at the bottom.
Thanks again,
Jason
Imports Autodesk.AutoCAD.Runtime Imports Autodesk.AutoCAD.ApplicationServices Imports Autodesk.AutoCAD.DatabaseServices Imports Autodesk.AutoCAD.EditorInput Imports Autodesk.AutoCAD.Geometry Imports System.Collections.Generic Imports System.IO 'Namespace BlockJig Class CBlockJig Inherits EntityJig Private _pos As Point3d Private _attPos As Dictionary(Of String, Point3d) Private _tr As Transaction Public Sub New(ByVal tr As Transaction, ByVal br As BlockReference) MyBase.New(br) _pos = br.Position ' Initialize our dictionary with the tag / ' AttributeDefinition position _attPos = New Dictionary(Of String, Point3d)() _tr = tr Dim btr As BlockTableRecord = DirectCast(_tr.GetObject(br.BlockTableRecord, OpenMode.ForRead), BlockTableRecord) If btr.HasAttributeDefinitions Then For Each id As ObjectId In btr Dim obj As DBObject = tr.GetObject(id, OpenMode.ForRead) Dim ad As AttributeDefinition = TryCast(obj, AttributeDefinition) If ad IsNot Nothing Then _attPos.Add(ad.Tag, ad.Position) End If Next End If End Sub Protected Overrides Function Update() As Boolean Dim br As BlockReference = TryCast(Entity, BlockReference) br.Position = _pos If br.AttributeCollection.Count <> 0 Then For Each id As ObjectId In br.AttributeCollection Dim obj As DBObject = _tr.GetObject(id, OpenMode.ForRead) Dim ar As AttributeReference = TryCast(obj, AttributeReference) ' Apply block transform to att def position If ar IsNot Nothing Then ar.UpgradeOpen() ar.Position = _attPos(ar.Tag).TransformBy(br.BlockTransform) End If Next End If Return True End Function Protected Overrides Function Sampler(ByVal prompts As JigPrompts) As SamplerStatus Dim opts As New JigPromptPointOptions(vbLf & "Select insertion point:") opts.BasePoint = New Point3d(0, 0, 0) opts.UserInputControls = UserInputControls.NoZeroResponseAccepted Dim ppr As PromptPointResult = prompts.AcquirePoint(opts) If _pos = ppr.Value Then Return SamplerStatus.NoChange End If _pos = ppr.Value Return SamplerStatus.OK End Function Public Function Run() As PromptStatus Dim doc As Document = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument Dim ed As Editor = doc.Editor Dim promptResult As PromptResult = ed.Drag(Me) Return promptResult.Status End Function End Class Public Class Insert_Lib Public Shared Sub BlockJig(ByVal blkname As String) Dim doc As Document = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument Dim db As Database = doc.Database Dim ed As Editor = doc.Editor Dim bt As BlockTable Dim tr As Transaction = doc.TransactionManager.StartTransaction() Using tr bt = DirectCast(tr.GetObject(db.BlockTableId, OpenMode.ForRead), BlockTable) If Not bt.Has(blkname) Then insb(blkname) End If End Using Using tr bt = DirectCast(tr.GetObject(db.BlockTableId, OpenMode.ForRead), BlockTable) Dim space As BlockTableRecord = DirectCast(tr.GetObject(db.CurrentSpaceId, OpenMode.ForRead), BlockTableRecord) space.UpgradeOpen() Dim btr As BlockTableRecord = DirectCast(tr.GetObject(bt(blkname), OpenMode.ForRead), BlockTableRecord) Dim br As New BlockReference(New Point3d(), btr.ObjectId) space.AppendEntity(br) tr.AddNewlyCreatedDBObject(br, True) If btr.HasAttributeDefinitions Then For Each id As ObjectId In btr Dim obj As DBObject = tr.GetObject(id, OpenMode.ForRead) Dim ad As AttributeDefinition = TryCast(obj, AttributeDefinition) If ad IsNot Nothing AndAlso Not ad.Constant Then Dim ar As New AttributeReference() ar.SetAttributeFromBlock(ad, br.BlockTransform) ar.Position = ad.Position.TransformBy(br.BlockTransform) ar.TextString = ad.TextString br.AttributeCollection.AppendAttribute(ar) tr.AddNewlyCreatedDBObject(ar, True) End If Next End If ' Run the jig Dim myJig As New CBlockJig(tr, br) If myJig.Run() <> PromptStatus.OK Then Return End If ' Commit changes if user accepted, otherwise discard tr.Commit() End Using End Sub Public Shared Sub insb(ByVal blkname As String) Dim curdwg As Document = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument Dim ed As Editor = curdwg.Editor Dim dwg As Database = ed.Document.Database Dim blockpath As String = GetSetting("AMG", "SupportIT", "BlockPath") If blockpath = "" Then MsgBox("Path to blocks is not valid.") Return Else Dim fullpath As String = blockpath & blkname & ".dwg" If File.Exists(fullpath) Then Try Dim blkdwg As New Database(False, False) blkdwg.ReadDwgFile(fullpath, FileOpenMode.OpenForReadAndAllShare, True, vbNullString) dwg.CloseInput(True) dwg.Insert(blkname, blkdwg, False) blkdwg.Dispose() Catch ex As Exception MsgBox("Error: " & ex.Message) Return End Try Else MsgBox(fullpath & vbCr & "Does not exist") Return End If End If End Sub End Class 'End Namespace
(false, True)
Here are C#, VB, & F# examples
http://www.theswamp.org/index.php?topic=37686.msg427172#msg427172
(False, True) didn't seem to do it for me....I pulled my code out of the class I borrowed and it, on its own works fine. The blockjig portion of the code I have modified and am using after the code I have written works when ran independantly but when it is called direclty after my code it errors on me. Ill lay things out and maybe clear soem things up:
Here is my code...independantly it seems to run (I will spare you the Imports)
Public Class Block_Lib Public Sub insb(ByVal blkname As String) Dim blockpath As String = GetSetting("AMG", "SupportIT", "BlockPath") If blockpath = "" Then MsgBox("Path to blocks is not valid.") Return Else Dim fullpath As String = blockpath & blkname & ".dwg" Dim curdwg As Document = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument Dim ed As Editor = curdwg.Editor If File.Exists(fullpath) Then Dim dwg As Database = ed.Document.Database Dim blkdwg As New Database '(False, False) blkdwg.ReadDwgFile(fullpath, FileOpenMode.OpenForReadAndAllShare, True, vbNullString) Using docklock As DocumentLock = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.LockDocument() dwg.Insert(blkname, blkdwg, False) End Using MsgBox("block inserted into database") blkdwg.Dispose() dwg.Dispose() Else MsgBox(fullpath & vbCr & "Does not exist") Return End If End If End Sub End Class
Here is the code I borrowed and modified...It calls a custom class that I have removed for clairity...it is in my earlier post
Public Class Commands '<CommandMethod("BJ")> _ Public Sub BlockJig() '(ByVal blkname As String) Dim blkname As String = "PS01-L2x2x0.25" Dim doc As Document = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument Dim db As Database = doc.Database Dim ed As Editor = doc.Editor 'Dim pso As New PromptStringOptions(vbLf & "Enter block name: ") 'Dim pr As PromptResult = ed.GetString(pso) 'If pr.Status <> PromptStatus.OK Then 'Return 'End If Dim tr As Transaction = doc.TransactionManager.StartTransaction() Using tr Dim bt As BlockTable = DirectCast(tr.GetObject(db.BlockTableId, OpenMode.ForRead), BlockTable) Dim space As BlockTableRecord = DirectCast(tr.GetObject(db.CurrentSpaceId, OpenMode.ForRead), BlockTableRecord) 'If Not bt.Has(pr.StringResult) Then If Not bt.Has(blkname) Then ed.WriteMessage(vbLf & "Block """ + blkname & """ not found.") 'ed.WriteMessage(vbLf & "Block """ + pr.StringResult & """ not found.") Return End If space.UpgradeOpen() Dim btr As BlockTableRecord Try btr = DirectCast(tr.GetObject(bt(blkname), OpenMode.ForRead), BlockTableRecord) 'btr = DirectCast(tr.GetObject(bt(pr.StringResult), OpenMode.ForRead), BlockTableRecord) Catch ex As Exception MsgBox(ex.Message) Return End Try ' Block needs to be inserted to current space before ' being able to append attribute to it Dim br As New BlockReference(New Point3d(), btr.ObjectId) space.AppendEntity(br) tr.AddNewlyCreatedDBObject(br, True) If btr.HasAttributeDefinitions Then For Each id As ObjectId In btr Dim obj As DBObject = tr.GetObject(id, OpenMode.ForRead) Dim ad As AttributeDefinition = TryCast(obj, AttributeDefinition) If ad IsNot Nothing AndAlso Not ad.Constant Then Dim ar As New AttributeReference() ar.SetAttributeFromBlock(ad, br.BlockTransform) ar.Position = ad.Position.TransformBy(br.BlockTransform) ar.TextString = ad.TextString br.AttributeCollection.AppendAttribute(ar) tr.AddNewlyCreatedDBObject(ar, True) End If Next End If ' Run the jig Dim myJig As New CBlockJig(tr, br) If myJig.Run() <> PromptStatus.OK Then Return End If ' Commit changes if user accepted, otherwise discard tr.Commit() End Using End Sub End Class 'End Namespace
I commented out the prompt stuff and gave it the block name (the same as what is added in my code) and if I call this by typing bj it does exactly what you would expect, it inserts the block using a jig.
If I take the next step and call this code with a byval for the blockname it errors out on me, seems like inconsistant places, the last place I found it doing it was during the upgradeopen statement.
Feels like I am getting close, just not there yet....
Thanks everyone for your help so far,
Jason
Just looking real quick and will have more time tommorrow
need to commit transaction
Using tr bt = DirectCast(tr.GetObject(db.BlockTableId, OpenMode.ForRead), BlockTable) If Not bt.Has(blkname) Then insb(blkname) End If End Using
When nested transaction are commited the objects used with them are passed up to the containing transaction.
Objects are updated when the outermost transaction is commited so anything modified in insb method even if inside a transaction and commited will not be modified, since all outer transactions must be commited to take effect
I Use this code:
Where "bln" is the blockname, "dwg" is the path to the dwg, "pt" is the insertionpoint
Public Sub BlockInvoegen(ByVal bln As String, ByVal dwg As String, ByVal pt As Point3d) Dim doc As Document = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument Dim ed As Editor = doc.Editor Dim db As Database = doc.Database Using tr As Transaction = db.TransactionManager.StartTransaction() Dim btbl As BlockTable = tr.GetObject(db.BlockTableId, OpenMode.ForRead) Dim btblr As BlockTableRecord = tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) If btbl.Has(bln) = True Then Dim bref As New BlockReference(pt, btbl(bln)) btblr.AppendEntity(bref) tr.AddNewlyCreatedDBObject(bref, True) Dim refbtr As BlockTableRecord = tr.GetObject(bref.BlockTableRecord, OpenMode.ForRead) Dim attent As Entity For Each attid As ObjectId In refbtr attent = tr.GetObject(attid, OpenMode.ForRead) If TypeOf attent Is AttributeDefinition Then Dim attdef As AttributeDefinition = attent Dim attref As New AttributeReference() attref.SetAttributeFromBlock(attdef, bref.BlockTransform) Dim attrefid As ObjectId = bref.AttributeCollection.AppendAttribute(attref) If attref.HasFields Then Dim dic As DBDictionary = tr.GetObject(attref.ExtensionDictionary, OpenMode.ForRead) Dim flddic As DBDictionary = tr.GetObject(dic.GetAt("ACAD_FIELD"), OpenMode.ForRead) Dim fld As Field = tr.GetObject(flddic.GetAt("TEXT"), OpenMode.ForWrite) Dim strid As String = bref.ObjectId.OldIdPtr.ToString() Dim updtstr As String = "%<\_ObjID " & strid & ">%" Dim fldcode As String = fld.GetFieldCode(FieldCodeFlags.AddMarkers) Dim nwfldcode As String = fldcode.Replace("?BlockRefId", updtstr) fld.SetFieldCode(nwfldcode) End If Dim occ As ObjectContextCollection = db.ObjectContextManager.GetContextCollection("ACDB_ANNOTATIONSCALES") bref.UpgradeOpen() bref.Annotative = AnnotativeStates.True ObjectContexts.AddContext(bref, occ.CurrentContext) tr.AddNewlyCreatedDBObject(attref, True) End If Next Else If File.Exists(dwg) Then Using oDB As New Database(True, False) oDB.ReadDwgFile(dwg, IO.FileShare.Read, True, "") Dim oBlockID As ObjectId = db.Insert(dwg, oDB, True) Dim bref As New BlockReference(pt, oBlockID) btblr.AppendEntity(bref) tr.AddNewlyCreatedDBObject(bref, True) Dim refbtr As BlockTableRecord = tr.GetObject(bref.BlockTableRecord, OpenMode.ForRead) Dim attent As Entity For Each attid As ObjectId In refbtr attent = tr.GetObject(attid, OpenMode.ForRead) If TypeOf attent Is AttributeDefinition Then Dim attdef As AttributeDefinition = attent Dim attref As New AttributeReference() attref.SetAttributeFromBlock(attdef, bref.BlockTransform) Dim attrefid As ObjectId = bref.AttributeCollection.AppendAttribute(attref) If attref.HasFields Then Dim dic As DBDictionary = tr.GetObject(attref.ExtensionDictionary, OpenMode.ForRead) Dim flddic As DBDictionary = tr.GetObject(dic.GetAt("ACAD_FIELD"), OpenMode.ForRead) Dim fld As Field = tr.GetObject(flddic.GetAt("TEXT"), OpenMode.ForWrite) Dim strid As String = bref.ObjectId.OldIdPtr.ToString() Dim updtstr As String = "%<\_ObjID " & strid & ">%" Dim fldcode As String = fld.GetFieldCode(FieldCodeFlags.AddMarkers) Dim nwfldcode As String = fldcode.Replace("?BlockRefId", updtstr) fld.SetFieldCode(nwfldcode) End If Dim occ As ObjectContextCollection = db.ObjectContextManager.GetContextCollection("ACDB_ANNOTATIONSCALES") bref.UpgradeOpen() bref.Annotative = AnnotativeStates.True ObjectContexts.AddContext(bref, occ.CurrentContext) tr.AddNewlyCreatedDBObject(attref, True) End If Next If btbl.Has("") And Not btbl.Has(bln) Then Dim bad As BlockTableRecord = tr.GetObject(btbl(""), OpenMode.ForWrite, True) bad.Name = bln bad.Dispose() End If End Using End If End If tr.Commit() ed.Regen() End Using End Sub
I think I have all working now, I realised that with moving between forms I needed to be smarter about using document locking. So with that tidbit and the code and help you have all provided I am in good shape....This one has been a lesson, thanks.
-Jason
Hopefully, I won't need to come back to this thread again 🙂