최근에 스크린 샷 (원격 데스크톱 시스템의 경우)을 찍는 작업을 많이했는데 여러 모니터에 대한 지원을 구현하는 동안 문제가 발생했습니다. 스크린 샷을 찍는 것은 괜찮지 만 커서를 그리는 데 사용하는 방법은 한 화면 만 가정합니다. 추가 화면에 포인터를 놓으면 (추가 화면의 스크린 샷을 찍을 때) 커서가 표시되지 않습니다. 포인터를 메인 화면으로 이동하면 표시됩니다 (물론 잘못된 화면이기 때문에 잘못된 위치에 있음).
내 코드는 완전히 아래에 있습니다.
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.
문제는 가져온 커서 좌표 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] 삭제
몇 마디 만하겠습니다