I am facing a puzzling disposed object issue when I shut down my WPF application. If you spot any mistakes in my logic please point them out.
I have a ColorManager
class with update()
method, as shown below.
public void Update(ColorImageFrame frame)
{
byte[] pixelData = new byte[frame.PixelDataLength];
frame.CopyPixelDataTo(pixelData);
if (Bitmap == null)
{
Bitmap = new WriteableBitmap(frame.Width,
frame.Height,
96,
96,
PixelFormats.Bgr32,
null);
}
// draw bitmap
RaisePropertyChanged(() => Bitmap);
}
I run this method in a separate thread. In my MainWindow.xaml.cs
I have the following:
private void Initialise()
{
if (kinectSensor == null)
return;
// start kinect sensor
kinectSensor.Start();
updateColourStreamThread = new Thread(new ThreadStart(colorStreamDisplay));
updateColourStreamThread.Name = "updateColourStreamThread";
updateColourStreamThread.Start();
// ...some more codes
}
void colorStreamDisplay()
{
while(isDisplayActive)
{
using (var frame = kinectSensor.ColorStream.OpenNextFrame(500))
{
if (frame == null) continue;
if (displayDepthStream) continue;
Dispatcher.Invoke(new Action(() => colorManager.Update(frame)));
}
}
}
I have the following method in MainWindow.xaml.cs
to do the clean up after clicking the close button.
private void Clean()
{
isDisplayActive = false;
// some other codes
if (kinectSensor != null)
{
updateColourStreamThread.Abort();
updateDepthStreamThread.Abort();
updateSkeletonStreamThread.Abort();
kinectSensor.Stop();
kinectSensor = null;
Console.WriteLine("Closed successfully");
}
My application throws "cannot access a disposed object" on frame.CopyPixelDataTo(pixelData);
after I click the close button.
I switch the bool value to false to stop the loop, then I abort the thread, and stop the kinect device.
What did I miss?
When you set your boolean to false, the application will exit the while loop:
1) Setting your bool
to false
isDisplayActive = false;
2) will exit this loop:
while(isDisplayActive)
{
using (var frame = kinectSensor.ColorStream.OpenNextFrame(500))
{
if (frame == null) continue;
if (displayDepthStream) continue;
Dispatcher.Invoke(new Action(() => colorManager.Update(frame)));
}
}
3) So your frame will go out of scope too. Consequently it will be disposed...
using (var frame = kinectSensor.ColorStream.OpenNextFrame(500))
4) While your main thread did not yet execute the Thread.Abort
yet.
5) And thus, your CopyPixelDataTo
will be executed on an already disposed frame
object.
frame.CopyPixelDataTo(pixelData);
6) And kaboom, you have your object disposed exception.
You never know how far the executing thread was before it got executed, which can lead to all kinds of nasty side effects. Read more in this Q&A: What's wrong with using Thread.Abort()
What I would do in your situation is replace the
while(isDisplayActive)
with something like
while(colorThingyThreadIsBusy)
And set the colorThingyThreadBusy
bool to false when your Thread
is ready (=done processing).
In order to gracefully close your application I'd implement a CancellationToken instead of aborting Threads.