Jump to content

OS/2 2.X Journal Hook Summary: Difference between revisions

From EDM2
Created page with "==Abstract== A few excerpts on how to use the Journal Hooks. This can enable recording and playback of mouse and keyboard events on a OS/2 2.x system. <PRE> /****************..."
 
Ak120 (talk | contribs)
mNo edit summary
 
(One intermediate revision by one other user not shown)
Line 1: Line 1:
==Abstract==
==Abstract==
A few excerpts on how to use the Journal Hooks. This can enable recording and playback of mouse and keyboard events on a OS/2 2.x system.
A few excerpts on how to use the Journal Hooks. This can enable recording and playback of mouse and keyboard events on a OS/2 2.x system.
<PRE>
<PRE>
/************************************************************************/
/************************************************************************/
Line 31: Line 30:
/************************************************************************/
/************************************************************************/
</PRE>
</PRE>
==Hook Procedures - general rules==
==Hook Procedures - general rules==
* Include the EXPENTRY linkage keyword on all hook procedures.
* Include the EXPENTRY linkage keyword on all hook procedures.
* As with an interrupt handler, the time spent in a hook procedure must be kept to an absolute minimum. (No I/O or Win calls - use a separate thread.)
* As with an interrupt handler, the time spent in a hook procedure must be kept to an absolute minimum. (No I/O or Win calls - use a separate thread.)
* The playback hook procedure could be called AFTER WinReleaseHook, so leave a dummy mouse move message to be returned.
* The playback hook procedure could be called AFTER WinReleaseHook, so leave a dummy mouse move message to be returned.


Line 43: Line 43:
                                             PQMSG  lpqmsg
                                             PQMSG  lpqmsg
                                             )
                                             )
  hab     is the application thread anchor block
</PRE>
  fSkip   is TRUE if the last message has been activated, and
;hab:is the application thread anchor block
                    a new message is to be prepared
;fSkip:is TRUE if the last message has been activated, and a new message is to be prepared
              FALSE if the same message is to be passed again
:FALSE if the same message is to be passed again
  lpqmsg is a pointer to the QMSG structure to be filled
;lpqmsg:is a pointer to the QMSG structure to be filled
</PRE> 
 
==Message timing==
==Message timing==
Whenever your hook procedure provides an event to PM, you have to
Whenever your hook procedure provides an event to PM, you have to ensure that the time at which the event is due to be dispatched is kept updated. The return value from your procedure has to reflect the number of milliseconds until the message is due.
ensure that the time at which the event is due to be dispatched is
kept updated. The return value from your procedure has to reflect the
number of milliseconds until the message is due.


==Sample Hook Procedure==
==Sample Hook Procedure==
This procedure assumes that there is a second thread which is reading,
This procedure assumes that there is a second thread which is reading, parsing and formatting the events, and placing them in a circular array accessed via two indices, ulQmsgBottom and ulQmsgTop.
parsing and formatting the events, and placing them in a circular array
accessed via two indices, ulQmsgBottom and ulQmsgTop.


The module handle used in the call to WinRelease hook is a variable set
The module handle used in the call to WinRelease hook is a variable set by the playback initiation routine, which it gets from a call to the DosQueryModuleHandle API.
by the playback initiation routine, which it gets from a call to the
DosQueryModuleHandle API.
<PRE>
<PRE>
#define MAXQMSGBUFFERSIZE      5000
#define MAXQMSGBUFFERSIZE      5000
Line 84: Line 77:
   static      LONG            lDelayRemaining        = 0L;
   static      LONG            lDelayRemaining        = 0L;
   static      LONG            lLastSequencedEventTime = 0L;
   static      LONG            lLastSequencedEventTime = 0L;
                 
                 /*------------------------------------------------------*/
                 /*------------------------------------------------------*/
                 /* Store the passed time as the current message time    */
                 /* Store the passed time as the current message time    */
Line 115: Line 108:
         memcpy (&qmsgCurrent, &qmsgMouse, sizeof (QMSG));
         memcpy (&qmsgCurrent, &qmsgMouse, sizeof (QMSG));
       }
       }
                         
                         /*----------------------------------------------*/
                         /*----------------------------------------------*/
                         /* Prepare and pass the next message            */
                         /* Prepare and pass the next message            */
Line 224: Line 217:
}
}
</PRE>
</PRE>
==Journal Record Hook==
==Journal Record Hook==
<PRE>
<PRE>
Line 233: Line 227:
   lpqmsg  is  a pointer to the QMSG structure containing the event
   lpqmsg  is  a pointer to the QMSG structure containing the event
