delphi - Delphi 6 TWinControl 后代的 WndProc() 有时如何在主 VCL 线程之外执行?

标签 delphi subclass message-queue vcl wndproc

我有一个高度多线程的 Delphi 6 应用程序。我创建了一个源自 TWinControl 的组件。当我第一次构建它时,我使用了一个隐藏窗口,它是 WndProc 来处理消息,并使用 AllocateHwnd() 进行分配。最近,我开始清理代码中的 WndProc,并决定删除辅助 WndProc()。我更改了组件以重写基类 WndProc() 方法,并从那里进行自定义 Windows 消息处理。在该 WndProc() 中,我首先调用继承的处理程序,然后处理我的自定义消息(WM_USER 偏移量),如果找到我的自定义消息之一并处理它,则将消息结果字段设置为 1。

一个重要的说明。我在 WndProc() 重写的顶部放置了一行代码,如果当前线程 id 不是 VCL 主线程,则该代码会抛出异常。我想确保 WndProc() 仅在主 VCL 线程的上下文中执行。

完成此操作并运行我的程序后,我遇到了一些看起来非常奇怪的事情。我正常运行我的程序并执行各种任务,没有出现错误。然后,当我转到与我的 TWinControl 后代位于同一页面上的 TMemo 控件时。如果我在 TMemo 控制内部单击,则会触发主线程检查我的 WndProc() 覆盖。我在上面设置了一个断点,当我进入调用堆栈时,在我的 WndProc() 覆盖之上没有任何内容。

据我所知,并且我已经仔细检查过,我没有对 WndProc() 重写进行显式调用。那不是我会做的事情。但考虑到我的 TWinControl 组件会像所有其他组件一样在主 VCL 线程上创建,我无法理解 WndProc() 重写如何在后台线程的上下文中执行,特别是只有当像这样的 UI 操作时鼠标点击就会发生。我了解我的 WndProc() 是如何与 TMemo 控件绑定(bind)的,因为所有子窗口都卡在顶级窗口 WndProc() 之外,至少这是我的理解。但由于所有组件窗口都是在主 VCL 线程上创建的,那么它们的所有消息队列也应该在该上下文中执行,对吗?

那么我可以创建什么样的情况来让我的 WndProc() 运行,并且只是有时在后台线程的上下文中运行?

最佳答案

在工作线程上下文中可以通过两种方式调用主线程组件的 WndProc() 方法:

  1. 工作线程直接调用组件的 WindowProc 属性或其 Perform() 方法。

  2. 工作线程通过不安全地使用 TWinControl.Handle 属性窃取了组件窗口的所有权。 Handle 属性 getter 不是线程安全的。如果工作线程在主线程重新创建组件窗口的同一时刻从 Handle 属性中读取数据(TWinControl 窗口不是持久的 - 各种运行时条件可以动态地重新创建它们不会影响大部分 UI 逻辑),然后存在竞争条件,可能允许工作线程在其自己的上下文中分配新窗口(并导致主线程泄漏另一个窗口)。这将导致主线程停止在其上下文中接收和发送消息。如果工作线程有自己的消息循环,那么它将接收和分派(dispatch)消息,从而在错误的线程上下文中调用 WndProc() 方法。

不过,我发现没有生成任何调用堆栈,这很奇怪。应该始终存在某种可用的跟踪。

此外,请确保 MainThreadId 变量(或用于跟踪主线程的任何变量)不会被意外损坏。确保其当前值与启动时的初始值一致。

您应该做的另一件事是在调试器中命名所有线程实例(此功能在 Delphi 6 中引入)。这样,当您的线程验证被触发时,调试器可以向您显示正在调用 WndProc() 方法的线程上下文的确切名称(即使没有调用堆栈跟踪),然后您可以查看查找该线程代码中的错误。

关于delphi - Delphi 6 TWinControl 后代的 WndProc() 有时如何在主 VCL 线程之外执行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9060045/

相关文章:

android - ui-threads messagequeue、looper 和 handler 类之间的行为

c - mq_send 和 msgsnd 的区别

c# - 如何使用 ASF Writer 更快地压缩 WMV?有什么提示吗?

mysql - Delphi - 将 TStringStream 保存到 SQL

go - 每个用户处理一条消息

java - 将子类对象存储为父类(super class)并稍后检索子类

C++ 遍历某个子类的列表

delphi - TIdBlockCipherIntercept 的等效服务器端组件是什么?

delphi - 如何处理遗留应用程序?

java - 从父类继承时如何更改其构造函数的一部分?