如何仅捕获屏幕上的鼠标单击区域?

Posted

技术标签:

【中文标题】如何仅捕获屏幕上的鼠标单击区域?【英文标题】:How capture only the region of mouse click on screen? 【发布时间】:2021-06-25 02:51:13 【问题描述】:

每次单击鼠标左键时,以下代码都会对桌面进行屏幕截图。 但是我只想对发生鼠标单击的区域进行屏幕截图,例如,如果在某个网站上单击某个按钮,则屏幕截图必须仅是该按钮的屏幕截图。

GIF

这可能吗?

如果是的话,如果有人显示代码示例,我将非常高兴!提前致谢。

program Project1;

$APPTYPE CONSOLE
$R *.res

uses
  Windows,
  Messages,
  SysUtils,
  Graphics,
  Imaging.PngImage;
  
type
  MouseLLHookStruct = record
  end;
  
const
  WH_MOUSE_LL = 14;
  
var
  Msg: TMsg;
  mHook: Cardinal;

procedure GetCursor(ScreenShotBitmap: TBitmap);
var
  R: TRect;
  Icon: TIcon;
  II: TIconInfo;
  CI: TCursorInfo;
begin
  R := ScreenShotBitmap.Canvas.ClipRect;
  Icon := TIcon.Create;
  try
    CI.cbSize := SizeOf(CI);
    if GetCursorInfo(CI) then
      if CI.Flags = CURSOR_SHOWING then
      begin
        Icon.Handle := CopyIcon(CI.hCursor);
        if GetIconInfo(Icon.Handle, II) then
        begin
          ScreenShotBitmap.Canvas.Draw(CI.ptScreenPos.X - Integer(II.xHotspot) -
            R.Left, CI.ptScreenPos.Y - Integer(II.yHotspot) - R.Top, Icon);
        end;
      end;
  finally
    Icon.Free;
  end;
end;

procedure ScreenCapture;
var
  DC: HDC;
  Rect: TRect;
  png: TPngImage;
  Bitmap: TBitmap;
begin
  png := TPngImage.Create;
  Bitmap := TBitmap.Create;
  GetWindowRect(GetDesktopWindow, Rect);
  DC := GetWindowDC(GetDesktopWindow);
  try
    Bitmap.Width := Rect.Right - Rect.Left;
    Bitmap.Height := Rect.Bottom - Rect.Top;
    BitBlt(Bitmap.Canvas.Handle, 0, 0, Bitmap.Width, Bitmap.Height, DC, 0,
      0, SRCCOPY);
    GetCursor(Bitmap);
    png.Assign(Bitmap);
    png.SaveToFile('screenshot.png');
  finally
    ReleaseDC(GetDesktopWindow, DC);
    png.Free;
    Bitmap.Free;
  end;
end;

function LowLevelMouseHookProc(nCode: LongInt; WPARAM: WPARAM; lParam: lParam)
  : LRESULT; stdcall;
var
  info: ^MouseLLHookStruct absolute lParam;
begin
  Result := CallNextHookEx(mHook, nCode, WPARAM, lParam);
  if (WPARAM = WM_LBUTTONUP) then
    ScreenCapture;
end;

begin
  mHook := SetWindowsHookEx(WH_MOUSE_LL, @LowLevelMouseHookProc, HInstance, 0);
  
  while GetMessage(Msg, 0, 0, 0) do
  begin
    TranslateMessage(Msg);
    DispatchMessage(Msg);
  end;
  UnhookWindowsHookEx(mHook);

end.

编辑:

我在 VB.NET 中找到了替代方案。但是怎么能用 Delphi 代码解决呢?

Private Shared Function CaptureCursor(ByRef x As Integer, ByRef y As Integer) As Bitmap
        Dim bmp As Bitmap
        Dim hicon As IntPtr
        Dim ci As New CURSORINFO()
        Dim icInfo As ICONINFO
        ci.cbSize = Marshal.SizeOf(ci)
        If GetCursorInfo(ci) Then
            hicon = CopyIcon(ci.hCursor)
            If GetIconInfo(hicon, icInfo) Then
                x = ci.ptScreenPos.X - CInt(icInfo.xHotspot)
                y = ci.ptScreenPos.Y - CInt(icInfo.yHotspot)
                Dim ic As Icon = Icon.FromHandle(hicon)
                bmp = ic.ToBitmap()
                ic.Dispose()
                Return bmp
            End If
        End If
        Return Nothing
    End Function

