As the programmer in question has never been prepared to elaborate on
his position, I am curious as to how widespread may be the support for
his position. Anyone want to take a position and defend it?
William Meyer
Hmm... The only "problem" with Application.ProcessMessages is that it is
essentially a "yield" methodology that puts the main thread to sleep so
that the application can "process messages." With Win32 came the ability to
create multi-threaded programs, so performing a yield to allow processing
of other things is not as preferable as creating another thread. BUT, it
all depends on what you're doing. I don't really see a problem with
employing Application.ProcessMessages within a simple loop that will
iterate through its logic quickly. That means that the call to
ProcessMessages will occur frequently.
However, the weakness of Application.ProcessMessages becomes readily
apparent if you have multiple steps in a loop and one or more steps
requires a bit of time to execute. In that case, you have to wait awhile
before the application will yield to process messages. On the other hand,
putting the logic in a background thread will allow the application to
receive messages at any time (CPU time-slicing allowing, of course).
So I'm not sure why your colleague considers this to be "bad." It's useful
in some situations, but less efficient in others.
Brendan
ProcessMessages does a thing like
<as long as a message is found in the queue>
<Translate message>
<Dispatch message>
< no more messages -> exit >
The Win32 function PeekMessages is used for this
purpose.
I think, there is no real reason why someone
should not
use this technique, except that multi threading is
a better
'programming style'.
However, if there are no problems with
synchronizing are
exspected, for smaller taks it's much more
convinient
to use another 'message pump' than implementing a
background
thread.
roger
William Meyer schrieb:
>
> I have been told (repeatedly by one programmer) that
> Application.ProcessMessages is a Bad Thing. While I grant that it may be
> misused, I will also allow that there are cases in which even the
> infamous goto is the right thing to use.
>
> As the programmer in question has never been prepared to elaborate on
> his position, I am curious as to how widespread may be the support for
> his position. Anyone want to take a position and defend it?
>
> William Meyer
--
____________________________________
Roger Lunatschek
Mail : Roger.Lu...@t-online.de
Phone : +49 (0)241 9019-671
Fax : +49 (0)241 9019-896
>I have been told (repeatedly by one programmer) that
>Application.ProcessMessages is a Bad Thing. While I grant that it may be
>misused, I will also allow that there are cases in which even the
>infamous goto is the right thing to use.
>
>As the programmer in question has never been prepared to elaborate on
>his position, I am curious as to how widespread may be the support for
>his position. Anyone want to take a position and defend it?
There are situations where it can be harmful, especially if you have
not thought through the situation _completely_ (and how often do we do
that :-)
Say you have a timer ticking once every second.
This timer calls a routine which needs a long time to execute.
Without Application.ProcessMessages in this long routine, it is
allowed to take the time it needs - the next timer tick will be
discarded if the routine is not finished after one second.
But, enter App.PM and suddenly the timer can tick _while_ the routine
is running, and start it again. Mostly this will cause BIG problems.
In connection with DDE conversations, it is mandatory that you do not
initiate a new DDE conversation from inside the DDE event handlers.
Putting an App.PM here can make the crash inevitable!
You can also have problems with global variables (the long procedure
uses a global, does App.PM and suddenly the global has changed it's
value), if not correctly protected - yes, even without threads you can
get concurrency and reentrancy problems.
My advice is: Do not, ever, just put in an App.PM, to see what happens
(you might have a long time to wait before the problems show up), you
really have to analyze the program flow(s) first!
Only put it in, if you can make sure you are not risking any of the
above mentioned problems (and all the others which I don't remember /
know of).
In 32-bit land, you really should use threads instead (and get the
same problems, until you really understand the protection /
synchronisation mechanisms)
------------------------------------
Anders Isaksson, Sweden
BlockCAD: http://user.tninet.se/~hbh828t/proglego.htm
Gallery: http://user.tninet.se/~hbh828t/gallery.htm
>I have been told (repeatedly by one programmer) that
>Application.ProcessMessages is a Bad Thing.
As is often the case, your programmer friend is reciting dogma based
on an oversimplification of the facts.
It _is_ true that misusing Application.ProcessMessages is easy to do,
by calling Application.ProcessMessages in a tight loop without
yielding CPU time to other threads. But it is also true that there is
no other simple way for an application to maintain a responsive user
interface while waiting for some background operation to complete. (An
alternative, more complicated way is to do the background operation in
a secondary thread, and include two-way notifications between that
thread and the primary thread.)
Pose the following question to your programmer friend: An application
needs to perform some lengthy task, and while doing so it displays a
dialog box containing a progress bar and a Cancel button. Ask him how
to implement the progress bar and Cancel button functionality without
using Application.ProcessMessages (or the equivalent, using
PeekMessage, etc.). If he understands how to set up and use a
secondary thread, he will probably be able to come up with an
implementation using the alternative technique I mentioned above, but
the CPU consumption of that implementation will be no better than that
of a well-designed implementation using Application.ProcessMessages.
-Steve
>It _is_ true that misusing Application.ProcessMessages is easy to do,
>by calling Application.ProcessMessages in a tight loop without
>yielding CPU time to other threads. But it is also true that there is
>no other simple way for an application to maintain a responsive user
>interface while waiting for some background operation to complete. (An
>alternative, more complicated way is to do the background operation in
>a secondary thread, and include two-way notifications between that
>thread and the primary thread.)
PMJI: I can agree with that, but only to a point. Processing messages
(as opposed to Application.ProcessMessages) is probably the simplest
way to "enable" UI elements during some processing loop, but using
Application.ProcessMessages for that is often asking for trouble.
Here's a contrived example that shows what often ends up happening (in
my experience) when people use PM without understanding what it really
does:
unit PMMain;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
FStatusLabel: TLabel;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
var
x: integer;
begin
for x := 0 to 5000 do
begin
FStatusLabel.Caption := IntToStr(x);
Application.ProcessMessages;
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
Close;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
FStatusLabel := TLabel.Create(Self);
FStatusLabel.SetBounds(Button1.Left, Button1.Top + Button1.Height +
10, 50, 25);
FStatusLabel.Parent := Self;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
FStatusLabel.Free;
end;
end.
If you press Button1 and kick off the for loop, and then you press
Button2 before the loop ends, you'll get an access violation. Now,
obviously this is a very simplified example, and is easy to track down
and fix, but when you do something like this in a much larger and more
complex app it gets nasty very fast. The obvious solution (to me) in
this case is to use FStatusLabel.Update instead of PM because that's
what I really wanted to do -- get the label to catch up with its
pending paint message. But, this is the situation I probably see the
most where people toss in a call to PM.
>Pose the following question to your programmer friend: An application
>needs to perform some lengthy task, and while doing so it displays a
>dialog box containing a progress bar and a Cancel button. Ask him how
>to implement the progress bar and Cancel button functionality without
>using Application.ProcessMessages (or the equivalent, using
>PeekMessage, etc.).
The "or equivalent" is the right way in my experience, and PM is
wrong. It just brings in too much baggage. You have to make your app
aware that it may be re-entered while it is in this background task
processing. The "or equivalent" is what I try to do, where that
generally involves writing my own message loop and processing messages
only intended for the dialog box containing the progress bar and
cancel button. That way, only the UI that "knows" about the
background task can respond. I mean, I know if you show the status
dialog modally, ProcessMessages wouldn't cause this problem , but in
the "real world" it rarely works out that way. :) My point is that
sometimes it is OK to use ProcessMessages, but more often it
introduces problems you never dreamed of if you haven't done API level
coding (and sometimes even if you have).
The only true solution is to get yourself a good (or several good)
general purpose Windows programming books and learn about the gory
details of how Windows apps work well down deep. Then, make an
informed decisions about when to write a secondary message loop, when
calling ProcessMessages is appropriate, and, most of all, what
side-effects these could introduce into your code. I know, I know,
Delphi is for hiding these gory details so you don't have to worry
about them. Well, in this case, I don't think ProcessMessages hides
the gory details well enough and I haven't yet found any substitute
for fully understanding what it really does.
Regards,
Brad Stowers
Delphi Free Stuff -- http://www.delphifreestuff.com/
"If you're a professional Delphi programmer who hasn't read
the Object Pascal Language Guide, then you're not."
Precisely my thought, but as I work mostly on my own, I wanted to sample
opinions from the community, and see what views would turn up.
> It _is_ true that misusing Application.ProcessMessages is easy to do,
> by calling Application.ProcessMessages in a tight loop without
> yielding CPU time to other threads. But it is also true that there is
> no other simple way for an application to maintain a responsive user
> interface while waiting for some background operation to complete. (An
> alternative, more complicated way is to do the background operation in
> a secondary thread, and include two-way notifications between that
> thread and the primary thread.)
I have an old app in which a call to Application.ProcessMessages is made
from within a loop, and it seems as though the loop is not re-entered
until the termination condition is reached. It is difficult to get a
grip on this in a debug environment, and I still find it hard to credit,
but other evidence suggests it to be the case. I have no wish to rewrite
the rather complex chunk of code in which this is found, as the program
is in its twilight existence, but it makes me nervous when I have to
work on code in that area.
> Pose the following question to your programmer friend: An application
> needs to perform some lengthy task, and while doing so it displays a
> dialog box containing a progress bar and a Cancel button. Ask him how
> to implement the progress bar and Cancel button functionality without
> using Application.ProcessMessages (or the equivalent, using
> PeekMessage, etc.). If he understands how to set up and use a
> secondary thread, he will probably be able to come up with an
> implementation using the alternative technique I mentioned above, but
> the CPU consumption of that implementation will be no better than that
> of a well-designed implementation using Application.ProcessMessages.
He is difficult to converse with, and language problems intrude (he is
German), but I am comfortable from what I have seen of his code that he
would insist upon a threaded solution. My own view is that this
overlooks the issue of thread overhead, which is as foolish as
indiscriminately using Application.ProcessMessages, IMHO.
William Meyer
I agree that using App.PM indiscriminately could lead to problems, and
that they would be difficult to debug. But I don't see why it shouldn't
be used judiciously. The comments I had received from this guy seemed
arbitrary and closed minded.
> In 32-bit land, you really should use threads instead (and get the
> same problems, until you really understand the protection /
> synchronisation mechanisms)
Yes, threads are often better, but still, there will be some simple
cases in which App.PM seems appropriate, and is certainly less effort to
use.
William Meyer
Welcome to the pool. My original comment was intended to provoke a
discussion, and to elicit opinions from as many as possible. And my use
of the phrase "considered harmful" probably should have been with credit
given to Edsger W. Dijkstra, as it is borrowed from his famous "GOTO
Considered Harmful" paper of 1968.
Your example makes the point clearly that indiscriminate use of
Application.ProcessMessages can cause problems. But then, indiscriminate
programming often does cause problems, even without the use of
Application.ProcessMessages.
My own view is that program flow and application goals should dictate
practice, and that while there are certainly cases in which
Application.ProcessMessages may cause problems, there are also cases in
which its use is benign, and as it is quite simple to use, it ought not
to be banned.
> The "or equivalent" is the right way in my experience, and PM is
> wrong. It just brings in too much baggage. You have to make your app
> aware that it may be re-entered while it is in this background task
> processing. The "or equivalent" is what I try to do, where that
> generally involves writing my own message loop and processing messages
> only intended for the dialog box containing the progress bar and
> cancel button. That way, only the UI that "knows" about the
> background task can respond. I mean, I know if you show the status
> dialog modally, ProcessMessages wouldn't cause this problem , but in
> the "real world" it rarely works out that way. :) My point is that
> sometimes it is OK to use ProcessMessages, but more often it
> introduces problems you never dreamed of if you haven't done API level
> coding (and sometimes even if you have).
The use of a more selective loop seems to be a better path, though in a
complex application, it seems to me that we might find ourselves
building quite a few of those. Not to abuse an overused word, it seems
that a different paradigm might be appropriate, but it isn't clear to me
that there is a better one, given the underlying nature of the OS.
> The only true solution is to get yourself a good (or several good)
> general purpose Windows programming books and learn about the gory
> details of how Windows apps work well down deep. Then, make an
> informed decisions about when to write a secondary message loop, when
> calling ProcessMessages is appropriate, and, most of all, what
> side-effects these could introduce into your code. I know, I know,
> Delphi is for hiding these gory details so you don't have to worry
> about them. Well, in this case, I don't think ProcessMessages hides
> the gory details well enough and I haven't yet found any substitute
> for fully understanding what it really does.
And there's the rub: if we all wanted to immerse ourselves in such gory
details, we might all be using VC++ (ugh). Windows makes the OS serve
the user, without the need for each user to be a student of arcana, but
it does so at the expense of imposing a tremendous burden on the
programmer. Delphi's best feature is that it does for the programmer of
Windows what Windows does for the end-user: it allows us to focus on
solving the problem, not on programming the UI.
I'm still hoping to find something better than
Application.ProcessMessages, but which does not involve lots of
selective message loops.
William Meyer
> On Thu, 16 Sep 1999 00:15:58 -0700, William Meyer
> <wme...@earthlink.net> wrote:
>
> >I have been told (repeatedly by one programmer) that
> >Application.ProcessMessages is a Bad Thing.
>
> As is often the case, your programmer friend is reciting dogma based
> on an oversimplification of the facts.
>
> It _is_ true that misusing Application.ProcessMessages is easy to do,
> by calling Application.ProcessMessages in a tight loop without
> yielding CPU time to other threads. But it is also true that there is
> no other simple way for an application to maintain a responsive user
> interface while waiting for some background operation to complete. (An
> alternative, more complicated way is to do the background operation in
> a secondary thread, and include two-way notifications between that
> thread and the primary thread.)
I've just found a nasty little problem I've been having for ages regarding
erratic program flow during printing (See Thread "message processing and
the LineTo API function").
It would appear that the drawing API functions (e.g. lineto, polyline,
perhaps others) allow a callback function that allows aborting of a print
job (AbortDoc, SetAbortDoc, etc).
The TPrinter hooks into this, and uses the application.ProcessMessages
call. This is Borland code, in the VCL source (D4, printers.pas, line
171). In my humble opinion, it should be up to the programmer whether or
not they want to processmessages during a print job, and what messages
they want to look for.
I think Application.Processmessages is something programmers have to use
very carefully, and must consider the implications. Using it like this in
the VCL is a Bad Thing indeed.
Murray.
>I've just found a nasty little problem I've been having for ages regarding
>erratic program flow during printing (See Thread "message processing and
>the LineTo API function").
>It would appear that the drawing API functions (e.g. lineto, polyline,
>perhaps others) allow a callback function that allows aborting of a print
>job (AbortDoc, SetAbortDoc, etc).
>
>The TPrinter hooks into this, and uses the application.ProcessMessages
>call. This is Borland code, in the VCL source (D4, printers.pas, line
>171). In my humble opinion, it should be up to the programmer whether or
>not they want to processmessages during a print job, and what messages
>they want to look for.
Your analysis of the problem is incorrect. The abort proc is _not_
being called as a result of the call to LineTo; it is being called as
a result of the call to Abort (and it would also be called as a result
of a call to EndDoc). Therefore, it cannot be causing the problem that
you're seeing.
>I think Application.Processmessages is something programmers have to use
>very carefully, and must consider the implications. Using it like this in
>the VCL is a Bad Thing indeed.
If you can put together a credible argument as to _why_ using
Application.ProcessMessages in this context is a Bad Thing, you should
submit a bug report.
-Steve
> On Fri, 17 Sep 1999 11:16:50 +0100, Murray McGowan
> <mmcg...@guralp.com> wrote:
>
> >I've just found a nasty little problem I've been having for ages regarding
> >erratic program flow during printing (See Thread "message processing and
> >the LineTo API function").
> >It would appear that the drawing API functions (e.g. lineto, polyline,
> >perhaps others) allow a callback function that allows aborting of a print
> >job (AbortDoc, SetAbortDoc, etc).
> >
> >The TPrinter hooks into this, and uses the application.ProcessMessages
> >call. This is Borland code, in the VCL source (D4, printers.pas, line
> >171). In my humble opinion, it should be up to the programmer whether or
> >not they want to processmessages during a print job, and what messages
> >they want to look for.
>
> Your analysis of the problem is incorrect. The abort proc is _not_
> being called as a result of the call to LineTo; it is being called as
> a result of the call to Abort (and it would also be called as a result
> of a call to EndDoc). Therefore, it cannot be causing the problem that
> you're seeing.
Ok, here's another sample for you to try. If I run this one, I get the
ShowMessage every time. That's BEFORE the abort.
From my observations, LineTo does not call the abortproc every time (hence the
for loop).
Maybe it's something to do with a particular printer driver?
I would appreciate anyone else's input, if they observe the same thing.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, Printers;
const
WM_MYMESSAGE = WM_USER+1;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
MsgRcvd : boolean;
procedure MyMessage(var msg : TMessage); message WM_MYMESSAGE;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
var
i : integer;
begin
with printer do
begin
begindoc;
PostMessage(form1.handle,WM_MYMESSAGE,0,0);
MsgRcvd := False;
for i := 1 to 100000 do
canvas.lineto(i div 1000, i mod 1000);
if MsgRcvd then
ShowMessage('The message was processed!');
abort;
end;
end;
procedure TForm1.MyMessage(var msg: TMessage);
begin
MsgRcvd := True;
end;
end.
>Maybe it's something to do with a particular printer driver?
Indirectly. I can duplicate the behavior in NT, but not in Win95. I
don't know if it's an NT vs. Win95/98 thing, or a printer driver
thing.
If I run your program on Win95, when I click the button, the program
is unresponsive to further mouse clicks, paint requests, etc., while
the FOR loop is running. This demonstrates that there is no message
processing going on within that loop.
Under NT, however, the program _is_ responsive to mouse clicks,
repaints itself properly, etc., even while the FOR loop is running.
You can see that the behavior is in the API (or the printer driver),
and not Delphi, because the behavior doesn't change even if you use
API calls exclusively, rather than Delphi's wrapper functions.
-Steve
"Steve Schafer (TeamB)" wrote:
> Under NT, however, the program _is_ responsive to mouse clicks,
> repaints itself properly, etc., even while the FOR loop is running.
> You can see that the behavior is in the API (or the printer driver),
> and not Delphi, because the behavior doesn't change even if you use
> API calls exclusively, rather than Delphi's wrapper functions.
Whilst I agree that it is a difference in the API, I have found that
TPrinter hooks into the AbortProc in the BeginDoc call. TPrinter has a
ProcessMessages in this hook which is doing the damage here.
If I use
with Printer do
begin
BeginDoc;
SetAbortProc(handle,nil);
...
Then I get the same behaviour in both 95 and NT (ie no message handling
during printing). It's not the API that's causing a PM, it's delphi. The
API's just allowing it to under different circumstances.
Murray.
>Whilst I agree that it is a difference in the API, I have found that
>TPrinter hooks into the AbortProc in the BeginDoc call. TPrinter has a
>ProcessMessages in this hook which is doing the damage here.
But an abort proc that _doesn't_ allow message processing can't
possibly work! An abort proc _must_ allow pending messages to be
processed, or else there is no way for it know, for example, that the
user has clicked a Cancel button to abort the print job.
The only difference between NT and Win95/98 in this regard is that NT
allows the user's abort attempt to be detected after every GDI call,
while Win95/98 doesn't detect it until immediately before NewPage or
EndDoc.
-Steve
"Steve Schafer (TeamB)" wrote:
> But an abort proc that _doesn't_ allow message processing can't
> possibly work! An abort proc _must_ allow pending messages to be
> processed, or else there is no way for it know, for example, that the
> user has clicked a Cancel button to abort the print job.
I would argue that _only_ the programmer knows whether they have provided a
Cancel button in the first place (not Delphi). If they have (in a
'printing' dialog box, for example), then surely it is better to allow the
program to check for messages (within their printing routine), so that they
can control which messages they handle? Also that way, the print routine
can terminate without running to the end before discovering the user has
aborted.
I'm now thinking, why has TPrinter got this AbortProc hook in the first
place? If the programmer wants to abort the print, they will call
printer.abort, which does all the necessary cleanup anyway.
I still think that forcing an all-encompasing PM on an application at ANY
time during a print is bad practice. The programmer should have the right
to choose when to execute PM's.
Murray.
>I would argue that _only_ the programmer knows whether they have provided a
>Cancel button in the first place (not Delphi). If they have (in a
>'printing' dialog box, for example), then surely it is better to allow the
>program to check for messages (within their printing routine), so that they
>can control which messages they handle? Also that way, the print routine
>can terminate without running to the end before discovering the user has
>aborted.
I think you've probably noticed by now that Delphi was designed to be
easy to use by people who are not professional programmers. That means
that quite a number of decisions had to be made by its designers
regarding how best to achieve that goal. Many of those decisions
result in the VCL making various assumptions which aren't always
applicable. You may not like all of the decisions (_I_ certainly don't
like all of them), but that's just the way it is.
The power of Delphi is that in most cases, you can work around those
design decisions with very little effort.
-Steve