屏幕截图在移至第二台显示器时不显示鼠标光标

戴维森

最近,我一直在进行屏幕快照(用于远程桌面系统)方面的大量工作,但在尝试实现对多台显示器的支持时,偶然发现了一个问题。虽然截取屏幕截图还可以,但我用来绘制光标的方法仅假定1个屏幕。如果我将指针放在其他屏幕上(在对该屏幕进行截图时),则不会显示光标。我将指针移到主屏幕,它显示了(当然在错误的位置,因为它是错误的屏幕)。

我的代码完全在下面。

program Test;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  Windows,
  vcl.Graphics,
  SysUtils;

function GetCursorInfo2: TCursorInfo;
var
  hWindow: HWND;
  pt: TPoint;
  dwThreadID, dwCurrentThreadID: DWORD;
begin
  Result.hCursor := 0;
  ZeroMemory(@Result, SizeOf(Result));
  if GetCursorPos(pt) then
  begin
    Result.ptScreenPos := pt;
    hWindow := WindowFromPoint(pt);
    if IsWindow(hWindow) then
    begin
      dwThreadID := GetWindowThreadProcessId(hWindow, nil);
      dwCurrentThreadID := GetCurrentThreadId;
      if (dwCurrentThreadID <> dwThreadID) then
      begin
        if AttachThreadInput(dwCurrentThreadID, dwThreadID, True) then
        begin
          Result.hCursor := GetCursor;
          AttachThreadInput(dwCurrentThreadID, dwThreadID, False);
        end;
      end
      else
        Result.hCursor := GetCursor;
    end;
  end;
end;

procedure TakeScreenshot(var Bmp: TBitmap; WndHdc: HDC; Width, Height, Left, Top: Integer);
const
  CAPTUREBLT = $40000000;
var
  DesktopCanvas: TCanvas;
  MyCursor: TIcon;
  CursorInfo: TCursorInfo;
  IconInfo: TIconInfo;
  DC: HDC;
begin
  DC := GetDC(WndHdc);
  try
    if (DC = 0) then
      Exit;
    Bmp.Width := Width;
    Bmp.Height := Height;
    DesktopCanvas := TCanvas.Create;
    try
      DesktopCanvas.Handle := DC;
      BitBlt(Bmp.Canvas.Handle, 0, 0, Bmp.Width, Bmp.Height, DesktopCanvas.Handle, Left, Top, SRCCOPY or CAPTUREBLT);
      MyCursor := TIcon.Create;
      try
        CursorInfo := GetCursorInfo2;
        if CursorInfo.hCursor <> 0 then
        begin
          MyCursor.Handle := CursorInfo.hCursor;
          GetIconInfo(CursorInfo.hCursor, IconInfo);
          Bmp.Canvas.Draw(CursorInfo.ptScreenPos.X - IconInfo.xHotspot, CursorInfo.ptScreenPos.Y - IconInfo.yHotspot, MyCursor);
        end;
      finally
        MyCursor.ReleaseHandle;
        MyCursor.Free;
      end;
    finally
      DesktopCanvas.Free;
    end;
  finally
    if (DC <> 0) then
      ReleaseDC(0, DC);
  end;
end;

function EnumDisplayMonitors(dc: HDC; rect: PRect; EnumProc: pointer; lData: Integer): Boolean; stdcall; external user32 name 'EnumDisplayMonitors';

type
  TMonInfo = record
    h: THandle;
    DC: HDC;
    R: TRect;
  end;

var
  MonList: array of TMonInfo;

function MonitorEnumProc(hMonitor: THandle; hdcMonitor: HDC; lprcMonitor: DWORD; dwData: Integer): Boolean; stdcall;
var
  I, Width, Height, Left, Top: Integer;
  Bmp: TBitmap;
begin
  I := High(MonList) + 1;
  SetLength(MonList, I + 1);
  MonList[I].h := hMonitor;
  MonList[I].DC := hdcMonitor;
  MonList[I].R := PRect(lprcMonitor)^;

  Left := PRect(lprcMonitor)^.Left;
  Top := PRect(lprcMonitor)^.Top;
  Width := PRect(lprcMonitor)^.Width;
  Height := PRect(lprcMonitor)^.Height;

  Bmp := TBitmap.Create;
  try
    TakeScreenshot(Bmp, hdcMonitor, Width, Height, Left, Top);
    Bmp.SaveToFile('C:\Screen' + IntToStr(I + 1) + '.bmp');
  finally
    Bmp.Free;
  end;

  Result := True;
end;

procedure Main;
var
  S: string;
  I: Integer;
