두 번째 모니터로 이동할 때 마우스 커서가 표시되지 않는 스크린 샷

데이비슨

최근에 스크린 샷 (원격 데스크톱 시스템의 경우)을 찍는 작업을 많이했는데 여러 모니터에 대한 지원을 구현하는 동안 문제가 발생했습니다. 스크린 샷을 찍는 것은 괜찮지 만 커서를 그리는 데 사용하는 방법은 한 화면 만 가정합니다. 추가 화면에 포인터를 놓으면 (추가 화면의 스크린 샷을 찍을 때) 커서가 표시되지 않습니다. 포인터를 메인 화면으로 이동하면 표시됩니다 (물론 잘못된 화면이기 때문에 잘못된 위치에 있음).

내 코드는 완전히 아래에 있습니다.

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.
Drake Wu-MSFT

문제는 가져온 커서 좌표 GetCursorInfo2가 Bitmap과 관련된 올바른 좌표가 아니라는 것입니다. 첫째, 커서 포인트가 있는지 여부를 확인 lprcMonitor, 사용 ccould PtInRect하고 사용 DrawIcon이 true를 돌려주는 경우, 비트 맵에 hcursor을 그릴 수 있습니다. 다음은 코드에서 C ++ 샘플 변환입니다 (델파이에 익숙하지 않기 때문에).

#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

두 번째 div가 페이지에서 스크롤 될 때 div가 표시되지 않습니다.

분류에서Dev

두 번째 부팅 할 때마다 마우스 커서가 사라지는 Ubuntu 13.10

분류에서Dev

USB 장치 (키보드 또는 마우스)가`lsusb`에 표시되지만 연결할 두 번째 USB 장치 인 경우 작동하지 않습니다.

분류에서Dev

Windows 명령 줄을 통해 두 대의 모니터로 데스크톱을 확장 할 때 두 번째 데스크톱의 스크린 샷을 찍는 방법 요청

분류에서Dev

Windows 명령 줄을 통해 두 대의 모니터로 데스크톱을 확장 할 때 두 번째 데스크톱의 스크린 샷을 찍는 방법 요청

분류에서Dev

시스템이 제대로 부팅되지 않음-화면이 꺼졌다 켜집니다. 두 번째 모니터가 연결되었을 때만

분류에서Dev

UIView가 보이지 않을 때 스크린 샷을 찍습니다.

분류에서Dev

두 번째로 추가 할 때 UIWindow에보기가 표시되지 않음

분류에서Dev

세로 작업 표시 줄이있는 Windows 8.1 다중 모니터 설정에서 테두리를 이동할 때 마우스를 잡습니다.

분류에서Dev

스크린 샷에 비디오 이미지가 표시되지 않음

분류에서Dev

창 크기가 설정된 경우 ChromeDriver 스크린 샷에 전체 문서가 표시되지 않습니까?

분류에서Dev

JS (jQuery)는 스크롤 할 때 div가 두 번째로 나타나지 않도록합니다.

분류에서Dev

Swift에서 프로그래밍 방식으로 앱의 스크린 샷을 찍으려고 할 때 스크린 샷이 흰색으로 표시되는 이유는 무엇입니까?

분류에서Dev

첫 번째 플러터 이미지가 표시되지만 두 번째는 표시되지 않습니다.

분류에서Dev

스크린 샷을 찍으면 내 활동에 스크린 샷 이미지가 표시됩니다.

분류에서Dev

두 번째 클릭 할 때까지 체크 박스가 확인되지 않음

분류에서Dev

활동이 두 번째로 호출 될 때 진행률 표시 줄로드가 중지되지 않습니다.

분류에서Dev

Xcode : 시뮬레이션을 처음 실행할 때는 내 사용자 위치가 표시되지 않지만 두 번째에는 표시됩니다.

분류에서Dev

스크린 샷을 만들 때 메뉴가 나타나지 않습니다.

분류에서Dev

스크린 샷을 만들 때 메뉴가 나타나지 않습니다.

분류에서Dev

첫 번째 편집 텍스트에서 두 번째 편집 텍스트로 이동, 입력 마스크가 작동하지 않음

분류에서Dev

Flutter : 스냅 샷에 데이터가 표시되지 않음

분류에서Dev

게스트 창을 두 번째 모니터로 이동할 때 가상 상자에서 마우스 캡처가 작동하도록하려면 어떻게해야합니까?

분류에서Dev

자식 프로세스의 stdout을 다른 프로세스의 stdin으로 사용할 때 데이터가 두 번째 자식 프로세스로 전달되지 않는 경우가 있습니다.

