Closed constlhq closed 2 years ago
Thanks for the advice, I've found this official example https://docs.microsoft.com/en-us/windows/win32/gdi/capturing-an-image and translated it into rust, not elegant but works.
unsafe fn CaptureAnImage(hWnd: HWND) {
let hdcScreen: HDC;
let hdcWindow: HDC;
let mut hdcMemDC: CreatedHDC;
let hbmScreen: HBITMAP;
let mut bmpScreen: BITMAP = BITMAP::default();
let mut dwBytesWritten: u32 = 0;
let mut dwSizeofDIB: u32 = 0;
let hFile: HANDLE;
let lpbitmap: *mut c_void;
let hDIB: isize;
let mut dwBmpSize: u32 = 0;
// Retrieve the handle to a display device context for the client
// area of the window.
hdcScreen = GetDC(None);
hdcWindow = GetDC(hWnd);
// Create a compatible DC, which is used in a BitBlt from the window DC.
hdcMemDC = CreateCompatibleDC(hdcWindow);
if (hdcMemDC.is_invalid()) {
println!("CreateCompatibleDC has failed");
}
// Get the client area for size calculation.
let mut rcClient = RECT::default();
GetClientRect(hWnd, &mut rcClient);
// This is the best stretch mode.
SetStretchBltMode(hdcWindow, HALFTONE);
// The source DC is the entire screen, and the destination DC is the current window (HWND).
if (!StretchBlt(hdcWindow,
0, 0,
rcClient.right, rcClient.bottom,
hdcScreen,
0, 0,
GetSystemMetrics(SM_CXSCREEN),
GetSystemMetrics(SM_CYSCREEN),
SRCCOPY).as_bool()) {
println!("StretchBlt has failed");
}
// Create a compatible bitmap from the Window DC.
hbmScreen = CreateCompatibleBitmap(hdcWindow, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top);
if (hbmScreen.is_invalid()) {
println!("CreateCompatibleBitmap Failed");
}
// Select the compatible bitmap into the compatible memory DC.
SelectObject(hdcMemDC, hbmScreen);
// Bit block transfer into our compatible memory DC.
if (!BitBlt(hdcMemDC,
0, 0,
rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,
hdcWindow,
0, 0,
SRCCOPY).as_bool()) {
println!("BitBlt has failed");
}
// Get the BITMAP from the HBITMAP.
GetObjectA(hbmScreen, size_of::<BITMAP>() as i32, &mut bmpScreen as *mut BITMAP as *mut c_void);
let mut bmfHeader: BITMAPFILEHEADER = BITMAPFILEHEADER::default();
let mut bi: BITMAPINFOHEADER = BITMAPINFOHEADER {
biSize: size_of::<BITMAPINFOHEADER>() as u32,
biWidth: bmpScreen.bmWidth,
biHeight: bmpScreen.bmHeight,
biPlanes: 1,
biBitCount: 32,
biCompression: BI_RGB as u32,
biSizeImage: 0,
biXPelsPerMeter: 0,
biYPelsPerMeter: 0,
biClrUsed: 0,
biClrImportant: 0,
};
dwBmpSize = (((bmpScreen.bmWidth * bi.biBitCount as i32 + 31) / 32) * 4 * bmpScreen.bmHeight) as u32;
// Starting with 32-bit Windows, GlobalAlloc and LocalAlloc are implemented as wrapper functions that
// call HeapAlloc using a handle to the process's default heap. Therefore, GlobalAlloc and LocalAlloc
// have greater overhead than HeapAlloc.
hDIB = GlobalAlloc(GHND, dwBmpSize as usize);
lpbitmap = GlobalLock(hDIB);
// Gets the "bits" from the bitmap, and copies them into a buffer
// that's pointed to by lpbitmap.
GetDIBits(hdcWindow, hbmScreen, 0,
bmpScreen.bmHeight as u32,
lpbitmap,
&mut bi as *mut BITMAPINFOHEADER as *mut c_void as *mut BITMAPINFO, DIB_RGB_COLORS);
// A file is created, this is where we will save the screen capture.
hFile = CreateFileA("captureqwsx.bmp",
FILE_ACCESS_FLAGS(GENERIC_WRITE),
FILE_SHARE_MODE(0),
std::ptr::null(),
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, None).unwrap();
// Add the size of the headers to the size of the bitmap to get the total file size.
dwSizeofDIB = dwBmpSize + size_of::<BITMAPFILEHEADER>() as u32 + size_of::<BITMAPINFOHEADER>() as u32;
// Offset to where the actual bitmap bits start.
bmfHeader.bfOffBits = (size_of::<BITMAPFILEHEADER>() + size_of::<BITMAPINFOHEADER>()) as u32;
// Size of the file.
bmfHeader.bfSize = dwSizeofDIB;
// bfType must always be BM for Bitmaps.
bmfHeader.bfType = 0x4D42; // BM.
WriteFile(hFile, &bmfHeader as *const BITMAPFILEHEADER as *const c_void, size_of::<BITMAPFILEHEADER>() as u32, &mut dwBytesWritten, std::ptr::null_mut());
WriteFile(hFile, &bi as *const BITMAPINFOHEADER as *const c_void, size_of::<BITMAPINFOHEADER>() as u32, &mut dwBytesWritten, std::ptr::null_mut());
WriteFile(hFile, lpbitmap, dwBmpSize, &mut dwBytesWritten, std::ptr::null_mut());
// Unlock and Free the DIB from the heap.
GlobalUnlock(hDIB);
GlobalFree(hDIB);
// Close the handle for the file that was created.
CloseHandle(hFile);
// Clean up.
DeleteObject(hbmScreen);
DeleteDC(hdcMemDC);
ReleaseDC(None, hdcScreen);
ReleaseDC(hWnd, hdcWindow);
}
The windows uiautomation API doesn't support to get screenshot of an uielement. You can do this with windows GDI APIs now.
This is a goog idea. I will try to support this feature on the next version. But it will take some time.