C++ GetDC All Monitors - c++

Basically, I'm making something that imitates a screen melting effect, but I can only get it working on my primary monitor. I've looked up as much as I could and there was only one forum on GetDC for all monitors but it was to no use, all it done was make a rectangle from my primary monitor to my secondary monitor with the effect still only working on my primary monitor. This is the thread I read: GetDC(NULL) gets primary monitor or virtual screen?
LRESULT CALLBACK Melter(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam) {
switch (Message) {
case WM_CREATE: {
HDC Desktop = GetDC(HWND_DESKTOP);
HDC Window = GetDC(hWnd);
BitBlt(Window, 0, 0, ScreenWidth, ScreenHeight, Desktop, 0, 0, SRCCOPY);
ReleaseDC(hWnd, Window);
ReleaseDC(HWND_DESKTOP, Desktop);
SetTimer(hWnd, 0, Interval, 0);
ShowWindow(hWnd, SW_SHOW);
break;
}
case WM_PAINT: {
ValidateRect(hWnd, 0);
break;
}
case WM_TIMER: {
HDC Window = GetDC(hWnd);
int uX = (rand() % ScreenWidth) - (150 / 2), uY = (rand() % 15), Width = (rand() % 150);
BitBlt(Window, uX, uY, Width, ScreenHeight, Window, uX, 0, SRCCOPY);
ReleaseDC(hWnd, Window);
break;
}
case WM_DESTROY: {
KillTimer(hWnd, 0);
PostQuitMessage(EXIT_SUCCESS);
break;
}
return EXIT_SUCCESS;
}
return DefWindowProc(hWnd, Message, wParam, lParam);
}
The line I changed was HDC Window = GetDC(Window) to HDC Window = GetDC(NULL) and then some other stuff like the RECT. It'd be great if someone could help me, thanks :)
PS, ScreenWidth = 3600, ScreenHeight = 1080 whilst PMScreenWidth = 1920, PMScreenHeight = 1080. PM as in Primary Monitor, so I've got all the stuff in that function set to ScreenWidth/ScreenHeight so it's the width/height of all monitors. Still doesn't work though.

GetDC(HWND_DESKTOP) (same as GetDC(0)) already returns the DC for all monitors. The problem with above code is mainly with the usage of BitBlt and choosing the coordinates. See the MCVE below that addresses the question.
Don't draw in response to WM_CREATE, it's just going to get erased in WM_PAINT or when background is erased.
Don't call ValidateRect in response to WM_PAINT. If you want to erase the window then just use FillRect, or force repaint from a command or another route.
Use GetSystemMetrics(SM_CXVIRTUALSCREEN) and GetSystemMetrics(SM_CYVIRTUALSCREEN) to return width and height for the virtual monitor.
Also be sure the process is DPI aware. For testing you can call SetProcessDPIAware(); at the start of the program. Ideally DPI awareness should be set in manifest file.
int uX = (rand() % ScreenWidth) - (150 / 2);
int uY = (rand() % 15);
int Width = (rand() % 150);
BitBlt(Window, uX, uY, Width, ScreenHeight, Window, uX, 0, SRCCOPY);
Above code is copying bits from client DC the the same client DC, it's not going to do anything. Presumably you want to copy from desktop DC to client DC.
Moreover, the coordinates are basically picked at random. It assumes the primary monitor is on top-left. If uX is greater than window's own width, it will not get copied, unless the window stretches the whole virtual monitor.
LRESULT CALLBACK Melter(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam) {
switch(Message)
{
case WM_PAINT:
{
PAINTSTRUCT ps;
auto hdc = BeginPaint(hWnd, &ps);
RECT rc;
GetClientRect(hWnd, &rc);
HDC hdesktop = GetDC(0);
int screenx = GetSystemMetrics(SM_XVIRTUALSCREEN);
int screeny = GetSystemMetrics(SM_YVIRTUALSCREEN);
int screenw = GetSystemMetrics(SM_CXVIRTUALSCREEN);
int screenh = GetSystemMetrics(SM_CYVIRTUALSCREEN);
StretchBlt(hdc, 0, 0, rc.right, rc.bottom,
hdesktop, screenx, screeny, screenw, screenh, SRCCOPY);
ReleaseDC(0, hdesktop);
EndPaint(hWnd, &ps);
break;
}
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hWnd, Message, wParam, lParam);
}

You can use
HDC dc = CreateDC(("DISPLAY"), NULL, NULL, NULL);
Check below links:
Using Multiple Monitors as Independent Displays
CreateDCA function
Multiple Display Monitors Functions

Related

How to draw text on a bitmap in memory [no MFC]

