I have a VB application that creates a large number of block references to a dynamic block very quickly. Each block reference is created in it's own transaction.
It is my understanding that the transaction.commit functions does not actually commit the transaction; rather it queue the transaction to be committed. Is that true?
If that is the case, is there a way of forcing the current thread to sleep until the transaction committal has been completed? My app crashes after a pretty random number of block reference creations and I think that the crash is caused because previous transactions have not completed.
If I follow the "transaction.commit" call with a call to "Threading.Thread.Sleep(0)", it delays the crash. Without the sleep statement, my app crashes after the creation of a few to a few dozen block references. With the sleep statement, the app happily creates a few hundred block references before it crashes.
Is there a way to force my app to sleep until the previously committed transaction has been completed?
' (C) Copyright 2013 by Don Stevens-Rayburn
'
Imports System
Imports Autodesk.AutoCAD.Runtime
Imports Autodesk.AutoCAD.ApplicationServices
Imports Autodesk.AutoCAD.DatabaseServices
Imports Autodesk.AutoCAD.Geometry
Imports Autodesk.AutoCAD.EditorInput
' This line is not mandatory, but improves loading performances
<Assembly: CommandClass(GetType(TestCode.TestClass))>
Namespace TestCode
Public Class TestClass
Private _parentBlockID As ObjectId
Private _parentBlockName As String = "DynamicRectangle"
<CommandMethod("Test")> Public Sub Test()
Dim XbyX As XbyXEndView
Dim point As New Point3d(0, 0, 0)
Dim offset As New Vector3d(2.5, 0, 0)
Dim num As Integer = 1
Try
Do While num <= 1000
AcadEditor.WriteLine("Generating block reference # " & num.ToString)
XbyX = New XbyXEndView
XbyX.Height = 5.5
XbyX.Width = 1.5
Dim acTransaction As Transaction = GetAutocadTransaction()
Using acTransaction
Dim modelSpace As BlockTableRecord
Dim bt As BlockTable = CType(acTransaction.GetObject(Application.DocumentManager.MdiActiveDocument.Database.BlockTableId, OpenMode.ForRead), BlockTable)
modelSpace = CType(acTransaction.GetObject(bt(BlockTableRecord.ModelSpace), OpenMode.ForWrite), BlockTableRecord)
XbyX.CreateReference(point, modelSpace)
XbyX.SetProperties()
acTransaction.Commit()
End Using
acTransaction.Dispose()
num += 1
point += offset
Loop
Catch ex As Autodesk.AutoCAD.Runtime.Exception
AcadEditor.ReportError(ex)
End Try
End Sub
Public Shared Function GetAutocadTransaction() As Transaction
Threading.Thread.Sleep(0)
Dim tr As Transaction = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction()
tr.TransactionManager.QueueForGraphicsFlush()
Threading.Thread.Sleep(0)
Return tr
End Function
' There had to be a better way of getting the objectId from the name!
End Class
Public Class XbyXEndView
Private Shared _parentBlockID As ObjectId = Nothing
Private _parentBlockName As String
Private _acBlockReference As BlockReference = Nothing
Private _layer As String = "<use current>"
Private _rotation As Double
Private _scaleX As Double = 1.0
Private _scaleY As Double = 1.0
Private _scaleZ As Double = 1.0
Public Sub New()
_parentBlockName = "XbyX End View"
If _parentBlockID = Nothing Then
_parentBlockID = GetParentBlockIdFromName(_parentBlockName)
End If
End Sub
Public Property Width As Double = 1.5
Public Property Height As Double = 3.5
Public Sub SetProperties()
AcadEditor.WriteLine("Attempting to set the properties of block: " & _acBlockReference.Name)
SetProperty("Height", Height)
SetProperty("Width", Width)
End Sub
' There has to be a better way of doing this...
Protected Function GetParentBlockIdFromName(blockName As String) As ObjectId
Dim blockId As ObjectId = Nothing
Dim acTransaction As Transaction = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction()
Using acTransaction
Dim blockTable As BlockTable = CType(acTransaction.GetObject(Application.DocumentManager.MdiActiveDocument.Database.BlockTableId, OpenMode.ForRead), BlockTable)
Dim bTIterator As SymbolTableEnumerator = blockTable.GetEnumerator
If blockTable.Has(blockName) Then
Dim moreRecords As Boolean
Dim btr As BlockTableRecord
Dim btrID As ObjectId
bTIterator.Reset()
moreRecords = bTIterator.MoveNext
Dim success As Boolean = False
Do While moreRecords
btrID = bTIterator.Current
btr = CType(acTransaction.GetObject(btrID, OpenMode.ForRead), BlockTableRecord)
If btr.Name = blockName Then
blockId = btr.ObjectId
success = True
Exit Do
End If
moreRecords = bTIterator.MoveNext
Loop
If Not success Then
Throw New System.Exception("Look up failure for block: " & blockName)
End If
Else
Throw New System.Exception("Block" & blockName & " does not exist in the current document...")
End If
acTransaction.Commit()
End Using
Return blockId
End Function
Public Sub CreateReference(Location As Point3d, blockTableRec As BlockTableRecord)
If _acBlockReference = Nothing Then
Dim transaction As Transaction = TestClass.GetAutocadTransaction()
Using transaction
_acBlockReference = New BlockReference(Location, _parentBlockID)
Dim id As ObjectId = blockTableRec.AppendEntity(_acBlockReference)
If _acBlockReference.IsNewObject Then
transaction.AddNewlyCreatedDBObject(_acBlockReference, True)
End If
transaction.Commit()
_acBlockReference.ScaleFactors = New Scale3d(_scaleX, _scaleY, _scaleZ)
If _layer <> "<use current>" Then
_acBlockReference.Layer = _layer
End If
_acBlockReference.Rotation = _rotation
End Using
Threading.Thread.Sleep(50)
transaction.Dispose()
Else
Throw New System.Exception("Reference already created...")
End If
End Sub
Protected Sub SetProperty(ByVal Name As String, ByVal Value As Double)
If _acBlockReference <> Nothing Then
Threading.Thread.Sleep(0)
Dim success As Boolean = False
Dim propColl As DynamicBlockReferencePropertyCollection = _acBlockReference.DynamicBlockReferencePropertyCollection
Dim prop As DynamicBlockReferenceProperty
For Each prop In propColl
If prop.PropertyName.ToUpper = Name.ToUpper Then
prop.Value = Value
AcadEditor.WriteLine("Successfully set property " & Name & " on block reference: " & _acBlockReference.Name)
success = True
Exit For
End If
Next
If Not success Then
AcadEditor.WriteLine("Failed to set property " & Name & " on block reference: " & _acBlockReference.Name)
Throw New System.Exception(_parentBlockName & " does not have a property " & Name)
End If
Else
Throw New System.Exception(_parentBlockName & " is not a dynamic block.")
End If
End Sub
End Class
Public Class AcadEditor
Public Shared Sub ExecuteCommand(command As String)
Application.DocumentManager.MdiActiveDocument.SendStringToExecute(Chr(27) & Chr(27) & command, True, False, True)
End Sub
Public Shared Sub WriteLine(ByVal message As String)
Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(vbCrLf & message & vbCrLf)
End Sub
Public Shared Sub ReportError(ByVal ex As System.Exception)
Dim message As String = ex.Message & vbCrLf & vbCrLf & ex.StackTrace
message &= vbCrLf & vbCrLf & ex.Source
If ex.InnerException IsNot Nothing Then
message &= vbCrLf & vbCrLf & ex.InnerException.Message
End If
MsgBox(message, MsgBoxStyle.Critical, "Error")
End Sub
End Class
End Namespace
' (C) Copyright 2013 by Don Stevens-Rayburn
'
Imports System
Imports Autodesk.AutoCAD.Runtime
Imports Autodesk.AutoCAD.ApplicationServices
Imports Autodesk.AutoCAD.DatabaseServices
Imports Autodesk.AutoCAD.Geometry
Imports Autodesk.AutoCAD.EditorInput
' This line is not mandatory, but improves loading performances
<Assembly: CommandClass(GetType(TestCode.TestClass))>
Namespace TestCode
Public Class TestClass
Private _parentBlockID As ObjectId
Private _parentBlockName As String = "DynamicRectangle"
<CommandMethod("Test")> Public Sub Test()
Dim XbyX As XbyXEndView
Dim point As New Point3d(0, 0, 0)
Dim offset As New Vector3d(2.5, 0, 0)
Dim num As Integer = 1
Try
Do While num <= 1000
AcadEditor.WriteLine("Generating block reference # " & num.ToString)
XbyX = New XbyXEndView
XbyX.Height = 5.5
XbyX.Width = 1.5
Dim acTransaction As Transaction = GetAutocadTransaction()
Using acTransaction
Dim modelSpace As BlockTableRecord
Dim bt As BlockTable = CType(acTransaction.GetObject(Application.DocumentManager.MdiActiveDocument.Database.BlockTableId, OpenMode.ForRead), BlockTable)
modelSpace = CType(acTransaction.GetObject(bt(BlockTableRecord.ModelSpace), OpenMode.ForWrite), BlockTableRecord)
XbyX.CreateReference(point, modelSpace, acTransaction)
acTransaction.Commit()
End Using
acTransaction.Dispose()
num += 1
point += offset
Loop
Catch ex As Autodesk.AutoCAD.Runtime.Exception
AcadEditor.ReportError(ex)
End Try
End Sub
Public Shared Function GetAutocadTransaction() As Transaction
Threading.Thread.Sleep(0)
Dim tr As Transaction = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction()
tr.TransactionManager.QueueForGraphicsFlush()
Threading.Thread.Sleep(0)
Return tr
End Function
End Class
Public Class XbyXEndView
Private Shared _parentBlockID As ObjectId = Nothing
Private _parentBlockName As String
Private _acBlockReference As BlockReference = Nothing
Private _layer As String = "<use current>"
Private _rotation As Double
Private _scaleX As Double = 1.0
Private _scaleY As Double = 1.0
Private _scaleZ As Double = 1.0
Public Sub New()
_parentBlockName = "XbyX End View"
If _parentBlockID = Nothing Then
_parentBlockID = GetParentBlockIdFromName(_parentBlockName)
End If
End Sub
Public Property Width As Double = 1.5
Public Property Height As Double = 3.5
' There has to be a better way of doing this...
Protected Function GetParentBlockIdFromName(blockName As String) As ObjectId
Dim blockId As ObjectId = Nothing
Dim acTransaction As Transaction = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction()
Using acTransaction
Dim blockTable As BlockTable = CType(acTransaction.GetObject(Application.DocumentManager.MdiActiveDocument.Database.BlockTableId, OpenMode.ForRead), BlockTable)
Dim bTIterator As SymbolTableEnumerator = blockTable.GetEnumerator
If blockTable.Has(blockName) Then
Dim moreRecords As Boolean
Dim btr As BlockTableRecord
Dim btrID As ObjectId
bTIterator.Reset()
moreRecords = bTIterator.MoveNext
Dim success As Boolean = False
Do While moreRecords
btrID = bTIterator.Current
btr = CType(acTransaction.GetObject(btrID, OpenMode.ForRead), BlockTableRecord)
If btr.Name = blockName Then
blockId = btr.ObjectId
success = True
Exit Do
End If
moreRecords = bTIterator.MoveNext
Loop
If Not success Then
Throw New System.Exception("Look up failure for block: " & blockName)
End If
Else
Throw New System.Exception("Block" & blockName & " does not exist in the current document...")
End If
acTransaction.Commit()
End Using
Return blockId
End Function
Public Sub CreateReference(Location As Point3d, blockTableRec As BlockTableRecord, acTransaction As Transaction)
If _acBlockReference = Nothing Then
_acBlockReference = New BlockReference(Location, _parentBlockID)
Dim id As ObjectId = blockTableRec.AppendEntity(_acBlockReference)
If _acBlockReference.IsNewObject Then
acTransaction.AddNewlyCreatedDBObject(_acBlockReference, True)
End If
_acBlockReference.ScaleFactors = New Scale3d(_scaleX, _scaleY, _scaleZ)
If _layer <> "<use current>" Then
_acBlockReference.Layer = _layer
End If
_acBlockReference.Rotation = _rotation
SetProperty("Height", Height)
SetProperty("Width", Width)
Else
Throw New System.Exception("Reference already created...")
End If
End Sub
Protected Sub SetProperty(ByVal Name As String, ByVal Value As Double)
If _acBlockReference <> Nothing Then
Threading.Thread.Sleep(0)
Dim success As Boolean = False
Dim propColl As DynamicBlockReferencePropertyCollection = _acBlockReference.DynamicBlockReferencePropertyCollection
Dim prop As DynamicBlockReferenceProperty
For Each prop In propColl
If prop.PropertyName.ToUpper = Name.ToUpper Then
prop.Value = Value
AcadEditor.WriteLine("Successfully set property " & Name & " on block reference: " & _acBlockReference.Name)
success = True
Exit For
End If
Next
If Not success Then
AcadEditor.WriteLine("Failed to set property " & Name & " on block reference: " & _acBlockReference.Name)
Throw New System.Exception(_parentBlockName & " does not have a property " & Name)
End If
Else
Throw New System.Exception(_parentBlockName & " is not a dynamic block.")
End If
End Sub
End Class
Public Class AcadEditor
Public Shared Sub ExecuteCommand(command As String)
Application.DocumentManager.MdiActiveDocument.SendStringToExecute(Chr(27) & Chr(27) & command, True, False, True)
End Sub
Public Shared Sub WriteLine(ByVal message As String)
Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(vbCrLf & message & vbCrLf)
End Sub
Public Shared Sub ReportError(ByVal ex As System.Exception)
Dim message As String = ex.Message & vbCrLf & vbCrLf & ex.StackTrace
message &= vbCrLf & vbCrLf & ex.Source
If ex.InnerException IsNot Nothing Then
message &= vbCrLf & vbCrLf & ex.InnerException.Message
End If
MsgBox(message, MsgBoxStyle.Critical, "Error")
End Sub
End Class
End Namespace
OK, here is a somewhat simpler version of the code that uses only a single transaction to insert and configure each block. It makes no sense to wrap the entire outer loop into a single transaction because this test code is only a small part of a much larger application. That app needs to create varying number or x bx x's at various times, including, often, having to add a single instance of the x by x. In addition, I am trying to build a framework that will allow me to manipulate a wide variety of dynamic blocks.
The block that I am using here is particularly simple; it has only height and width properties. Again, in the test code they are all the same, but in the real application they change with almost every instance and so, replacing the x by x with a non-dynamic block does not solve the problem.
The error message that I get is one that I trap for on line 150 in the code; namely that the SetProperty method fails to find the property it is trying to set. If that happened on the first instance of setting a group of properties, the debugging would be trivial. What makes it harder, is that the code successfully sets the properties for several dozen instances of the block reference and then fails. In, fact, it will often successfully set the Height property and then fail on its attempt to set the Width.Code: [Select]
Missed your comment about getting the objectID of the blocktablerecord. Can you give me a code snippet showing how to more efficiently get the objectID of a block given the name of the block. As noted in the code, I said that there must be a better way than iterating through the entire blocktable but the AutoDesk documentation does not show one.
If you know of a similar "better way" of finding a specific dynamic block property given its name, I would love to see a code snippet.
Both of those methods are extremely kludgey. One should not have to iterate though an entire collection to find a given item!
Thanks
- The thread.sleep calls were put in only to see if the problem was a simple timing issue. If fact, having them delays the problem from occurring (without the thread.sleep, the problem occurs within a dozen or so block reference creations; with them, it occurs within several dozen.
- I have tried caching the dynamic block property collection when the block reference is first created and using that cached collection for the setting of the properties. Does not have any effect (although it probably makes the code run a little faster.
It makes no sense to wrap the entire outer loop into a single transaction because this test code is only a small part of a much larger application. That app needs to create varying number or x bx x's at various times, including, often, having to add a single instance of the x by x. In addition, I am trying to build a framework that will allow me to manipulate a wide variety of dynamic blocks.
It makes no sense to wrap the entire outer loop into a single transaction because this test code is only a small part of a much larger application. That app needs to create varying number or x bx x's at various times, including, often, having to add a single instance of the x by x.
' (C) Copyright 2013 by Don Stevens-Rayburn
'
Imports System
Imports Autodesk.AutoCAD.Runtime
Imports Autodesk.AutoCAD.ApplicationServices
Imports Autodesk.AutoCAD.DatabaseServices
Imports Autodesk.AutoCAD.Geometry
Imports Autodesk.AutoCAD.EditorInput
' This line is not mandatory, but improves loading performances
<Assembly: CommandClass(GetType(TestCode.TestClass))>
Namespace TestCode
Public Class TestClass
<CommandMethod("Test")> Public Sub Test()
Dim XbyX As XbyXEndView
Dim point As New Point3d(0, 0, 0)
Dim offset As New Vector3d(2.5, 0, 0)
Dim num As Integer = 1
Dim acTransaction As Transaction = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction()
Using acTransaction
Try
Dim acBlockTable As BlockTable = CType(acTransaction.GetObject(Application.DocumentManager.MdiActiveDocument.Database.BlockTableId, OpenMode.ForRead), BlockTable)
Dim acModelSpace As BlockTableRecord = CType(acTransaction.GetObject(acBlockTable(BlockTableRecord.ModelSpace), OpenMode.ForWrite), BlockTableRecord)
Do While num <= 1000
XbyX = New XbyXEndView(acBlockTable)
XbyX.Height = 5.5
XbyX.Width = 1.5
XbyX.CreateReference(point, acModelSpace, acTransaction)
num += 1
point += offset
Loop
Catch ex As Autodesk.AutoCAD.Runtime.Exception
AcadEditor.ReportError(ex)
End Try
End Using
End Sub
End Class
Public Class XbyXEndView
Private Shared _parentBlockID As ObjectId = Nothing
Private _parentBlockName As String = "XbyX End View"
Private _acBlockReference As BlockReference = Nothing
Private _layer As String = "<use current>"
Private _rotation As Double
Private _scaleX As Double = 1.0
Private _scaleY As Double = 1.0
Private _scaleZ As Double = 1.0
Private _propertyInfo As New Dictionary(Of String, Double)
Public Sub New(ByVal bt As BlockTable)
If _parentBlockID = ObjectId.Null Then
If bt.Has(_parentBlockName) Then
_parentBlockID = bt(_parentBlockName)
End If
End If
End Sub
Public Property Width As Double
Get
If _propertyInfo.ContainsKey("Width") Then
Return _propertyInfo("Width")
Else
Return -1
End If
End Get
Set(value As Double)
If _propertyInfo.ContainsKey("Width") Then
_propertyInfo("Width") = value
Else
_propertyInfo.Add("Width", value)
End If
End Set
End Property
Public Property Height As Double
Get
If _propertyInfo.ContainsKey("Height") Then
Return _propertyInfo("Height")
Else
Return -1
End If
End Get
Set(value As Double)
If _propertyInfo.ContainsKey("Height") Then
_propertyInfo("Height") = value
Else
_propertyInfo.Add("Height", value)
End If
End Set
End Property
Public Sub CreateReference(Location As Point3d, blockTableRec As BlockTableRecord, acTransaction As Transaction)
If _acBlockReference = Nothing Then
Try
_acBlockReference = New BlockReference(Location, _parentBlockID)
Dim id As ObjectId = blockTableRec.AppendEntity(_acBlockReference)
If _acBlockReference.IsNewObject Then
acTransaction.AddNewlyCreatedDBObject(_acBlockReference, True)
End If
_acBlockReference.ScaleFactors = New Scale3d(_scaleX, _scaleY, _scaleZ)
If _layer <> "<use current>" Then
_acBlockReference.Layer = _layer
End If
_acBlockReference.Rotation = _rotation
Dim propColl As DynamicBlockReferencePropertyCollection = _acBlockReference.DynamicBlockReferencePropertyCollection
For Each prop As DynamicBlockReferenceProperty In propColl
If _propertyInfo.ContainsKey(prop.PropertyName) Then
prop.Value = _propertyInfo(prop.PropertyName)
End If
Next
Catch ex As System.Exception
Throw New System.Exception("Failed to insert block at " & Location.ToString, ex)
End Try
Else
Throw New System.Exception("Reference already created...")
End If
End Sub
End Class
Public Class AcadEditor
Public Shared Sub ReportError(ByVal ex As System.Exception)
Dim message As String = ex.Message & vbCrLf & vbCrLf & ex.StackTrace
message &= vbCrLf & vbCrLf & ex.Source
If ex.InnerException IsNot Nothing Then
message &= vbCrLf & vbCrLf & ex.InnerException.Message
End If
MsgBox(message, MsgBoxStyle.Critical, "Error")
End Sub
End Class
End Namespace
I'm just about stumped. If you watch your ram usage while this operation is happening does it go over about 1GB for acad.exe during the course of the transaction (that's about what I saw in my version)
Also, not sure how this might impact http://msmvps.com/blogs/carlosq/archive/2011/03/15/vs-2010-sp1-changing-quot-byval-quot-vb-net-code-editor-experience.aspx (http://msmvps.com/blogs/carlosq/archive/2011/03/15/vs-2010-sp1-changing-quot-byval-quot-vb-net-code-editor-experience.aspx) but in C# the default way to pass parameters into a subroutine is by ref, it appears vb.net is the opposite?
I'm just about stumped. If you watch your ram usage while this operation is happening does it go over about 1GB for acad.exe during the course of the transaction (that's about what I saw in my version)
Also, not sure how this might impact http://msmvps.com/blogs/carlosq/archive/2011/03/15/vs-2010-sp1-changing-quot-byval-quot-vb-net-code-editor-experience.aspx (http://msmvps.com/blogs/carlosq/archive/2011/03/15/vs-2010-sp1-changing-quot-byval-quot-vb-net-code-editor-experience.aspx) but in C# the default way to pass parameters into a subroutine is by ref, it appears vb.net is the opposite?
with vb.net the default is by value. Isn't it the same with c#? I thought in C# the default is by value too.
Default behavior is By Value.I'm just about stumped. If you watch your ram usage while this operation is happening does it go over about 1GB for acad.exe during the course of the transaction (that's about what I saw in my version)
Also, not sure how this might impact http://msmvps.com/blogs/carlosq/archive/2011/03/15/vs-2010-sp1-changing-quot-byval-quot-vb-net-code-editor-experience.aspx (http://msmvps.com/blogs/carlosq/archive/2011/03/15/vs-2010-sp1-changing-quot-byval-quot-vb-net-code-editor-experience.aspx) but in C# the default way to pass parameters into a subroutine is by ref, it appears vb.net is the opposite?
with vb.net the default is by value. Isn't it the same with c#? I thought in C# the default is by value too.
To Jeff H.
When I put the commit in it still fails randomly. However, if I also set the Height to 3.5 so both the Height and Width are simply being set to the default value that the block has, it works fine. However, what is the point of having a dynamic block if you can't set the properties to anything but their defaults?
What versions of acad, windows etc. are you running?
I am running Acad 2010 on a 64 bit quad-core Win7 machine. Because I am running Acad 2010, i have to retarget the VB code from the Net Framework V4 to V3.5.
I would be interested to know whether you are running a newer version of AutoCad or are running a 32 bit version.
Try
prop.Value = _propertyInfo(prop.PropertyName)
Catch ex As System.Exception
ExceptionDumper.Dump(prop, propColl, _propertyInfo, ex)
Throw New System.Exception("Failed to write dynamic block property value")
End Try
Dump of DynamicBlockReferenceCollection Follows
Name Height
Value 5.5
BlockID (8796088145280)
Description
Allowed Values System.Object[]
Type Code 1
ReadOnly False
Name Origin
Value (-0.000378755066321901,-0.00319671684420086,0)
BlockID (8796088145280)
Description
Allowed Values System.Object[]
Type Code 12
ReadOnly True
Name Width
Value 1.5
BlockID (8796088145280)
Description
Allowed Values System.Object[]
Type Code 1
ReadOnly False
Name Origin
Value (-0.000378755066321901,-0.00319671684420086,0)
BlockID (8796088145280)
Description
Allowed Values System.Object[]
Type Code 12
ReadOnly True
Dump of _propertyInfo Follows
Height 5.5
Width 1.5
Dump of the Top Level exception follows...
Message eAtMaxReaders
ErrorStatus AtMaxReaders
Source acdbmgd
TargetSite Void set_Value(System.Object)
StackTrace at Autodesk.AutoCAD.DatabaseServices.DynamicBlockReferenceProperty.set_Value(Object Value)
at TestCode.TestCode.XbyXEndView.CreateReference(Point3d Location, BlockTableRecord blockTableRec, Transaction acTransaction) in C:\Users\Don Stevens-Rayburn\Documents\Visual Studio 2010\Projects\TestCode\TestCode\TestClass.vb:line 117
If attempting to open AcDb::kForRead and the object is already opened for read the maximum of 256 times, then the open attempt will fail and this function will return Acad::eAtMaxReaders.
So, the error code changes from eNotApplicable to eAtMaxReaders. Anyone know what that means and how to get around it?
Frankly, AutoDesk's error handling sucks. They might a well replace every error code with a simple "An error has occurred" and leave it at that.
To Jeff H.
When I put the commit in it still fails randomly. However, if I also set the Height to 3.5 so both the Height and Width are simply being set to the default value that the block has, it works fine. However, what is the point of having a dynamic block if you can't set the properties to anything but their defaults?
Thanks for doing that Stevray! My OCD is involved at this point and I'd like to learn the real issue here to avoid it in the future. :ugly:
So, the error code changes from eNotApplicable to eAtMaxReaders. Anyone know what that means and how to get around it?
Frankly, AutoDesk's error handling sucks. They might a well replace every error code with a simple "An error has occurred" and leave it at that.To Jeff H.
When I put the commit in it still fails randomly. However, if I also set the Height to 3.5 so both the Height and Width are simply being set to the default value that the block has, it works fine. However, what is the point of having a dynamic block if you can't set the properties to anything but their defaults?
After looking through your code for a nested item being explicitly opened and not finding one I came across your post where you had success by not setting the properties.
I hope somebody can confirm, but I'm guessing that each time we are accessing the block's property collection we are opening the definition for read. Since in each iteration of the main loop we are creating a new instance of the XbyX_End_View class which we are not explicitly disposing then perhaps the definition is not being closed until the garbage collector cleans up?
If we explicitly dispose the object at the end of the main loop, or in the property setting loop we could possibly eliminate the problem???
What says you Jeff?
Your width and height properties are set to list.
Are you using values defined in the list?
That was my first thought would your problem could be but it looked liked you were setting them to values in the list.
Tested on Win 7, AutoCAD 2013, 64bit
As a side question do you work for a envelope company?
docsQuoteIf attempting to open AcDb::kForRead and the object is already opened for read the maximum of 256 times, then the open attempt will fail and this function will return Acad::eAtMaxReaders.
docsQuoteIf attempting to open AcDb::kForRead and the object is already opened for read the maximum of 256 times, then the open attempt will fail and this function will return Acad::eAtMaxReaders.
Does not the END USING statement close all references to the db that were opened withing the scope of the transaction?
yes, but only the top transaction. The routine is crashing before we get there.
Public Sub InsertBlockTest3(ByVal blkName As String, ByVal px As Point3d, ByVal Theta As Double, ByVal sx As Double, ByVal sy As Double, ByVal sz As Double, ByVal Height As Double, ByVal Width As Double, ByVal Layer As String)
Dim Db As Database
Db = HostApplicationServices.WorkingDatabase()
Using acTrans As Transaction = Db.TransactionManager.StartTransaction()
Dim bt As BlockTable = acTrans.GetObject(Db.BlockTableId, OpenMode.ForRead)
Dim btr As BlockTableRecord
Dim id As ObjectId
btr = acTrans.GetObject(bt.Item(BlockTableRecord.ModelSpace), OpenMode.ForWrite)
If bt.Has(blkName) Then
Dim btrSrc As BlockTableRecord
btrSrc = acTrans.GetObject(bt.Item(blkName), OpenMode.ForRead)
id = btrSrc.Id
Else
MsgBox("Block definition not found for Block:" + blkName)
End If
Dim blkRef As New BlockReference(px, id)
Dim sc As New Scale3d(sx, sy, sz)
blkRef.Layer = Layer
blkRef.Rotation = Theta
blkRef.ScaleFactors = sc
btr.AppendEntity(blkRef)
acTrans.AddNewlyCreatedDBObject(blkRef, True)
Dim dPropsCollection As DynamicBlockReferencePropertyCollection
dPropsCollection = blkRef.DynamicBlockReferencePropertyCollection
Dim t1 As Date = Now
For Each dprop As DynamicBlockReferenceProperty In dPropsCollection
Select Case dprop.PropertyName
Case "Heigth"
dprop.Value = Height
Case "Width"
dprop.Value = Width
End Select
Next
Dim t2 As Date = Now
Debug.Print("Time in props:" + (t2 - t1).TotalMilliseconds.ToString + "[ms]")
Try
btr.UpdateAnonymousBlocks()
blkRef.RecordGraphicsModified(True)
Catch ex As Autodesk.AutoCAD.Runtime.Exception
MsgBox("Error Updating RefBTR:blkName :=" + blkName + ": " + ex.Message)
End Try
acTrans.Commit()
End Using
End Sub
<CommandMethod("BTest3")> _
Public Sub btest3()
Dim blkName As String = "A"
Dim px As New Point3d(0, 0, 0)
Dim offset As New Vector3d(4.5, 0, 0)
Debug.Print(Now.TimeOfDay.ToString)
For i = 0 To 199
InsertBlockTest3(blkName, px, 0, 1, 1, 1, 2, 4, "0")
px += offset
Next
Debug.Print(Now.TimeOfDay.ToString)
End Sub
Has anyone with problems crashing checked the AnonymousBlockTableRecord & DynamicBlockTableRecord properties before and after changing its properties?