"Object is currently in use elsewhere" error.

I was debugging what I thought was a strange exception the other day.  The exception was an InvalidOperationException and the message was "Object is currently in use elsewhere".  Unless you're familiar with this exception, it really doesn't offer much as to why the exception is occurring.  There seems to be several stale threads on the Web about this issue, so I'd thought I'd post about it.

As it turns out it had to do with some code that was PInvoking some native graphics functions and the interaction with the WinForm that was hosting the drawing surface was to blame.

What's really happening with "Object is currently in use elsewhere" is that GDI+ is complaining that the device context (DC) that it is trying to use is already "in use".  With WinForms, this generally means there is a recursive Graphics.GetHdc occurring.  GetHdc must match a ReleaseHdc before any other GetHdc.  Recursive means you have something like GetHdc->GetHdc->ReleaseHdc->ReleaseHdc, instead of GetHdc->ReleaseHdc->GetHdc->ReleaseHdc.  Another possibility is that there is a missing call to ReleaseHdc. (i.e. GetHdc->GetHdc->ReleaseHdc)

Now, in my case there was some seemingly innocuous code like this:

    SafeNativeMethods.DrawSomeStuff(e.Graphics.GetHdc(), parameters);

    e.Graphics.DrawString(text, this.Font, Brushes.Black, point);

...no matching ReleaseHdc(), before the DrawString call.

The fix turned out to be really simple:

    try

    {

        SafeNativeMethods.DrawSomeStuff(e.Graphics.GetHdc(), parameters);

    }

    finally

    {

        e.Graphics.ReleaseHdc();

    }

    e.Graphics.DrawString(text, this.Font, Brushes.Black, point);

You can also encounter this exception if you're drawing to a form from multiple threads.  You'll likely also be encountering a cross-threading exception as well.  The solution in this case is to not use multiple threads when accessing a form, including drawing.

Published Mon, Jan 28 2008 11:04 by PeterRitchie

Comments

# re: "Object is currently in use elsewhere" error.

Ugh... GDI+ is a minefield of handle leaking carnage.

Take a look at this one:

msdn2.microsoft.com/.../system.drawing.bitmap.gethicon.aspx

(System.Drawing.Bitmap.GetHicon() method)

It returns an IntPtr, but there is no FreeHicon() method, you have to PInvoke DestroyIcon() yourself. Ouch!

And if you don't, you get a nasty handle leak that will take down the server and no windows will show up.

Monday, January 28, 2008 1:15 PM by Chad Myers

# re: "Object is currently in use elsewhere" error.

You could use the Icon class instead of manualling PInvoking DestroyIcon, like this:

Icon icon = new Icon(bitmap.GetHIcon(), true);

But, yes, that's not intuitive...  There should be an Image.GetIcon() instead that does that for you.

Monday, January 28, 2008 1:43 PM by PeterRitchie

# re: "Object is currently in use elsewhere" error.

The new Icon(IntPtr, bool) constructor is internal...

Tuesday, January 29, 2008 3:25 AM by Steve

# re: "Object is currently in use elsewhere" error.

Yeah, you'd have to use reflection to use Icon(IntPtr)...

Tuesday, January 29, 2008 6:12 AM by PeterRitchie

# re: "Object is currently in use elsewhere" error.

Hi,

I've the same issue not using the getHdc method, but assigning to the pictureBox Image property an Image contained in a resx file (Windows form application).

What I cannot reach to understand is if this exception happens cause my multithread architecture or because the assignment??

Can you help me?

Thursday, February 14, 2008 6:11 AM by Napoleone

# Object is currently in use elsewhere | keyongtech

Pingback from  Object is currently in use elsewhere | keyongtech

Sunday, January 18, 2009 11:40 AM by Object is currently in use elsewhere | keyongtech

# re: "Object is currently in use elsewhere" error.

Hi I am calling the following method in loop, but its firing an error that object is used elsewhere.. please help .. Thanks in advance

if (CameraGlobal.CameraOnePic.Image != null)

                   {

//Sav.Invoke(CameraGlobal.CameraOnePic.Image);

                       s.Width = 240;

                       s.Height = 320;

                       //s = CameraGlobal.CameraOnePic

                       BitMapImage = new Bitmap(CameraGlobal.CameraOnePic.Image, s);

                       BitMapImage.Save("C:\\" + CameraGlobal.ImageCount.ToString() + ".jpg");

                       //ImageList.Add(CameraGlobal.ImageCount.ToString(), CameraGlobal.CameraOnePic.Image);

                       //SaveBitmap(CameraGlobal.CameraOnePic.Image);

                       //SaveBitmap();

                       //SaveJpeg("C:\\2891.jpg", CameraGlobal.CameraOnePic.Image, 1);

                       // SaveBitmap(CameraGlobal.CameraOnePic.Image);

                       //CameraGlobal.CameraOnePic.Invoke(new SaveImageHandler(SaveBitmap));

                       //SaveBitmap(CameraGlobal.CameraOnePic.Image);

                       //CameraGlobal.CameraOnePic.Image.Save(TrimPath(SurveyGlobal.PrimaryImageFilePath) + "\\" + CameraGlobal.ImageCount + ".jpg", System.Drawing.Imaging.ImageFormat.Jpeg);

                   }

