views:

204

answers:

4

Hi,

My MFC application has a CView and a couple of floating non-modal dialogs. I am currently trying to figure why an invalidation/repaint of my view also causes the dialogs to be redrawn. This even happens if the dialogs are not overlapping with the view.

Does anybody know how to debug/track who request a specific dialog redraw? Intercepting the WM_PAINT message on the dialog seems to be too late.

Thanks in advance for any help!

Best wishes,

Fabian

A: 

Have a look at this:

http://msdn.microsoft.com/en-us/library/01c9aaty%28VS.80%29.aspx

You could probably override OnPaint and find out where the message comes from...

EDIT:

The framework calls this member function when Windows or an application makes a request to repaint a portion of an application's window.

So either the OS is requesting your redraw or it is done from within your application.

Also have a look at the first answer on this forum thread: http://social.msdn.microsoft.com/Forums/en/vcgeneral/thread/3f53fce3-38dd-441b-b112-82eff4dafc9e

Tony
Thanks for the link. Maybe a stupid question, but how do I find out where the message comes from? The MSG structure doesn't seem to have this info.
Fabian
Additional explanation: The OnPaint call is initiated by the message queue. Thus this call is too late for giving me any information (at least according to my understanding).
Fabian
Thanks for the edit. Let me be more precise. I know why WM_PAINT is triggered. But how do I figure out WHO initially caused the repaint - not the WM_PAINT message (that's done by windows) but the invalidation that tells Windows that a WM_PAINT should be sent once the message queue is empty?
Fabian
This doesn't work. WM_PAINT messages are never in the queue, it's just a flag that is set by InvalidateRect/InvalidateRgn/RedrawWindow and looked at when the queue is empty or when someone calls UpdateWindow. By the time the paint message happens, it's too late to know who did the invalidate.
John Knoeller
A: 

I would do it the same way as I debug anything in MFC I don't quite understand -- I try to isolate the problem by reproducing the behaviour in a brand new project.

So make a brand new MFC application, add one modeless dialog to it, call Invalidate on your CView and see if it still occurs.

If it does still occur, then it's got to be your mainframe sending that paint message. So then you could try to capture that in your mainframe's PreTranslateMessage.

If it doesn't occur, then something you've done in your application is causing it. If so you could try to work out what you've done by removing the complexities out of your application, until you find the thing that causes it to not occur anymore.

demoncodemonkey
A: 

Hi Fabian,

the reason why the WM_PAINT message is received by the dialogs as well is that they are child windows of the CView as already suggested by Karim.

Most probably what happens is that after the invalidation which causes a redraw of your view a WM_PAINT message is sent to this window and the standard OnPaint() handler of this window (member of CWnd) sends WM_PAINT messages to its child windows (the dialogs).

The WM_PAINT message itself is send by windows in reaction to either a call to UpdateWindow() or RedrawWindow(), see MSDN

The WM_PAINT message is generated by the system and should not be sent by an application.

The closest you can get to the scene behind the message handling mechanism is to override the WindowProc of the CView class which is derived from CWnd. This is the callback where WM_PAINT and any other message is received.

Cheers Holger

Holger Kretzschmar
+2  A: 

I presume that your non-modal dialogs are WS_POPUP windows, right? (i.e. they are floating windows that can appear anywhere on the screen, not just inside your app window.).

If they are floating windows then technically they aren't child windows of your frame window, but the documentation tends to use the term parent in places where it should really use owner so this is easy to get confused about. Child windows can't appear outside the client area of their parent, owned windows can. A child window will be invalidated whenever its parent window is invalidated. But an owned window will not.

Only windows with the WS_CHILD style flag can be child windows, otherwise they are owned windows.

If these are owned windows, they won't inherit invalidation from your main app window, so you need to look for places in your code where there is a call ::InvalidateRect() with a NULL handle as the first parameter. Usually this will be because of an uninitialized variable.

When you call ::InvalidateRect(NULL, ...) this tells Windows to invalidate ALL windows. (it actually tells Windows to invalidate the desktop window, which is the parent of all windows). With MFC, the base class for all windows has a method for InvalidateRect that turns around and calls the windows api but with the window handle of the current object. as a first order approximation, I think you can assume that THAT window handle will be initialized correctly. You should start by looking for calls in your own code.

Once you get the OnPaint, its too late to know where the invalidation request came from. So in order to find this bug, you are going to have to either inspect your code or intercept InvalidateRect() and look for NULL in the first param.

This is a HARD problem in the general case, because there are several ways to invalidate all or part of a window, and lots of code in header files to 'help' you by creating variants of this functions that do some things automatically.

You might try to set a breakpoint at the top of InvalidateRect (it's in user32.dll) and make that conditional on the first parameter being null. But depending on how your debugger is setup, you may not be allowed to do this.

You could also try and force all of your code to compile so that calls to InvalidateRect are redirected through a function that you control.

// in some header file that gets included early by all of your code
#define InvalidateRect my_InvalidateRect

// in one of your .cpp files.
BOOL WINAPI my_InvalidateRect(HWND hwnd, CONST RECT *prc, BOOL bErase)
{
  #undef InvalidateRect
  assert(hwnd != NULL);
  InvalidateRect(hwnd, prc, bErase);
  #define InvalidateRect my_InvalidateRect
};

If that doesn't find it, then do the same thing to InvalidateRgn and RedrawWindow

These kinds of bugs are nasty to find. I don't envy you. In my own code I have a permanent ban on direct calls to InvalidateRect, they must ALWAYS pass through a wrapper function so I can always check for NULL window handles in debug builds. But then again, I don't use MFC so enforcing this sort of policy is easier.

John Knoeller
Excellent answer, thanks. I will follow your suggestion and hopefully track down the problem.
Fabian