Can somebody explain how to draw text on a bitmap in memory?
I have the following code but i can't figure out how to do it.
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
HDC buffDC = CreateCompatibleDC(hdc);
SelectObject(buffDC, hFnt);
SetTextColor(buffDC, RGB(1, 1, 1));
SetBkColor(buffDC, RGB(0, 255, 0));
RECT rc;
GetClientRect(hWnd, &rc);
HBITMAP buffBitmap = CreateCompatibleBitmap(buffDC, rc.right, rc.bottom);
int savedDC = SaveDC(buffDC);
HBRUSH hBrush = CreateSolidBrush(RGB(0, 255, 0));
FillRect(buffDC, &rc, hBrush);
DeleteObject(hBrush);
//This is the part where i would like to draw to the bitmap
TextOutA(buffDC, 0, 0, "Hello", 6);
SelectObject(buffDC, buffBitmap);
BitBlt(hdc, 0, 0, rc.right, rc.bottom, buffDC, 0, 0, SRCCOPY);
RestoreDC(buffDC, savedDC);
DeleteObject(buffBitmap);
DeleteDC(buffDC);
EndPaint(hWnd, &ps);
break;
}
I have seen a lot of different solutions mostly using MFC, however I would like to avoid that approach if possible.
EDIT: I have checked the other already asked questions but I couldn't find one wich would cover this without MFC.
My original problem was that I'm using a timer to call RedrawWindow and update the position of the text and make a sort of scrolling text which moves from right to left.
When I was in the testing process I have noticed that on some machines the app runs with up to 25% CPU usage and on some others it uses < 1%. I have tested the app one two machines with exactly the same specs running Windows 7 and the app ran on one with ~10% and the other with 0%.
(By the way my timer is getting called every 33ms and RedrawWindow uses RDW_UPDATENOW and I didn't handle the WM_ERASEBKGND message either :P
Since WM_TIMER (as far as I know) is a low priority message I was not concerned about the timer causeing the issue with the CPU usage.)
I satrted to think that maybe I should be using a bitmap and BitBlt it to the screen rather than just simply drawing to the dc and updating the x coordinate every time I repaint the screen.
Thanks
Before you can draw onto a bitmap, you have to select it into a memory device context.
Move SelectObject(buffDC, buffBitmap); before the first call to a drawing function, but usually as soon as possible after you created the bitmap.
In your sample code it appears suitable to insert it after the SaveDC() call so the original bitmap will be restored later when you call RestoreDC():
int savedDC = SaveDC(buffDC);
SelectObject(buffDC, buffBitmap);
As commenters noted, CreateCompatibleBitmap(buffDC, rc.right, rc.bottom) should be changed to CreateCompatibleBitmap(hdc, rc.right, rc.bottom).
From the reference of CreateCompatibleBitmap():
When a memory device context is created, it initially has a 1-by-1
monochrome bitmap selected into it. If this memory device context is
used in CreateCompatibleBitmap, the bitmap that is created is a
monochrome bitmap. To create a color bitmap, use the HDC that was used
to create the memory device context
Finally a suggestion: If you just need a temporary bitmap (as in your sample code), there is a more efficient API available since Windows Vista. It is called the buffered paint API. MSDN does not appear to provide a good overview, here is a tutorial and the reference (all functions that have "BufferedPaint" in their name).
Here is the Window Procedure which worked for me and is based on the answer from zett42.
This piece of code is just for testing purposses as I cannot post the original source code of the application I'm working on due to work.
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static int xPos;
const bool bIsBufferedPaint = true;
switch (message)
{
case WM_CREATE:
{
BufferedPaintInit();
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
if(bIsBufferedPaint)
{
HDC newDC;
RECT rc;
RECT dstrc;
GetClientRect(hWnd, &rc);
dstrc = rc;
dstrc.left = rc.right + xPos;
HPAINTBUFFER hBufferedPaint = BeginBufferedPaint(hdc, &rc, BPBF_COMPATIBLEBITMAP, NULL, &newDC);
if(hBufferedPaint)
{
BufferedPaintClear(hBufferedPaint, NULL);
SetTextColor(newDC, RGB(0, 0, 0));
DrawText(newDC, L"Using buffered paint", -1, &dstrc, DT_SINGLELINE | DT_VCENTER | DT_LEFT);
Sleep(2);
EndBufferedPaint(hBufferedPaint, TRUE);
}
else
{
// buffer paint did not work.
}
}
else
{
HDC buffDC = CreateCompatibleDC(hdc);
SetTextColor(buffDC, RGB(0, 0, 0));
SetBkColor(buffDC, RGB(255, 255, 255));
RECT rc;
GetClientRect(hWnd, &rc);
HBITMAP buffBitmap = CreateCompatibleBitmap(hdc, rc.right, rc.bottom);
int savedDC = SaveDC(buffDC);
SelectObject(buffDC, buffBitmap);
HBRUSH hBrush = CreateSolidBrush(RGB(255, 255, 255));
FillRect(buffDC, &rc, hBrush);
DeleteObject(hBrush);
std::string testText = "Not using the buffered paint API";
TextOutA(buffDC, xPos, 0, testText.c_str(), testText.size());
BitBlt(hdc, 0, 0, rc.right, rc.bottom, buffDC, 0, 0, SRCCOPY);
RestoreDC(buffDC, savedDC);
DeleteObject(buffBitmap);
DeleteDC(buffDC);
}
EndPaint(hWnd, &ps);
}
break;
case WM_ERASEBKGND:
return 1;
case WM_TIMER:
{
switch(wParam)
{
case TIMER1:
{
xPos--;
RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_ERASE);
if(bIsBufferedPaint)
{
if(xPos <= -500)
xPos = 0;
}
else
{
if(xPos <= -50)
xPos = 1000;
}
}
break;
}
}
break;
case WM_NCDESTROY:
{
BufferedPaintUnInit();
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}

