OS/2 2.X Journal Hook Summary: Difference between revisions
No edit summary |
mNo edit summary |
||
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 | * 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 | ||
) | ) | ||
</PRE> | |||
;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== | ==Message timing== | ||
Whenever your hook | 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 | |||
kept updated. The return value from your procedure has to reflect | |||
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. | |||
To filter out mouse messages (you'll get a mass of messages otherwise), set bMouseCritical false. | |||
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 288: | Line 278: | ||
</PRE> | </PRE> | ||
[[Category: | [[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"); } } }