I want to catch a Desktop frame and store it in a HBITMAP struct.
Then, after creating a proper memory device context out of my application main window's device context, I would select the HBITMAP into it and the use StretchBlt to display the bitmap.
But this doesn't work as expected, because it just shows a black frame.
Both hdc and mem_hdc are respectively the device context and memory device context of main window initialized before.
Here's the code:
...
hDC desk_hdc, desk_mem_hdc;
BITMAP bitmap;
HBITMAP hbitmap;
desk_hdc = GetDC(NULL);
hbitmap = CreateCompatibleBitmap(desk_hdc, GetDeviceCaps(desk_hdc, HORZRES), GetDeviceCaps(desk_hdc, VERTRES));
GetObject(hbitmap, sizeof(BITMAP), &bitmap);
SelectObject(mem_hdc, hbitmap);
StretchBlt(hdc, 0, 0, 1024, 768, mem_hdc, 0, 0, bitmap.bmWidth, bitmap.bmHeight, SRCCOPY|CAPTUREBLT|NOMIRRORBITMAP);
...
The source dc of your StretchBlt operation is mem_hdc, which has a compatible uninitialized bitmap. That's why you get a black frame.
If you want to capture the desktop contents, you have to first copy it to the bitmap in your mem_hdc. Just after SelectObject do:
BitBlt( mem_hdc, 0, 0, bitmap.bmWidth, bitmap.bmHeight, desk_hdc, 0, 0, SRCCOPY );
Related
I have this code
// get the device context of the screen
HDC hScreenDC = GetDC(NULL);
// and a device context to put it in
HDC hMemoryDC = CreateCompatibleDC(hScreenDC);
int width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
int height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
HBITMAP hBitmap = CreateCompatibleBitmap(hScreenDC, width, height);
// get a new bitmap
HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemoryDC, hBitmap);
BitBlt(hMemoryDC, 0, 0, width, height, hScreenDC, 0, 0, SRCCOPY);
hBitmap = (HBITMAP)SelectObject(hMemoryDC, hOldBitmap);
// clean up
DeleteDC(hMemoryDC);
DeleteDC(hScreenDC);
that generates an HBITMAP of the screen. It works perfectly most of the time, but if I try to run it when I have something on full screen on my browser (Youtube, for example), the image captured is not of the video, rather other program running on the background (Visual Studio for me). I suspect the problem is on getting the device context, but I've tried a few alternatives and had the same problems. How can I solve this?
Use DirectX method from this article: https://www.codeproject.com/Articles/5051/Various-methods-for-capturing-the-screen
After calling GetFrontBufferData you can simply save your bitmap to disk or dig out actual image data and use SetDIBits to assign it to HBITMAP.
I need do copy HDC content but my code is not working - any ideas why? Everything is well until I'm trying to copy between HDC objects. It seems that bits goes nowhere. I'm new to GDI programming.
I'm not sure how SelectObject should work here.
PAINTSTRUCT ps;
HDC paintDC = BeginPaint(hWnd, &ps);
HDC imageDC = ::CreateCompatibleDC(paintDC);
HDC bufferDC = ::CreateCompatibleDC(paintDC);
BITMAPINFO bitmapInfo;
memset ( &bitmapInfo, 0, sizeof(BITMAPINFOHEADER) );
bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
int scanLines = GetDIBits(imageDC, // handle to DC
m_bitmap, // handle to bitmap
0, // first scan line to set
0, // number of scan lines to copy
NULL, // array for bitmap bits
&bitmapInfo, // bitmap data buffer
DIB_RGB_COLORS ); // RGB or palette index
// Paint the bitmap image.
HBITMAP pOldBitmap = (HBITMAP)SelectObject( imageDC, m_bitmap );
int width = bitmapInfo.bmiHeader.biWidth;
int height = bitmapInfo.bmiHeader.biHeight;
// Copy imageDC to bufferDC
BitBlt(bufferDC, 0, 0,
width, height,
imageDC, 0, 0, SRCCOPY) ;
BitBlt(paintDC, 0, 0,
width, height,
imageDC, 0, 0, SRCCOPY);
SelectObject(imageDC, pOldBitmap);
When you call CreateCompatibleDC function it'll create so called "memory DC" object. Memory DC doesn't have any image data attached to it by default (in fact id does, 1x1 monochrome bitmap, which is not what you want).
You do select a bitmap object for your imageDC, but you don't select any bitmap object for your bufferDC, that's why "bits go nowhere".
Create a HBITMAP object for your bufferDC (you can use CreateCompatibleBitmap for this) and select it for your bufferDC before performing any blits into it.
Remember one thing: DC object is an interface to something you can paint on, it doesn't contain any bitmap data itself.
I'm looking for a way how this GDI code to get a bitmap from a Device Context...
void CMFCDlg::OnPaint()
{
CDC dc(this); // Device Context for painting
CBitmap backgroundBmp;
// Get Client Area
CRect clientRect;
GetClientRect(&clientRect);
// Create memory DC
CDC memDC;
memDC.CreateCompatibleDC(&dc);
// Create compatible bitmap
backgroundBmp.CreateCompatibleBitmap(&memDC, clientRect.Width(), clientRect.Height());
// Copy Blt Bits from DC to Bitmap
CBitmap* pOldBmp = dc.SelectObject(&backgroundBmp);
memDC.BitBlt(0, 0, clientRect.Width(), clientRect.Height(), &dc, 0, 0, SRCCOPY);
dc.SelectObject(pOldBmp);
// Release the memory DC
memDC.DeleteDC();
}
...can be done in GDI+ to receive a GDI+ Image or Bitmap.
I'm looking for some way to do it close to this draft:
void CMFCDlg::OnPaint()
{
CDC dc(this); // Device Context for painting
Bitmap backgroundBmp;
// Get Client Area
CRect clientRect;
GetClientRect(&clientRect);
// Get graphics object from device context
Graphics gr(dc);
// Somehow create a compatible GDI+ bitmap
backgroundBmp = gr.??????
}
I've only seen code involving GDI Objects and resources which later get converted to GDI+ Objects. But none of them worked for me yet and I feel like there is an (easy) other way to do this with the much more comfortable GDI+ environment.
You can only draw GDI+ to device context. But you can use memory device context and then draw to dc or save to file:
CClientDC dc(this);
CDC memdc;
memdc.CreateCompatibleDC(&dc);
CBitmap bitmap;
bitmap.CreateCompatibleBitmap(&dc, 100, 100);
CBitmap *oldbitmap = (CBitmap*)memdc.SelectObject(&bitmap);
Gdiplus::Graphics graphics_memdc(memdc);
Gdiplus::Pen pen(Gdiplus::Color(255, 0, 128, 255), 5);
Gdiplus::Rect rect(10, 10, 80, 80);
graphics_memdc.DrawRectangle(&pen, rect);
Now draw to dc
dc.BitBlt(0, 0, 100, 100, &memdc, 0, 0, SRCCOPY);
memdc.SelectObject(oldbitmap);
or use CImage to save memdc to a file
CImage cimage;
cimage.Create(100, 100, 32);
HDC imageHDC = cimage.GetDC();
::BitBlt(imageHDC, 0, 0, 100, 100, memdc.GetSafeHdc(), 0, 0, SRCCOPY);
cimage.Save(L"c:\\test\\fileName.jpg", GUID_NULL);
cimage.ReleaseDC();
I have two classes with windows in one program (the view, and dialog), that must show pictures.
The view take picture from disk like:
CClientDC dc(this);
GetClientRect(&rc);
dcMem.DeleteDC();
hBmp = NULL;;
m_bmpBack.DeleteObject();
hBmp = LoadImage(NULL,"d:/USBconn.bmp",IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
GetObject ( hBmp, sizeof(bm), &bm );
m_bmpBack.Attach(hBmp);
dcMem.CreateCompatibleDC(&dc);
hbmpOld = (HBITMAP)dcMem.SelectObject(m_bmpBack);
dc.StretchBlt(rc.left, rc.top, rc.right, rc.bottom,&dcMem, 0, 0, (int)bm.bmWidth, (int)bm.bmHeight, SRCCOPY);
How to "BitBlt" image from this dc in to the dc in the other class and window?
I have created an animation which works fine, but it flicks. I need help with double-buffering since I don't know anything about it.
This is the code in my onPaint():
VOID onPaint(HDC hdc)
{
Graphics graphics(hdc);
Pen pen(Color(255, 0, 0, 255));
graphics.DrawEllipse(&pen, sf , 0, 10, 10);
}
It works fine but with flicker. I tried this code but it didn't work:
VOID onPaint(HDC hdc,HWND hWnd)
{
HDC hDC=GetDC(hWnd);;
HDC memDC = CreateCompatibleDC(hDC);
HBITMAP hMemBmp = CreateCompatibleBitmap(hDC,10,10);
HBITMAP hOldBmp = (HBITMAP)SelectObject(memDC,hMemBmp);
BitBlt(hDC, 0, 0, 10, 10, memDC, 0, 0, SRCCOPY);
Graphics graphics(memDC);
Pen pen(Color(255, 0, 0, 255));
graphics.DrawEllipse(&pen, sf , 0, 10, 10);
// Always select the old bitmap back into the device context
SelectObject(memDC, hOldBmp);
DeleteObject(hMemBmp);
DeleteDC(memDC);
}
It looks like you're just prematurely copying the offscreen DC to the display. Try moving the call to BitBlt down four lines, to make it the last line before you start the clean-up, like so:
VOID onPaint(HDC hdc,HWND hWnd)
{
// this line looks a little odd :
HDC hDC = GetDC(hWnd);
// .. usually the hdc parameter passed to onPaint would already refer to
// the on-screen DC that windows wants updated. Also worth noting is that
// when you use GetDC(), you should have a matching ReleaseDC()
// As a quick test, you might just replace the above line with
// HDC hDC = hdc;
HDC memDC = CreateCompatibleDC(hDC);
HBITMAP hMemBmp = CreateCompatibleBitmap(hDC,10,10);
HBITMAP hOldBmp = (HBITMAP)SelectObject(memDC,hMemBmp);
// draw to the off-screen map ..
Graphics graphics(memDC);
Pen pen(Color(255, 0, 0, 255));
graphics.DrawEllipse(&pen, sf , 0, 10, 10);
// now that you've drawn on the offscreen map, go ahead
// and put it on screen.
BitBlt(hDC, 0, 0, 10, 10, memDC, 0, 0, SRCCOPY);
// Always select the old bitmap back into the device context
SelectObject(memDC, hOldBmp);
DeleteObject(hMemBmp);
DeleteDC(memDC);
}
Another thing about this code, you've passed the constant '10' as the width and height of your off-screen bitmap, as well as using it for the width and height params to the BitBlt() that does the copy. Chances are the window client area being updated is very much larger than that. The 'black square' is a consequence of blitting the 10x10 off-screen map onto the window client area. Instead of hard-coding 10 there, you might try using another GDI function to obtain the dimensions of the on-screen bitmap, or at the very least you could #define width and height values, and use these in the params.
The other thing killing you is probably the 'sf' in the line "graphics.DrawEllipse(&pen, sf , 0, 10, 10)" -- since you've created an incredibly tiny 10x10 map, if the value of 'sf' is anything outside of 0..10, the DrawEllipse() call will place the ellipse entirely outside of the available pixels in your offscreen map.
So, bottom line, you probably want to make the offscreen map the same size as the window client area, and be sure to move the BitBlt() call down so that it happens after all the drawing ops on the off-screen map.