Reputation: 513
I have a timer that throws a function (GetColorAtPoint
) to retrieve a pixel using GDI every 10ms (time is critical). After a few seconds, I get the following exception:
[GetColorAtPoint]System.ArgumentException: Parameter is not valid.
at System.Drawing.Graphics.GetHdc()
at SGetColorAtPoint(Point p, IntPtr hWnd)
It turns out my app is creating >10.000 GDI objects and therefore, it crashes.
I've been reading that I may miss some dispose but can't see it (and I'm using using
). I've tried to check it with ANTS performance profiler but I'm not sure where to look at. Can tell hit count there is extremely high for BitBlt function.
So, how do I fix it? Or how to discover where the problem is using the memory profiler?
Relevant function:
[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);
[DllImport("user32.dll")]
static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);
public static Color GetColorAtPoint(Point p, IntPtr hWnd)
{
try
{
Bitmap screenPixel = new Bitmap(1, 1);
Color c = System.Drawing.Color.Black;
//method to test
if (IsWindow(hWnd))
using (Graphics gdest = Graphics.FromImage(screenPixel))
{
if (IsWindow(hWnd))
{
using (Graphics gsrc = Graphics.FromHwnd(hWnd))
{
IntPtr hSrcDC = gsrc.GetHdc();
IntPtr hDestDC = gdest.GetHdc();
int retval = BitBlt(hDestDC, 0, 0, 1, 1, hSrcDC, p.X, p.Y, (int)CopyPixelOperation.SourceCopy);
gdest.ReleaseHdc();
gsrc.ReleaseHdc();
DeleteDC(hSrcDC);
DeleteDC(hDestDC);
}
}
}
c = screenPixel.GetPixel(0, 0);
return c;
}
catch (Exception ex)
{
Console.WriteLine("[GetColorAtPoint]"+ex.ToString());
return System.Drawing.Color.Black;
}
}
Also, the exception is thrown in the getHDC() function, in case it's relevant:
public IntPtr GetHdc() {
IntPtr hdc = IntPtr.Zero;
int status = SafeNativeMethods.Gdip.GdipGetDC(new HandleRef(this, this.NativeGraphics), out hdc);
if (status != SafeNativeMethods.Gdip.Ok) {
throw SafeNativeMethods.Gdip.StatusException(status);
}
this.nativeHdc = hdc; // need to cache the hdc to be able to release with a call to IDeviceContext.ReleaseHdc().
return this.nativeHdc;
}
Any ideas?
Upvotes: 0
Views: 466
Reputation: 513
As suggested by @Sam Axe, Bitmap must be disposed as well, not only Graphics.
Here is the relevant code solved:
using (Bitmap screenPixel = new Bitmap(1, 1))
{
if (IsWindow(hWnd))
{
using (Graphics gdest = Graphics.FromImage(screenPixel))
{
using (Graphics gsrc = Graphics.FromHwnd(hWnd))
{
IntPtr hSrcDC = gsrc.GetHdc();
IntPtr hDestDC = gdest.GetHdc();
int retval = BitBlt(hDestDC, 0, 0, 1, 1, hSrcDC, p.X, p.Y, (int)CopyPixelOperation.SourceCopy);
gdest.ReleaseHdc();
gsrc.ReleaseHdc();
DeleteDC(hSrcDC);
DeleteDC(hDestDC);
}
}
}
c = screenPixel.GetPixel(0, 0);
}
return c;
Upvotes: 1