Board index » delphi » Intercepting Keyboard interrupt

Intercepting Keyboard interrupt

Hello!
    I am trying to write a program that will launch an external
program, and while it's running, intercept any calls to the keyboard
interrupt ($16, if I'm not mistaken), filter any requests for
keypresses, and insert characters from a buffer (previously read
from a file) into the appropriate spot so that it simulates the
pressing of keys in the external program.
    I have tried fiddling with some of the example source code in
the help files, in order to just get a program running so that it
will hook on, and just call the original interrupt (as it would do
for any function calls my interrupt vector doesn't handle when it's
done), but it just ends up freezing whenever int $16 is called in
the external program.
    At the end of this post is the source code for what I've tried,
and just a test program to launch from within the other code (just
one readln).
    So, what I'd like to know is whether or not this is anywhere
near the correct way to write an interrupt to do this.
    Is there any other way that this could be done that would be
simpler?  Maybe attaching to the timer interrupt, and putting
characters directly into the keyboard buffer if it's not full (if
you can do that from within an interrupt)?  Or if there's a way to
attach this process to a hot key or something (I tried a hotkey-tsr
unit, calling the functions to insert text into the keyboard buffer,
but because of the size of the text and size of the keyboard buffer,
I could not send all of it at once).
    Thanks in advance for your help!  Comments of any kind would be
greatly appreciated!

    jimb

// macro.pas:
{$M $2000,0,0 }   { 8K stack, no heap }
{$C FIXED PRELOAD PERMANENT}

uses Dos, Crt;

var
  ProgramName, CmdLine: string;
  KbdIntVec : Procedure;
{$F+}
procedure Keyclick; interrupt;
var
  wrd:word;
begin
  inline ($9C); { PUSHF -- Push flags }
  { Call old ISR using saved vector }
  KbdIntVec;
end;
{$F-}
begin
  { Insert ISR into keyboard chain }
  WriteLn('Hooking vectors...');
    GetIntVec($16,@KbdIntVec);
    SetIntVec($16,Addr(Keyclick));
  Write('Program to Exec (full path): ');
    ReadLn(ProgramName);
  WriteLn('About to Exec...');
    Exec(ProgramName, '');
  WriteLn('...back from Exec');
  if DosError <> 0 then{ Error? }
    WriteLn('Dos error #', DosError)
  else
    WriteLn('Exec successful. ',
            'Child process exit code = ',
            DosExitCode);
  WriteLn('Unhooking vectors...');
    SetIntVec($16,@KbdIntVec);
end.

// test.pas
var s:string;
begin
  Write('Enter something : ');
  ReadLn(s);
  WriteLn(s);
end.

--
jimsoft at geocities d com
http://www.horningabout.com/jimb

 

Re:Intercepting Keyboard interrupt


Int 16H is the software interrupt to get a key from the BIOS keyboard
buffer.

Int 9 is the actual keyboard hardware interrupt service routine.

Here's some old assembly code which shows how to re-vector Int9 (to look
for a hot key) and stuff keystrokes into the BIOS buffer (to fake an
application program).  The same basic principles can be used in your
Pascal program to achieve what you are trying to do:

; STUFFS THE FOLLOWING KEYSTROKES INTO THE
; BIOS KBD BUFFER when Alt-X is pressed:
; ^]CQ<CR>
;
; This sequence is used to exit Kermit.

jmp start

credit: db ' Written by NetNews ',26

active db 0  ; switch turns macroing on or off

ID      equ  07743h     ;change ID for each application

LOADED  equ  03334h     ; function number to test if already loaded

DISABLE EQU  03335h       ; function number to disable macro.
ENABLE  EQU  03336H       ; function number to enable macroing.

SCANCODE EQU 45        ; SCAN CODE FOR 'X' HOT KEY

parm macro
mov bx,80h
cs mov bl,[bx]
cmp bl,2
jne >M1
mov bx,81h
cs mov bl,[bx]
cmp bl,' '
jne >M1
mov bx,82h
cs mov bl,[bx]
cmp bl,#1
M1:
#EM

