Get Thumbnail of background window - c++

I'm trying to get thumbnail pictures of windows that are not visible.
Here's the code I have so far
BOOL CALLBACK WindowProc(HWND hWnd, LPARAM lParam)
{
RECT WindRect;
GetWindowRect(hWnd, &WindRect)
CurrentScreenShot->Next = new ScreenShotList();
CurrentScreenShot = CurrentScreenShot->Next;
HDC SourceDC = GetDC(hWnd);
HDC TargetDC = CreateCompatibleDC(SourceDC);
CurrentScreenShot->ScreenShot = CreateCompatibleBitmap(SourceDC, WindRect.right - WindRect.left, WindRect.bottom - WindRect.top);
BitBlt(TargetDC, 0, 0, WindRect.right - WindRect.left, WindRect.bottom - WindRect.top, SourceDC, 0, 0, SRCCOPY);
ReleaseDC(hWnd, SourceDC);
g_iWindows++;
return TRUE;
}
For now, WindowProc is being called directly using FindWindow to get a handle, though, I eventually want to use EnumWindows to loop through all of the windows to get their thumbnails and store them in a linked list.
WindowProc(FindWindow(NULL, L"File Explorer"), 0);
This code is in a DLL, which is called from a C# Forms application. For now the C# application just takes the bitmap and saves it to a file.
The problem is that unless I use FindWindow to get the visible window (which also happens to be the C# application), the picture ends up being a black box.
Is it possible to get an picture of a background window?
EDIT: This is a Windows Mobile application

There is no redrawing going on for invisible Windows, thats why you cannot get their content from the DC. Try sending a WM_PRINT message to the target window to request that it draws its content to your DC.
Edit:
Sorry, i did not notice this was for Windows Mobile. Other than WM_PRINT, i don't know a way to get the content of an invisible window. Of course you can still show the window (and make sure it is on top / not covered by other windows) and then run the code you have, but thats probably a bit messy.

Related

Capturing invisible window with win32 API

Trying to capture contents of windows using BitBlt. In this particular case, which is probably important, i am speaking of invisible windows/invisible areas on windows - invisible because of z-order, they are visible but covered with other windows. All i get is a black box.
HDC winDC = GetWindowDC(hwnd);
HDC hdc_offscreen = CreateCompatibleDC(winDC);
HBITMAP bmp = CreateCompatibleBitmap(winDC, areaWidth, areaHeight);
HGDIOBJ origHandle = SelectObject(hdc_offscreen, bmp);
BitBlt(hdc_offscreen, rect.left, rect.top,
rect.right - rect.left,
rect.bottom - rect.top,
winDC, copy_from.left, copy_from.top, SRCCOPY);
// at this point i expect pixels to be there, but nah!
ReleaseDC(hwnd, winDC);
SelectObject(hdc_offscreen, origHandle);
DeleteDC(hdc_offscreen);
ReleaseDC(hWnd_main, winDC);
hwnd is the window i am trying to capture. Same things works like a charm with Aero theme enabled. I am using Windows 7 Professional.
Any idea what i am missing?
Your best bet (though it's not guaranteed to work) is to ask the other window to paint itself to your DC by sending it WM_PRINT.
HDC returned by GetWindowDC just doesn't contain a window's image. I can imagine that this HDC refers to the same "bitmap" as other windows. When Aero enabled, they apparently doesn't share the same "bitmap" and that's why you get a good result.
I would rather try to force window to paint to your HDC instead of reading HDC returned by GetWindowDC. To get client area you can try to call BeginPaint with your HDC, but who knows what if a window will check whether it (or it's part) is visible or not?
To get non client area you could pass WM_NCPAINT with your HDC.
Standard controls support HDC passed with WM_PAINT. Other windows may support it, but it's not required. If I remember correctly PrintWindow relies on this behavior.
In general I would say that no a 100% way to get a window's image but BeginPaint and WM_NCPAINT should be a good point of start.

C++ DrawText And TextOut Displaying Nothing

Where do I use TextOut or DrawText?
TCHAR text[]= "My First Window";
RECT rc;
HDC wdc = GetWindowDC(hWnd);
GetClientRect (hWnd, &rc);
SetTextColor(wdc, 0x00000000);
SetBkMode(wdc,TRANSPARENT);
rc.left=40;
rc.top=10;
TextOut(hdc,rc.left,rc.top,text,ARRAYSIZE(text));
EndPath(hdc);
SelectClipPath(hdc, RGN_AND);
I'm placing this in WM_CREATE:
The result is a blank window. I can provide more code if need be, but it's just a standard Win32 blank window.
This is my first real Win32 application and I've googled and searched for an hour without finding the answer to my question.
Thank you
Windows doesn't work like that. You can't just paint once and expect what you painted to be displayed forever. Your window probably isn't even visible when WM_CREATE is handled.
(as an aside, you are also leaking wdc in the above code, and interchanging wdc with hdc).
You need to handle the WM_PAINT message and do your painting in there. Call BeginPaint() to get an HDC that you can draw on, and call EndPaint() when finished.
You should get a beginner's book in Win32 programming as handling WM_PAINT is pretty basic stuff. Start with the MSDN documentation:
Painting and Drawing

Static Control Background Color with C++

I am creating a basic GUI with the Windows API and I have run into an issue. It starts with a main window that opens with a custom background color I set (RGB(230,230,230)). It then displays text in the upper left corner with the static control.
settingstext = CreateWindow("STATIC",
"SETTINGS",
SS_LEFT | WS_CHILD,
12,
20,
100,
20,
hwnd,
NULL,
proginstance,
NULL);
ShowWindow(settingstext, 1);
This works, but when the text is displayed I need a way to change the background of it to match the main window or else it just looks like it doesn't blend in.
My question is, how do I do this? I currently use the method below and it works, but I wanted to know, is there a way to permanently set the background color somehow, right after the CreateWindow function for the static control without changing system colors, and just have it apply to that one control and not anything that sends the WM_CTLCOLORSTATIC message. I have experimented around with using the GetDC function and SetBkColor function outside of the message loop but nothing works.
case WM_CTLCOLORSTATIC:
{
HDC hdcStatic = (HDC) wParam;
SetTextColor(hdcStatic, RGB(0,0,0));
SetBkColor(hdcStatic, RGB(230,230,230));
return (INT_PTR)CreateSolidBrush(RGB(230,230,230));
}
I want to do this because...
I don't want to fill up my message loop with functions that need to be called every time the window repaints.
Have the changes apply to only this static control.
I would be very thankful for any help that could be provided, at least pointing me in the right direction, thanks.
For static text controls there's no permanent way to set the text color or their background. Even if you want to apply the changes to a single static control; you would still have to handle WM_CTLCOLORSTATIC notification message in parent dlgproc just when the control is about to be drawn.
This is due to the DefWindowProc overwriting your changes to the device context each time it handles WM_CTLCOLORSTATIC as stated in the MSDN:
By default, the DefWindowProc function selects the default system colors for the static control.
static HBRUSH hBrush = CreateSolidBrush(RGB(230,230,230));
case WM_CTLCOLORSTATIC:
{
if (settingstext == (HWND)lParam)
//OR if the handle is unavailable to you, get ctrl ID
DWORD CtrlID = GetDlgCtrlID((HWND)lParam); //Window Control ID
if (CtrlID == IDC_STATIC1) //If desired control
{
HDC hdcStatic = (HDC) wParam;
SetTextColor(hdcStatic, RGB(0,0,0));
SetBkColor(hdcStatic, RGB(230,230,230));
return (INT_PTR)hBrush;
}
}
If you're looking to make the control's background transparent over a parent dialog you could use SetBkMode(hdcStatic, TRANSPARENT).
I think there is a permanent way to do it.
Just after you create the label,use GetDC() function to get the Device Context.
Then use:
SetTextColor(hdcStatic, RGB(0,0,0));
SetBkColor(hdcStatic, RGB(230,230,230)); // Code Copied from the above answer by cpx.
And it should do .
Have you considered subclassing the static window and doing owner draw?

How to set background color of window after I have registered it?

I am not using a dialog, I'm using my own custom class which I have registered and then used the CreateWindow call to create it, I have preset the background color to red when registering:
WNDCLASSEX wc;
wc.hbrBackground = CreateSolidBrush(RGB(255, 0, 0));
But now I want to change the background color at runtime, by e.g. clicking a button to change it to blue.
I have tried to use SetBkColor() call in the WM_PAINT, and tried returning a brush from the WM_CTLCOLORDLG message, they don't work.
Any help?
From Window Background comes:
...The system paints the background for a
window or gives the window the
opportunity to do so by sending it a
WM_ERASEBKGND message when the
application calls BeginPaint. If an
application does not process the
message but passes it to
DefWindowProc, the system erases the
background by filling it with the
pattern in the background brush
specified by the window's class.....
...... An application can process the
WM_ERASEBKGND message even though a
class background brush is defined.
This is typical in applications that
enable the user to change the window
background color or pattern for a
specified window without affecting
other windows in the class. In such
cases, the application must not pass
the message to DefWindowProc. .....
So, use the WM_ERASEBKGND message's wParam to get the DC and paint the background.
You may try the following:
HBRUSH brush = CreateSolidBrush(RGB(0, 0, 255));
SetClassLongPtr(hwnd, GCLP_HBRBACKGROUND, (LONG_PTR)brush);
Short answer: Handle WM_ERASEBKGND.
Longer answer:
When you register the WNDCLASS, you're providing information about all windows of that class. So if you want to change the color of just one instance of the window, you'll need to handle it yourself.
When it's time to repaint your window, the system will send your wndproc a WM_ERASEBKGND message. If you don't handle it, the DefWindowProc will erase the client area with the color from the window class. But you can handle the message directly, painting whatever color (or background pattern) you like.

How to draw in the nonclient area?

I'd like to be able to do some drawing to the right of the menu bar, in the nonclient area of a window.
Is this possible, using C++ / MFC?
Charlie hit on the answer with WM_NCPAINT. If you're using MFC, the code would look something like this:
// in the message map
ON_WM_NCPAINT()
// ...
void CMainFrame::OnNcPaint()
{
// still want the menu to be drawn, so trigger default handler first
Default();
// get menu bar bounds
MENUBARINFO menuInfo = {sizeof(MENUBARINFO)};
if ( GetMenuBarInfo(OBJID_MENU, 0, &menuInfo) )
{
CRect windowBounds;
GetWindowRect(&windowBounds);
CRect menuBounds(menuInfo.rcBar);
menuBounds.OffsetRect(-windowBounds.TopLeft());
// horrible, horrible icon-drawing code. Don't use this. Seriously.
CWindowDC dc(this);
HICON appIcon = (HICON)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDR_MAINFRAME), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
::DrawIconEx(dc, menuBounds.right-18, menuBounds.top+2, appIcon, 0,0, 0, NULL, DI_NORMAL);
::DestroyIcon(appIcon);
}
}
In order to draw in the non-client area, you need to get the "window" DC (rather than "client" DC), and draw in the "window" DC.
You should try handling WM_NCPAINT. This is similar to a normal WM_PAINT message, but deals with the entire window, rather than just the client area. The MSDN documents on WM_NCPAINT provide the following sample code:
case WM_NCPAINT:
{
HDC hdc;
hdc = GetDCEx(hwnd, (HRGN)wParam, DCX_WINDOW|DCX_INTERSECTRGN);
// Paint into this DC
ReleaseDC(hwnd, hdc);
}
This code is intended to be used in the message loop of your applicaton, which is canonically organized using a large 'switch' statement.
As noted in the MFC example from Shog, make sure to call the default version, which in this example would mean a call to DefWindowProc.
If you just want something in the menu bar, maybe it is easier/cleaner to add it as a right-aligned menu item. This way it'll also work with different Windows themes, etc.