BitBlt函数鼠标闪烁

BitBlt 用于从原设备中复制位图到目标设备,语法格式如下:

  1. BOOL BitBlt(int x,int y,int nWidth,int nHeight,CDC*pSrcDC,int xSrc,int ySrc,DWORDdwRop);

各参数含义:

  • x:目标矩形区域的左上角x轴坐标点。
  • y:目标矩形区域的左上角y轴坐标点。
  • nWidth:在目标设备中绘制位图的宽度。
  • nHight:在目标设备中绘制位图的高度。
  • pSrcDC:源设备上下文对象指针。
  • xSrc:源设备上下文的起点x轴坐标,函数从该起点复制位图到目标设备。
  • ySrc:源设备上下文的起点y轴坐标,函数从该起点复制位图到目标设备。
  • dwRop:光栅操作代码

其中 dwRop 可以设置的值如下:

  1. BLACKNESS 使用黑色填充目标区域
  2. DSTINVERT 目标矩阵区域颜色取反
  3. MERGECOPY 使用与运算组合原设备矩形区域的颜色和目标设备的画刷
  4. MERGEPAINT 使用或运算将反向的源矩形区域的颜色和目标矩形区域的颜色合并
  5. NOTSRCCOPY 复制源设备区域的反色到目标设备中
  6. NOTSRCERASE 使用或运算组合源设备区域与目标设备区域的颜色,然后对结果颜色取反
  7. PATCOPY 复制源设备当前选中的画刷到目标设备
  8. PATINVERT 使用异或运算组合目标设备选中的画刷和目标设备区域的颜色
  9. PATPAINT 通过或运算组合目标区域当前选中的画刷和源设备区域反转的颜色
  10. SRCAND 使用与运算组合源设备和目标设备区域的颜色
  11. SRCCOPY 直接复制源设备区域到目标设备中
  12. SRCERASE 使用与运算组合目标设备区域的反色与源设备区域的颜色
  13. SRCINVERT 使用异或运算组合源设备区域颜色和目标设备区域颜色
  14. SRCPAINT 使用或运算组合源设备区域颜色和目标设备区域颜色
  15. WHITENESS 使用白色填充目标区域

但是我们在用BitBlt函数进行屏幕捕捉时,若传递了 CAPTUREBLT (捕捉alpha blending,即半透明窗口)标志,鼠标就会闪烁。

  1. BitBlt(hDCMem,0,0,xScrn,yScrn,hDCSource,0,0,SRCCOPY|CAPTUREBLT);

原因情况博客《windows屏幕捕捉BitBlt函数鼠标闪烁问题》。以防丢失,记录结论:

在windows2000及以后的系统上,鼠标及半透明窗口这两种图形对象是浮于桌面其他图形对象之上的,这里我们称它们为层叠窗口。层叠窗口并不存在于通常的显示场景(具体是在显示的哪一层,我也不清楚,姑且称它为场景M)中,只有在显示到屏幕的最后一刻,Windows才把层叠窗口绘制到屏幕上。

仅使用SRCCOPY标志时,Windows只需要从M中拷贝屏幕图像就行了。而若使用了CAPTUREBLT标志,导致的结果是鼠标及半透明窗口均被捕捉下来。但在设计上,BitBlt函数是不允许捕捉鼠标的。于是,系统只好先隐藏鼠标,然后捕捉图像,再恢复鼠标,结果就导致了鼠标的闪烁。

所以修改这个问题方法是,去除 CAPTUREBLT 参数。

  1. BitBlt(hDCMem,0,0,xScrn,yScrn,hDCSource,0,0,SRCCOPY);

显示鼠标光标

我们在进行屏幕截图时,使用Windows API获取的数据中,是没有鼠标光标的。是因为 鼠标图层位于桌面图层之上。image.png
如图,桌面图层使我们通过 HDC 可以获取的数据部分,如果想要在截图中显示出鼠标图形,需要手动绘制绘制到图层数据上。即先通过鼠标层获取到鼠标位置,然后绘制到数据上。基本实现逻辑如下:

  1. //宽、高仅仅是为了占位置,设置实际参数即可。
  2. int width = 0;
  3. int heigth = 0;
  4. HWND curhWind = GetDesktopWindow();
  5. HDC curhDDC = GetDC(curhWind);
  6. HDC curhCDC = CreateCompatibleDC(curhDDC); //创建与桌面窗口兼容的DC
  7. HBITMAP curBitMap = CreateCompatibleBitmap(curhDDC, width, heigth);
  8. SelectObject(curhCDC, curBitMap);
  9. if (!BitBlt(curhCDC, 0, 0, width, heigth, curhDDC, width, heigth, SRCCOPY))
  10. {
  11. return;
  12. }
  13. CURSORINFO cursinf = { sizeof(cursinf) };
  14. GetCursorInfo(&cursinf); //获取鼠标信息
  15. if (cursinf.flags == CURSOR_SHOWING)
  16. {
  17. ICONINFO iconinf = { sizeof(iconinf) };
  18. GetIconInfo(cursinf.hCursor, &iconinf);
  19. int x = cursinf.ptScreenPos.x - iconinf.xHotspot;
  20. int y = cursinf.ptScreenPos.y - iconinf.yHotspot;
  21. BITMAP bmpCur = { 0 };
  22. GetObject(iconinf.hbmColor, sizeof(bmpCur), &bmpCur);
  23. DrawIconEx(curhCDC, x, y, cursinf.hCursor, bmpCur.bmWidth, bmpCur.bmHeight, 0, NULL, DI_NORMAL);
  24. DeleteObject(iconinf.hbmColor);
  25. DeleteObject(iconinf.hbmMask);
  26. DestroyCursor(cursinf.hCursor);
  27. }