[comp.windows.ms.programmer] What does Yield do?

dmurdoch@watstat.waterloo.edu (Duncan Murdoch) (05/16/91)

I'm porting a DOS program to Windows (using TP for Windows), and am having 
trouble getting it to be civilized.  It has some long loops where it 
occasionally writes to one window; the original never gives up control,
so in Windows everything else comes to a halt until it's done.

I'm trying to civilize it by putting occasional calls to Yield into the 
loop, but it's ignoring mouse events..  If other windows have
been invalidated, the Yield works, they get redrawn, and mouse actions
get processed.  However, if all other windows are idle, the Yield doesn't
seem to do anything, even if I've clicked the mouse outside the current
application.  

Is there some way to configure what sort of behaviour Yield has?  It's not
even mentioned in Petzold, and the TPW documentation is minimal.  I don't
have the SDK manual.

Duncan Murdoch

ebergman@isis.cs.du.edu (Eric Bergman-Terrell) (05/16/91)

About Yield():

"This function halts the current task and starts any waiting task...

Applications that contain windows should use a DispatchMessage, PeekMessage,
or TranslateMessage loop rather than calling the Yield function directly.  The
PeekMessage loop handles message synchronization properly and yeilds at the
appropraite times".

Prog. Ref., 4-469

BTW consider getting the Prog. Ref. at a bookstore.  IMHO it's worth the 
money.

Terrell

press@venice.SEDD.TRW.COM (Barry Press) (05/16/91)

In article <1991May16.121553.3876@maytag.waterloo.edu> dmurdoch@watstat.waterloo.edu (Duncan Murdoch) writes:
>
>I'm porting a DOS program to Windows (using TP for Windows), and am having 
>trouble getting it to be civilized.  It has some long loops where it 
>occasionally writes to one window; the original never gives up control,
>so in Windows everything else comes to a halt until it's done.
>
>I'm trying to civilize it by putting occasional calls to Yield into the 
>loop, but it's ignoring mouse events..  If other windows have

One of your problems may be that while Yield lets other programs process
messages, it doesn't let YOUR program do so.  

The problem this presents is this:  Assume your program has the focus.
When some other program is to get the focus, you get sent a message
(WM_KILLFOCUS, WM_FOCUS, WM_SETFOCUS, or something along those lines)
that tells you you're losing it.

If I recall correctly, the other program won't get the focus (and, therefore,
process user input) until you process this message.

One way to fix this is to put a call not to Yield, but to Peek Message in
your loop, where the PeekMessage parameters are set to restrict things to
the focus change message.

-- 
Barry Press                                 Internet: press@venice.sedd.trw.com

srw@cci632.cci.com (Steve Windsor) (05/17/91)

In article <1991May16.121553.3876@maytag.waterloo.edu> dmurdoch@watstat.waterloo.edu (Duncan Murdoch) writes:
>
>Is there some way to configure what sort of behaviour Yield has?  It's not
>even mentioned in Petzold, and the TPW documentation is minimal.  I don't
>have the SDK manual.
>
>Duncan Murdoch

Windows 3.0 is a non-preemptive multitasker.  As such, when a process has
control, no one else can gain access to the processor.  This is why windows
is messaged-based, to process a message and then get out, allowing the
scheduler to service another process.  

Yield() is supposed to relenquish the processor inside a process.  However,
it does not work.  This comes straight from Paul Yao in an Advanced Windows
class, Microsoft University.  In testing, Yield() does not appear to do a
damn thing...

Instead, look at PeekMessage().  If there is a message waiting in the
process queue, you can service that message.  If there is no message
waiting to be serviced, you can relenquish control...the code is
an if (PeekMessage(...))...else construct.  I even think it's mentioned
in Petzold.  I can email you the sample if you wish.

stephen windsor   srw@op632.cci.com

dmurdoch@watstat.waterloo.edu (Duncan Murdoch) (05/17/91)

In article <1095@venice.SEDD.TRW.COM> press@venice.sedd.trw.com (Barry Press) writes:
>In article <1991May16.121553.3876@maytag.waterloo.edu> dmurdoch@watstat.waterloo.edu (Duncan Murdoch) writes:
>>
>>I'm trying to civilize it by putting occasional calls to Yield into the 
>>loop, but it's ignoring mouse events..  If other windows have
>
>One of your problems may be that while Yield lets other programs process
>messages, it doesn't let YOUR program do so.  
>
>The problem this presents is this:  Assume your program has the focus.
>When some other program is to get the focus, you get sent a message
>(WM_KILLFOCUS, WM_FOCUS, WM_SETFOCUS, or something along those lines)
>that tells you you're losing it.

Thanks for the help (and thanks to Terrell for the SDK manual quote).  Yes,
that looks like what's happening.  What I've done, which seems to work, is
to write my own Yield that does one cycle through a standard message
processing loop:

procedure yield;
var
  msg : TMsg;