begin
  Writeln('Number of monitors: ' + IntToStr(High(MonList) + 1) + #13#10);
  Writeln('-----------------');
  for I := 0 to High(MonList) do
    with MonList[I] do
    begin
      S := #13#10 + 'Handle: ' + IntToStr(h) + #13#10 + 'Dc: ' + IntToStr(DC) + #13#10 + 'Size: ' + IntToStr(R.Right) + 'x' + IntToStr(R.Bottom) + #13#10;
      Writeln(S);
      Writeln('-----------------');
    end;
end;

begin
  try
    EnumDisplayMonitors(0, nil, Addr(MonitorEnumProc), 0);
    Main;
    Writeln(#13#10 + 'Connected: ' + IntToStr(GetSystemMetrics(SM_CMONITORS)) + #13#10);
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.
吴德瑞-MSFT

问题是您从中获得的光标GetCursorInfo2坐标相对于位图而言不是正确的坐标。首先,确定光标点是否位于中lprcMonitor,您可以使用PtInRect,然后使用DrawIcon将hcursor绘制到位图中(如果返回true)。这是从您的代码转换而来的C ++示例(因为我对delphi不熟悉):

#include <windows.h>
#include <iostream>
#include <string>
#include <string.h>
#include <stdlib.h>
#include <gdiplus.h>
#include <stdio.h>
using namespace Gdiplus;
using namespace std;
#pragma comment(lib, "Gdiplus.lib")
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
    UINT  num = 0;          // number of image encoders
    UINT  size = 0;         // size of the image encoder array in bytes

    ImageCodecInfo* pImageCodecInfo = NULL;

    GetImageEncodersSize(&num, &size);
    if (size == 0)
        return -1;  // Failure

    pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
    if (pImageCodecInfo == NULL)
        return -1;  // Failure

    GetImageEncoders(num, size, pImageCodecInfo);

    for (UINT j = 0; j < num; ++j)
    {
        if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0)
        {
            *pClsid = pImageCodecInfo[j].Clsid;
            free(pImageCodecInfo);
            return j;  // Success
        }
    }

    free(pImageCodecInfo);
    return -1;  // Failure
}

