Reputation: 1881
I work on a small real-time project where a fast bitmap rendering technique is quite necessary. I need to display many(hundreds) small blocks in a picturebox per second,i found the bitblt
example from the pinvoke.net website.
I use a while loop(right now it's infinite ),to retrieve a particular bitmap,and then calling the Invalidate()
method to trigger the Paint
event.
This is my code:
protected override void OnPaint(PaintEventArgs e)
{
IntPtr pTarget = e.Graphics.GetHdc();
IntPtr pSource = CreateCompatibleDC(pTarget);
IntPtr pOrig = SelectObject(pSource, bmp.GetHbitmap());
BitBlt(pTarget, 0, 0, bmp.Width, bmp.Height, pSource, 0, 0, TernaryRasterOperations.SRCCOPY);
DeleteObject(pOrig);
DeleteDC(pSource);
e.Graphics.ReleaseHdc(pTarget);
}
private void Display()
{
while (true)
{
frame = desktopDuplicator.GetLatestFrame();
if (frame != null)
{
bmp = frame.DesktopImage;//retrieve image.
this.Invoke(new Action(() => this.Invalidate()));//trigger the repaint event
}
}
}
It works fine for few seconds,then i'm getting an System.ArgumentException
on this line:
BitBlt(pTarget, 0, 0, bmp.Width, bmp.Height, pSource, 0, 0, TernaryRasterOperations.SRCCOPY);
Does anyone have an idea what is wrong here? i keep releasing the used resources(in the paint event)...why i'm getting this error?
Thanks in advance.
Upvotes: 1
Views: 2677
Reputation: 205909
Does anyone have an idea what is wrong here? i keep releasing the used resources(in the paint event)...why i'm getting this error?
Actually you are not releasing all the used resources, specifically the bitmap handle returned by the bmp.GetHbitmap()
call. The correct sequence is to select back the original default bitmap handle into device context and then delete your bitmap handle, as explained in the SelectObject
documentation:
This function returns the previously selected object of the specified type. An application should always replace a new object with the original, default object after it has finished drawing with the new object.
IntPtr targetDC = e.Graphics.GetHdc();
IntPtr sourceDC = CreateCompatibleDC(targetDC);
IntPtr sourceBitmap = bmp.GetHbitmap();
IntPtr originalBitmap = SelectObject(sourceDC, sourceBitmap);
BitBlt(targetDC, 0, 0, bmp.Width, bmp.Height, sourceDC, 0, 0, TernaryRasterOperations.SRCCOPY);
SelectObject(sourceDC, originalBitmap);
DeleteObject(sourceBitmap);
DeleteDC(sourceDC);
e.Graphics.ReleaseHdc(targetDC);
Upvotes: 2
Reputation: 38128
From the documentation for the Bitmap.GetHbitmap
method:
You are responsible for calling the GDI DeleteObject method to free the memory used by the GDI bitmap object.
You don't currently appear to be calling that, which will lead to a leak. You should call DeleteObject
once you're done with the resource, so maybe something like:
protected override void OnPaint(PaintEventArgs e)
{
IntPtr pTarget = e.Graphics.GetHdc();
IntPtr pSource = CreateCompatibleDC(pTarget);
IntPtr pOrig = SelectObject(pSource, bmp.GetHbitmap());
BitBlt(pTarget, 0, 0, bmp.Width, bmp.Height, pSource, 0, 0, TernaryRasterOperations.SRCCOPY);
DeleteObject(pOrig);
DeleteDC(pSource);
e.Graphics.ReleaseHdc(pTarget);
}
Upvotes: 1