Mark
Mark

Reputation: 179

Tidying up background threads when app is killed by Windows Mobile 6.x

I am working on a C# application that runs on Windows Mobile 6, so .NET Compact Framework. It uses background threads to handle network communication and keep the UI free.

These threads are within objects (written in as part of another project). The main app instantiates the objects, and tells them to start the connection. All of the thread control is done by the 'connection' object.

When the app exits via the menu, it calls on the connection.Stop() method which tells the connection object to disconnect, release its' resources and shut down the thread. This is all fine, however if the app is killed from task manager, or by hitting 'X' from the 'running programs' dropdown then the main GUI of the app closes but the threads that have been started by these objects do not.

Using a 3rd party task manager on the device, I can see that there is no sign of the app under 'running applications' but it is there under 'running processes' listed with the same binary as the application. I attempt to relaunch the application, then it flicks onto the screen and then off again.

How do I catch the event of the app being closed, catching a form closing isn't what I'm looking to do - the Closing event doesn't seem to fire anyway.

I've set isBackgroundWorker on the threads but this had no effect.

If I can't catch the app being closed, how would I instead get the threads to check if the UI of the app is still there and shut down if it isn't.

**I've had a though whilst typing this.. the objects are instantiated by the main app, if the main app is killed and its' objects are released.. is that leaving these threads in limbo? Should I be clearing them up in the onDispose of the 'connection' object?

EDIT - adding code as requested, these are snippets pulled out where I think it is relevant, the app is huge. This is the within the main app

    public Connection = new Connection(id, server, port, onStatusChange);

    this.Connection.Start();

    onStatusChange is a delegate on the main app

and in the connection object.. Connection.Start() looks like;

    public void Start()
    {
        //Since we can't check the thread state then we have to kill the existing thread
        Stop();
        //Should not be able to start a new thread if one is already running
        lock (this.pushLock)
        {
            ConnectionThreadStart = new ThreadStart(ConnectionThreadMethod);
            ConnectionThread = new Thread(ConnectionThreadStart);
            ConnectionThread.Name = "Connection";
            ConnectionThread.IsBackground = true;
            //Start the new thread
            this.start = true;
            ConnectionThread.Start();
        }
    }

    /// <summary>
    /// Method ran by the connection thread
    /// </summary>
    private void ConnectionThreadMethod()
    {
        //Take ownership of ConnectionThread until we exit
        lock (this.pushLock)
        {
            //Keep trying to connect until flag tells us not to
            while (this.start)
            {
              //establish and maintain connection
             }
         }
     }

    public void Stop()
    {
        //Indicate to thread that it should not continue running.
        this.start = false;
        //Attempt to close the socket on which the connection listens, this should cause it to fail with an exception
        try
        {
            Socket.Client.Close();
        }
        catch(Exception e)
        {
        }
        try
        {
            Socket.Close();
        }
        catch(Exception e)
        {
        }
        //If for some reason the thread is still going ask it to abort which, again, should cause it to throw an exception and exit
        try
        {
            ConnectionThread.Abort();
        }
        catch(Exception e)
        {
        }
        //Finally join the thread if it is still going and wait for it to exit. It will give up waiting after 10 seconds
        try
        {
            if (!ConnectionThread.Join(10000))
            {
                //The thread failed to stop, we can do no more, the thing is borken
            }
        }
        catch (Exception e)
        {
        }
    }

So when the app exits normally, I simply called Connection.Stop() and it closes down.

Further edit; - added this.. still doesn't do as I expect.

        private bool disposed = false;

    //Implement IDisposable.
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                Stop();
            }
            disposed = true;
        }
    }

    ~Connection()
    {
        // Simply call Dispose(false).
        Dispose (false);
    }
}

Upvotes: 1

Views: 1115

Answers (1)

pdriegen
pdriegen

Reputation: 2039

if the app is terminated forcibly via a Task Manager, you're managed app doesn't get the luxury of running clean up code. It's simply terminated, stopped in it's tracks in whatever it was doing. If you can see that it's still left running, it may be due to a lower level driver that refuses to yield. Without seeing what work these threads are doing, it's not possible to know with any certainty.

If you terminate the app via the WM "running processes" utility, I believe a WM_QUIT message is posted to your form window, so you do get a chance to handle it properly and clean up before exit.

What you ought to do to handle eventualities like unexpected exit requests is to put a try/finally block around the Application.Run(). The code in the finally block will execute every time your app shuts down gracefully, and as such is the ideal place to write these types of clean-up routines.

Here's some pseudo code describing how this might look:

            try
        {
            //application startup code here//


            System.Windows.Forms.Application.Run(new YourMainForm());
        }

        catch (Exception x)
        {
            log.Fatal("FATAL application exception occurred.", x);
           //TODO: decide how to handle fatal exception. Feedback to user?

        }

        finally
        {
            try
            {
                //shut down application activities 
                //shutdown your connection object here                  
            }
            catch (Exception ex)
            {
                log.Error("A fatal error occurred while attempting to shutdown the application", ex);

            }
        }

Upvotes: 1

Related Questions