ClearKeyboard:
;entry: nothing.
;all registers are preserved.
;
;example usage:
;
;call ClearKeyboard
;pop ax  (assuming it was pushed before call)
;iret
;
push ax
IN      AL,61h          ;get value of kbd control port (61h)
MOV     AH,AL           ;save a copy
OR      AL,80h          ;set enable kbd bit
OUT     61h,AL          ;write it out
XCHG    AL,AH           ;get original value
OUT     61h,AL          ;put it back
CLI
MOV     AL,20h          ;send end-of-interrupt code (20h)
OUT     20h,AL          ;to 8259 interrupt controller chip port (20h)
pop ax
ret

GetVector macro
mov al,#1
call GetIntVec
mov #2,bx
mov #3,es
#EM

SetVector macro
mov al,#1
mov dx, #2
call SetIntVec
#EM

old028h:                ; allow DOS interrupt
old028hofs dw ?
old028hseg dw ?

old09h:                 ; KEYBOARD
old09hofs dw ?
old09hseg dw ?

old016h:                ; BIOS KBD BUFFER
old016hofs dw ?
old016hseg dw ?

;;;;;;;;;;;;;;;;;;;;;;;;;;       028H
;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;; allow DOS Interrupt
;;;;;;;;;;;;;;;;;;;;;;;;;;;

my028h:
sti

cli
cs jmp far old028h

HotKey:
push ax,bx,cx,dx,di,si,es,ds
cmp al,SCANCODE
jne >L9

mov ax,040h
mov ds,ax
mov al,[0017h]
test al,08h
jz >L9

mov bx,001eh
cli
mov [001ah],bx
mov [001ch],bx

mov b [bx],29
inc bx
mov b [bx],']'
dec bx

add bx,2
mov b [bx],'c'
add bx,2
mov b [bx],'q'
add bx,2
mov b [bx],13

add bx,2
mov [001c],bx
sti

L9:
pop  ds,es,si,di,dx,cx,bx,ax
ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 09H ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;; KEYBOARD INTERRUPT ;;;;;;;;;;;;;;;;;;;;;;;;

my09h:
sti
push ax
in al,60h

cs cmp active,1
if e call HotKey

pop ax
cli
cs jmp far old09h

;;;;;;;;;;;;;;;;;;;;;;;;;;;          16H         ;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;; BIOS KEYBOARD BUFFER ;;;;;;;;;;;;;;;;;

my016h:
sti
cmp bx,ID
jne >L9    ; don't service the interrupt if the ID is wrong.

; determine which function is requested:
cmp ax,LOADED
je >L7
cmp ax,DISABLE
je >L8
cmp ax,ENABLE
je >L6

L9:
; unrecognized function - don't do anything
cli
cs jmp far old016h

L6:
; enable macroing
cs mov active,1
iret

L7:
; yes - already loaded - tell caller:
mov ax,bx
iret

L8:
; disable macroing
cs mov active,0
iret

START:

mov bx,ID
mov ax,loaded
int 16h
cmp ax,ID
if ne jmp LOAD

; already loaded.  disable or enable?

parm '0'
je >L1

; parm is not 0, already loaded, so enable:
mov bx,id
mov ax,enable
int 16h
mov dx,mss3
mov ah,9
int 21h
int 20h

L1:
; already installed, parm is 0, so disable macroing:
mov bx,id
mov ax,disable
int 16h
mov dx,mss0
mov ah,9
int 21h
int 20h

LOAD:

; this is first invokation. not yet loaded. so load it:

; load without enabling?
mov active,1
parm '0'
if e mov active,0

GetVector 028h, Old028hOfs, Old028hSeg          ; allow DOS

GetVector 009h, Old09hOfs, Old09hSeg            ; KEYBOARD
GetVector 016h, Old016hOfs, Old016hSeg          ; BIOS KBD BUFFER

SetVector 028h, My028h

SetVector 009h, My09h
SetVector 016h, My016h

cmp active,1
je >L1
mov dx,mss4
mov ah,9
int 21h
mov dx, start
int 27h

L1:
mov dx,mss1
mov ah,9
int 21h

mov dx, start
int 27h

GetIntVec:
;entry: al contains the int to get
;exit:  es:bx contains the int address
;
        mov ah,35h
        int 21h
        ret

SetIntVec:
;entry: al contains interrupt to set
;       ds:dx contains the address of the ISR
;all other regs preserved
;
        mov ah,25h
        int 21h
        ret