Monday, March 09, 2009 6:59 AM by Imran Khan

# re: "Object is currently in use elsewhere" error.

private void btnstart_Click(object sender, EventArgs e)

{

Thread grey = new Thread(new ThreadStart(greyscale));

           Thread hidden = new Thread(new ThreadStart(hide));

           Thread colour = new Thread(new ThreadStart(col));

           grey.Start();

           hidden.Start();

           colour.Start();

       }

       public void greyscale()

       {

           Bitmap grays = (Bitmap)pictureBox1.Image;

           int width = grays.Size.Width;

           int height = grays.Size.Height;

           for (int j = 0; j < height; j++)

           {

               for (int i = 0; i < width; i++)

               {

                   Color col;

                   col = grays.GetPixel(i, j);

                   grays.SetPixel(i, j, Color.FromArgb((col.R + col.G + col.B) / 3, (col.R + col.G + col.B) / 3, (col.R + col.G + col.B) / 3));

               }

           }

           pictureBox1.Image = grays;

       }

       public void hide()

       {

           Bitmap grays1 = (Bitmap)pictureBox1.Image;

           int width1 = grays1.Size.Width;

           int height1 = grays1.Size.Height;

           for (int j = 0; j < height1; j++)

           {

               for (int i = 0; i < width1; i++)

               {

                   Color col;

                   col = grays1.GetPixel(i, j);

                   grays1.SetPixel(i, j, Color.FromArgb(255,255,255));

               }

           }

           pictureBox1.Image = grays1;

       }

Monday, March 23, 2009 3:04 AM by Ranjith

# re: "Object is currently in use elsewhere" error.

@Ranjith.  I'm assuming you posted that code because you're getting the "Object is currenty in use elsewhere error".  One reason this may be happening is you're modifying form data (pictureBox1) on a different thread from that which created the control.

In your case, I would suggest you create a method that sets pictureBox1.Image and have it marshal itself back to the GUI thread.  For example:

private void SetImage(Bitmap bitmap)

{

if(InvokeRequired())

{

BeginInvoke((MethodInvoker)delegate() { SetImage(bitmap); });

}

else

{

pictureBox1.Image = bitmap;

}

}

Monday, March 23, 2009 8:49 AM by PeterRitchie

# "Object is currently in use elsewhere" a workaround

I've been getting this error since I moved some graphics code to a background thread.

Every Graphics.FromImage () had a matching Dispose () call, so that wasn't the problem.

It was happening in the Graphics.DrawImage () method.  But only the first time.

So I put a try/catch block around the DrawImage () call, and a Thread.Sleep (5) in the catch block before trying the DrawImage () a second time.  This worked.  Sleep parameters <= 3 ms didn't work.

There's a noticeable pause when this happens, which suggests it's a Microsoft/.NET threading problem.  Maybe it will go away in a later version of .NET.

Tuesday, October 20, 2009 2:29 PM by Alan8

# re: "Object is currently in use elsewhere" error.

@AlanB: GDI+ (the underlying Windows library that Graphics uses) is not thread safe.  Adding a Sleep() call like that, as you've noticed, simply slows down your code and reduces the likelihood of having another exception because it simply re-tries that code that failed and it's less likely that another conflicting GDI+ call is being made at the very same time on another thread.

This isn't a solution.  Since GDI+ isn't thread-safe you need to synchronize calls to GDI+ on multiple threads.  This simply means having a shared lock object and using the C# lock keyword where ever you perform GDI+ operations (i.e. around acquisition of a resource and disposal of the resource).

Since you code isn't the only code that can perform GDI+ operations you can't synchronize all GDI+ code like this.  This leaves you to marshal the code back to the GUI thread--which is a drastic design change from what you've got now.

Long story short: you can't perform GDI+ operations reliably on any thread other than the main (GUI) thread.

Tuesday, October 20, 2009 4:16 PM by PeterRitchie

Leave a Comment

(required) 
(required) 
(optional)
(required)