//HCURSOR GetCursorInfo2(POINT * pt)
//{
//    POINT p = { 0 };
//    HWND hWindow = NULL;
//    HCURSOR hCursor = NULL;
//    if (GetCursorPos(&p))
//    {
//        pt->x = p.x;
//        pt->y = p.y;
//        hWindow = WindowFromPoint(*pt);
//        if (IsWindow(hWindow))
//        {
//            DWORD dwThreadID = GetWindowThreadProcessId(hWindow, NULL);
//            DWORD dwCurrentThreadID = GetCurrentThreadId();
//            if (dwCurrentThreadID != dwThreadID)
//            {
//                if (AttachThreadInput(dwCurrentThreadID, dwThreadID, TRUE))
//                {
//                    hCursor = GetCursor();
//                    AttachThreadInput(dwCurrentThreadID, dwThreadID, FALSE);
//                }
//            }
//        }
//    }
//    return hCursor;
//}
void TakeScreenshot(HDC hdcbmp, HDC WndHdc, int Width, int Height, int Left, int Top)
{
    HDC hdc = GetDC(NULL);
    if (hdc == 0) exit(-1);
    BitBlt(hdcbmp, 0, 0, Width, Height, hdc, Left, Top, SRCCOPY | CAPTUREBLT);
    CURSORINFO cursorinfo = { 0 };
    cursorinfo.cbSize = sizeof(CURSORINFO);
    if (GetCursorInfo(&cursorinfo))
    {
        RECT rc = { Left ,Top,Left + Width ,Top + Height };

        if (PtInRect(&rc, cursorinfo.ptScreenPos))
        {
            DrawIcon(hdcbmp, cursorinfo.ptScreenPos.x - Left, cursorinfo.ptScreenPos.y - Top, cursorinfo.hCursor);
        }
    }
    /*ICONINFO IconInfo = { 0 };
    GetIconInfo(hCursor, &IconInfo);*/
}
BOOL CALLBACK Monitorenumproc(HMONITOR  hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
{
    static int count = 0;
    int Left = lprcMonitor->left;
    int Top = lprcMonitor->top;
    int Width = lprcMonitor->right - lprcMonitor->left;
    int Height = lprcMonitor->bottom - lprcMonitor->top;

    HDC dev = GetDC(NULL);
    HDC CaptureDC = CreateCompatibleDC(dev);
   
    HBITMAP CaptureBitmap = CreateCompatibleBitmap(dev, Width, Height);
    HGDIOBJ old_obj = SelectObject(CaptureDC, CaptureBitmap);
    TakeScreenshot(CaptureDC, dev, Width, Height, Left, Top);
    Gdiplus::Bitmap bitmap(CaptureBitmap, NULL);
    
    CLSID pngClsid;
    GetEncoderClsid(L"image/bmp", &pngClsid);
    wstring BmpNameString = L"C:\\screen";
    BmpNameString = BmpNameString + std::to_wstring(count) + L".bmp";
    count++;
    bitmap.Save(BmpNameString.c_str(), &pngClsid, NULL);

    SelectObject(CaptureDC, old_obj);
    DeleteDC(CaptureDC);
    ReleaseDC(NULL, dev);
    DeleteObject(CaptureBitmap);
    return TRUE;
}
int main(void)
{
    GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR gdiplusToken;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    EnumDisplayMonitors(0, NULL, Monitorenumproc, 0);

    GdiplusShutdown(gdiplusToken);
    return 0;
}

注意功能中的这些行TakeScreenshot

CURSORINFO cursorinfo = { 0 };
cursorinfo.cbSize = sizeof(CURSORINFO);
if (GetCursorInfo(&cursorinfo))
{
    RECT rc = { Left ,Top,Left + Width ,Top + Height };

    if (PtInRect(&rc, cursorinfo.ptScreenPos))
    {
        DrawIcon(hdcbmp, cursorinfo.ptScreenPos.x - Left, cursorinfo.ptScreenPos.y - Top, cursorinfo.hCursor);
    }
}

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

插入第二台显示器时,屏幕变黑

来自分类Dev

Lubuntu第二台显示器问题,windows不显示

来自分类Dev

连接第二台显示器时2560 x 1600屏幕分辨率不可用

来自分类Dev

系统无法正常启动-屏幕关闭并打开。仅在插入第二台显示器时

来自分类Dev

在Wayland上的Ubuntu 17.10的第二台显示器上重复鼠标指针

来自分类Dev

断开第二台显示器后如何恢复屏幕外窗口?

来自分类Dev

登录屏幕出现在第二台显示器上

来自分类Dev

在第二台显示器上登录屏幕的分辨率是错误的

来自分类Dev

仅当连接了第二台显示器时,如何才能显示gnome面板?

来自分类Dev

使Chrome在第二台显示器上打开?

来自分类Dev

查找禁用第二台显示器的过程

来自分类Dev

使用Nvidia旋转第二台显示器

来自分类Dev

如何获得第二台显示器的预览?

来自分类Dev

不再检测到第二台显示器

来自分类Dev

使第二台显示器显示位于主显示器左侧的工作区

来自分类Dev

使第二台显示器显示位于主显示器左侧的工作区

来自分类Dev

Linux Mint 18 Cinnamon-连接第二台显示器时冻结

来自分类Dev

Linux Mint 18 Cinnamon-连接第二台显示器时冻结

来自分类Dev

重新启动时重置第二台显示器的分辨率

来自分类Dev

设置第二台显示器时Xrandr崩溃

来自分类Dev

第二台显示器在启动时直接进入省电模式

来自分类Dev

每当我使用第二台显示器时,FPS就会减半

来自分类Dev

当第二台显示器通过 hdmi 连接时,Ubuntu 18.04 崩溃

来自分类Dev

在第二台显示器上向下移动鼠标指针时,鼠标指针卡住了一点

来自分类Dev

无法提高第二台显示器上的显示器刷新率

来自分类Dev

如何使我的应用程序出现在第二台显示器的中央而不是主屏幕的中央?

来自分类Dev

当笔记本电脑屏幕被禁用时移除了第二台显示器显示器保持关闭

来自分类Dev

使用AHK使第二台显示器全屏显示

来自分类Dev

如何在Qt的第二台显示器上全屏显示窗口形式?

Related 相关文章

  1. 1

    插入第二台显示器时,屏幕变黑

  2. 2

    Lubuntu第二台显示器问题,windows不显示

  3. 3

    连接第二台显示器时2560 x 1600屏幕分辨率不可用

  4. 4

    系统无法正常启动-屏幕关闭并打开。仅在插入第二台显示器时

  5. 5

    在Wayland上的Ubuntu 17.10的第二台显示器上重复鼠标指针

  6. 6

    断开第二台显示器后如何恢复屏幕外窗口?

  7. 7

    登录屏幕出现在第二台显示器上

  8. 8

    在第二台显示器上登录屏幕的分辨率是错误的

  9. 9

    仅当连接了第二台显示器时,如何才能显示gnome面板?

  10. 10

    使Chrome在第二台显示器上打开?

  11. 11

    查找禁用第二台显示器的过程

  12. 12

    使用Nvidia旋转第二台显示器

  13. 13

    如何获得第二台显示器的预览?

  14. 14

    不再检测到第二台显示器

  15. 15

    使第二台显示器显示位于主显示器左侧的工作区

  16. 16

    使第二台显示器显示位于主显示器左侧的工作区

  17. 17

    Linux Mint 18 Cinnamon-连接第二台显示器时冻结

  18. 18

    Linux Mint 18 Cinnamon-连接第二台显示器时冻结

  19. 19

    重新启动时重置第二台显示器的分辨率

  20. 20

    设置第二台显示器时Xrandr崩溃

  21. 21

    第二台显示器在启动时直接进入省电模式

  22. 22

    每当我使用第二台显示器时,FPS就会减半

  23. 23

    当第二台显示器通过 hdmi 连接时,Ubuntu 18.04 崩溃

  24. 24

    在第二台显示器上向下移动鼠标指针时,鼠标指针卡住了一点

  25. 25

    无法提高第二台显示器上的显示器刷新率

  26. 26

    如何使我的应用程序出现在第二台显示器的中央而不是主屏幕的中央?

  27. 27

    当笔记本电脑屏幕被禁用时移除了第二台显示器显示器保持关闭

  28. 28

    使用AHK使第二台显示器全屏显示

  29. 29

    如何在Qt的第二台显示器上全屏显示窗口形式?

热门标签

归档