在Delphi 2010中,尽管禁用了相应的按钮,但仍会处理Click事件

时间:2013-07-11 07:18:21

标签: delphi winapi delphi-2010

考虑在给定按钮的OnClick事件中执行的以下代码:

procedure TForm1.Button1Click(Sender: TObject);
begin
  button1.enabled := false;    //Line 1  
  application.processmessages; //Line 2  
  Sleep(3000);                 //Line 3
  button1.enabled := True;     //Line 4
  Release;                     //Line 5
end;

在Delphi 2010中,如果单击此按钮后您又设法执行另一个按钮 在第3行执行繁忙时单击它,随后单击 事件显然会存储在命令队列中,因此 调用Release(第5行)程序,应用程序将尝试处理 它。因此,将再次触发点击事件。第二次 周围,​​按钮组件已经被破坏,因此“访问违规”错误被提出。

整个概念确认系统在第二次点击时各自 按钮被禁用似乎没有声音。对这种阴暗行为的任何解释?

2 个答案:

答案 0 :(得分:4)

系统的行为与设计完全一致,但要注意您的代码违反所有声音设计原则。具体而言,在输入事件处理程序中使用SleepProcessMessages都是不受欢迎的。

程序以这种方式运行的原因如下:

  1. 用户通过单击鼠标生成输入消息。
  2. 此输入事件放在相应线程的输入队列中。
  3. 该线程没有为其输入队列提供服务(它处于休眠状态),所以输入消息(鼠标按下,鼠标按下组合)就在那里。
  4. 线程唤醒并启用按钮。
  5. 按钮OnClick处理程序返回,应用程序的消息循环继续。
  6. 在适当的时候处理鼠标按下和鼠标按下消息(在CM_RELEASE消息之前),因此按钮OnClick处理程序再次运行。
  7. 按钮OnClick处理程序调用{​​{1}}然后处理ProcessMessages并杀死表单。
  8. BOOM!
  9.   

    当相应按钮被禁用时,确认系统第二次点击的整个概念似乎不合理。

    关键是当输入消息处理时检查按钮的启用状态,而不是输入消息生成时检查。它必须是这种方式,因为输入消息是非常低级的东西,并且只有应用程序可以将它们解释为按钮点击之类的东西。

    有很多方法可以修复你的代码,但我不愿意提出任何建议,因为这显然是用于说明的代码。但我会说所有声音解决方案都将涉及删除对CM_RELEASE和`ProcessMessages的调用。

答案 1 :(得分:0)

在睡眠期间,您的应用程序无响应。单击消息排队,仅在重新启用按钮后处理(实际上在事件处理程序方法完全执行且应用程序再次空闲之后)。

要解决此问题,请在启用睡眠后执行Application.ProcessMessages,然后再启用该按钮。这将首先清空您的邮件队列,并将丢弃单击邮件。

或者根本就不要启用按钮。如果你打算发布表格,为什么会这样呢?

一个(可能)更好的解决方案是在一个单独的线程中执行Sleep,但由于这里的Sleep可能只是一些实际代码的存根,所以很难说它需要付出多少努力才能做到这一点。

无论如何,你当前的应用程序并不好,在这种情况下调用Application.ProcessMessages很可能会偶尔产生“随机”错误。你能做的最好的事情就是限制风险,但除了从根本上改变这种实现之外,没有好办法解决它。