</PRE>
</PRE>
==Sample Hook Procedure==
==Sample Hook Procedure==
This procedure assumes that there is a second thread which is formatting and writing the events to disk. The hook procedure places them in a circular array accessed via two indices, ulQmsgBottom and ulQmsgTop.


This procedure assumes that there is a second thread which is formatting
To filter out mouse messages (you'll get a mass of messages otherwise), set bMouseCritical false.
and writing the events to disk. The hook procedure places them in a circular
array accessed via two indices, ulQmsgBottom and ulQmsgTop.
 
To filter out mouse messages (you'll get a mass of messages otherwise),  
set bMouseCritical false.
<PRE>
<PRE>
#define MAXQMSGBUFFERSIZE      5000
#define MAXQMSGBUFFERSIZE      5000
Line 248: Line 239:
   static      ULONG          ulQmsgTop      = 0;
   static      ULONG          ulQmsgTop      = 0;
   static      BOOL            bMouseCritical  = FALSE;
   static      BOOL            bMouseCritical  = FALSE;
 
</PRE>
Note that the hwnd you receive is not significant at this point in the
Note that the hwnd you receive is not significant at this point in the input stream.
input stream.
<PRE>
 
VOID  EXPENTRY  MyJournalRecordHookProc (
VOID  EXPENTRY  MyJournalRecordHookProc (
                                         HAB    hab,
                                         HAB    hab,
Line 287: Line 277:
}
}
</PRE>
</PRE>
[[Category:Presentation Manager]]

Latest revision as of 03:54, 2 February 2020

Abstract

A few excerpts on how to use the Journal Hooks. This can enable recording and playback of mouse and keyboard events on a OS/2 2.x system.

/************************************************************************/
/* Copyright 1993 IBM Corporation and CI, Software & Graphic Arts, Inc. */
/*                                                                      */
/*                        All Rights Reserved                           */
/*                                                                      */
/* Permission to use, copy, modify, and distribute this software for    */
/* any purpose and without fee is hereby granted, provided that the     */
/* above copyright notice appear in all copies and that both the        */
/* copyright notice and this permission notice appear in supporting     */
/* documentation, and that the names of IBM or CI not be used in        */
/* advertising or publicity pertaining to distribution of the software  */
/* without specific, written prior permission.                          */
/*                                                                      */
/* IBM & CI DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,       */
/* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN  */
/* NO EVENT SHALL IBM OR CI BE LIABLE FOR ANY SPECIAL, INDIRECT OR      */
/* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS  */
/* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE*/
/* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE   */
/* USE OR PERFORMANCE OF THIS SOFTWARE.                                 */
/************************************************************************/
/*                                                                      */
/* The following information represents summaries from an upcoming book,*/
/* "Insider Information: Developing, Debugging and Testing OS/2 2.x     */
/* Applications", by Ivan Biddles and Kelvin Lawrence [QED Publishing]. */
/*                                                                      */
/************************************************************************/

Hook Procedures - general rules

  • Include the EXPENTRY linkage keyword on all hook procedures.
  • As with an interrupt handler, the time spent in a hook procedure must be kept to an absolute minimum. (No I/O or Win calls - use a separate thread.)
  • The playback hook procedure could be called AFTER WinReleaseHook, so leave a dummy mouse move message to be returned.

Journal Playback Hook

ULONG   EXPENTRY  MyJournalPlaybackHookProc (
                                             HAB    hab,
                                             BOOL   fSkip,
                                             PQMSG  lpqmsg
                                            )
hab
is the application thread anchor block
fSkip
is TRUE if the last message has been activated, and a new message is to be prepared
FALSE if the same message is to be passed again
lpqmsg
is a pointer to the QMSG structure to be filled

Message timing

Whenever your hook procedure provides an event to PM, you have to ensure that the time at which the event is due to be dispatched is kept updated. The return value from your procedure has to reflect the number of milliseconds until the message is due.

Sample Hook Procedure

This procedure assumes that there is a second thread which is reading, parsing and formatting the events, and placing them in a circular array accessed via two indices, ulQmsgBottom and ulQmsgTop.

The module handle used in the call to WinRelease hook is a variable set by the playback initiation routine, which it gets from a call to the DosQueryModuleHandle API.

#define MAXQMSGBUFFERSIZE       5000

   static       QMSG            qmsgBuffer      [MAXQMSGBUFFERSIZE];
   static       ULONG           ulQmsgBottom    = 0;
   static       ULONG           ulQmsgTop       = 0;
   static       HMODULE         hModule;

ULONG EXPENTRY MyJournalPlaybackHookProc (
                                          HAB   hab,
                                          BOOL  fSkip,
                                          PQMSG lpqmsg 
                                         )
{
   static       QMSG            qmsgMouse;
   static       QMSG            qmsgCurrent;
   static       LONG            lLastMsgTime            = 0L;
   static       LONG            lCurrentMsgTime         = 0L;
   static       LONG            lMsgDelay               = 0L;
   static       LONG            lDelayRemaining         = 0L;
   static       LONG            lLastSequencedEventTime = 0L;
 
                /*------------------------------------------------------*/
                /* Store the passed time as the current message time    */
                /*------------------------------------------------------*/
                        /*----------------------------------------------*/
                        /* Note: if fSkip is TRUE, the lpqmsg pointer   */
                        /*       will be NULL.                          */
                        /*----------------------------------------------*/
   if (lpqmsg) {
      lCurrentMsgTime = lpqmsg->time;
   }
                /*------------------------------------------------------*/
                /* The system requests that a NEW message be prepared   */
                /*------------------------------------------------------*/
   if (fSkip) {
                        /*----------------------------------------------*/
                        /* No more messages on queue - release the hook */
                        /*----------------------------------------------*/
      if (ulQmsgBottom == ulQmsgTop) {
         WinReleaseHook (
                         hab,
                         0L,
                         HK_JOURNALPLAYBACK,
                         (PFN) MyJournalPlaybackHookProc,
                         hModule
                        );
                                /*--------------------------------------*/
                                /* Set a mouse move "in place" message  */
                                /*--------------------------------------*/
         memcpy (&qmsgCurrent, &qmsgMouse, sizeof (QMSG));
      }
 
                        /*----------------------------------------------*/
                        /* Prepare and pass the next message            */
                        /*----------------------------------------------*/
      else {
                                /*--------------------------------------*/
                                /* Update the time of last message      */
                                /*--------------------------------------*/
         lLastMsgTime = lLastSequencedEventTime;
                                /*--------------------------------------*/
                                /* Copy current message from the queue  */
                                /*--------------------------------------*/
         memcpy (&qmsgCurrent, &(qmsgBuffer [ulQmsgBottom]), sizeof (QMSG));
                                /*--------------------------------------*/
                                /* Update the current mouse position so */
                                /* that the mouse stays put when we     */
                                /* playback                             */
                                /*--------------------------------------*/
         if (qmsgCurrent.msg == WM_MOUSEMOVE) {
            qmsgMouse.mp1   = qmsgCurrent.mp1;
            qmsgMouse.mp2   = qmsgCurrent.mp2;
            qmsgMouse.ptl.x = qmsgCurrent.ptl.x;
            qmsgMouse.ptl.y = qmsgCurrent.ptl.y;
         }
                                /*--------------------------------------*/
                                /* Update (and wrap) the queue pointer  */
                                /*--------------------------------------*/
         ulQmsgBottom++;
         if (ulQmsgBottom >= MAXQMSGBUFFERSIZE) {
            ulQmsgBottom=0L;
         }
                                /*--------------------------------------*/
                                /* New message - so set up the delay    */
                                /*               timing variables       */
                                /*--------------------------------------*/
         lMsgDelay               = qmsgCurrent.time;
         lDelayRemaining         = lMsgDelay - (lCurrentMsgTime - lLastMsgTime);
         if (lDelayRemaining < 0) {
            lDelayRemaining  = 0L;
         }
         lLastSequencedEventTime = lCurrentMsgTime + lDelayRemaining;
         qmsgCurrent.time        = lLastSequencedEventTime;
      }
   } 
                /*------------------------------------------------------*/
                /* The system requests a PEEK at the CURRENT message    */
                /*------------------------------------------------------*/
   else {
                        /*----------------------------------------------*/
                        /* Initialize the variables on the first pass   */
                        /*----------------------------------------------*/
      if (bFirstTimePlaybackHook) {
         bFirstTimePlaybackHook  = 0;
         lLastMsgTime            = 0L;
         lCurrentMsgTime         = 0L;
         lMsgDelay               = 0L;
         lDelayRemaining         = 0L;
         lLastSequencedEventTime = 0L;
                                /*--------------------------------------*/
                                /* Initialize the default mouse move    */
                                /*--------------------------------------*/
         qmsgMouse.hwnd  = HWND_DESKTOP;
         qmsgMouse.msg   = WM_MOUSEMOVE;
         qmsgMouse.mp1   = (MPARAM) 0;
         qmsgMouse.mp2   = (MPARAM) 0;
         qmsgMouse.time  = 0L;
         qmsgMouse.ptl.x = 0L;
         qmsgMouse.ptl.y = 0L;
                                /*--------------------------------------*/
                                /* Send the same message as before      */
                                /*--------------------------------------*/
         if (ulQmsgBottom == ulQmsgTop) {
            memcpy (&qmsgCurrent, &qmsgMouse, sizeof (QMSG));
         }
                                /*--------------------------------------*/
                                /* Use the next message in the queue    */
                                /*--------------------------------------*/
         else {
            memcpy (&qmsgCurrent, &(qmsgBuffer[ulQmsgBottom]), sizeof (QMSG));
                                        /*------------------------------*/
                                        /* New message - so set up the  */
                                        /*      delay timing variables  */
                                        /*------------------------------*/
            lMsgDelay               = qmsgCurrent.time;
            lDelayRemaining         = lMsgDelay - (lCurrentMsgTime - lLastMsgTime);
            if (lDelayRemaining < 0) {
               lDelayRemaining  = 0L;
            }
            lLastSequencedEventTime = lCurrentMsgTime + lDelayRemaining;
            qmsgCurrent.time        = lLastSequencedEventTime;
         }
      }
                        /*----------------------------------------------*/
                        /* Copy current message to the passed pointer   */
                        /*----------------------------------------------*/
      if (lpqmsg) {
         memcpy (lpqmsg, &qmsgCurrent, sizeof (QMSG));
      }
   }
                /*------------------------------------------------------*/
                /* Update & return the remaining delay for this message */
                /*------------------------------------------------------*/
   lDelayRemaining = lMsgDelay - (lCurrentMsgTime - lLastMsgTime);
   if (lDelayRemaining < 0 ) {
      lDelayRemaining = 0L;
   }
   return ((ULONG) delayRemaining);
}

Journal Record Hook

VOID  EXPENTRY  MyJournalRecordHookProc (
                                         HAB    hab,
                                         PQMSG  lpqmsg
                                        )
   hab     is  the application thread anchor block
   lpqmsg  is  a pointer to the QMSG structure containing the event

Sample Hook Procedure

This procedure assumes that there is a second thread which is formatting and writing the events to disk. The hook procedure places them in a circular array accessed via two indices, ulQmsgBottom and ulQmsgTop.

To filter out mouse messages (you'll get a mass of messages otherwise), set bMouseCritical false.

#define MAXQMSGBUFFERSIZE       5000

   static       QMSG            qmsgBuffer      [MAXQMSGBUFFERSIZE];
   static       ULONG           ulQmsgBottom    = 0;
   static       ULONG           ulQmsgTop       = 0;
   static       BOOL            bMouseCritical  = FALSE;

Note that the hwnd you receive is not significant at this point in the input stream.

VOID  EXPENTRY  MyJournalRecordHookProc (
                                         HAB    hab,
                                         PQMSG  lpqmsg
                                        )
{
                /*------------------------------------------------------*/
                /* Filter out mouse moves unless required               */
                /*------------------------------------------------------*/
   if (bMouseCritical || lpqmsg->msg != WM_MOUSEMOVE) {
      memcpy (& (qmsgBuffer [ulQmsgTop]), lpqmsg, sizeof (QMSG));
                        /*----------------------------------------------*/
                        /* Set the hwnd to null since it is useless at  */
                        /* this point.                                  */
                        /*----------------------------------------------*/
      qmsgBuffer[ulQmsgTop].hwnd = 0L;
                        /*----------------------------------------------*/
                        /* Update the buffer pointer (with wraparound)  */
                        /*----------------------------------------------*/
      ulQmsgTop++;
      if (ulQmsgTop >= MAXQMSGBUFFERSIZE) {
         ulQmsgTop = 0L;
      }
                        /*----------------------------------------------*/
                        /* Circular queue collision situation - the     */
                        /* buffer may need to be expanded               */
                        /*----------------------------------------------*/
      if (ulQmsgTop == ulQmsgBottom) {
                                /*--------------------------------------*/
                                /* Output a message - piped to a file   */
                                /*--------------------------------------*/
         fprintf (stderr, "-- Circular Queue collision: expand MAXQMSGBUFFERSIZE");
      }
   }
}