Hello, I'm trying to get from the screen, the region, or rectangle (the coordinated) of the part that is being painted. For example when I move some window or I have some action on the screen I want to get only that part of the screen image that is being painted (because it changed).
I try calling it inside WM_PAINT event before BeginPaint().

LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
	switch(Message)
	{
          [...]
case WM_PAINT:{
				
					   RECT    rectUpd;
				
				if(hwnd!=NULL){
					char s[100]="";
				
					
//hDesktop is desktop windown handler - GetDC(GetDesktopWindow());
					GetUpdateRect (hDesktop, &rectUpd, FALSE);
					c++;
					
					sprintf(s,"Top:%d Left:%d Right:%d Bottom:%d",
						rectUpd.top,
						rectUpd.left,
						rectUpd.right,
						rectUpd.bottom);
//I set the window title with the values of the rectangle 
						SetWindowText(hwnd,s);

					
					
				}
			BeginPaint(hwnd,NULL);//hwnd is application window handler
			
				  }break;
          [...]
        }
}

I obtained only "-858993460" value for rectangle coordinates... Can somebody help me out how to detect the coordonates of the region painted on the screen?

Recommended Answers

All 17 Replies

The invalid region is contained within the RECT member (.rcPaint) of the PAINTSTRUCT you used as an output parameter in the BeginPaint() call. It can be easily retrieved from there.

I wrote the above without looking at your code. All your code is totally bad. You are using BeginPaint() incorrectly. You need to declare a PAINTSTRUCT variable in your WM_PAINT handler. Its address should be the 2nd parameter of BeginPaint(). When BeginPaint() returns, you'll not only have the HDC you need, but also the coordinates of the invalid rectangle that needs painted. Its up to you what you do with this information. Passing a NULL as the 2nd parameter of BeginPaint() is something I've never seen done. Your BeginPaint() call would certainly have failed.

Also, I'd be ashamed posting code with such poor formatting.

Hmmm... I'm glad you pointed out how noob I am in Windows API, even if I never pointed out such thing about persons of this forums at java or c# who don't even know to declare an array... Yes, I admit I have a lot to learn.. for example how API messages work and hooking and thanks for the hint. I thinks a don't really have this in order... here is the code.

LPPAINTSTRUCT lp; 
HDC hdcInvRect; 
char s[100]="";

LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
	switch(Message)
	{
case WM_PAINT:{
				

				hdcInvRect = BeginPaint(hwnd,&lp);

						  
					   sprintf(s,"Top:%d Left:%d Right:%d Bottom:%d",
						lp->rcPaint.top,
						lp->rcPaint.left,
						lp->rcPaint.right,
						lp->rcPaint.bottom);

				        SetWindowText(hwnd,s);
				
					  


			    

				  }break;
}
}

I guess it's poor code again... I don't need to use GetUpdateRect()? I knew it should be called before BeginPaint().
When I compile it throws an unhandled exception at sprintf line. Sorry... I must understand API and make some huge mistakes, then learn from that. Can you help me please?

Usually in my WM_PAINT handlers I just declare a...

PAINTSTRUCT ps;

...instead of a LPPAINTSTRUCT. However, that might be just me. When you do declare a LPPAINTSTRUCT though, this...

hDC=BeginPaint(hwnd,&lpPS);

won't work. I'd just do this...

PAINTSTRUCT ps;
HDC hDC;

hDC=BeginPaint(hwnd,&ps);

Here's a little program that demonstrates invalid regions by opening a text file in whatever directory you are running the program from, and outputs invalid regions as you move another window on top of the client window. Experiment by opening Notepad and resizing it to pretty small. Then move Notepad just barely over your client window, then close out and inspect your Output.txt file.

//Form1.h

typedef struct    WindowsEventArguments
{
 HWND             hWnd;
 WPARAM           wParam;
 LPARAM           lParam;
 HINSTANCE        hIns;
}WndEventArgs, *lpWndEventArgs;


struct EVENTHANDLER
{
 unsigned int    Code;
 long            (*fnPtr)(lpWndEventArgs);
};
//Main.cpp
#include <windows.h>
#include <stdio.h>
#include "Form1.h"
EVENTHANDLER  EventHandler[3];
FILE* fp;