mss0: db 'already installed, disabled$'
mss1: db 'ALTX 1.0.0T  (c) 1988 NetNews, enabled$'
mss3: db 'already installed, enabled$'
mss4: db 'ALTX 1.0.0T  (c) 1988 NetNews, disabled$'

Good Luck.

In article <7i1kcl$m8...@news1.tc.umn.edu>,
  "Jimb Esser" <jims...@R3M0VEMEDOTgeocities.com> wrote:

Quote
> Hello!
>     I am trying to write a program that will launch an external
> program, and while it's running, intercept any calls to the keyboard
> interrupt ($16, if I'm not mistaken), filter any requests for
> keypresses, and insert characters from a buffer (previously read
> from a file) into the appropriate spot so that it simulates the
> pressing of keys in the external program.
>     I have tried fiddling with some of the example source code in
> the help files, in order to just get a program running so that it
> will hook on, and just call the original interrupt (as it would do
> for any function calls my interrupt vector doesn't handle when it's
> done), but it just ends up freezing whenever int $16 is called in
> the external program.
>     At the end of this post is the source code for what I've tried,
> and just a test program to launch from within the other code (just
> one readln).
>     So, what I'd like to know is whether or not this is anywhere
> near the correct way to write an interrupt to do this.
>     Is there any other way that this could be done that would be
> simpler?  Maybe attaching to the timer interrupt, and putting
> characters directly into the keyboard buffer if it's not full (if
> you can do that from within an interrupt)?  Or if there's a way to
> attach this process to a hot key or something (I tried a hotkey-tsr
> unit, calling the functions to insert text into the keyboard buffer,
> but because of the size of the text and size of the keyboard buffer,
> I could not send all of it at once).
>     Thanks in advance for your help!  Comments of any kind would be
> greatly appreciated!

>     jimb

> // macro.pas:
> {$M $2000,0,0 }   { 8K stack, no heap }
> {$C FIXED PRELOAD PERMANENT}

> uses Dos, Crt;

> var
>   ProgramName, CmdLine: string;
>   KbdIntVec : Procedure;
> {$F+}
> procedure Keyclick; interrupt;
> var
>   wrd:word;
> begin
>   inline ($9C); { PUSHF -- Push flags }
>   { Call old ISR using saved vector }
>   KbdIntVec;
> end;
> {$F-}
> begin
>   { Insert ISR into keyboard chain }
>   WriteLn('Hooking vectors...');
>     GetIntVec($16,@KbdIntVec);
>     SetIntVec($16,Addr(Keyclick));
>   Write('Program to Exec (full path): ');
>     ReadLn(ProgramName);
>   WriteLn('About to Exec...');
>     Exec(ProgramName, '');
>   WriteLn('...back from Exec');
>   if DosError <> 0 then{ Error? }
>     WriteLn('Dos error #', DosError)
>   else
>     WriteLn('Exec successful. ',
>             'Child process exit code = ',
>             DosExitCode);
>   WriteLn('Unhooking vectors...');
>     SetIntVec($16,@KbdIntVec);
> end.

> // test.pas
> var s:string;
> begin
>   Write('Enter something : ');
>   ReadLn(s);
>   WriteLn(s);
> end.

> --
> jimsoft at geocities d com
> http://www.horningabout.com/jimb

--== Sent via Deja.com http://www.deja.com/ ==--
---Share what you know. Learn what you don't.---

Re:Intercepting Keyboard interrupt


In article <7i1kcl$m8...@news1.tc.umn.edu>,

Quote
Jimb Esser <jims...@R3M0VEMEDOTgeocities.com> wrote:
>{$F+}
>procedure Keyclick; interrupt;
>var
>  wrd:word;
>begin
>  inline ($9C); { PUSHF -- Push flags }
>  { Call old ISR using saved vector }
>  KbdIntVec;
>end;
>{$F-}

The problem is that you do not pass the registers. The hardware
registers and the ones that are given parameters to the interrupt
procedure are entirely different things. You could for example load the
registers manually before calling the KbdIntVec and store them after, or
you could write the whole thing in ASM.

There is no need to use $f+ in interrupts.

Osmo

Other Threads