Any help is appreciated.
(Autocad 2008 & Visual Studio 2005)
//////////////////////////////Comando/////////////////////////////////////////// namespace BackGroundWorker { public class oComando { [CommandMethod("TEST",CommandFlags.Modal)] public static void TEST() { try { oFrmDraw myFrm = new oFrmDraw(); myFrm.StartPosition = FormStartPosition.CenterScreen; myFrm.ShowInTaskbar = false; Autodesk.AutoCAD.ApplicationServices.Application.ShowModalDialog(myFrm); } catch (Autodesk.AutoCAD.Runtime.Exception ex) { Autodesk.AutoCAD.ApplicationServices.Application.ShowAlertDialog(ex.Message); } } } } /////////////////////////Form////////////////////////////////////////////////////////// namespace BackGroundWorker { public partial class oFrmDraw : Form { public oFrmDraw() { InitializeComponent(); } private void frmSetUp() { this.btnCancel.Visible = false; this.barra.Visible = false; this.barra.Maximum = 1000; this.barra.Minimum = 0; } private void myWorker_DoWork(object sender, DoWorkEventArgs e) { // Get the BackgroundWorker that raised this event. BackgroundWorker worker = sender as BackgroundWorker; //Creo el Objeto MEtodo 2 oDrawBackGround myDraw = new oDrawBackGround(); //myDraw.Start((Point3d)e.Argument, worker, e); myDraw.Start((Point3d)e.Argument, worker, e); } private void myWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) { barra.Value = e.ProgressPercentage; } private void myWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { // First, handle the case where an exception was thrown. if (e.Error != null) { MessageBox.Show(e.Error.Message); } else if (e.Cancelled) { lblInfo.Text = "Operación Cancelada Usuario"; this.Close(); this.Dispose(); } else { // Finally, handle the case where the operation lblInfo.Text = "Operación Terminada"; this.Close(); this.Dispose(); } } //START PROCESS WITH THREAD AND WORKINGDATABASE private void btnStartWorkingDatabase_Click(object sender, EventArgs e) { this.btnCancel.Visible = true; this.barra.Visible = true; double myCx = Convert.ToDouble(txtX.Text); double myCy = Convert.ToDouble(txtY.Text); Point3d myPto3d = new Point3d(myCx, myCy, 0); //Start Process ; Call DoWork myWorker.RunWorkerAsync(myPto3d); } //CANCEL PROCESS private void btnCancel_Click(object sender, EventArgs e) { myWorker.CancelAsync(); this.btnCancel.Visible = false; } } } ////////////////CLASS ODRAW namespace BackGroundWorker { class oDrawBackGround { //Constructor public oDrawBackGround() { } public void Start(cadgeo.Point3d iPto, BackgroundWorker iWork, DoWorkEventArgs iWorkEvent) { //¿¿¿¿¿¿¿¿¿¿¿Don´t Work ; ISNULL ????????? // cadapp.Document myCadDoc = cadapp.Application.DocumentManager.MdiActiveDocument; //Get DataBase caddat.Database myCadBase = caddat.HostApplicationServices.WorkingDatabase; //Get Document cadapp.Document myCadDoc = cadapp.Application.DocumentManager.GetDocument(myCadBase); //Get Editor //¿¿¿¿¿¿¿¿¿¿ Don´t Work Is NULL ?????? //cadedi.Editor myCadEdi = myCadDoc.Editor; //Set Format Point myCadBase.Pdmode = 34; myCadBase.Pdsize = 1; using (caddat.Transaction tr = myCadBase.TransactionManager.StartTransaction()) { //Get BlockTable caddat.BlockTable myBtr = (caddat.BlockTable)tr.GetObject(myCadBase.BlockTableId, caddat.OpenMode.ForWrite); //Get Table Record caddat.BlockTableRecord acBlockTableRec = (caddat.BlockTableRecord)tr.GetObject(myBtr[caddat.BlockTableRecord.ModelSpace], caddat.OpenMode.ForWrite); //Draw 1000 Points for (int i = 0; i <= 1000; i++) { //Salgo del Bucle if (iWork.CancellationPending) { tr.Abort(); //Dont Work, Autocad CRASH iWorkEvent.Cancel = true; return; } else { cadgeo.Point3d myPtoCoor = new cadgeo.Point3d(iPto.X + i, iPto.Y + i, 0); caddat.DBPoint myPtoCad = new caddat.DBPoint(myPtoCoor); //Set Value Defaults. myPtoCad.SetDatabaseDefaults(); //Add acBlockTableRec.AppendEntity(myPtoCad); //Add Object Transaction tr.AddNewlyCreatedDBObject(myPtoCad, true); //Progress iWork.ReportProgress(i); //Process Slow ; Can Cancel System.Threading.Thread.Sleep(5); } } tr.Commit(); } } } }
Thanks
You can't call many AutoCAD APIs from a background thread.
Much of the API is not thread-safe, so what you're trying to do
will not work.
You can't run the process from a BackgroundWorker thread.
You have to run the process from the AutoCAD thread, and in the loop
you are running, you can check HostApplicationServices.UserBreak
to see if the user pressed escape.
You may be able to update your UI from a BackgroundWorker
thread, but you can't use AutoCAD API from that thread.
It works is all I can say about it
Imports System.Threading Imports Autodesk.AutoCAD Imports Autodesk.AutoCAD.ApplicationServices Imports Autodesk.AutoCAD.ApplicationServices.Application Imports Autodesk.AutoCAD.DatabaseServices Imports Autodesk.AutoCAD.EditorInput Imports Autodesk.AutoCAD.Geometry Imports Autodesk.AutoCAD.Runtime Imports Autodesk.AutoCAD.Windows Imports System.Windows.Forms Public Class Examples <CommandMethod("Test")> _ Public Sub Test() Dim frm As New Form1 ShowModalDialog(frm) End Sub End Class Public Class Form1 Private Sub UpdateStatus(ByVal i As Integer) DemoProgressBar.Value = i End Sub Private Sub BackgroundWorkerDemo_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load DemoProgressBar.Visible = False End Sub Private Sub Setup() DemoProgressBar.Visible = True DemoLabel.Text = String.Empty End Sub Private Sub StartButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles StartButton.Click Setup() bgw.RunWorkerAsync() End Sub Private Sub CancelButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CancelFormButton.Click bgw.CancelAsync() End Sub Private Sub bgw_DoWork( _ ByVal sender As System.Object, _ ByVal e As System.ComponentModel.DoWorkEventArgs) _ Handles bgw.DoWork Dim db As Database = HostApplicationServices.WorkingDatabase Using tr As Transaction = db.TransactionManager.StartTransaction Dim bt As BlockTable = db.BlockTableId.GetObject(OpenMode.ForRead) Dim btr As BlockTableRecord = bt(BlockTableRecord.ModelSpace).GetObject(OpenMode.ForWrite) For i As Integer = 0 To 1000 Dim c As Circle = New Circle(New Point3d(0 + i * 2, 0 + i * 2, 0 + i * 2), Vector3d.ZAxis, 1) btr.AppendEntity(c) tr.AddNewlyCreatedDBObject(c, True) bgw.ReportProgress(i) Thread.Sleep(10) If bgw.CancellationPending Then e.Cancel = True tr.Commit() Exit Sub End If Next tr.Commit() End Using End Sub Private Sub bgw_ProgressChanged( _ ByVal sender As System.Object, _ ByVal e As System.ComponentModel.ProgressChangedEventArgs) _ Handles bgw.ProgressChanged UpdateStatus(e.ProgressPercentage) End Sub Private Sub bgw_RunWorkerCompleted( _ ByVal sender As System.Object, _ ByVal e As System.ComponentModel. _ RunWorkerCompletedEventArgs) _ Handles bgw.RunWorkerCompleted DemoProgressBar.Visible = False If e.Cancelled Then Dim doc As Document = DocumentManager.MdiActiveDocument doc.SendStringToExecute("U ", True, False, False) DemoLabel.Text = "Cancelled" Else DemoLabel.Text = "Complete" End If End Sub End Class
thank for reply
I don´t can open the solution because I work with VS2005.
The sample works fine when you put tr.commit, but Autocad becomes unstable if you put "tr.abort()", if you cancel the process, you would put tr.abort().(I don´t want to save the transaction.)
If you change tr.Commit by tr.Abort, works it fine?
------------------------------------------------------------------------------------
If bgw.CancellationPending Then
e.Cancel = True
tr.Commit()
Exit Sub
End If
-----------------------------------------------------------------------------------
P.S.'HostApplicationServices.UserBreak only works for Autocad 2010 or Upper.
best regards
What that one was doing was after the background worker is complete it checks to see if it was canceled and if so it does the sendstringtoexecte "U " to undo the command and remove the objects
I posted it to see maybe if it would spark a idea of a better way of doing it.
I can see a couple of reasons why calling Transaction.Abort might call issues on a BGW thread.
Managed Threading Best Practices
If the API calls Thread.Abort on one of its own, then the thread is denied a chance to release unmanaged resources and clean up after itself. This could lead to an apparent memory leak.....and unhandled exceptions on spawned threads.
For a BGW, it relies on a SynchronizationContext, SC, object to find its way. A BGW makes a few assumptions. It assumes that the same thread has declared, instantiated, and later called RunWorkerAsynch. I shall call it the invoker thread. When it fires off its' events, it uses an SC object to find the proper thread and invoke the callbacks, or event handlers.
When the invoker thread lacks an SC object, which means the property is null or Nothing, the CLR can pick a random thread associated with the same or even another AppDomain. It literally can pick a random thread. This condition invariably throws an exception when no method is found to invoke. No exception is reported because no messages are coming to or from that thread. So, it hangs. The SC property was null.
It should be a safe bet that ACAD performs NETLOAD into a separate child AppDomain. Perhaps it doesn't. I dunno. Exceptions thrown in a chld AppDomain do not bubble up into the parent AppDomain. They stay where they are in the child AppDomain. But, it is up to the parent to watch over the child for signs of trouble.
STAThreads, such as a windows form, have an SC object instance. The BGW was concieved and designed to serve as an Asynch Progamming Model for WinForms apps. It should be noted that a BGW thread lacks an SC object, whcih means that worker threads should not try to be invoker threads. Workers should not invoke other workers.
Hope this helps. Rudy =8^D.