long fnWndProc_OnCreate(lpWndEventArgs Wea)
{
 fp=fopen("Output.txt","w");
 fprintf(fp,"Entering fnWndProc_OnCreate()\n");
 fprintf(fp,"  Output.txt Opened In fnWndProc_OnCreate()\n");
 fprintf(fp,"Leaving fnWndProc_OnCreate()\n\n");

 return 0;
}

long fnWndProc_OnPaint(lpWndEventArgs Wea)
{
 PAINTSTRUCT ps;
 HDC hDC;

 fprintf(fp,"Entering fnWndProc_OnPaint()\n");
 hDC=BeginPaint(Wea->hWnd,&ps);
 fprintf(fp,"  ps.rcPaint.left   = %u\n",(unsigned)ps.rcPaint.left);
 fprintf(fp,"  ps.rcPaint.top    = %u\n",(unsigned)ps.rcPaint.top);
 fprintf(fp,"  ps.rcPaint.right  = %u\n",(unsigned)ps.rcPaint.right);
 fprintf(fp,"  ps.rcPaint.bottom = %u\n",(unsigned)ps.rcPaint.bottom);
 EndPaint(Wea->hWnd,&ps);
 fprintf(fp,"Leaving fnWndProc_OnPaint()\n\n");

 return 0;
}


long fnWndProc_OnClose(lpWndEventArgs Wea)
{
 fprintf(fp,"Entering fnWndProc_OnClose()\n");
 DestroyWindow(Wea->hWnd);
 PostQuitMessage(0);
 fprintf(fp,"  Output.txt Closed In fnWndProc_OnClose()\n");
 fprintf(fp,"Leaving fnWndProc_OnClose()\n\n");
 fclose(fp);

 return 0;
}


void AttachEventHandlers(void)         //This procedure maps windows messages to the
{                                      //procedure which handles them.
 EventHandler[0].Code=WM_CREATE,       EventHandler[0].fnPtr=fnWndProc_OnCreate;
 EventHandler[1].Code=WM_PAINT,        EventHandler[1].fnPtr=fnWndProc_OnPaint;
 EventHandler[2].Code=WM_CLOSE,        EventHandler[2].fnPtr=fnWndProc_OnClose;
}


long __stdcall fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam,LPARAM lParam)
{
 WndEventArgs Wea;                  //This procedure loops through the EVENTHANDER array
                                    //of structs to try to make a match with the msg parameter
 for(unsigned int i=0; i<3; i++)    //of the WndProc.  If a match is made the event handling
 {                                  //procedure is called through a function pointer -
     if(EventHandler[i].Code==msg)  //(EventHandler[i].fnPtr).  If no match is found the
     {                              //msg is passed onto DefWindowProc().
        Wea.hWnd=hwnd, Wea.lParam=lParam, Wea.wParam=wParam;
        return (*EventHandler[i].fnPtr)(&Wea);
     }
 }

 return (DefWindowProc(hwnd, msg, wParam, lParam));
}


int __stdcall WinMain(HINSTANCE hIns, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
 char szClassName[]="Form1";
 WNDCLASSEX wc;
 MSG messages;
 HWND hWnd;

 AttachEventHandlers();
 wc.lpszClassName=szClassName;                          wc.lpfnWndProc=fnWndProc;
 wc.cbSize=sizeof (WNDCLASSEX);                         wc.style=CS_VREDRAW|CS_HREDRAW;
 wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);               wc.hInstance=hIns;
 wc.hIconSm=LoadIcon(NULL, IDI_APPLICATION);            wc.hCursor=LoadCursor(NULL,IDC_ARROW);
 wc.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);  wc.cbWndExtra=0;
 wc.lpszMenuName=NULL;                                  wc.cbClsExtra=0;
 RegisterClassEx(&wc);
 hWnd=CreateWindowEx(0,szClassName,szClassName,WS_OVERLAPPEDWINDOW,100,100,350,300,HWND_DESKTOP,0,hIns,0);
 ShowWindow(hWnd,iShow);
 while(GetMessage(&messages,NULL,0,0))
 {
    TranslateMessage(&messages);
    DispatchMessage(&messages);
 }

 return messages.wParam;
}