begin
  if Getmessage(msg,0,0,0) then
  begin
    Translatemessage(msg);
    Dispatchmessage(msg);
  end;
end;

As long as I call this frequently enough, things are fine.  (I have a 
window procedure that handles all the focus messages by passing them
on to the default handler.)

Duncan Murdoch

jciccare@adobe.COM (John Ciccarelli) (05/17/91)

In article <1991May16.121553.3876@maytag.waterloo.edu> dmurdoch@watstat.waterloo.edu (Duncan Murdoch) writes:

> I'm porting a DOS program to Windows [which] has some long loops where it 
> occasionally writes to one window... so everything else comes to a 
> halt.... I'm trying to civilize it by putting occasional calls to Yield...

Don't use Yield.  The docs say Yield isn't for windowed apps.  
Use PeekMessage instead.  The basic idea is:

  while ( something that takes a long time ) {
	<one iteration of stuff that takes a long time>
	MyYield();
	} /* end while */

  /* "Yield to other apps" function */
  void MyYield() {
    while ( PeekMessage( lpMsg, hWndApp, 0, 0, PM_NOREMOVE ) ) {
      ProcessMessage( lpMsg ); /* Get/Translate/Dispatch logic of WinMain */ 
      } /* end while */
    } /* end function */

Your ProcessMessage function should contain the Get/Translate/Dispatch 
body of your WinMain message loop.  WinMain's message loop and MyYield's
while loop both call it, guaranteeing that messages always get processed 
the same way.  

Since the GetMessage contained in ProcessMessage will remove the message,
PeekMessage is called with PM_NOREMOVE.  Even if there are no messages 
in the queue, PeekMessage will allow other apps to run, which is what 
you want.  The interface to ProcessMessage must handle the WM_QUIT
properly, of course.

/John
-- 
John Ciccarelli (jciccare@adobe.com, {sun|decwrl}!adobe!jciccare, 415-962-6677)
Adobe Systems, 1585 Charleston Road, Mountain View, California USA 94039-7900

press@venice.SEDD.TRW.COM (Barry Press) (05/17/91)

