I use CreateDC / BitBlt / GetDIBits etc. to capture the screen, but the cursor is not captured. Is there some simple argument or something to have it included?
-
1In a word, no. The cursor is drawn by the OS and, as such, it's not actually a part of the image held by the HBITMAP selected into an HDC. It's drawn ontop of (the copy of) this image when drawing to screen. The way to handle this is to get the mouse position when you perform the capture. You then draw a copy of the cursor's image at this position. Note that the cursor isn't captured when you press the PrtSc button either. The software InstantDemo (a screen recording app) takes the approach I mention to displaying the mouse cursor. – enhzflep Jun 22 '14 at 04:54
-
How do I get the image of the cursor? – Tez Jun 22 '14 at 05:42
-
By googling "pointer cursor" then downloading one that you like. There's only 5 million hits or so - I'd imagine you'll find one you like. – enhzflep Jun 22 '14 at 05:43
-
But I need the cursor to be the same as what is on the screen since it will be captured in software that have custom cursors. – Tez Jun 22 '14 at 08:46
-
1Its funny you should say that, I considered just that possibility shortly after posting my reply. In that case, you can get the cursor with the GetCursor function, although, I reckon this wont work in another process or thread. In that case, I suspect that you should use GetCursorInfo. http://msdn.microsoft.com/en-us/library/windows/desktop/ms648388(v=vs.85).aspx seems like a good place to start. For what it's worth, they're quite similar to HICON, albeit with extra vars to define the position of the hotspot. (i.e top-left for the 'normal' cursor, middle center for a cross-hair) – enhzflep Jun 22 '14 at 09:04
-
@enhzflep: want to write all of this out as an answer? It is a good question. – Jongware Jun 22 '14 at 11:24
-
@Jongware - consider it done. Thanks for the prompt - your comment caused me to further investigate obtaining the cursor's HBITMAP (and mask). – enhzflep Jun 22 '14 at 13:43
2 Answers
#include <Windows.h>
#include <stdio.h>
#include <assert.h>
void scrshot() {
HWND hwnd = GetDesktopWindow();
HDC hdc = GetWindowDC(hwnd);
HDC hdcMem = CreateCompatibleDC(hdc);
int cx = GetDeviceCaps(hdc, HORZRES);
int cy = GetDeviceCaps(hdc, VERTRES);
HBITMAP hbitmap(NULL);
hbitmap = CreateCompatibleBitmap(hdc, cx, cy);
SelectObject(hdcMem, hbitmap);
BitBlt(hdcMem, 0, 0, cx, cy, hdc, 0, 0, SRCCOPY);
CURSORINFO cursor = { sizeof(cursor) };
GetCursorInfo(&cursor);
if (cursor.flags == CURSOR_SHOWING) {
RECT rect;
GetWindowRect(hwnd, &rect);
ICONINFO info = { sizeof(info) };
GetIconInfo(cursor.hCursor, &info);
const int x = cursor.ptScreenPos.x - rect.left - rect.left - info.xHotspot;
const int y = cursor.ptScreenPos.y - rect.top - rect.top - info.yHotspot;
BITMAP bmpCursor = { 0 };
GetObject(info.hbmColor, sizeof(bmpCursor), &bmpCursor);
DrawIconEx(hdcMem, x, y, cursor.hCursor, bmpCursor.bmWidth, bmpCursor.bmHeight,
0, NULL, DI_NORMAL);
}
}
int main(){
scrshot();
return 0;
}
Further to the discussion that occurred in the comments, I had the chance to further investigate the question. As a result, I came up with the following code that will grab the current cursor's HBITMAP and draw it to the screen.
Since the cursor is actually an HICON, it comes with a mask. Initially, I just did a simple BitBlt - however, I got a 32x32 black sqaure with the cursor in the top left 1/4 or so.
I then investigated using MaskBlt. Depending on where the cursor is when the app is started, I get either the wait cursor, a NS resize cursor, or the standard pointer. I guess you could start a timer and add a WM_TIMER handler to fire a couple of times a second in order to get a real-time update of the cursor as it was used in other windows in the system. It seemed like a mere curiosity to do something like that so I didn't bother.
EDIT: I actually did start a timer in WM_INITDIALOG and handle it in WM_TIMER. You can now see the image updated 10 times a second. For some reason, the I-beam cursor doesn't seem to be displayed at all - a case for further investigation as needed, I guess.
Here's the complete listing (except for resource.rc and resource.h - just create a dialog app and make sure the dialog's resource ID is used inside Main in the call to DialogBox)
#include <windows.h>
#include <commctrl.h>
#include <stdio.h>
#include "resource.h"
HINSTANCE hInst;
HBITMAP getCursorHBITMAP(HBITMAP *maskBmp)
{
CURSORINFO pci;
ICONINFO iconinfo;
HBITMAP result;
pci.cbSize = sizeof(pci);
GetCursorInfo(&pci);
if (GetIconInfo(pci.hCursor,&iconinfo))
{
result = iconinfo.hbmColor;
if (maskBmp)
*maskBmp = iconinfo.hbmMask;
}
else
result = NULL;
return result;
}
BOOL CALLBACK DlgMain(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_INITDIALOG:
{
SetTimer(hwndDlg, 1, 100, NULL);
}
return TRUE;
case WM_TIMER:
{
InvalidateRect(hwndDlg, NULL, true);
}
return 0;
case WM_ERASEBKGND:
{
HDC hdc = (HDC)wParam;
RECT mRect;
GetClientRect(hwndDlg, &mRect);
FillRect(hdc, &mRect, (HBRUSH)GetStockObject(GRAY_BRUSH));
}
return 1;
case WM_PAINT:
{
HBITMAP oldBm, cursorBmp, maskBmp;
cursorBmp = getCursorHBITMAP(&maskBmp);
if (cursorBmp)
{
HDC hdc;
PAINTSTRUCT ps;
HDC memDC;
BITMAP bm;
hdc = BeginPaint(hwndDlg, &ps);
memDC = CreateCompatibleDC(hdc);
oldBm = (HBITMAP) SelectObject(memDC, cursorBmp);
GetObject(cursorBmp, sizeof(bm), &bm);
// printf("Cursor size: %d x %d\n", bm.bmWidth, bm.bmHeight);
// BitBlt(hdc, 10,10, 32,32, memDC, 0,0, SRCCOPY);
MaskBlt(hdc, 10,10, bm.bmWidth, bm.bmHeight, memDC, 0,0, maskBmp, 0,0, MAKEROP4(SRCPAINT,SRCCOPY) );
SelectObject(memDC, oldBm);
DeleteDC(memDC);
EndPaint(hwndDlg, &ps);
}
}
return 0;
case WM_CLOSE:
{
EndDialog(hwndDlg, 0);
}
return TRUE;
case WM_COMMAND:
{
switch(LOWORD(wParam))
{
}
}
return TRUE;
}
return FALSE;
}
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
hInst=hInstance;
InitCommonControls();
return DialogBox(hInst, MAKEINTRESOURCE(DLG_MAIN), NULL, (DLGPROC)DlgMain);
}

- 12,927
- 2
- 32
- 51