Sorry about the comment about the badly formatted code. Mine doesn't come through too good either.

Nice program... (MFC isn't it?). Thats nice :). But I must get the painted region from the screen... and that's tricky cause the handler of the desktop from GetDesktopWindow() doesn't really work because it's hidden by all other top windows. Now I try to use GetDCEx(). If you have an idea I would be glad to help me...

hDesktop = GetDCEx(GetDesktopWindow(),CreateRectRgn(0, 0, 1280,1024),1000);
hwndDesk = WindowFromDC(hDesktop);

I tried to use hwndDesk in the OnPaint event but I obtain only 0 values...
Thanks.

No, that's my own style Win32 program. I don't use MFC.

I've no experience with what you are trying to do, but I can say for absolute certainty that you can't use the WM_PAINT message of one window to draw into another. Just recently there was a post about this in the PowerBASIC forums. While I realize you are doing C++, discussions of this nature about how the Windows Api work are rather language agnostic, as the Api can be used from any number of different languages. Here is the link...

http://www.powerbasic.com/support/pbforums/showthread.php?t=42470

Well, I'm not trying to paint on the screen; I'm trying to detect if something, anything new changed on the screen, anything that was painted. I wanted to do this in C# but I should understand winAPI first, in C++. For example the clock just changed. So I should get the rectangle coordonated of that part of the screen that was painted at that moment of time.
So I want to get it, not to draw it. Thx.

I've never tried it with the desktop window, but what I'm going to say might work with the desktop window, unless its some special case and is disallowed there. If you can get the hwnd of any window (and you can get the hwnd of the desktop), you can 'hook' the internal window procedure of the window by setting an address of a WNDPROC you've created in your app in the chain of window procedures windows calls when a message is destined for that window. The technique is known as subclassing, and while it sounds complicated; it isn't. You use SetWindowLong() with the GWL_WNDPROC in the 2nd parameter. In that way you might be able to retrieve the update regions from the desktop. To me, it seems pretty bizarre and I don't do stuff like that (trying to mess around with Windows Desktop), but you might look into it. Search on Window Subclassing and SetWindowLong(). If you are interested I'll dig up an example.

Sure I'm interested, that's what I search for a long time. On some forums others wanted to do something similar like me but it was said that Desktop Window is hidden behind everything (top windows). I actually don't even care about desktop... what I need is to detect the coordonates of "what changed in the image that I see on the screen". However none came with the idea of subclassing... I'm new to it but I will search about it and an example from you would be great. Thanks a lot!

Well, I took a shot at it and failed. However, I'll post the code I got. What seems to be happening is that Windows isn't allowing me to subclass the desktop window. When I call GetLastError() after the call to SetWindowLong() that would set the subclass proc - here MyDesktopHook(), I'm getting the error #5 which is "ACCESS DENIED".

So my subclass isn't working. Perhaps I've a dumb error; was coding pretty fast. Anyway here's the code and I've a tutorial on subclassing here...

http://www.jose.it-berater.org/smfforum/index.php?topic=3392.0

Check Out ProgEx40f

//Form1.h

typedef struct    WindowsEventArguments
{
 HWND             hWnd;
 WPARAM           wParam;
 LPARAM           lParam;
 HINSTANCE        hIns;
}WndEventArgs, *lpWndEventArgs;


struct EVENTHANDLER
{
 unsigned int    Code;
 long            (*fnPtr)(lpWndEventArgs);
};
//Main.cpp
#include <windows.h>
#include <stdio.h>
#include "Form1.h"
EVENTHANDLER  EventHandler[3];
WNDPROC fnDesktopProc=0;
FILE* fp;

long __stdcall fnMyDesktopHook(HWND hwnd, unsigned int msg, WPARAM wParam,LPARAM lParam)
{
 if(msg==WM_PAINT)
    fprintf(fp,"Got WM_PAINT Message For Desktop Window!\n");
 else
    fprintf(fp,"Got Something Else!\n");

 return CallWindowProc(fnDesktopProc,hwnd,msg,wParam,lParam);
}



long fnWndProc_OnCreate(lpWndEventArgs Wea)
{
 HWND hDesktop;

 fp=fopen("Output.txt","w");
 fprintf(fp,"Entering fnWndProc_OnCreate()\n");
 fprintf(fp,"  Output.txt Opened In fnWndProc_OnCreate()\n");
 hDesktop=GetDesktopWindow();
 fprintf(fp,"  hDesktop = %u\n",hDesktop);
 if(hDesktop)
 {
    fnDesktopProc=(WNDPROC)SetWindowLong(hDesktop,GWL_WNDPROC,(long)fnMyDesktopHook);
    fprintf(fp,"  GetLastError() = %u\n",GetLastError());
    fprintf(fp,"  fnDesktopProc = %u\n",(unsigned)fnDesktopProc);
 }
 fprintf(fp,"Leaving fnWndProc_OnCreate()\n\n");

 return 0;
}

long fnWndProc_OnLButtonDown(lpWndEventArgs Wea)
{
 RedrawWindow(NULL,NULL,NULL,RDW_INVALIDATE|RDW_UPDATENOW|RDW_ALLCHILDREN);
 return 0;
}


long fnWndProc_OnClose(lpWndEventArgs Wea)
{
 HWND hDesktop;
 fprintf(fp,"Entering fnWndProc_OnClose()\n");
 hDesktop=GetDesktopWindow();
 if(fnDesktopProc)
    SetWindowLong(hDesktop,GWL_WNDPROC,(long)fnDesktopProc);
 DestroyWindow(Wea->hWnd);
 PostQuitMessage(0);
 fprintf(fp,"  Output.txt Closed In fnWndProc_OnClose()\n");
 fprintf(fp,"Leaving fnWndProc_OnClose()\n\n");
 fclose(fp);

 return 0;
}


void AttachEventHandlers(void)         //This procedure maps windows messages to the
{                                      //procedure which handles them.
 EventHandler[0].Code=WM_CREATE,       EventHandler[0].fnPtr=fnWndProc_OnCreate;
 EventHandler[1].Code=WM_LBUTTONDOWN,  EventHandler[1].fnPtr=fnWndProc_OnLButtonDown;
 EventHandler[2].Code=WM_CLOSE,        EventHandler[2].fnPtr=fnWndProc_OnClose;
}


long __stdcall fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam,LPARAM lParam)
{
 WndEventArgs Wea;                  //This procedure loops through the EVENTHANDER array
                                    //of structs to try to make a match with the msg parameter
 for(unsigned int i=0; i<3; i++)    //of the WndProc.  If a match is made the event handling
 {                                  //procedure is called through a function pointer -
     if(EventHandler[i].Code==msg)  //(EventHandler[i].fnPtr).  If no match is found the
     {                              //msg is passed onto DefWindowProc().
        Wea.hWnd=hwnd, Wea.lParam=lParam, Wea.wParam=wParam;
        return (*EventHandler[i].fnPtr)(&Wea);
     }
 }

 return (DefWindowProc(hwnd, msg, wParam, lParam));
}