WINapi. Can't erase background of window with ellipse

I'm just trying to draw an ellipse:
case WM_PAINT:
hdc = BeginPaint(parentWindow, &ps);
Ellipse(hdc, x, y, width, height);
EndPaint(parentWindow, &ps);
, and then erase it with drawing a new ellipse with some new parameters every second using timer:
case WM_CREATE:
SetTimer(hWnd, 1, 1000, NULL);
break;
case WM_TIMER:
x += 5;
InvalidateRect(hWnd, NULL, TRUE);
break;
But ellipses are not erased and layered:
However, i tried to trace WM_ERASEBKGND and it really is sent every InvalidateRect.
Full code:
#include <Windows.h>
#include <windowsx.h>
#include <tchar.h>
#include <iostream>
TCHAR szWindowClass[] = TEXT("CreateThreadWindow");
TCHAR szAppName[] = TEXT("CreateThreadExample");
BOOL InitWindow(HINSTANCE, int);
ATOM MyRegisterClass(HINSTANCE);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HWND parentWindow;
MSG msg;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MyRegisterClass(hInstance);
if (!InitWindow(hInstance, nCmdShow))
return FALSE;
BOOL bRet;
while ((bRet = GetMessage(&msg, (HWND)NULL, 0, 0)) != 0)
{
if (bRet == -1)
return FALSE;
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int)msg.wParam;
}
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASS wndClass;
memset(&wndClass, 0, sizeof(wndClass));
wndClass.lpfnWndProc = WndProc;
wndClass.hInstance = hInstance;
wndClass.lpszMenuName = NULL;
wndClass.lpszClassName = szWindowClass;
return RegisterClass(&wndClass);
}
BOOL InitWindow(HINSTANCE hInstance, int nCmdShow)
{
parentWindow = CreateWindow(szWindowClass, szAppName, WS_OVERLAPPEDWINDOW,
300, 0, 600, 600, NULL, NULL, hInstance, NULL);
ShowWindow(parentWindow, nCmdShow);
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wparam, LPARAM lparam)
{
PAINTSTRUCT ps;
HDC hdc;
static int x = 0, y = 0, width = 200, height = 100;
switch (message) {
case WM_ERASEBKGND:
_RPT1(0, "%s\n", "erase");
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
Ellipse(hdc, x, y, width, height);
EndPaint(hWnd, &ps);
break;
case WM_CREATE:
SetTimer(hWnd, 1, 1000, NULL);
break;
case WM_TIMER:
x += 5;
InvalidateRect(hWnd, NULL, TRUE);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hWnd, message, wparam, lparam);
}
}
Your code isn't erasing anything. It's just drawing an ellipse at the specified coordinates. The previously-drawn ellipse is still there.
You mention the WM_ERASEBKGND message, but there are two reasons why that isn't working for you:
In your window procedure (WndProc), you handle the WM_ERASEBKGND message explicitly, which means that it doesn't get passed to the default window procedure (DefWindowProc). A better way to write your window procedure would be the following:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wparam, LPARAM lparam)
{
static int x = 0, y = 0, width = 200, height = 100;
switch (message) {
case WM_ERASEBKGND:
{
_RPT1(0, "%s\n", "erase");
break;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
Ellipse(hdc, x, y, width, height);
EndPaint(hWnd, &ps);
return 0;
}
case WM_CREATE:
{
SetTimer(hWnd, 1, 1000, NULL);
break;
}
case WM_TIMER:
{
x += 5;
InvalidateRect(hWnd, NULL, TRUE);
return 0;
}
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
default:
break;
}
return DefWindowProc(hWnd, message, wparam, lparam);
}
Now, the default window procedure gets called every time, unless you explicitly return from inside of a case label.
When you register your window class (inside of MyRegisterClass), you zero all fields of the WNDCLASS structure and then explicitly initialize a couple of them. You don't explicitly initialize the hbrBackground field, so it is being set to 0. And when hbrBackground is 0,
When this member is NULL, an application must paint its own background whenever it is requested to paint in its client area. To determine whether the background must be painted, an application can either process the WM_ERASEBKGND message or test the fErase member of the PAINTSTRUCT structure filled by the BeginPaint function.
This means that the default window procedure isn't doing anything in response to the WM_ERASEBKGND message because you didn't give your window a background brush.
You will either need to set hbrBackground to something like COLOR_WINDOW + 1, or you will need to add code to your WM_ERASEBKGND message handler to erase the window's background yourself.
Or, perhaps an even better option would be to forget about the WM_ERASEBKGND message altogether, as many Windows programmers do, because this two-step erase-and-paint approach tends to cause flicker. Leave the hbrBackground field set to NULL, don't do anything in response to the WM_ERASEBKGND message, and do your erasing at the top of the WM_PAINT handler:
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// Erase background of entire client area.
RECT rcClient;
GetClientRect(hWnd, &rcClient);
FillRect(hdc, &rcClient, reinterpret_cast<HBRUSH>(COLOR_WINDOW+1));
// Do normal drawing.
Ellipse(hdc, x, y, width, height);
EndPaint(hWnd, &ps);
return 0;
}

