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.