'Insert on Timer tick event
    Private Sub Screenshot()
        Dim x As Integer
        Dim y As Integer

        Dim cursorBmp As Bitmap = CaptureCursor(x, y)

        Dim bmp As New Bitmap(Cursor.Size.Width, Cursor.Size.Height)
        Dim sourceLocation As Point = Control.MousePosition

        sourceLocation.Offset(-16, -16)

        Using g As Graphics = Graphics.FromImage(bmp)
            g.CopyFromScreen(sourceLocation, Point.Empty, bmp.Size)
            g.DrawImage(cursorBmp, x - sourceLocation.X, y - sourceLocation.Y)
            cursorBmp.Dispose()
        End Using

        Me.PictureBox1.Image = bmp
    End Sub

【问题讨论】:

BitBlt. 你需要GIF显示的效果吗? the screenshot must be only of this button. 如果只是点击按钮的一角,比如按钮的右下角。如何显示屏幕截图?我不知道如何获取网页上的按钮实例,但我认为它与 CSS 有关。如果你需要点击按钮的某个部分来显示按钮的整个屏幕截图,我认为这不是一件简单的事情。至少你需要熟悉网页上控件的布局和属性。 @StriveSun-MSFT,Do you need the effect that GIF shows? - 是的。 【参考方案1】:

有一个简单的方法,可以参考这个thread中的代码,

program Project2;

$APPTYPE CONSOLE

uses
  SysUtils, Windows, Graphics;

procedure DrawCursor (ACanvas:TCanvas; Position:TPoint) ;
var
  HCursor : THandle;
begin
  HCursor := GetCursor;
  DrawIconEx(ACanvas.Handle, Position.X, Position.Y,
              HCursor, 32, 32, 0, 0, DI_NORMAL) ;
end;

function CaptureWindow(const WindowHandle: HWnd): TBitmap;
var
  DC: HDC;
  wRect: TRect;
  CurPos: TPoint;
begin
  DC := GetWindowDC(WindowHandle);
  Result := TBitmap.Create;
  try
    GetWindowRect(WindowHandle, wRect);
    Result.Width := wRect.Right - wRect.Left;
    Result.Height := wRect.Bottom - wRect.Top;
    BitBlt(Result.Canvas.Handle, 
           0, 
           0, 
           Result.Width, 
           Result.Height, 
           DC, 
           0, 
           0, 
           SRCCOPY);
    GetCursorPos(CurPos);
    DrawCursor(Result.Canvas, CurPos);
  finally
    ReleaseDC(WindowHandle, DC);
  end;
end;

// Sample usage starts here
var
  Bmp: TBitmap;

begin
  Bmp := CaptureWindow(GetDesktopWindow);
  Bmp.SaveToFile('D:\TempFiles\FullScreenCap.bmp');
  Bmp.Free;
  WriteLn('Screen captured.');
  ReadLn;
end.

这段代码可以获取包含鼠标位置的截屏,只需使用BitBlt指定鼠标坐标和矩形大小(按钮大小),最终得到你需要的BMP图像。使用DrawImage将BMP图像绘制成一个矩形框,如GIF所示。

鼠标坐标可以通过调用GetCursorInfo获取,矩形的大小可以根据需要指定。

注意,有了鼠标坐标后,传入BitBlt时,需要分别减去左右半个矩形的大小。

例如,

BitBlt(Newhdc, 
           0, 
           0, 
           rect_x,  //size of the rect 
           rect_y, 
           HDC, 
           x - half_rect_x,  //x,y => mouse coordinates
           y - half_rect_y,  //half_rect_x, half_rect_y  => the size of half the rectangle
           SRCCOPY);

【讨论】:

以上是关于如何仅捕获屏幕上的鼠标单击区域?的主要内容,如果未能解决你的问题,请参考以下文章

使用鼠标选择要捕获的区域

仅保存最后一次鼠标单击坐标的变量

MFC中如何在指定的区域响应鼠标单击事件

如何使用 Python 图像库裁剪通过鼠标单击选择的区域?

在打开的cv c ++中单击鼠标右键时如何从视频中捕获图像

如何检查鼠标是不是在 freeglut 的三角形区域内单击?