views:

1861

answers:

3

Hello

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?

Can anyone help please? I am really stuck Thank you in advance

A: 

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

Ahmed Said
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.
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 thread
Ahmed Said
A: 

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.

arbiter
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 images
I tied locking the panel but it did not work I still got the error
You do not need to lock panel, you need to lock particular image you work with.
arbiter
I wonder what would happen if he was to call `LockBits()` and then `UnlockBits()` and do the rendering in between.
Dmitri Nesteruk
A: 

I tryed locking e in my procedure I cannot lock the image because I am not working with it I am drawing lines on top of it

lock (e)
        {


            //draw left | upper half
            e.Graphics.DrawLine(dp, iCropLeft, iCropRight + iLittleSquareSide / 2, iCropLeft, iCropRight + iCropHeight / 2 - iLittleSquareSide / 2);

            //there was a lot more code here just like this but I deleted it 
            //in order to keep it concise

            //draw crosshair -

            e.Graphics.DrawLine(dp, 0, pbPreviewCamera.Size.Height / 2, pbPreviewCamera.Size.Width, pbPreviewCamera.Size.Height / 2);
        }

I still get the error:

************** Exception Text **************

System.InvalidOperationException: Object is currently in use elsewhere. at System.Drawing.Image.get_Height() at System.Windows.Forms.PictureBox.ImageRectangleFromSizeMode(PictureBoxSizeMode mode) at System.Windows.Forms.PictureBox.OnPaint(PaintEventArgs pe) at System.Windows.Forms.Control.PaintWithErrorHandling(PaintEventArgs e, Int16 layer, Boolean disposeEventArgs) at System.Windows.Forms.Control.WmPaint(Message& m) at System.Windows.Forms.Control.WndProc(Message& m) at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m) at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m) at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

Should I lock something else?