int __stdcall WinMain(HINSTANCE hIns, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
 char szClassName[]="Form1";
 WNDCLASSEX wc;
 MSG messages;
 HWND hWnd;

 AttachEventHandlers();
 wc.lpszClassName=szClassName;                          wc.lpfnWndProc=fnWndProc;
 wc.cbSize=sizeof (WNDCLASSEX);                         wc.style=CS_VREDRAW|CS_HREDRAW;
 wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);               wc.hInstance=hIns;
 wc.hIconSm=LoadIcon(NULL, IDI_APPLICATION);            wc.hCursor=LoadCursor(NULL,IDC_ARROW);
 wc.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);  wc.cbWndExtra=0;
 wc.lpszMenuName=NULL;                                  wc.cbClsExtra=0;
 RegisterClassEx(&wc);
 hWnd=CreateWindowEx(0,szClassName,szClassName,WS_OVERLAPPEDWINDOW,100,100,350,300,HWND_DESKTOP,0,hIns,0);
 ShowWindow(hWnd,iShow);
 while(GetMessage(&messages,NULL,0,0))
 {
    TranslateMessage(&messages);
    DispatchMessage(&messages);
 }

 return messages.wParam;
}

I added a WM_LBUTTONDOWN handler that causes the desktop to redraw and that is working because you can see the screen flicker. However, I'm not picking anything up in the subclass proc because the call to set it is failing (Windows won't let me do it).

