delphi应用程序中application.processmessages的黑暗面

Marcus Junglas提交的文章...

Marcus Junglas提交的文章

Application.ProcessMessages Test Application.ProcessMessages Test

在Delphi中编写事件处理程序(如TButton的OnClick事件)时,有时应用程序需要忙碌一段时间,例如,代码需要编写一个大文件或压缩一些数据。

如果这样做,您会注意到您的应用程序似乎被锁定。您的表单无法再移动,并且按钮没有显示任何生命迹象。它好像被撞坏了。

原因是Delpi应用程序是单线程的。您正在编写的代码只表示一组过程,每当发生事件时,Delphi的主线程都会调用这些过程。剩下的时间,主线程处理系统消息和其他事情,比如表单和组件处理函数。

因此,如果您没有通过做一些冗长的工作来完成事件处理,您将阻止应用程序处理这些消息。

此类问题的常见解决方案是调用“Application.ProcessMessages”。“应用程序”是TApplication类的全局对象。

Processmessages处理所有等待的消息,如窗口移动、按钮单击等。它通常被用作一种简单的解决方案,以保持应用程序“正常工作”。

不幸的是,“ProcessMessages”背后的机制有其自身的特点,这可能会引起很大的混乱!

什么是消息?

PprocessMessages处理应用程序消息队列中所有等待的系统消息。Windows使用消息与所有正在运行的应用程序“对话”。用户交互通过消息引入表单,“ProcessMessages”处理它们。

例如,如果鼠标落在某个TButton上,ProgressMessages会执行此事件中应该发生的所有操作,例如将按钮重新绘制为“按下”状态,当然,如果您指定了一个处理过程,还会调用OnClick()处理过程。

这就是问题所在:对ProcessMessages的任何调用都可能再次包含对任何事件处理程序的递归调用。下面是一个例子:

对按钮的OnClick偶数处理程序(“work”)使用以下代码。for语句模拟一个长的处理作业,不时调用ProcessMessages。

为了更好的可读性,简化了此操作:

{in MyForm:} WorkLevel : integer; {OnCreate:} WorkLevel := 0; procedure TForm1.WorkBtnClick(Sender: TObject) ; var cycle : integer; begin inc(WorkLevel) ; for cycle := 1 to 5 do begin Memo1.Lines.Add('- Work ' + IntToStr(WorkLevel) + ', Cycle ' + IntToStr(cycle) ; Application.ProcessMessages; sleep(1000) ; // or some other work end; Memo1.Lines.Add('Work ' + IntToStr(WorkLevel) + ' ended.') ; dec(WorkLevel) ; end;

如果在短时间内按下按钮两次,则在没有“ProcessMessages”的情况下,将以下行写入备忘录:

- Work 1, Cycle 1 - Work 1, Cycle 2 - Work 1, Cycle 3 - Work 1, Cycle 4 - Work 1, Cycle 5 Work 1 ended. - Work 1, Cycle 1 - Work 1, Cycle 2 - Work 1, Cycle 3 - Work 1, Cycle 4 - Work 1, Cycle 5 Work 1 ended.

当过程繁忙时,表单不会显示任何反应,但第二次单击是由Windows放入消息队列的。在“OnClick”完成后,将再次调用它。

包括“ProcessMessages”,输出可能会非常不同:

- Work 1, Cycle 1 - Work 1, Cycle 2 - Work 1, Cycle 3 - Work 2, Cycle 1 - Work 2, Cycle 2 - Work 2, Cycle 3 - Work 2, Cycle 4 - Work 2, Cycle 5 Work 2 ended. - Work 1, Cycle 4 - Work 1, Cycle 5 Work 1 ended.

这一次,表单似乎再次工作,并接受任何用户交互。因此,在您的第一次“工作者”功能再次执行时,按钮被按下一半,这将立即得到处理。所有传入事件的处理方式与任何其他函数调用相同。

理论上,在每次调用“ProgressMessages”期间,任何数量的点击和用户消息都可能“就地”发生。

所以要小心你的代码!

不同的示例(在简单的伪代码中!):

procedure OnClickFileWrite() ; var myfile := TFileStream; begin myfile := TFileStream.create('myOutput.txt') ; try while BytesReady > 0 do begin myfile.Write(DataBlock) ; dec(BytesReady,sizeof(DataBlock)) ; DataBlock[2] := #13; {test line 1} Application.ProcessMessages; DataBlock[2] := #13; {test line 2} end; finally myfile.free; end; end;

此函数写入大量数据,并在每次写入数据块时使用“ProcessMessages”尝试“解锁”应用程序。

如果用户再次单击该按钮,则在文件仍在写入时将执行相同的代码。因此,无法再次打开该文件,并且该过程失败。

也许你的应用程序会做一些错误恢复,比如释放缓冲区。

可能的结果是,“数据块”将被释放,第一个代码在访问它时“突然”引发“访问冲突”。在这种情况下:测试线1将工作,测试线2将崩溃。

更好的方法是:

为了方便起见,您可以将整个表单设置为“enabled:=false”,这会阻止所有用户输入,但不会向用户显示(所有按钮都不是灰色的)。

更好的方法是将所有按钮设置为“禁用”,但如果您想保留一个“取消”按钮,这可能会很复杂。您还需要检查所有组件以禁用它们,当它们再次启用时,您需要检查是否还有一些组件处于禁用状态。

当启用的属性更改时,可以禁用容器子控件。

正如类名“TNotifyEvent”所示,它应该只用于对事件的短期反应。对于耗时的代码,最好的方法是将所有“慢”代码放到自己的线程中。

关于“进程消息”和/或组件的启用和禁用问题,第二个线程的使用似乎一点也不复杂。

请记住,即使是简单而快速的代码行也可能会挂起几秒钟,例如,打开光盘驱动器上的文件可能需要等待驱动器启动完成。如果您的应用程序因为驱动器太慢而出现崩溃,这看起来不是很好。

就这样。下次添加“Application.ProcessMessages”时,请三思而后行;)

  • 发表于 2021-10-13 10:32
  • 阅读 ( 135 )
  • 分类:数学