In article <1991May16.190243.27809@maytag.waterloo.edu> dmurdoch@watstat.waterloo.edu (Duncan Murdoch) writes:
> ...
>begin
>  if Getmessage(msg,0,0,0) then
>  begin
>    Translatemessage(msg);
>    Dispatchmessage(msg);
>  end;
>end;
>
>As long as I call this frequently enough, things are fine.  (I have a 

Unless I misunderstand what you've done, you're processing any and all
messages here.

What this implies is that you've got this loop that runs, effectively, as
a background process WHILE ALL FUNCTIONS OF YOUR PROGRAM ARE OPERATIONAL
AND ACCESSIBLE TO USERS -- i.e., you can (recursively) call any function
within your software.

Did you really intend that?  What you desribed led me to believe that
you just wanted to let other Windows programs (not yours) run while
you finished this loop.  I concluded that your program itself was "busy"
during that time.  If that's really what you wanted, then you'd best not
allow any message at all, just the ones related to focus.


-- 
Barry Press                                 Internet: press@venice.sedd.trw.com

press@venice.SEDD.TRW.COM (Barry Press) (05/17/91)

Also, the loop you put in yield should contain PeekMessage, should it not?

-- 
Barry Press                                 Internet: press@venice.sedd.trw.com

ebergman@isis.cs.du.edu (Eric Bergman-Terrell) (05/17/91)

Rather than processing only one entry in the message queue, you might want
to "empty it out" and process them all.  Can't say what difference that it
would make, but it might make switching to another program happen faster.
Just change your "if" to a while, and your "then" to a do...

General question:  How is programming Windows in T. Pascal?  What's easier
than using C?  What's more difficult?  How do you get statically bound
variables?

Terrell

dmurdoch@watstat.waterloo.edu (Duncan Murdoch) (05/17/91)

In article <1097@venice.SEDD.TRW.COM> press@venice.sedd.trw.com (Barry Press) writes:
>In article <1991May16.190243.27809@maytag.waterloo.edu> dmurdoch@watstat.waterloo.edu (Duncan Murdoch) writes:
>> ...
>>begin
>>  if Getmessage(msg,0,0,0) then
>>  begin
>>    Translatemessage(msg);
>>    Dispatchmessage(msg);
>>  end;
>>end;
>>
>>As long as I call this frequently enough, things are fine.  (I have a 
>
>Unless I misunderstand what you've done, you're processing any and all
>messages here.
>
>What this implies is that you've got this loop that runs, effectively, as
>a background process WHILE ALL FUNCTIONS OF YOUR PROGRAM ARE OPERATIONAL
>AND ACCESSIBLE TO USERS -- i.e., you can (recursively) call any function
>within your software.
>
>Did you really intend that?  What you desribed led me to believe that
>you just wanted to let other Windows programs (not yours) run while
>you finished this loop.  I concluded that your program itself was "busy"
>during that time.  If that's really what you wanted, then you'd best not
>allow any message at all, just the ones related to focus.

First of all, further testing showed that I really needed a Peekmessage
loop, not a single Getmessage call.  The current version of my Yield function
has first line

 while Peekmessage(msg,0,0,0,pm_remove) do

and works better than the above.  (I'm new at Windows programming.  Or could
you tell that already? :-).  That one happened to work because I
always had a paint message waiting.

As to whether it does what I want or not:  I'll have to give more background.  

As a first project in TPW, I decided to port a little DOS program I've got that
just does a scatterplot of data read from a file.  It was written in TP, and
used the BGI graphics there.  Since TPW doesn't come with anything remotely
resembling the BGI Graph unit that my program used, and it was written using
a toolkit that relied on BGI, I decided to get ambitious and write a Graph
unit replacement.  Ideally it would just be a drop in replacement to let
DOS graphics programs run under Windows and produce nicer output that could
be resized, moved, etc.

The way BGI graphics works is quite a bit different from Windows GDI.  For one
thing, it expects to own the output device exclusively, and to be able to 
paint an image a little bit at a time.  Since the programs that call it also
think they're single-tasking, they'll never voluntarily give up time slices:
hence the need for some sort of Yield function, which I decided to put
in the graphics output routines, hoping that they'd be called often enough
for it to work.  (The scatterplot application satisfies this:  it does almost
nothing but build the image.)

Since the programs that are going to be using this are warmed over DOS 
programs rather than Windows programs, they don't really need to know anything
about events coming in, other than the command to close the window, which
halts the program.  Thus it's fine to process any and all messages that come
along:  in fact it's necessary to process more than just messages about
the focus, because I want the output window to be resizable.

One outstanding problem:  I'd love to use a metafile to save the graphics 
commands (right now I'm maintaining my own list of them, and running into
trouble when there are too many of them).  The problem is that it doesn't
seem possible to write some stuff to a metafile, play it to the screen,
and reopen it to write some more.  Am I missing something?

Duncan Murdoch
dmurdoch@watstat.waterloo.edu

ronb@burklabs (Ron Burk ) (05/21/91)

ebergman@isis.cs.du.edu (Eric Bergman-Terrell) writes:

> General question:  How is programming Windows in T. Pascal?  What's easier
> than using C?  What's more difficult?  How do you get statically bound
> variables?

Having just written the same program in TPW, C++, and C, I can give
my preliminary opinion.  I prefer the TPW development environment to
any of my C/C++ compilers because it is a fast and it is a real Windows
app (no shelling to DOS).  The debugger is still character-mode, however.
TPW gives me more compile-time checking than C, but (surprisingly) less
than C++.  There are several object-oriented-related things that C++
detects at compile time, whereas TPW detects it only at runtime.  For
example, you make a TPW class "abstract" (in the C++ sense) by defining
a virtual func that calls the proc "Abstract".  Unfortunately, that
just causes a runtime error if the user fails to replace the virtual func.
In C++, of course, the compiler detects attempts to instantiate a
virtual class.

Other than the standard complaints when switching to Pascal from C (no
break/return statements, etc.), the main hassle for my 1000-line program
was null-terminated strings.  TPW adds some functions for switching
between C-style strings and Pascal-style strings, but converting back
and forth is a pain.  Windows, of course, wants null- terminated
strings.  I guess another hassle was the way constructors and
destructors work in TPW.  You have to call the dang things explicitly!
First you declare (or allocate) a variable, then call it's constructor.
If you forget to call the constructor, well, you must've had
some good reason to not want it initialized.  A more inconvenient
example occurs with inheritance.  Suppose I want to inherit
from a TPW class called TWindow and override it's constructor
(assume TWindow has a constructor that you pass a string containing
the window title):

type
    MyWindow = object(TWindow)
        constructor Init(Title:AString);
        {...}
        end;

Well, if you want the TWindow constructor to still get called,
you have to remember to do it yourself, like this:

constructor MyWindow.Init(Title:AString);
begin
    TWindow.Init( AString );
    { my added initialization }
end;

It is also possible to end up with a constructor that assigns
the parent's virtual function table to the object by mistake.
This took me several hours to track down.  Of course, once
you learn where the major pitfalls are in a language, you avoid
them without thinking.  C++ has it's problems, too, but it does
catch more problems at compile-time than TPW does, in my opinion.

I'm not sure what you mean by "statically bound variables".  The TPW
combination of units and objects gives you the most-used facilities of
C++ classes.  For example, the static data members of my C++ class
became global variables in the implementation of the unit containing the
corresponding TPW object.  Pretty much the same effect (only functions
in the unit's implementation can access them).  C++ still has
finer-grain facilities than any OO language I know of.  In TPW you
get private or public object members, no protected.  Inheritance
is public, no other options.

I was pleasantly surprised by TPW.

> 
> Terrell