Unless I've got some dumb error I missed that is causing my subclass of the desktop to fail, my guess is that Windows won't allow it. However, if you are just using the desktop as an example and really have some other user created (as opposed to Windows system window) window in mind, the technique might work. However, the best you could hope for I guess would be the update region.

I will try it. Sorry for delay... I have to study for university exams.. :|. I have a question. Let us say we can solve this issue with the desktop. If I have a window above the desktop, it will be detected. But if I have a window above that window... no changed rectangle of the desktop will be detected? cause it's not directly drawn to desktop, it would be drawn to that window.
Thanks.

I'm glad you're off the DESKTOP Window theme. That made me uneasy. I kind of figgured it was a 'special' window where speciial rules might apply, and my tests seemed to bear that out. What other coders do I'm not sure, but I never mess with the desktop.

In terms of getting handles to other windows, the various Enum functions are useful for that, e.g., EnumChildWindows, EnumWindows, etc.

So your answer is.... I any window above another window and not directly to the desktop will not trigger the desktop window paint window? (will not be detected?). If so... is there any solution? any idea? thank you.

In fact I am working at a desktop remote application and I need to optimize the transfer of the images from server to client and I thought the applications like TeamViewer just send the invalidated regions to the client so thats the key of high refresh rate and speed. That's why I need this. I tested your program and see the desktop and child windows flicker. (or just the invalidated regions were flickering?). So now I told you why I need the "updated" rectagles from the screen. Maybe you have a better idea... thanks however.

I expect what you are attempting to do does not involve a process created by your program, so my idea about subclassing won't work. I found this as the 1st line in the remarks for SetWindowLong()...

Remarks

The SetWindowLong function fails if the window specified by the hWnd parameter does not belong to the same process as the calling thread.

In the example I posted, while I was able to get the hWnd of the desktop, Windows would not let me subclass it and returned an "ACCESS VIOLATION" error at my attempt. I tend to doubt Windows will allow you to intercept WM_PAINT messages for any Window not in your process.

I've no experience with the software you are describing; never even heard of it. Nor have I ever tried anything like you are describing. However, if you have an application with various Windows, and its one of those, i.e., your windows in your process, that you want to obtain the update region for, then I'd say that's doable. However, if you are wanting to get the update region for some window not in your process, such as the Windows desktop, well, I don't know how to do that. Maybe someone else knows.

I just looked at MSDN for GetUpdateRect(). It doesn't say anything about the window having to be in your process, so right now I'm thinking that if you can get the hWnd of the window you want the update rectangle for, you could get it with the above described function. Getting the update region is probably not viewed by Windows as being nearly as invasive as hooking it as I was trying to do. Maybe later if I have time I'll try to modify my program to see if I can get it to work that way. Might want to check out GetUpdateRgn(). I havn't looked at it, but it sounds promising.

Just one more thought. Assumming GetUpdateRect() works, the only context in which I can see it being used for something like you want is to launch a seperate thread of execution that polls with the function so that you'll get a continuous reading of update regions for whatever purpose you need them. If you had written the app which will be doing the screen updating, then I'd say either broadcast a custom message to other apps, or signal a mutex which could be WaitForSingleObject()'ed on. But I'm not sure you have a cooperating object in mind.

Basically I need the coordonates of the region that Windows paints on screen because I'm going to screenshot that area, make an image of it then send at the client. Let us say I would like to see your screen in my application. The server sends me your screen by taking screenshots and transfer them over a socket connection. Instead of sending a lot of fullscreen images I would like to send only what changed on your screen, only that part of the image. So it would be much smaller image and faster transfer, faster refresh rate. Hope I was clear enough :). Thanks

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.