분류에서Dev

클릭 할 때 두 번째로 "팝업 창"이 표시되지 않는 이유는 무엇입니까?

분류에서Dev

마우스를 두 번째 화면 모니터로 이동하는 방법은 무엇입니까?

분류에서Dev

UIView가 두 번째로 표시되지 않는 이유는 무엇입니까?

분류에서Dev

SwiftUI는 사용자가 스크린 샷을 찍거나 화면을 녹화 할 때 감지합니다.

분류에서Dev

Process Monitor가 스크린 샷이 찍혔는지 감지 할 수 있습니까?

Related 관련 기사

  1. 1

    두 번째 div가 페이지에서 스크롤 될 때 div가 표시되지 않습니다.

  2. 2

    두 번째 부팅 할 때마다 마우스 커서가 사라지는 Ubuntu 13.10

  3. 3

    USB 장치 (키보드 또는 마우스)가`lsusb`에 표시되지만 연결할 두 번째 USB 장치 인 경우 작동하지 않습니다.

  4. 4

    Windows 명령 줄을 통해 두 대의 모니터로 데스크톱을 확장 할 때 두 번째 데스크톱의 스크린 샷을 찍는 방법 요청

  5. 5

    Windows 명령 줄을 통해 두 대의 모니터로 데스크톱을 확장 할 때 두 번째 데스크톱의 스크린 샷을 찍는 방법 요청

  6. 6

    시스템이 제대로 부팅되지 않음-화면이 꺼졌다 켜집니다. 두 번째 모니터가 연결되었을 때만

  7. 7

    UIView가 보이지 않을 때 스크린 샷을 찍습니다.

  8. 8

    두 번째로 추가 할 때 UIWindow에보기가 표시되지 않음

  9. 9

    세로 작업 표시 줄이있는 Windows 8.1 다중 모니터 설정에서 테두리를 이동할 때 마우스를 잡습니다.

  10. 10

    스크린 샷에 비디오 이미지가 표시되지 않음

  11. 11

    창 크기가 설정된 경우 ChromeDriver 스크린 샷에 전체 문서가 표시되지 않습니까?

  12. 12

    JS (jQuery)는 스크롤 할 때 div가 두 번째로 나타나지 않도록합니다.

  13. 13

    Swift에서 프로그래밍 방식으로 앱의 스크린 샷을 찍으려고 할 때 스크린 샷이 흰색으로 표시되는 이유는 무엇입니까?

  14. 14

    첫 번째 플러터 이미지가 표시되지만 두 번째는 표시되지 않습니다.

  15. 15

    스크린 샷을 찍으면 내 활동에 스크린 샷 이미지가 표시됩니다.

  16. 16

    두 번째 클릭 할 때까지 체크 박스가 확인되지 않음

  17. 17

    활동이 두 번째로 호출 될 때 진행률 표시 줄로드가 중지되지 않습니다.

  18. 18

    Xcode : 시뮬레이션을 처음 실행할 때는 내 사용자 위치가 표시되지 않지만 두 번째에는 표시됩니다.

  19. 19

    스크린 샷을 만들 때 메뉴가 나타나지 않습니다.

  20. 20

    스크린 샷을 만들 때 메뉴가 나타나지 않습니다.

  21. 21

    첫 번째 편집 텍스트에서 두 번째 편집 텍스트로 이동, 입력 마스크가 작동하지 않음

  22. 22

    Flutter : 스냅 샷에 데이터가 표시되지 않음

  23. 23

    게스트 창을 두 번째 모니터로 이동할 때 가상 상자에서 마우스 캡처가 작동하도록하려면 어떻게해야합니까?

  24. 24

    자식 프로세스의 stdout을 다른 프로세스의 stdin으로 사용할 때 데이터가 두 번째 자식 프로세스로 전달되지 않는 경우가 있습니다.

  25. 25

    클릭 할 때 두 번째로 "팝업 창"이 표시되지 않는 이유는 무엇입니까?

  26. 26

    마우스를 두 번째 화면 모니터로 이동하는 방법은 무엇입니까?

  27. 27

    UIView가 두 번째로 표시되지 않는 이유는 무엇입니까?

  28. 28

    SwiftUI는 사용자가 스크린 샷을 찍거나 화면을 녹화 할 때 감지합니다.

  29. 29

    Process Monitor가 스크린 샷이 찍혔는지 감지 할 수 있습니까?

뜨겁다태그

보관