你可能感兴趣的文章

用delphi创建、解析和操作xml文档

...数据的通用语言。XML使开发人员能够将结构化数据从各种应用程序传递到桌面,以进行本地计算和表示。XML也是服务器到服务器传输结构化数据的理想格式。软件使用XML解析器评估文档的层次结构,提取文档的结构、内容或两者...

  • 发布于 2021-09-06 15:05
  • 阅读 ( 166 )

如何将媒体文件嵌入delphi可执行文件(rc/.res)(embed media files into a delphi executable (rc/.res))

使用声音和动画等多媒体文件的游戏和其他类型的应用程序必须随应用程序一起分发额外的多媒体文件,或者将文件嵌入可执行文件中。 您可以将原始数据作为资源添加到应用程序中,而不是分发单独的文件供应用程序使用...

  • 发布于 2021-09-06 15:08
  • 阅读 ( 195 )

delphi编译器版本指令

...NUX MSWINDOWS-表示操作环境为MS Windows/li] CONSOLE—表示正在将应用程序编译为CONSOLE应用程序 通过了解上述符号,就可以通过使用编译器指令为每个版本编译适当的源代码来编写与多个版本的Delphi协同工作的代码。 注:例如,符...

  • 发布于 2021-09-06 15:09
  • 阅读 ( 164 )

了解delphi编程的基础知识

...Turbo)Pascal到Delphi 2005的演变,例如Delphi演变为一个快速应用程序部署框架,旨在为在线和移动交付提供高性能、可扩展的应用程序。 之后,探索Delphi实际上是什么,以及如何安装和配置其开发环境。在此基础上,探索DelphiIDE...

  • 发布于 2021-09-06 15:13
  • 阅读 ( 185 )

理解delphi项目和单元源文件

简而言之,Delphi项目只是组成由Delphi创建的应用程序的文件集合。DPR是用于Delphi项目文件格式的文件扩展名,用于存储与项目相关的所有文件。这包括其他Delphi文件类型,如表单文件(DFM)和单元源文件(.PASs)。 由于Delphi...

  • 发布于 2021-09-06 15:15
  • 阅读 ( 175 )

程序退出时delphi中的内存泄漏通知

...更丰富。 “新”内存管理器最出色的功能之一是允许应用程序注册(和注销)预期内存泄漏,并在程序关闭时报告意外内存泄漏。 使用Delphi创建WIN32应用程序时,必须确保释放动态创建的所有对象(内存)。 当程序无法释...

  • 发布于 2021-09-06 15:15
  • 阅读 ( 168 )

德尔福单元剖析(德尔福初学者版)

...占有特殊的位置。 德尔福项目 当我们创建一个Delphi应用程序时,我们可以从一个空白项目、一个现有项目或Delphi的一个应用程序或表单模板开始。项目由创建目标应用程序所需的所有文件组成。 选择“查看项目管理器”...

  • 发布于 2021-09-10 20:53
  • 阅读 ( 181 )

如何构建没有gui的控制台应用程序(build console applications with no gui)

​控制台应用程序是纯32位Windows程序,在没有图形界面的情况下运行。启动控制台应用程序时,Windows会创建一个文本模式控制台窗口,用户可以通过该窗口与应用程序进行交互。这些应用程序通常不需要太多的用户输入。控制...

  • 发布于 2021-09-10 22:34
  • 阅读 ( 251 )

用delphi编写网络感知应用程序

...Delphi为支持通过网络(internet、intranet和local)交换数据的应用程序而提供的所有组件中,最常见的两个组件是TServerSocket和TClientSocket,这两个组件都旨在通过TCP/IP连接支持读写功能。 winsock和delphi套接字组件 Windows Sockets(Winso...

  • 发布于 2021-09-10 23:39
  • 阅读 ( 199 )

逆向工程(反编译)delphi应用程序

阅读全文,了解如何反编译Delphi应用程序。 相关的: 在Delphi程序的EXE中嵌入资源 动态链接库(DLL)和Delphi 免费源代码Delphi项目

  • 发布于 2021-09-11 18:00
  • 阅读 ( 178 )
b37874957
b37874957

0 篇文章

相关推荐