My program is taking screenshots of other application windows to automate some tasks on them. Those windows can be hidden offscreen or obscured by other windows from time to time.
To reduce clutter, I have removed any error checking from the code listings. I am preparing the both types of screenshots with
// Get size of the target window.
RECT clientRect;
GetClientRect(hwnd, &clientRect);
int width = clientRect.right - clientRect.left;
int height = clientRect.bottom - clientRect.top;
// Create a memory device context.
HDC windowDC = GetDC(hwnd);
HDC memoryDC = CreateCompatibleDC(windowDC);
// Create a bitmap for rendering.
BITMAPINFO bitmapInfo;
ZeroMemory(&bitmapInfo, sizeof(BITMAPINFO));
bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitmapInfo.bmiHeader.biWidth = width;
bitmapInfo.bmiHeader.biHeight = -height;
bitmapInfo.bmiHeader.biPlanes = 1;
bitmapInfo.bmiHeader.biBitCount = 32;
bitmapInfo.bmiHeader.biCompression = BI_RGB;
bitmapInfo.bmiHeader.biSizeImage = width * height * 4;
RGBQUAD* buffer;
HBITMAP bitmap = CreateDIBSection(windowDC, &bitmapInfo, DIB_RGB_COLORS, (void**)&buffer, 0, 0);
HGDIOBJ previousObject = SelectOBject(memoryDC, bitmap);
then I am taking the actual screenshots with either
PrintWindow(hwnd, memoryDC, PW_CLIENTONLY);
GdiFlush();
or
UpdateWindow(hwnd);
BitBlt(memoryDC, 0, 0, width, height, windowDC, 0, 0, SRCCOPY);
GdiFlush();
then I copy the buffer contents to a vector
std::vector<RGBQUAD> pixels;
pixels.resize(width * height, { 0, 0, 0, 0 });
memcpy(&pixels[0], buffer, bitmapInfo.bmiHeader.biSizeImage);
and finally I am cleaning everything up with
ReleaseDC(hwnd, windowDC);
SelectObject(memoryDC, previousObject);
DeleteDC(memoryDC);
DeleteObject(bitmap);
I have a three of questions about the code above:
Do I need to call GdiFlush()? I read the documentation and did some Google research, but I am still not sure if it makes sense to call it or not.
Does the call to UpdateWindow() right before the BitBlt() make a difference? Does this help, so that the Device Context contents are "more up to date"?
Am I right in assuming that for PrintWindow() all the work is done from within the target application (increasing the target application's CPU usage), while BitBlt() is fully executed from within the calling thread (thus increasing the CPU usage of my own application)?
Under which circumstances might any of the above functions fail? I know that BitBlt() only works for hidden windows if Desktop Composition (DWM) is enabled, but on a very small set of systems (Windows 8/10) BitBlt() and PrintWindow() seem to fail for windows for which they work just fine on other systems (even though DWM is enabled). I could not spot any patterns as to why, though.
Any information is appreciated, thanks.
finally, after some hours of investigation, I found a solution for that issue:
It's sufficient to call the following command within the ACTIVATE event of the form to be imaged (example in VB coding):
Call SetWindowLong(me.hwnd, GWL_EXSTYLE, WS_EX_LAYERED)
Whereas this command is defined as follows:
Private Declare Function SetWindowLong Lib "User32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Const GWL_EXSTYLE = (-20)
Private Const WS_EX_LAYERED = &H80000
Please try this!
Best regards,
Bambi66
After more than two months I finally realized that this only occurs with the Windows update 1809 from late 2018. Apparently, Windows changed the way it handles clipping with that update so that the Device Context contents are no longer updated for parts of windows that are located offscreen.
To answer the individual questions:
1) GdiFlush doesn't seem to make a difference, at least from what I can tell so far.
2) Still not 100% sure about it, but I also think it doesn't make a difference.
3) Still no idea.
4) See answer above.
Related
I am using Microsoft Windows (32 bit). Please, no Visual Studio anything, or .net.
I have the following that works if I have a windows handle to use for it:
// I have a handle to a window.
// HWND Handle_Of_SomeWindow
// I previously assigned a handle for that and use it.
// I have some Unicode text that I am using.
wstring SomeWideStringText = L"C++ stole my lunch money.";
// I convert that wstring to LPWSTR using
LPWSTR Text_Being_Added = const_cast<wchar_t*>(SomeWideStringText.c_str());
//I create a rectangle to use in my DrawTextExW
RECT rc;
// If I have a handle to a window then I can do this.
GetClientRect(Handle_Of_SomeWindow, & rc);
//But, if I do not have a handle to a window and if I only have a hdc, then that does not work.
When I have an HDC without a window handle (I think that this is a memory dc but I do not yet understand that so well) which I am using in double buffering, then there is no handle to use. And, I can not get a handle for it. Get handle from DC does not work.
So, my question is How do I get a rectangle or &rc for that to use in my command of :
DrawTextExW(HDC_of_FRONT_BUFFER_001, Text_Being_Added, -1, & rc, DT_WORDBREAK, nullptr);
?
Maybe there might be something else other than a rectangle &rc that I could use, but I have not found it.
I have been studying this and I do not understand how to get a rectangle or a &rc to use.
You're misunderstanding what the rectangle you provide to DrawTextEx is, it's not the size of your bitmap, it's the size you want your text to occupy. It should obviously be less than or equal to the size of your backing bitmap, but it has no other relation to it.
then there is no handle to use. And, I can not get a handle for it. Get handle from DC does not work.
Literally no idea what you're trying to express here.
I do not understand how to get a rectangle or a &rc to use
Again, you aren't handed this, you provide it.
A memory DC has a bitmap selected into it, and that bitmap has a size. In most cases your code created that bitmap, so it should already know the size.
But if it doesn't, you have a couple options.
Option 1: You can select the bitmap out of the DC, get its size, and then select it back in. This is kind of kludgy and I've omitted error checking:
// assuming you're given hdcMem...
HBITMAP hbmpTemp = CreateCompatibleDC(hdcMem, 1, 1);
HBITMAP hbmpActual = SelectObject(hdcMem, hdcTemp);
BITMAP bm = {0};
GetObject(hbmpActual, sizeof(bm), &bm);
// now your size is in bm.bmWidth and bm.bmHeight,
RECT rc = {0, 0, bm.bmWidth, bm.bmHeight};
SelectObject(hdcMem, hdcActual); // put the memory DC back
DeleteObject(hbmpTemp);
Option 2: [untested] You could try to query the DC for its "resolution" caps. I know this works for device DCs, like a monitor or printer. I don't know whether it works for a memory DC.
int width = GetDeviceCaps(hdcMem, HORZRES);
int height = GetDeviceCaps(hdcMem, VERTRES);
I have an MFC desktop application that generates a graphics image, and I want the user to be able to copy it to the clipboard as a bitmap. I use standard clipboard API routines and they have been working fine up to now. However, I have now activated GDI Scaling (via a manifest) so that the program can run (reasonably well) on HiDPI displays, and I find that the image placed on the clipboard is just the upper-left quadrant of the actual image.
This is presumably due to the internal scaling of the image - the display is running at 200%.
How do I copy the whole image?
The key is given in the answer to a previous question:
Saving Bitmaps on Windows 10 with GDI scaling active
but it took me some time to adapt that answer to my particular problem, so I thought I'd share in case it was helpful to others. (Disclaimer - I'm no expert, so there may be better ways...)
bool CopyRectToClipboard(CWnd *pW, CRect rctCopy)
{
if (!pW->OpenClipboard())
return false;
if (!EmptyClipboard())
return false;
CClientDC dc(pW);
CDC dcMem;
VERIFY(dcMem.CreateCompatibleDC(&dc));
CBitmap bmTrial; // need a trial bitmap to get GDI Scaled size
VERIFY(bmTrial.CreateCompatibleBitmap(&dc, rctCopy.Width(), rctCopy.Height()));
// see https://stackoverflow.com/questions/51169846/saving-bitmaps-on-windows-10-with-gdi-scaling-active
BITMAPINFO bi = {};
bi.bmiHeader.biSize = sizeof(bi);
int result = GetDIBits(dc.GetSafeHdc(), (HBITMAP)bmTrial.GetSafeHandle(), 0, 0, NULL, &bi, DIB_RGB_COLORS);
CBitmap bm;
VERIFY(bm.CreateCompatibleBitmap(&dc, bi.bmiHeader.biWidth, bi.bmiHeader.biHeight));
ASSERT(bm.m_hObject != NULL);
CBitmap* pbmOld = dcMem.SelectObject(&bm);
//dcMem.PatBlt(0, 0, bi.bmiHeader.biWidth, bi.bmiHeader.biHeight, WHITENESS); // legacy
VERIFY(dcMem.BitBlt(0, 0, bi.bmiHeader.biWidth, bi.bmiHeader.biHeight,
&dc, rctCopy.left - 1, rctCopy.top - 1, SRCCOPY));
HGDIOBJ hBM = bm.Detach();
VERIFY(::EmptyClipboard());
VERIFY(::SetClipboardData(CF_BITMAP, hBM));
VERIFY(::CloseClipboard());
dcMem.SelectObject(pbmOld);
dcMem.DeleteDC();
return true;
}
I'm searching for a way rendering a char buffer onto the content-area of a window. This is just pseudo but intended to demonstrate what I actually want to do:
char buffer[300][200][3]; // 300px x 200px x RGB bytes
// ... render stuff into buffer
FancyWindowsFunctionToRenderBufferOnWindow(my_hwnd, buffer, 300, 200, offset_x, offset_y);
Is there a way to do something similar?
I think you need to create a device independent bitmap (DIB). If you already have an array of pixels that is ready to be put on an application window, you may need to copy the whole array to the buffer allocated by the CreateDIBSection API and call BitBlt to transfer the DIB to the window. This is the only way I know to show a mere array of pixels as a visible picture on the computer screen on Win32 platform and it is highly complicated and difficult to understand.
Here are the steps I used to take to test things similar to what you want to do:
Creating DIB:
BITMAPINFO bmi;
memset(&bmi, 0, sizeof(bmi));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = /* Width of your image buffer */
bmi.bmiHeader.biHeight = - /* Height of your image buffer */
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
HDC hDesktopDC = GetDC(GetDesktopWindow());
HBITMAP hDib = CreateDIBSection(hDesktopDC, &bmi, DIB_RGB_COLORS, (void **)&buffer, 0, 0);
if (buffer == NULL) { /* ERROR */ }
HDC hDibDC = CreateCompatibleDC(hDesktopDC);
HGDIOBJ hOldObj = SelectObject(hDibDC, hDib);
/* Copy your array of pixels to buffer allocated above. */
ReleaseDC(GetDesktopWindow(), hDesktopDC);
Implementing WM_PAINT event handler (the hWnd variable holds the window handle below):
case WM_PAINT:
PAINTSTRUCT paint;
HDC hWndDc = BeginPaint(hWnd, &paint);
BitBlt(hWndDC, 0, 0, /* Width of DIB */, /* Height of DIB */,
/* HDC of DIB (hDibDC in the above) */, 0, 0, SRCCOPY);
EndPaint(hWnd, &paint);
break;
I don't really expect the above code snippets would directly help you. If you are determined to use GDI functions like ones in the above snippets, I recommend you to read very carefully their API documents on MSDN. Because it is very tricky to properly release or delete DCs or GDI objects acquired during using the APIs.
It sounds like you have an image (raster) stored as an array of chars (which is an odd choice, since you'd usually want an array of unsigned chars for raw bitmap images).
If you meet certain alignment constraints, you can display your bitmap pretty directly with SetDIBits. You fill out a BITMAPINFO structure that describes the pixel format and image dimensions, and then you pass that along with your data to SetDIBits. It'll paint them to a DC. It can be a little tricky to get all the parameters right.
The alignment requirement is that each scanline must begin on a 4-byte boundary. If you don't meet that requirement, you'll get garbage similar to having the wrong stride. You can make a copy of the data with the correct alignment if necessary.
Let's say I have a handle to device context (naturally, in Windows environment):
HDC hdc;
How can I get the width and height of it?
A device context (DC) is a structure that defines a set of graphic objects and their associated attributes, and the graphic modes that affect output.
By width and height I'm guessing you are referring to the bitmap painted ?
If so then i guess you can try the following :
BITMAP structBitmapHeader;
memset( &structBitmapHeader, 0, sizeof(BITMAP) );
HGDIOBJ hBitmap = GetCurrentObject(hDC, OBJ_BITMAP);
GetObject(hBitmap, sizeof(BITMAP), &structBitmapHeader);
//structBitmapHeader.bmWidth
//structBitmapHeader.bmHeight
I also know little about GDI, but it seems GetDeviceCaps might do the trick.
This simple piece of code I use always to get the dimensions of the rendering area, when I have only the HDC.
First, you must get a HWND from the HDC - is simple, then you can get the client rect of this HWND:
RECT rcCli;
GetClientRect(WindowFromDC(hdc), &rcCli);
// then you might have:
nWidth = rcCli.right-rcCli.left;
nHeight = rcCli.bottom-rcCli.top;
As a disclaimer, I know nothing about GDI or what you have to work with in your application. I'm just trying to be helpful if possible.
That said, I found a link which seems to suggest that it's appropriate to use GetClientRect to get the size of the drawing area:
RECT clientRect;
GetClientRect(hWnd,&clientRect);
http://www.toymaker.info/Games/html/gdi.html#winsize
You could WindowFromDC(...) to get the DC's window if it's associated with a window. You could then use #KevinK's answer to get the client rect from this.
but if get Calculator' window_dc dimension, it will failed at “GetCurrentObject” or "GetObject", i think maybe the window attribute include "ws_ex_noredirectionbitmap", how to get dismension in this case?
HDC win_dc = ::GetWindowDC(hwnd);
BITMAP bm = { 0 };
HGDIOBJ hBitmap = GetCurrentObject(win_dc, OBJ_BITMAP);
if (hBitmap)
{
if (GetObject(hBitmap, sizeof(BITMAP), &bm))
{
windc_dimension.cx = bm.bmWidth;
windc_dimension.cy = bm.bmHeight;
}
}
I have an image data in a buffer(type - long) from a scanner which is 32 bit.
For example, buffer[0]'s corresponding pixel value is 952 which is [184, 3, 0, 0] <-[R,G,B,A];
I want to display/Paint/draw on to the screen; I am confused when i tried to read about displying bitmaps. I looked at win32 functions, CBitmap class, windows forms (picture box) etc I am hard to understand the general idea/appraoch for displaying this buffer data on to a application window.
I have constructed the BITMAPFILEHEADER AND BITMAPINFOHEADER; Has the pixel data in a buffer, (unsigned char *)vInBuff whose size is vImageSz;
//construct the BMP file Header
vBmfh.bfType = 19778;
vBmfh.bfSize = 54+vImageSz;//size of the whole image
vBmfh.bfReserved2 = 0;
vBmfh.bfReserved1 = 0;
vBmfh.bfOffBits = 54;//offset from where the pixel data can be found
//Construct the BMP info header
vBmih.biSize = 40;//size of header from this point
vBmih.biWidth = 1004;
vBmih.biHeight = 1002;
vBmih.biPlanes = 1;
vBmih.biCompression = BI_RGB;
vBmih.biSizeImage = vBmih.biWidth*vBmih.biHeight*4;
vBmih.biBitCount = 32;
vBmih.biClrUsed = 0;
vBmih.biClrUsed = 0;
1.What is that i should be doing next to display this?
2 What should i be using to display the 32bit bitmap? I see people using createwindows functions, windows forms, MFC etc;
3.I also understand that BitBlt,createDIBSection, OnPaint etc are being used? I am confused by these various functions and coding platforms? Please suggest me a simple approach.
4.How can i create a palette to display a 32 bit image?
Thanks
Raj
EDITED TRYING TO IMPLEMENT DAVE'S APPROACH, CAN SOMEBODY COMMENT ON MY IMPLEMTATION. I couldn't continue to the bitblt as i donot have two HDC, i donot know how to get this one? Any help please
DisplayDataToImageOnScreen(unsigned char* vInBuff, int vImageSz) // buffer with pixel data, Size of pixel data
{
unsigned char* vImageBuff = NULL;
HDC hdcMem=CreateCompatibleDC(NULL);
HBITMAP hBitmap = CreateDIBSection(hdcMem,
(BITMAPINFO*)&vBmih,
DIB_RGB_COLORS,
(void **)&vImageBuff,
NULL, 0);
GetDIBits(hdcMem,
hBitmap,
0,
1,
(void**)&vImageBuff,
(BITMAPINFO*)&vBmih,
DIB_RGB_COLORS);
memcpy(vImageBuff,vInBuff,vImageSz);
}
An alternative if you just want to plot it on screen is to use TinyPTC ( http://sourceforge.net/projects/tinyptc/files/ ). It's just 3 functions and very simple if you just want to plot some pixels.
EDIT: Seems like http://www.pixeltoaster.com is a continuation of TinyPTC, probably preffered.
If you have image's bytes already in a buffer you can use:
a CBitmap object (MFC) and the method CBitmap::CreateBitmapIndirect
or
win32 routine CreateBitmapIndirect.
Now you can use BitBlt to draw it on a DC. To get a window DC use GetDC.
There is no need to create a pallete for 32 bit images.
Here's a simplified approach you can try, broken down into steps:
BITMAPINFO bitmapinfo = { 0 };
bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitmapinfo.bmiHeader.biWidth = 1004;
bitmapinfo.bmiHeader.biHeight = -1002;
bitmapinfo.bmiHeader.biPlanes = 1;
bitmapinfo.bmiHeader.biCompression = BI_RGB;
HBITMAP hBitmap = CreateDIBSection(NULL,
&bitmapinfo,
DIB_RGB_COLORS,
(void **)&vImageBuff,
NULL,
0);
Now party on vImageBuff and then cache hBitmap somewhere so that within your wndproc you can then in the WM_PAINT handler:
select hBitmap into temp compatible HDC
call BitBlt(..., SRCCOPY) from the compatible HDC to the window's HDC. the other parameters should be obvious. don't try to stretch or do anything fancy at first.
remember to select the original dummy bitmap into the temp HDC before destroying it.
If you aren't seeing results try looping through vImageBuff and just set every pixel to RGB(255, 0, 0), or something like that, just to sanity check the rest of the logic.
If nothing is drawing make sure that the alpha component for each pixel is 255.
If you're getting a garbled image then you need to double-check pixelformat, stride, etc.
Here's a strategy you might like:
Create a bitmap with the same size as your scanned data, and the same format (use CreateDIBSection).
Use GetDIBits to get the base address of the pixel data.
Copy your data (from the scanner) to the address GetDIBits returns.
Now render your bitmap! (use BitBlt, or somesuch).
Regarding palettes- a 32bit image does not, generally, have an explicit palette - you'd need 16.7million (assuming 8bits of alpha) values in the palette. Generally the palette is assumed to be 8bits red, 8bits green, 8bits blue, as you've described above.