PRB: JournalPlayback Hook Can Cause Windows NT to Hang

ID: Q124835

The information in this article applies to:

SYMPTOMS

Incorrect use of the delay return value from a journal playback hook can cause Windows NT to hang temporarily.

CAUSE

The menu loop in Windows NT calls PeekMessage() with the PM_NOREMOVE flag, does some processing, and then removes the message from the queue. This sequence is repeated until the menu goes away. When JournalPlayback is occurring, the PeekMessage( PM_NOREMOVE ) results in a callback to the application's JournalPlaybackProc with an HC_ACTION code. The subsequent PeekMessage( PM_REMOVE ) also calls the JournalPlaybackProc with an HC_ACTION code. If the peek is successful, it is followed by an HC_SKIP callback.

In order to have playback from a journal playback hook occur at a certain rate, Microsoft designed it so that the value returned by the JournalPlaybackProc can be non-zero. This value represents the number of clock-ticks the system should wait before processing the event. What the documentation doesn't make clear is that when the delay has expired, another callback to the JournalPlaybackProc is made to obtain the same event again; the event provided with the previous non-zero delay is not used at all. All subsequent HC_ACTION calls that request the same event should be returned with a zero delay value. Only after an HC_SKIP callback has been made, may an HC_ACTION callback return a non-zero delay value again. Some applications do not do this correctly, and simply alternate between returning a delay and returning a non-delay.

This alternating delay/no delay method made the Windows NT menu loop hang because the PeekMessage( PM_NOREMOVE ) would get an input event (with no delay), then the PeekMessage( PM_REMOVE ) would get a non-zero return value from the JournalPlaybackProc. This represents no message -- so instead of issuing an HC_SKIP callback to the JournalPlaybackProc to advance to the next event, the Windows NT menu loop code simply looped back to the PeekMessage( PM_NOREMOVE ) getting stuck in an infinite loop.

RESOLUTION

To work around this problem, make sure the JournalPlaybackProc correctly returns the delay only for the first request for an event.

Windows version 3.1 and Windows 95 do not have this problem.

STATUS

This behavior is by design.

MORE INFORMATION

The following sample code demonstrates correct and incorrect methods of handling delays in a journal playback hook.

Sample Code

   LRESULT CALLBACK JournalPlaybackProc(
       int nCode,
       WPARAM wParam,
       LPARAM lParam)
   {
       static BOOL     fDelay;
       static EVENTMSG event;
       static LRESULT  ticks_delay;
       BOOL            fCallNextHook = FALSE;
       LRESULT         lResult = 0;

       switch( nCode )
       {
           case HC_SKIP:
               fDelay = TRUE;      // <<<< CORRECT PLACE TO RESET fDelay

               // Get the next event from the list. If the routine returns
               // FALSE, then we are done - release the hook.
               if( !GetNextEvent( &event, &ticks_delay ))
                   SetJournalHook( FALSE, NULL );
               break;

           case HC_GETNEXT:
           {
               // Structure information returned from previous GetNextEvent
               // call.
               LPEVENTMSG lpEvent = (LPEVENTMSG) lParam;

               // Set the event
               *lpEvent = event;

               if( fDelay )
               {
                   // Toggle pause variable so that the next call will not
                   // pause. Return the pause length specified by
                   // ticks_delay since this is the first time the event
                   // has been requested.

                   fDelay = FALSE; // <<<< CORRECT PLACE TO CLEAR fDelay
                   return( ticks_delay );
               }
               break;
           }

           case HC_SYSMODALOFF:
                  // System modal dialog is going away. Something was
                  // corrupted.  Windows took care of removing our
                  // JournalPlayback hook, so no need to call
                  // SetJournalHook( FALSE ).

               fCallNextHook = TRUE;
               break;

           case HC_SYSMODALON:
           default:
               // Something is not right here. Let the next hook handle
               // it.

               fCallNextHook = TRUE;
               break;
       }

       // If the event was not processed by our code, call next hook.
          if( fCallNextHook )
              lResult = CallNextHookEx( s_journalHook, nCode, wParam,
                                        lParam    );

       // fDelay = TRUE;           // <<<< WRONG PLACE TO RESET bDelay !!!
       return lResult;
   }

Additional query words:
Keywords          : kbcode kbHook kbNTOS310 kbNTOS350 kbGrpUser 
Issue type        : kbprb

Last Reviewed: December 10, 1998