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

BackGroundWorker Doesn't Work for Transaction.Abort()

9 REPLIES 9
Reply
Message 1 of 10
Anonymous
2260 Views, 9 Replies

BackGroundWorker Doesn't Work for Transaction.Abort()

 

Hi guys
I´m working with BackGroungworker, the sample works fine, but when the user cancel  the action,
(Transaction.Abort()), Autocad becomes unstable (See Picture), but I haven´t any Exception

 

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

 

9 REPLIES 9
Message 2 of 10
Anonymous
in reply to: Anonymous

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.

Message 3 of 10
Anonymous
in reply to: Anonymous

 

Tony Thank you  for  reply
The Sample works fine when I Change Transaction.Abort() by Commit().
How Can I stop the process without use BackGroundWorker?

 

Message 4 of 10
Anonymous
in reply to: Anonymous

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.

Message 5 of 10
Anonymous
in reply to: Anonymous

I will try to use 'HostApplicationServices.UserBreak'

 

thanks

 

 

Message 6 of 10
Anonymous
in reply to: Anonymous

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

 

 

 

 

 

Message 7 of 10
Anonymous
in reply to: Anonymous

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

Message 8 of 10
Anonymous
in reply to: Anonymous

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.

Message 9 of 10
Anonymous
in reply to: Anonymous

fro2001, thank for reply

 

I just understand, it´s good idea.

 

best regards

Message 10 of 10
Anonymous
in reply to: Anonymous

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.

 

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