当对话框失效时如何调试/跟踪?

发布于 2024-08-14 20:52:43 字数 214 浏览 4 评论 0原文

我的 MFC 应用程序有一个 CView 和几个浮动非模式对话框。我目前正在尝试弄清楚为什么我的视图的失效/重画也会导致对话框被重新绘制。如果对话框不与视图重叠,甚至会发生这种情况。

有人知道如何调试/跟踪请求特定的对话框重画吗?拦截对话框上的 WM_PAINT 消息似乎为时已晚。

预先感谢您的任何帮助!

最美好的祝愿,

法比安

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

压抑⊿情绪 2024-08-21 20:52:43

我认为您的非模式对话框是 WS_POPUP 窗口,对吗? (即它们是浮动窗口,可以出现在屏幕上的任何位置,而不仅仅是出现在应用程序窗口内。)。

如果它们是浮动窗口,那么从技术上讲,它们不是框架窗口的子窗口,但文档倾向于在真正应该使用“所有者”的地方使用术语“父级”,因此这很容易让人困惑。子窗口不能出现在其父窗口的客户区之外,拥有的窗口可以。当父窗口失效时,子窗口也将失效。但拥有的窗口则不会。

只有具有 WS_CHILD 样式标志的窗口才能是子窗口,否则它们是拥有的窗口。

如果这些是自有窗口,它们不会从主应用程序窗口继承失效,因此您需要在代码中查找以 NULL 句柄作为第一个参数调用 ::InvalidateRect() 的位置。通常这是由于未初始化的变量造成的。

当您调用 ::InvalidateRect(NULL, ...) 时,这会告诉 Windows 使所有窗口无效。 (它实际上告诉 Windows 使桌面窗口无效,桌面窗口是所有窗口的父窗口)。对于 MFC,所有窗口的基类都有一个 InvalidateRect 方法,该方法反过来调用 Windows api,但使用当前对象的窗口句柄。作为一阶近似,我认为您可以假设该窗口句柄将被正确初始化。您应该首先在自己的代码中查找调用。

一旦获得 OnPaint,就无法知道失效请求来自哪里了。因此,为了找到这个错误,您必须检查代码或拦截 InvalidateRect() 并在第一个参数中查找 NULL。

在一般情况下,这是一个难题,因为有多种方法可以使整个或部分窗口无效,并且头文件中的大量代码可以通过创建此函数的变体来自动执行某些操作来“帮助”您。

您可以尝试在 InvalidateRect 顶部设置一个断点(位于 user32.dll 中),并以第一个参数为 null 为条件。但根据调试器的设置方式,您可能不被允许执行此操作。

您还可以尝试强制编译所有代码,以便通过您控制的函数重定向对 InvalidateRect 的调用。

// 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
};

如果找不到,则对 InvalidateRgnRedrawWindow 执行相同的操作,

这些类型的错误很难找到。我不羡慕你。在我自己的代码中,我永久禁止直接调用 InvalidateRect,它们必须始终通过包装函数,以便我始终可以在调试版本中检查 NULL 窗口句柄。但话又说回来,我不使用 MFC,因此执行此类策略更容易。

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.

↙厌世 2024-08-21 20:52:43

看看这个:

http://msdn.microsoft .com/en-us/library/01c9aaty(VS.80).aspx

您可能可以重写 OnPaint 并找出消息的来源...

编辑:

当 Windows 时,框架调用此成员函数或者应用程序请求重新绘制应用程序窗口的一部分。

因此,要么操作系统正在请求您重绘,要么它是在您的应用程序内部完成的。

另请查看此论坛主题的第一个答案:
http://social.msdn。 microsoft.com/Forums/en/vcgeneral/thread/3f53fce3-38dd-441b-b112-82eff4dafc9e

Have a look at this:

http://msdn.microsoft.com/en-us/library/01c9aaty(VS.80).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

皓月长歌 2024-08-21 20:52:43

我会以与在 MFC 中调试任何我不太理解的内容相同的方式来执行此操作 - 我尝试通过在全新项目中重现行为来隔离问题。

因此,创建一个全新的 MFC 应用程序,向其中添加一个无模式对话框,在 CView 上调用 Invalidate 并查看它是否仍然发生。

如果它仍然发生,那么一定是您的主机发送了该绘制消息。那么您可以尝试在大型机的 PreTranslateMessage 中捕获它。

如果没有发生,则说明您在应用程序中执行的某些操作导致了该情况。如果是这样,您可以尝试通过消除应用程序的复杂性来弄清楚您所做的事情,直到您找到导致它不再发生的事情。

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.

如果没结果 2024-08-21 20:52:43

对话框也接收 WM_PAINT 消息的原因是它们是 CView 的子窗口,正如 Karim 已经建议的那样。

最有可能发生的情况是,在导致视图重绘的失效之后,WM_PAINT 消息被发送到此窗口,并且此窗口的标准 OnPaint() 处理程序(CWnd 的成员)发送 WM_PAINT 消息发送到其子窗口(对话框)。

WM_PAINT 消息本身由 Windows 发送,以响应对 UpdateWindow()RedrawWindow() 的调用,请参阅MSDN

WM_PAINT消息是由
系统,不应由
一个应用程序。

最接近消息处理机制背后的场景是重写派生自 CWnd 的 CView 类的 WindowProc。这是接收 WM_PAINT 和任何其他消息的回调。

干杯
霍尔格

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文