home *** CD-ROM | disk | FTP | other *** search
/ PC World 1999 February / PCWorld_1999-02_cd.bin / temacd / HotKeys / hkSend.pas < prev    next >
Pascal/Delphi Source File  |  1998-08-25  |  8KB  |  333 lines

  1. unit hkSend;
  2.  
  3. interface
  4.  
  5. uses
  6.   SysUtils,
  7.   Messages,
  8.   Forms,
  9.   Windows,
  10.   Classes;
  11.  
  12. type
  13.   TSendKeyError = (skNone, skFailSetHook, skInvalidToken, skUnknownError);
  14.  
  15. function SendKeys(S: String; Wait: Boolean): TSendKeyError;
  16.  
  17. implementation
  18.  
  19. type
  20.   ESendKeyError = class(Exception);
  21.   ESetHookError = class(ESendKeyError);
  22.   EInvalidToken = class(ESendKeyError);
  23.  
  24.   TKeyDef = record
  25.     Key : String;
  26.     Code: UINT;
  27.   end;
  28.  
  29.   TMessageList = class(TList)
  30.   public
  31.     destructor Destroy; override;
  32.   end;
  33.  
  34. const
  35.   MaxKeys = 43;
  36.   ShiftKey = '+';
  37.   ControlKey = '^';
  38.   AltKey = '%';
  39.   EnterKey = '~';
  40.  
  41.   KeyGroupOpen = '{';
  42.   KeyGroupClose = '}';
  43.  
  44.   KeyTokens = '{}~%^+';
  45.  
  46.   KeyDefs : array[1..MaxKeys] of TKeyDef = (
  47.    (Key: 'BACKSPACE'  ; Code: VK_BACK),
  48.    (Key: 'BKSP'       ; Code: VK_BACK),
  49.    (Key: 'BS'         ; Code: VK_BACK),
  50.    (Key: 'CAPS'       ; Code: VK_CAPITAL),
  51.    (Key: 'CAPSLOCK'   ; Code: VK_CAPITAL),
  52.    (KEy: 'CLEAR'      ; Code: VK_CLEAR),
  53.    (Key: 'DEL'        ; Code: VK_DELETE),
  54.    (Key: 'DELETE'     ; Code: VK_DELETE),
  55.    (Key: 'DOWN'       ; Code: VK_DOWN),
  56.    (Key: 'END'        ; Code: VK_END),
  57.    (Key: 'ENTER'      ; Code: VK_RETURN),
  58.    (Key: 'ESC'        ; Code: VK_ESCAPE),
  59.    (Key: 'ESCAPE'     ; Code: VK_ESCAPE),
  60.    (Key: 'HOME'       ; Code: VK_HOME),
  61.    (Key: 'INS'        ; Code: VK_INSERT),
  62.    (Key: 'INSERT'     ; Code: VK_INSERT),
  63.    (Key: 'LEFT'       ; Code: VK_LEFT),
  64.    (Key: 'NUM'        ; Code: VK_NUMLOCK),
  65.    (Key: 'NUMLOCK'    ; Code: VK_NUMLOCK),
  66.    (Key: 'DOWN'       ; Code: VK_DOWN),
  67.    (Key: 'PAGEDOWN'   ; Code: VK_NEXT),
  68.    (Key: 'PGDN'       ; Code: VK_NEXT),
  69.    (Key: 'PAGEUP'     ; Code: VK_PRIOR),
  70.    (Key: 'PGUP'       ; Code: VK_PRIOR),
  71.    (Key: 'RIGHT'      ; Code: VK_RIGHT),
  72.    (Key: 'SCROLL'     ; Code: VK_SCROLL),
  73.    (Key: 'SCROLLLOCK' ; Code: VK_SCROLL),
  74.    (Key: 'PRINTSCREEN'; Code: VK_SNAPSHOT),
  75.    (Key: 'PRTSC'      ; Code: VK_SNAPSHOT),
  76.    (Key: 'TAB'        ; Code: VK_TAB),
  77.    (Key: 'UP'         ; Code: VK_UP),
  78.    (Key: 'F1'         ; Code: VK_F1),
  79.    (Key: 'F2'         ; Code: VK_F2),
  80.    (Key: 'F3'         ; Code: VK_F3),
  81.    (Key: 'F4'         ; Code: VK_F4),
  82.    (Key: 'F5'         ; Code: VK_F5),
  83.    (Key: 'F6'         ; Code: VK_F6),
  84.    (Key: 'F7'         ; Code: VK_F7),
  85.    (Key: 'F8'         ; Code: VK_F8),
  86.    (Key: 'F9'         ; Code: VK_F9),
  87.    (Key: 'F10'        ; Code: VK_F10),
  88.    (Key: 'F11'        ; Code: VK_F11),
  89.    (Key: 'F12'        ; Code: VK_F12));
  90.  
  91. var
  92.   bPlaying,
  93.   bAltPressed,
  94.   bControlPressed,
  95.   bShiftPressed    : Boolean;
  96.   Delay, CurDelay  : Integer;
  97.   Event            : TEventMsg;
  98.   MessageList      : TMessageList;
  99.   iMsgCount        : Integer;
  100.   HookHandle       : hHook;
  101.  
  102. destructor TMessageList.Destroy;
  103. var
  104.   i : Integer;
  105. begin
  106.   for i:=0 to Count-1 do
  107.    Dispose(PEventMsg(Items[i]));
  108.   inherited;
  109. end;
  110.  
  111. procedure StopPlayback;
  112. begin
  113.   if bPlaying then UnhookWindowsHookEx(HookHandle);
  114.   MessageList.Free;
  115.   bPlaying := False;
  116. end;
  117.  
  118. function Playback(nCode: Integer; wp: wParam; lp: lParam): Longint; stdcall; export;
  119. begin
  120.   Result := 0;
  121.   case nCode of
  122.     HC_SKIP:
  123.      begin
  124.        inc(iMsgCount);
  125.        if iMsgCount>=MessageList.Count then
  126.         StopPlayback
  127.        else
  128.         begin
  129.           Event := TEventMsg(MessageList.Items[iMsgCount]^);
  130.           CurDelay := Delay;
  131.         end;
  132.      end;
  133.     HC_GETNEXT:
  134.      begin
  135.        with PEventMsg(lp)^ do
  136.         begin
  137.           Message := Event.Message;
  138.           ParamL  := Event.ParamL;
  139.           ParamH  := Event.ParamH;
  140.           Time    := Event.Time;
  141.           hWnd    := Event.hWnd;
  142.         end;
  143.         Result := CurDelay;
  144.         CurDelay := 0;
  145.      end;
  146.     else
  147.      begin
  148.        Result := CallNextHookEx(HookHandle, nCode, wp, lp);
  149.      end;
  150.   end;
  151. end;
  152.  
  153. procedure StartPlayback;
  154. begin
  155.   Event := TEventMsg(MessageList.Items[0]^);
  156.   iMsgCount := 0;
  157.   HookHandle := SetWindowsHookEx(WH_JOURNALPLAYBACK, @Playback, hInstance, 0);
  158.   if HookHandle=0 then
  159.    raise ESetHookError.Create('Could not set hook')
  160.   else
  161.    bPlaying := True;
  162. end;
  163.  
  164. procedure MakeMessage(vKey, M: UINT);
  165. var
  166.   E: PEventMsg;
  167. begin
  168.   New(E);
  169.   with E^ do
  170.    begin
  171.      Message := M;
  172.      ParamL  := vKey;
  173.      ParamH  := MapVirtualKey(vKey, 0);
  174.      Time    := GetTickCount;
  175.      hWnd    := 0;
  176.    end;
  177.   MessageList.Add(E);
  178. end;
  179.  
  180. function FindKeyInArray(Key: String; var Code: UINT): Boolean;
  181. var
  182.   i : Integer;
  183. begin
  184.   Result := False;
  185.   for i:=Low(KeyDefs) to High(KeyDefs) do
  186.    if UpperCase(Key)=KeyDefs[i].Key then
  187.     begin
  188.       Code := KeyDefs[i].Code;
  189.       Result := True;
  190.       Exit;
  191.     end;
  192. end;
  193.  
  194. const
  195.   vkKeySet = [VK_SPACE, Ord('A')..Ord('Z'), VK_MENU, VK_F1..VK_F12];
  196.  
  197. procedure SimulateKey(Code: UINT; Down: Boolean);
  198. const
  199.   KeyMsg: array[Boolean] of UINT = (WM_KEYUP, WM_KEYDOWN);
  200.   SysMsg: array[Boolean] of UINT = (WM_SYSKEYUP, WM_SYSKEYDOWN);
  201. begin
  202.   if bAltPressed and (not bControlPressed) and (Code in vkKeySet) then
  203.    MakeMessage(Code, SysMsg[Down])
  204.   else
  205.    MakeMessage(Code, KeyMsg[Down])
  206. end;
  207.  
  208. procedure SimulateKeyPress(Code: UINT);
  209. begin
  210.   if bAltPressed then SimulateKey(VK_MENU, True);
  211.   if bControlPressed then SimulateKey(VK_CONTROL, True);
  212.   if bShiftPressed and not bControlPressed then SimulateKey(VK_SHIFT, True);
  213.   SimulateKey(Code, True);
  214.   SimulateKey(Code, False);
  215.   if bShiftPressed and not bControlPressed then
  216.    begin
  217.      SimulateKey(VK_SHIFT, False);
  218.      bShiftPressed := False;
  219.    end;
  220.   if bControlPressed then
  221.    begin
  222.      SimulateKey(VK_CONTROL, False);
  223.      bControlPressed := False;
  224.    end;
  225.   if bAltPressed then
  226.    begin
  227.      SimulateKey(VK_MENU, False);
  228.      bAltPressed := False;
  229.    end;
  230. end;
  231.  
  232. procedure NormalKeyPress(C: Char);
  233. var
  234.   KeyCode,
  235.   Shift  : UINT;
  236. begin
  237.   KeyCode := vkKeyScan(C);
  238.   Shift := HiByte(KeyCode);
  239.   if (Shift and 1)=1 then bShiftPressed := True;
  240.   if (Shift and 2)=2 then bControlPressed := True;
  241.   if (Shift and 4)=4 then bAltPressed := True;
  242.   SimulateKeyPress(LoByte(KeyCode))
  243. end;
  244.  
  245. function CheckDelay(Token: String): Boolean;
  246. begin
  247.   Token := UpperCase(Token);
  248.   Result := Pos('DELAY', Token)=1;
  249.   if Result then
  250.    begin
  251.      Delete(Token, 1, 5);
  252.      if (Length(Token)>0) and (Token[1]='=') then Delete(Token, 1, 1);
  253.      Delay := StrToIntDef(Token, 0);
  254.    end;
  255. end;
  256.  
  257. procedure ProcessKey(S: String);
  258. var
  259.   Index  : Integer;
  260.   Token  : String;
  261.   KeyCode: UINT;
  262. begin
  263.   Index := 1;
  264.   repeat
  265.     case S[Index] of
  266.       KeyGroupOpen:
  267.        begin
  268.          Token := '';
  269.          inc(Index);
  270.          while (Index<Length(S)) and (S[Index]<>KeyGroupClose) do
  271.           begin
  272.             Token := Token + S[Index];
  273.             inc(Index);
  274.             if (Length(Token)=12) and (S[Index]<>KeyGroupClose) then
  275.              raise EInvalidToken.Create('No closing brace')
  276.           end;
  277.          if (Length(Token)=1) and (Pos(Token, KeyTokens)>0) then
  278.           NormalKeyPress(Token[1])
  279.          else if FindKeyInArray(Token, KeyCode) then
  280.           SimulateKeyPress(KeyCode)
  281.          else if not CheckDelay(Token) then
  282.           raise EInvalidToken.Create('Invalid token');
  283.        end;
  284.       AltKey:
  285.        bAltPressed := True;
  286.       ControlKey:
  287.        bControlPressed := True;
  288.       ShiftKey:
  289.        bShiftPressed := True;
  290.       EnterKey:
  291.        SimulateKeyPress(VK_RETURN);
  292.       else
  293.        NormalKeyPress(S[Index]);
  294.     end;
  295.     inc(Index);
  296.   until Index > Length(S);
  297. end;
  298.  
  299. function SendKeys(S: String; Wait: Boolean): TSendKeyError;
  300. begin
  301.   bAltPressed := False;
  302.   bControlPressed := False;
  303.   bShiftPressed := False;
  304.   Result := skNone;
  305.   Delay := 0;
  306.   if bPlaying or (S='') then Exit;
  307.   try
  308.     MessageList := TMessageList.Create;
  309.     ProcessKey(S);
  310.     StartPlayback;
  311.     if Wait then
  312.      repeat
  313.        Application.ProcessMessages;
  314.      until bPlaying = False;
  315.   except
  316.    on E:ESendKeyError do
  317.     begin
  318.       MessageList.Free;
  319.       if E is ESetHookError then
  320.        Result := skFailSetHook
  321.       else if E is EInvalidToken then
  322.        Result := skInvalidToken;
  323.     end
  324.    else
  325.     Result := skUnknownError;
  326.   end;
  327. end;
  328.  
  329. exports
  330.    Playback;
  331.  
  332. end.
  333.