5
\$\begingroup\$

I'm currently reading a certain pixels color from a process window like this:

[DllImport("gdi32.dll")]
private static extern int BitBlt(IntPtr srchDc, int srcX, int srcY, int srcW, int srcH, 
                                 IntPtr desthDc, int destX, int destY, int op);

public static Color GetPixel(IntPtr hwnd, int x, int y)
{
    var screenPixel = new Bitmap(1, 1);

    using (Graphics gdest = Graphics.FromImage(screenPixel))
    {
        using (Graphics gsrc = Graphics.FromHwnd(MemoryHandler.GetMainWindowHandle()))
        {
            IntPtr hsrcdc = gsrc.GetHdc();
            IntPtr hdc = gdest.GetHdc();
            BitBlt(hdc, 0, 0, 1, 1, hsrcdc, x, y, (int)CopyPixelOperation.SourceCopy);
            gdest.ReleaseHdc();
            gsrc.ReleaseHdc();
        }
    }

    return screenPixel.GetPixel(0, 0);
}

It seems to be working.

I would like to know if I'm disposing everything that I use correctly to avoid memory leaks. If I inspect my app in the task manager I notice that the memory use is increasing a bit for each time i fetch a pixel, but I suppose the garbage collector will kick in when it's supposed to?

Also, is there any obvious "better" way of doing this?

\$\endgroup\$
  • \$\begingroup\$ You're not disposing the Bitmap. \$\endgroup\$ – svick Dec 8 '13 at 14:38
5
\$\begingroup\$

You'll actually need to wrap the Bitmap in a using as well because its base class, Image, implements IDisposable:

public static Color GetPixel(IntPtr hwnd, int x, int y)
{
    using (Bitmap screenPixel = new Bitmap(1, 1);
    {
        using (Graphics gdest = Graphics.FromImage(screenPixel))
        {
            using (Graphics gsrc = Graphics.FromHwnd(MemoryHandler.GetMainWindowHandle()))
            {
                IntPtr hsrcdc = gsrc.GetHdc();
                IntPtr hdc = gdest.GetHdc();
                BitBlt(hdc, 0, 0, 1, 1, hsrcdc, x, y, (int)CopyPixelOperation.SourceCopy);
                gdest.ReleaseHdc();
                gsrc.ReleaseHdc();
            }
        }

        return screenPixel.GetPixel(0, 0);
    }
}

Further, the garbage collector works almost immediately. Or more simply put, if you use a memory allocation tool like MemProfiler, the memory will get cleaned up by GC faster than you can collect it with a snapshot. The Bitmap is likely where you are seeing the little tick.

It may also be worth wrapping the API call in a try ... finally:

IntPtr hsrcdc = gsrc.GetHdc();
IntPtr hdc = gdest.GetHdc();

try
{
    using (Graphics gsrc = Graphics.FromHwnd(MemoryHandler.GetMainWindowHandle()))
    {
        BitBlt(hdc, 0, 0, 1, 1, hsrcdc, x, y, (int)CopyPixelOperation.SourceCopy);
    }
}
finally
{
    gdest.ReleaseHdc();
    gsrc.ReleaseHdc();
}

to ensure the ReleaseHdc gets called.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Yeah, the "not-disposing-the-bitmap"-part was a blunder on my end. However, after trying this yesterday (the disposing of the bitmap), the memory usage still increases with each timer tick that I run this in. I only tried running it for a few seconds, so perhaps the GC will kick in if I wait long enough? I don't see the point of the last code snippet. Can you give me an example where gdest.ReleaseHdc(); gsrc.ReleaseHdc(); wouldn't get called? Thanks for the feedback - much appreciated! \$\endgroup\$ – Johan Dec 9 '13 at 15:23
  • 1
    \$\begingroup\$ @Johan, they wouldn't get called in the current code if an unhandled exception were thrown. Therefore, handling the possible exception means that even if an exception is thrown they'll get disposed. \$\endgroup\$ – Mike Perrenoud Dec 9 '13 at 15:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.