【发布时间】:2018-11-22 06:52:47
【问题描述】:
我有一个 VB6 ActiveX EXE,它具有它使用的 OCX 控件的事件处理程序。在 ActiveX 的终止函数中,我需要确保所有事件都已完成处理,然后才允许终止完成。在终止函数中,我在开头添加了以下代码:
While EventsInProgressFlags <> 0
DoEvents
Sleep(200)
WEnd
EventsInProgressFlags 是一个整数,我用它来设置正在进行的事件。但是我观察到的是,当这个循环运行时,正在进行的事件永远不会完成。通过向事件和终止例程添加许多日志消息,我可以看到在调用 Terminate 函数时事件处理正在进行中,但即使我正在调用 DoEvents,这些正在进行的事件在 Terminate 函数处于活动状态时也不会执行.如果我强制 Terminate 函数退出循环,那么事件处理会从它停止的地方继续,但当然会出现错误,因为 Terminate 已经破坏了事件使用的组件。
在我的睡眠循环中有没有办法让正在进行的事件代码完成执行?
================2018 年 11 月 20 日更新===================
经过一些在线研究,我相信我更好地理解了原因,但还没有解决方案。
阅读Multithreading in VB6 后,我了解到 VB6 是单线程的,所以是的,我的 OCX 事件处理和 Terminate 函数都在同一个线程中。所以这就是为什么让 Terminate 函数进入睡眠状态不起作用的原因。因为它们都在同一个 VB6 线程中,所以我也将事件处理置于睡眠状态。我想知道我的 ActiveX Terminate 函数是如何在我的事件处理代码中间被调用的。我的理论是,由于我的事件处理在进行系统调用时执行 VB6 的系统调用,因此除了执行系统调用之外,它还调用 DoEvents 来为 Windows 事件队列提供服务。这样做是为了使 UI 更具响应性。调用 DoEvents 然后为 ActiveX Terminate Call 处理传入的 Windows 事件。如果一些 VB6 大师可以在这里证实我的结论,我会很感兴趣。
因此,一种可能的解决方案是,在我处理 OCX 事件时,是否有某种方法可以阻止系统调用 DoEvents。有谁知道这样做的方法吗?
另一种可能的解决方案是,如果有一种方法可以从 Terminate 函数返回,以便事件处理可以继续,但在事件处理完成之前不会将 Terminate 函数的响应发送到主机。不过,这听起来不太合理。
【问题讨论】:
-
我从来没有遇到过这种情况,也没有调查过,但我预计发生的事情是在您的终止事件触发之前,ocx 的事件处理程序已经被取消挂钩。如果是这样,则您无法在终止事件中执行任何操作来允许 ocx 事件运行。最好的办法可能是在所有预期的 ocx 事件完成之前,不要在主应用程序中释放(取消实例化)ActiveX exe(因此不会导致 ocx 事件处理程序解除挂钩并触发终止事件)。
-
DoEvents调用 API 的Sleep(0)。 Sleep 函数将当前线程的执行暂停指定的时间间隔。 MSDN. -
@MarkL 您的结论与我的观察不符。负责销毁表单的是 ActiveX EXE 的 API Terminate 命令,因此我的睡眠等待循环阻止了对关联表单和 ActiveX EXE COM 对象的任何破坏。我还知道(从日志消息中)正在进行的事件在 Terminate 命令从主机进入时已经在执行。主机不在我的控制之下,所以当终止呼叫发生时我无法更改。我能看到的唯一选择是一旦调用终止以延迟它,直到其他事件完成。
-
但是睡眠循环似乎没有释放执行线程以允许已经在进行的事件处理完成。我有兴趣了解与 vB6 中处理事件相关的线程。正在进行的事件由 OCX 控件触发,然后生成一些软件事件。我可以从日志中看到 OCX 硬件事件和相关的软件事件在 ActiveX EXE 时已经启动
-
Terminate 被调用,但事件处理的所有执行都会暂停,直到我从 Terminate 函数返回。在 VB6 中,睡眠(来自 kernel32)是否不会释放当前线程的执行,并且来自 OCX 的事件处理是在与 ActiveX EXE 主事件处理循环不同的线程或相同的线程中执行的吗?