InvalidOperationException - object is currently in use elsewhere - red cross
Asked Answered
H

4

30

I have a C# desktop application in which one thread that I create continously gets an image from a source(it's a digital camera actually) and puts it on a panel(panel.Image = img) in the GUI(which must be another thread as it is the code-behind of a control.

The application works but on some machines I get the following error at random time intervals(unpredictable)

************** Exception Text **************
System.InvalidOperationException: The object is currently in use elsewhere. 

Then the panel turns into a red cross, red X - i think this is the invalid picture icon that is editable from the properties. The application keeps working but the panel is never updated.

From what I can tell this error comes from the control's onpaint event where I draw something else on the picture.

I tried using a lock there but no luck :(

The way I call the function that puts the image on the panel is as follows:

if (this.ReceivedFrame != null)
{
    Delegate[] clients = this.ReceivedFrame.GetInvocationList();
    foreach (Delegate del in clients)
    {
        try
        {
            del.DynamicInvoke(new object[] { this, 
                new StreamEventArgs(frame)} );
        }
        catch { }
    }
}

this is the delegate:

public delegate void ReceivedFrameEventHandler(object sender, StreamEventArgs e);
    public event ReceivedFrameEventHandler ReceivedFrame;

and this is how the function inside the control code-behind registers to it:

Camera.ReceivedFrame += 
    new Camera.ReceivedFrameEventHandler(camera_ReceivedFrame);

I also tried

del.Method.Invoke(del.Target, new object[] { this, new StreamEventArgs(b) });

instead of

del.DynamicInvoke(new object[] { this, new StreamEventArgs(frame) });

but no luck

Does anyone know how I could fix this error or at least catch the error somehow and make the thread put the images on the panel once again?

Henebry answered 29/6, 2009 at 20:16 Comment(0)
G
20

This is because Gdi+ Image class is not thread safe. Hovewer you can avoid InvalidOperationException by using lock every time when you need to Image access, for example for painting or getting image size:

Image DummyImage;

// Paint
lock (DummyImage)
    e.Graphics.DrawImage(DummyImage, 10, 10);

// Access Image properties
Size ImageSize;
lock (DummyImage)
    ImageSize = DummyImage.Size;

BTW, invocation is not needed, if you will use the above pattern.

Gayla answered 29/6, 2009 at 20:29 Comment(4)
well my function that runs inside the onpaint event draws a lot of stuff onto the panel above the image that the other thread sets on the panel so how can I lock everithing that is drawn? this includes rectangles, lines and imagesHenebry
I tied locking the panel but it did not work I still got the errorHenebry
You do not need to lock panel, you need to lock particular image you work with.Gayla
I wonder what would happen if he was to call LockBits() and then UnlockBits() and do the rendering in between.Knepper
F
5

I had a similar problem with the same error message but try as I might, locking the bitmap didn't fix anything for me. Then I realized I was drawing a shape using a static brush. Sure enough, it was the brush that was causing the thread contention.

var location = new Rectangle(100, 100, 500, 500);
var brush = MyClass.RED_BRUSH;
lock(brush)
    e.Graphics.FillRectangle(brush, location);

This worked for my case and lesson learned: Check all the reference types being used at the point where thread contention is occurring.

Fencing answered 5/1, 2013 at 23:3 Comment(1)
In my case it was the brush as well. Just for fun I tried it with a font but that seems to work from multiple threads.Coridon
K
2

Seems to me, that the same Camera object is used several times.

E.g. try to use a new buffer for each received frame. It seems to me, that while the picture box is drawing the new frame, your capture library fills that buffer again. Therefore on faster machines this might not be an issue, with slower machines it might be an issue.

I've programmed something similar once, after each received frame, we had to request to receive the next frame and set the NEW frame receive buffer in that request.

If you can not do that, copy the received frame from the camera first to a new buffer and append that buffer to a queue, or just use 2 alternating buffers and check for overruns. Either use myOutPutPanel.BeginInvoke to call the camera_ReceivedFrame method, or better have a thread running, which checks the queue, when it has a new entry it calls mnyOutPutPanel.BeginInvoke to invoke your method to set the new buffer as image on the panel.

Furthermore, once you received the buffer, use the Panel Invoke Method to invoke the setting of the image (guarantee that it runs in the window thread and not the thread from your capture library).

The example below can be called from any thread (capture library or other separate thread):

void camera_ReceivedFrame(object sender, StreamEventArgs e)
{
    if(myOutputPanel.InvokeRequired)
    {
        myOutPutPanel.BeginInvoke( 
            new Camera.ReceivedFrameEventHandler(camera_ReceivedFrame), 
            sender, 
            e);
    }
    else
    {
        myOutPutPanel.Image = e.Image;
    }
}
Kielty answered 18/3, 2012 at 19:48 Comment(0)
R
0

I think this is multithreading problem Use windows golden rule and update the panel in the main thread use panel.Invoke This should overcome cross threading exception

Roadability answered 29/6, 2009 at 20:27 Comment(2)
I am updating the panel in the main thread but I am calling the function that updates it from the other thread and passing the image as a parameter.Henebry
if you call the function that updates the panel from another thread and in the function itself there is no context switching (switch to the main thread using invoke for example) this means your update done on the other thread not the main threadRoadability

© 2022 - 2024 — McMap. All rights reserved.