Smooth window resizing in Windows (using Direct2D 1.1)?

It annoys me that the resizing of windows in Windows is not as "smooth" as it I'd like it to be (this is the case with Windows programs in general, not just my own. Visual Studio is a good example). It makes the OS and its programs feel "flimsy" and "cheap" (yes, I care about how programs and user interfaces feel, in the same way I care about the sound and feel of closing a car door. It's a reflection of build quality), which in my view affects the overall UX and ultimately the perception of the brand.
The redrawing of window contents simply does not keep up with mouse movement during resize. Whenever I resize a window, there is a "stuttering" / "flickering" effect, seemingly due to the previous-size-contents of the window being redrawn in the new, resized window frame before the new, resized contents are drawn.
I am building a Win32 application (x64) that uses Direct2D 1.1 to draw its UI, and given the speed of Direct2D, i think it should be unnecessary to suffer such artifacts in an OS in 2014. I am on Windows 8.1 myself, but targeting Windows 7 and up with this application.
The "previous size" effect is especially discernible when maximizing a small window (since the difference in window size is sufficiently great to easily contrast the image of the old content as it flashes briefly in the upper left corner of the larger window with the new content subsequently being painted over it).
This is what appears to be going on:
(Let's say there's a fully rendered window on screen, size 500 x 500 pixels).
I maximize the window:
The window frame is maximized
The old 500 x 500 content is drawn in the new frame, before..
..the maximized window is repainted with properly sized content.
I'm wondering if there's any way to mitigate this (i.e. get rid of step 4) - via intercepting a Windows Message, for example - and avoid the window being repainted at the new size with the old content before the final re-rendering of the new content happens. It's like Windows does the window redrawing itself, using whatever graphics it already has available, BEFORE it bothers to ask me to provide updated content with a WM_PAINT message or similar.
Can it be done?
Edit: It seems that WM_WINDOWPOSCHANGING / WM_SIZING provides "early access" to the new size data, but I still haven't managed to suppress the painting of the old content.
My WndProc looks like this:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_ERASEBKGND:
return 1;
case WM_PAINT:
PAINTSTRUCT ps;
BeginPaint(hWnd, &ps);
D2DRender();
EndPaint(hWnd, &ps);
return 0;
case WM_SIZE:
if (DeviceContext && wParam != SIZE_MINIMIZED)
{
D2DResizeTargetBitmap();
D2DRender();
}
return 0;
case WM_DISPLAYCHANGE:
D2DRender();
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
The window does not have CS_HREDRAW or CS_VREDRAW set. The swapchain is double buffered and the Present call is made with SyncInterval = 0.
I am aware that recreating the swapchain buffers every time the window size changes does create some overhead compared to plain redrawing on a static window surface. However, the "stuttering" is not caused by that, as it happens even when buffer resizing is disabled and existing window contents are simply scaled during window resizing (although that does make it keep up better with mouse movement).
There is a way to prevent the needless BitBlt mentioned in step 4 above.
Until Windows 8 it could be done either by creating your own custom implementation of WM_NCCALCSIZE to tell Windows to blit nothing (or to blit one pixel on top of itself), or alternately you could intercept WM_WINDOWPOSCHANGING (first passing it onto DefWindowProc) and set WINDOWPOS.flags |= SWP_NOCOPYBITS, which disables the BitBlt inside the internal call to SetWindowPos() that Windows makes during window resizing. This has the same eventual effect of skipping the BitBlt.
However, nothing can be so simple. With the advent of Windows 8/10 Aero, apps now draw into an offscreen buffer which is then composited by the new, evil DWM.exe window manager. And it turns out DWM.exe will sometimes do its own BitBlt type operation on top of the one already done by the legacy XP/Vista/7 code. And stopping DWM from doing its blit is much harder; so far I have not seen any complete solutions.
So you need to get through both layers. For sample code that will break through the XP/Vista/7 layer and at least improve the performance of the 8/10 layer, see:
How to smooth ugly jitter/flicker/jumping when resizing windows, especially dragging left/top border (Win 7-10; bg, bitblt and DWM)?
What if you have a borderless childwindow (the type that only renders inside the parent) at a fixed size (same as fullscreen resolution), you should get a lot smoother results because there is no memory reallocation (which i think is what causes the jitterieness).
If it's still not perfect, look into both WM_SIZE and WM_SIZING and check if you can do some magic with them. For instance, on WM_SIZING you could return true telling Windows you handled the message (leaving the window as is) and you re-render your UI to a buffer with the size provided by WM_SIZING and when that is done, you send your own WM_SIZING but with a manipulated unused bit in WPARAM (along with its previous content) that tells you you have a pre-rendered buffer for this that you can just blit out. From the WM_SIZING documentation on msdn it looks like WPARAM should have a couple of bits at your disposal.
Hope this helps.
When calling CreateSwapChainForHwnd, ensure that you have set the swap chain description Scaling property to DXGI_SCALING_NONE. This is only supported on Windows 7 with the Platform Update, so you may need to fall back to the default DXGI_SCALING_STRETCH (the latter is what is causing the flickering).
Set WM_SETREDRAW to FALSE, do your resizing, then reenable drawing, invalidate the window and the OS will blit it.
I've done this for button enabling and disabling buttons when selecting different items from a list, never for an entire window.
This is the best I've come up with and resizes great although the backbuffer blitting causes some edge flickering, haven't tested with DX or OGL yet but it should work even better with hardware acceleration. It's a bit bulky but will do as a proof of concept.
If the canvas could be clipped without using MDI that would be even better, like using a bitmask buffer.
One thing i'm not happy about is the position coords of the child window because they might not work on all systems, but a combination of GetSystemMetrics calls to get border and caption sizes should fix that.
/* Smooth resizing of GDI+ MDI window
*
* Click window to resize, hit Escape or Alt+F4 to quit
*
* Character type is set to multibyte
* Project->Properties->Config Properties->General->Character Set = Multibyte
*
* Pritam 2014 */
// Includes
#include <Windows.h>
#include <gdiplus.h>
#pragma comment (lib,"Gdiplus.lib")
using namespace Gdiplus;
// Max resolution
#define XRES 1600
#define YRES 900
// Globals
bool resizing = false;
HWND parent, child; // child is the canvas window, parent provides clipping of child
Bitmap * buffer;
// Render
void Render() {
// Get parent client size
RECT rc;
GetClientRect(parent, &rc);
// Draw backbuffer
Graphics * g = Graphics::FromImage(buffer);
// Clear buffer
g->Clear(Color(100, 100, 100));
// Gray border
Pen pen(Color(255, 180, 180, 180));
g->DrawRectangle(&pen, 10, 10, rc.right - 20, rc.bottom - 20);
pen.SetColor(Color(255, 0, 0, 0));
g->DrawRectangle(&pen, 0, 0, rc.right - 1, rc.bottom - 1);
// Draw buffer to screen
PAINTSTRUCT ps;
HDC hdc = BeginPaint(child, &ps);
Graphics graphics(hdc);
graphics.DrawImage(buffer, Point(0, 0));
// Free
EndPaint(child, &ps);
}
// MDI Callback
LRESULT CALLBACK MDICallback(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
switch(message) {
case WM_LBUTTONDOWN:
resizing = true; // Start resizing
return 0;
break;
case WM_KEYDOWN:
if(wparam == VK_ESCAPE) { // Exit on escape
PostQuitMessage(0);
}
TranslateMessage((const MSG *)&message);
return 0;
break;
case WM_PAINT:
Render();
return 0;
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
break;
}
return DefMDIChildProc(hwnd, message, wparam, lparam);
}
// Parent window callback
LRESULT CALLBACK WndCallback(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
return DefFrameProc(hwnd, child, message, wparam, lparam);
}
// Create windows
bool CreateWindows(void) {
// Parent class
WNDCLASSEX wndclass;
ZeroMemory(&wndclass, sizeof(wndclass)); wndclass.cbSize = sizeof(wndclass);
wndclass.style = CS_NOCLOSE;
wndclass.lpfnWndProc = WndCallback;
wndclass.hInstance = GetModuleHandle(NULL);
wndclass.lpszClassName = "WNDCALLBACKPARENT";
wndclass.hIcon = LoadIcon(NULL, IDI_WINLOGO);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
if(!RegisterClassEx(&wndclass)) return false;
// MDI class
wndclass.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW;
wndclass.lpfnWndProc = MDICallback;
wndclass.lpszClassName = "MDICALLBACKCANVAS";
if(!RegisterClassEx(&wndclass)) return false;
// Parent window styles
DWORD style = WS_POPUP | WS_CLIPCHILDREN;
DWORD exstyle = 0;
// Set initial window size and position
RECT rc;
rc.right = 640;
rc.bottom = 480;
AdjustWindowRectEx(&rc, style, false, exstyle);
rc.left = 20;
rc.top = 20;
// Create window
if(!(parent = CreateWindowEx(exstyle, "MDICLIENT", "MDI Resize", style, rc.left, rc.top, rc.right, rc.bottom, NULL, NULL, wndclass.hInstance, NULL))) return false;
// MDI window styles
style = MDIS_ALLCHILDSTYLES;
exstyle = WS_EX_MDICHILD;
// Set MDI size
rc.left = - 8; // The sizes occupied by borders and caption, if position is not correctly set an ugly caption will appear
rc.top = - 30;
rc.right = XRES;
rc.bottom = YRES;
AdjustWindowRectEx(&rc, style, false, exstyle);
// Create MDI child window
if(!(child = CreateWindowEx(exstyle, "MDICALLBACKCANVAS", "", style, rc.left, rc.top, rc.right, rc.bottom, parent, NULL, wndclass.hInstance, NULL))) return 8;
// Finalize
ShowWindow(child, SW_SHOW);
ShowWindow(parent, SW_SHOWNORMAL);
// Success
return true;
}
// Resize
void Resize(void) {
// Init
RECT rc, rcmdi;
GetClientRect(child, &rcmdi); // Use mdi window size to set max resize for parent
GetWindowRect(parent, &rc);
// Get mouse position
POINT mp;
GetCursorPos(&mp);
// Set new size
rc.right = mp.x - rc.left + 10;
rc.bottom = mp.y - rc.top + 10;
// Apply min & max size
if(rc.right < 240) rc.right = 240; if(rc.bottom < 180) rc.bottom = 180;
if(rc.right > rcmdi.right) rc.right = rcmdi.right; if(rc.bottom > rcmdi.bottom) rc.bottom = rcmdi.bottom;
// Update window size
SetWindowPos(parent, NULL, rc.left, rc.top, rc.right, rc.bottom, SWP_NOZORDER | SWP_NOMOVE);
// Make sure client is entirely repainted
GetClientRect(child, &rc);
InvalidateRect(child, &rc, false);
UpdateWindow(child);
// Stop resizing if mousebutton is up
if(!(GetKeyState(VK_LBUTTON) & 1 << (sizeof(short) * 8 - 1)))
resizing = false;
}
// Main
int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE pinstance, LPSTR cmdline, int cmdshow) {
// Initiate GDI+
ULONG_PTR gdiplusToken;
GdiplusStartupInput gdiplusStartupInput;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
buffer = new Bitmap(XRES, YRES, PixelFormat24bppRGB);
// Create windows
if(!CreateWindows()) return 1;
// Main loop
bool running = true;
MSG message;
while(running) {
// Check message or pass them on to window callback
if(PeekMessage(&message, NULL, 0, 0, PM_REMOVE)) {
if(message.message == WM_QUIT) {
running = false;
} else {
if(!TranslateMDISysAccel(child, &message)) {
TranslateMessage(&message);
DispatchMessage(&message);
}
}
}
// Resize
if(resizing)
Resize();
// Sleep a millisecond to spare the CPU
Sleep(1);
}
// Free memmory and exit
delete buffer;
GdiplusShutdown(gdiplusToken);
return 0;
}
Edit: Another example using "bitmask"/layered window.
// Escape to quit, left mousebutton to move window, right mousebutton to resize.
// And again char set must be multibyte
// Include
#include <Windows.h>
#include <gdiplus.h>
#pragma comment (lib,"Gdiplus.lib")
using namespace Gdiplus;
// Globals
Bitmap * backbuffer;
int xres, yres;
bool move, size;
POINT framePos, frameSize, mouseOffset;
// Renders the backbuffer
void Render(void) {
if(!backbuffer) return;
// Clear window with mask color
Graphics * gfx = Graphics::FromImage(backbuffer);
gfx->Clear(Color(255, 0, 255));
// Draw stuff
SolidBrush brush(Color(120, 120, 120));
gfx->FillRectangle(&brush, framePos.x, framePos.y, frameSize.x, frameSize.y);
}
// Paints the backbuffer to window
void Paint(HWND hwnd) {
if(!hwnd) return;
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
Graphics gfx(hdc);
gfx.DrawImage(backbuffer, Point(0, 0));
EndPaint(hwnd, &ps);
}
void HandleMove(HWND hwnd) {
// Get mouse position
POINT mouse;
GetCursorPos(&mouse);
// Update frame position
framePos.x = mouse.x - mouseOffset.x;
framePos.y = mouse.y - mouseOffset.y;
// Redraw buffer and invalidate & update window
Render();
InvalidateRect(hwnd, NULL, false);
UpdateWindow(hwnd);
// Stop move
if(!(GetKeyState(VK_LBUTTON) & 1 << (sizeof(short) * 8 - 1)))
move = false;
}
void HandleSize(HWND hwnd) {
// Get mouse position
POINT mouse;
GetCursorPos(&mouse);
// Update frame size
frameSize.x = mouse.x + mouseOffset.x - framePos.x;
frameSize.y = mouse.y + mouseOffset.y - framePos.y;
//frameSize.x = mouse.x + mouseOffset.x;
//frameSize.y = mouse.y + mouseOffset.y;
// Redraw buffer and invalidate & update window
Render();
InvalidateRect(hwnd, NULL, false);
UpdateWindow(hwnd);
// Stop size
if(!(GetKeyState(VK_RBUTTON) & 1 << (sizeof(short) * 8 - 1)))
size = false;
}
LRESULT CALLBACK WindowCallback(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
POINTS p;
switch(msg) {
case WM_KEYDOWN:
if(wparam == VK_ESCAPE) PostQuitMessage(0);
return 0;
break;
case WM_LBUTTONDOWN:
p = MAKEPOINTS(lparam); // Get mouse coords
mouseOffset.x = p.x - framePos.x;
mouseOffset.y = p.y - framePos.y;
move = true;
break;
case WM_RBUTTONDOWN:
p = MAKEPOINTS(lparam);
mouseOffset.x = framePos.x + frameSize.x - p.x;
mouseOffset.y = framePos.y + frameSize.y - p.y;
size = true;
break;
case WM_PAINT:
Paint(hwnd);
return 0;
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
break;
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
// Main
int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE pinstance, LPSTR cmdline, int cmdshow) {
// Init resolution, frame
xres = GetSystemMetrics(SM_CXSCREEN);
yres = GetSystemMetrics(SM_CYSCREEN);
move = false; size = false;
framePos.x = 100; framePos.y = 80;
frameSize.x = 320; frameSize.y = 240;
mouseOffset.x = 0; mouseOffset.y = 0;
// Initiate GDI+
ULONG_PTR gdiplusToken;
GdiplusStartupInput gdiplusStartupInput;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
// Init backbuffer
backbuffer = ::new Bitmap(xres, yres, PixelFormat24bppRGB);
Render();
// Window class
WNDCLASSEX wc; ZeroMemory(&wc, sizeof(wc)); wc.cbSize = sizeof(wc);
wc.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW;
wc.lpfnWndProc = WindowCallback;
wc.hInstance = GetModuleHandle(NULL);
wc.lpszClassName = "SingleResizeCLASS";
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
if(!RegisterClassEx(&wc)) return 1;
// Create window
HWND hwnd;
DWORD style = WS_POPUP;
DWORD exstyle = WS_EX_LAYERED;
if(!(hwnd = CreateWindowEx(exstyle, wc.lpszClassName, "Resize", style, 0, 0, xres, yres, NULL, NULL, wc.hInstance, NULL)))
return 2;
// Make window fully transparent to avoid the display of unpainted window
SetLayeredWindowAttributes(hwnd, 0, 0, LWA_ALPHA);
// Finalize
ShowWindow(hwnd, SW_SHOWNORMAL);
UpdateWindow(hwnd);
// Make window fully opaque, and set color mask key
SetLayeredWindowAttributes(hwnd, RGB(255, 0, 255), 0, LWA_COLORKEY);
// Main loop
MSG msg;
bool running = true;
while(running) {
// Check message
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if(msg.message == WM_QUIT) {
running = false;
} else {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
// Move or size frame
if(move) { HandleMove(hwnd); }
if(size) { HandleSize(hwnd); }
Sleep(1);
}
// Free memory
::delete backbuffer;
backbuffer = NULL;
GdiplusShutdown(gdiplusToken);
// Exit
return 0;
}
While your aim is laudable, I suspect that any attempt to do this will just end up as a fight between you and Windows - which you will not win (though you might manage to fight your way to an honourable draw). Sorry to be negative.

How do I paint a child window with FillRect()?

I have a main window which is created with the following styles
WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_TABSTOP | WS_GROUP | WS_VISIBLE
and with ex-stles
WS_EX_ACCEPTFILES | WS_EX_CONTROLPARENT | WS_EX_LEFT | WS_EX_LTRREADING.
This main window has a child window on it, which is an edit control created with styles
WS_VISIBLE | WS_CHILD | ES_READONLY
and ex-style
WS_EX_CLIENTEDGE.
I am going to use this edit control as a progress-bar control. I don't want to use standard Wind32 progress-bar control (PROGRESS_CLASS), because I want to do a some custom painting on it (e.g.; dynamically changing fill color, displaying text on it, etc).
I can paint any region of the main window by the following code:
// hWnd: Handle of the main window
case WM_PAINT:
hDc = BeginPaint(hWnd, &Ps);
Rect = AFunctionToGetCornerThePointsOfTheEditControl();
Rect.right = Rect.left + 3 * (Rect.right - Rect.left) / 4; // Fill 3/4 (75%) of it
Rect.left -= 10; // Enlarge the paint region a little
Rect.top -= 10; // so that we can see it if it stays
Rect.bottom += 10; // under the edit control.
hBrush = CreateSolidBrush(RGB(50,100,255));
ret = FillRect(hDc, &Rect, hBrush); // ret = 1 always
ler = GetLastError(); // ler = 0
EndPaint(hWnd, &Ps);
break;
It looks like this:
I changed this code a little to paint the child control instead:
// hWndEdit: Handle of the edit control
case WM_PAINT:
hDc = BeginPaint(hWndEdit, &Ps);
Rect = AFunctionToGetCornerThePointsOfTheEditControl();
Rect.right = Rect.left + 3 * (Rect.right - Rect.left) / 4; // Fill 3/4 (75%) of it
Rect.left -= 10;
Rect.top -= 10;
Rect.bottom += 10;
hBrush = CreateSolidBrush(RGB(50,100,255));
ret = FillRect(hDc, &Rect, hBrush); // ret = 0 always
ler = GetLastError(); // ler = 6 (ERROR_INVALID_HANDLE)
EndPaint(hWndEdit, &Ps);
break;
This time it doesn't work. The main windows completely disappears as soon as I drag some part it out of the screen area, and it becomes totally unresponsive. Desktop icons under it are visible, but are not clickable.
So, what do I have to do in order to paint the child window (the edit control)?
This article helped me a lot: Subclassing Controls
First, I create a separate message processing function for processing child messages.
LRESULT CALLBACK MyClass::ChildWindowProc( HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam,
UINT_PTR uIdSubclass,
DWORD_PTR dwRefData)
{
static PAINTSTRUCT Ps;
static RECT Rect;
static HBRUSH hBrush1 = CreateSolidBrush(RGB(50,100,255));
static HBRUSH hBrush2 = CreateSolidBrush(RGB(255,100,50));
HDC hDc;
LRESULT lResult;
switch (uMsg)
{
case WM_PAINT:
switch (uIdSubclass)
{
case 1:
hDc = BeginPaint(hWnd, &Ps);
Rect.left = 0;
Rect.right = (LONG) (((double) ITEM2_WIDTH) * Status::GI()->Get_JobCurPercentage());
Rect.top = 0;
Rect.bottom = ITEM_HEIGHT - 3;
FillRect(hDc, &Rect, hBrush1);
EndPaint(hWnd, &Ps);
break;
case 2:
hDc = BeginPaint(hWnd, &Ps);
Rect.left = 0;
Rect.right = (LONG) (((double) ITEM2_WIDTH) * Status::GI()->Get_JobTotPercentage());
Rect.top = 0;
Rect.bottom = ITEM_HEIGHT - 3;
FillRect(hDc, &Rect, hBrush2);
EndPaint(hWnd, &Ps);
break;
default:
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
break;
case WM_NCDESTROY:
//ReleaseDC(hWnd, hDc);
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
break;
default:
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
Next, I bind this function to the controls:
SetWindowSubclass( /*_In_ HWND hWnd*/ ed_cur_Progress.hWnd,
/*_In_ SUBCLASSPROC pfnSubclass*/ ChildWindowProc,
/*_In_ UINT_PTR uIdSubclass*/ 1,
/*_In_ DWORD_PTR dwRefData*/ (DWORD_PTR) NULL);
SetWindowSubclass( /*_In_ HWND hWnd*/ ed_tot_Progress.hWnd,
/*_In_ SUBCLASSPROC pfnSubclass*/ ChildWindowProc,
/*_In_ UINT_PTR uIdSubclass*/ 2,
/*_In_ DWORD_PTR dwRefData*/ (DWORD_PTR) NULL);
And, that's all! The result is amazing.
WM_PAINT you are handling is main window's. you need to draw the editbox in its owner WM_PAINT message. I guess you get the error from "hDc = BeginPaint(hWndEdit, &Ps);", you may check it.

MFC - set a background image on a control

I apologize for the vagueness of the title, but i'm not entirely sure how i want to go about solving the issue at hand. Basically, i have 2 groups of buttons. When the user selects a button from one of the groups, i want to set an indicator that that group has been selected. So either a rectangle surrounding them (in blue) or coloring the background (or background image) behind the group. The end user indicated that a rectangle surrounding them would be nice so that would be preferential. I have tried overriding the onCtlColor on a group box, but I don't know how to limit the coloring to just the border. I do know that it absolutely needs to be the farthest back in z-order Any advice?
Example code (i'm aware that it pains the entirety of the box)
pDC->SetBkColor(GetSysColor(RGB(100,149,237)));
CRect rect;
testGb.GetClientRect(rect);
CBrush brushBlue(RGB(0, 0, 255));
CBrush* pOldBrush = pDC->SelectObject(&brushBlue);
pDC->Rectangle(rect);
pDC->SelectObject(pOldBrush);
I could find only a way to change the background colour of the Text of a group box:
EDIT You can paint anything you want on the Group box!
a) Declare a CBrush member variable m_br and create a coloured brush with it
b) Override WindowProc for a group box with ID = IDC_GROUPBOX:
LRESULT CTestMFCDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{ if (message == WM_CTLCOLORSTATIC)
if (::GetDlgCtrlID((HWND) lParam) == IDC_GROUPBOX)
return (LRESULT) m_br.GetSafeHandle();
return CDialogEx::WindowProc(message, wParam, lParam);
}
LRESULT CTestMFCDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{ if (message == WM_CTLCOLORSTATIC)
if (::GetDlgCtrlID((HWND) lParam) == IDC_GROUPBOX)
{ HWND hWnd = (HWND) lParam;
HDC hDC = (HDC) wParam;
RECT rc;
::GetClientRect(hWnd, &rc);
HBRUSH hOldBrush = (HBRUSH) ::SelectObject(hDC, m_br);
::Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);
::SelectObject(hDC, hOldBrush);
// return (LRESULT) m_br.GetSafeHandle();
}
return CDialogEx::WindowProc(message, wParam, lParam);
}
Better code (at least for me):
void MyClass::OnPaint()
{
CPaintDC dc(this); // device context for painting
COLORREF highlightFillColor;
CPen nPen, *pOldPen = NULL;
CBrush nBrush, *pOldBrush = NULL;
CRect rect;
GetWindowRect(rect);
ScreenToClient(rect);
BmsMemDC memDc(&dc, &rect);
memDc.SetBkMode(TRANSPARENT);
//dc.Re
highlightFillColor = RGB(0x99,0xB4,0xFF);
nPen.CreatePen( PS_SOLID, 4, highlightFillColor);
nBrush.CreateSolidBrush( GetSysColor(COLOR_3DFACE ));
pOldPen = memDc.SelectObject(&nPen);
pOldBrush = memDc.SelectObject(&nBrush);
if(leftGroupSelected)
{
rect.SetRect(rect.left + 4, rect.top+30, rect.left + 126, rect.bottom - 5);
memDc.FillRect(&rect,&nBrush);
memDc.RoundRect(rect.left, rect.top, rect.right, rect.bottom, 8, 8);
}
if (rightGroupSelected)
{
rect.SetRect(rect.left + 134, rect.top+30, rect.left + 256, rect.bottom - 5);
memDc.FillRect(&rect,&nBrush);
memDc.RoundRect(rect.left, rect.top, rect.right, rect.bottom, 8, 8);
}
}