Rob*_*ler 4 delphi vcl subclass wndproc message-queue
我有一个严重多线程的Delphi 6应用程序.我有一个我创建的组件,它来自TWinControl.当我第一次构建它时,我使用了一个隐藏窗口,它是WndProc来处理使用AllocateHwnd()分配的消息.最近我开始在我的代码中清理WndProc并决定删除辅助WndProc().我更改了组件以覆盖基类WndProc()方法,并从那里进行自定义窗口消息处理.在那个WndProc()中,我首先调用了继承的处理程序,然后处理了我的自定义消息(WM_USER偏移),如果找到我的一个自定义消息并将其处理,则将消息Result字段设置为1.
一个重要的说明.我在WndProc()覆盖的顶部放置了一行代码,如果当前线程id不是VCL主线程,则抛出异常.我想确保WndProc()仅在主VCL线程的上下文中执行.
在完成这个并运行我的程序后,我遇到了一些看起来很奇怪的东西.我正常运行我的程序并完成各种任务而没有错误.然后,当我转到与我的TWinControl后代位于同一页面的TMemo控件时.如果我在TMemo控件内部单击,则在我的WndProc()覆盖中触发主线程检查.我设置了断点,当我进入调用堆栈时,我的WndProc()覆盖上方没有任何内容.
据我所知,我已经仔细检查过,我没有明确调用WndProc()覆盖.那不是我做过的事.但鉴于我的TWinControl组件已经在主VCL线程上创建,就像所有其他组件一样,我无法理解WndProc()覆盖如何在后台线程的上下文中执行,尤其是在像UI这样的UI操作时鼠标点击会发生.我理解我的WndProc()是如何绑定到TMemo控件的,因为所有子窗口都挂在顶级窗口WndProc()上,至少这是我的理解.但由于所有组件窗口都是在主VCL线程上创建的,因此它们的所有消息队列也应该在该上下文中执行,对吧?
那么我可以创建什么样的情况来使我的WndProc()运行,有时只在后台线程的上下文中运行?
可以通过两种方式WndProc()在工作线程的上下文中调用主线程组件的方法:
工作线程直接调用组件的WindowProc属性或其Perform()方法.
工作线程通过不安全的TWinControl.Handle财产使用窃取了组件窗口的所有权.该Handle属性的getter不是线程安全的.如果工作线程Handle在主线程重新创建组件窗口的同一时刻从属性读取(TWinControl窗口不是持久的 - 各种运行时条件可以动态地重新创建它们而不影响大部分UI逻辑),那么就存在一个竞赛条件可能允许工作线程在其自己的上下文中分配新窗口(并导致主线程泄漏另一个窗口).这将导致主线程停止在其上下文中接收和分派消息.如果工作线程有自己的消息循环,那么它将接收和分派消息,从而WndProc()在错误的线程上下文中调用该方法.
但我觉得奇怪的是没有生成调用堆栈.应始终存在某种痕迹.
此外,确保MainThreadId变量(或用于跟踪主线程的任何内容)不会简单地被意外损坏.确保其当前值与启动时的初始值一致.
您应该做的另一件事是在调试器中命名所有线程实例(此功能是在Delphi 6中引入的).这样,当您的线程验证被触发时,调试器可以显示调用您的WndProc()方法的线程上下文的确切名称(即使没有调用堆栈跟踪),然后您可以查找该线程的代码中的错误.