try this unit:<br>unit hkSend;<br><br>interface<br><br>uses<br> SysUtils,<br> Messages,<br> Forms,<br> Windows,<br> Classes;<br><br>type<br> TSendKeyError = (skNone, skFailSetHook, skInvalidToken, skUnknownError);<br><br>function SendKeys(S: String; Wait: Boolean): TSendKeyError;<br><br>implementation<br><br>type<br> ESendKeyError = class(Exception);<br> ESetHookError = class(ESendKeyError);<br> EInvalidToken = class(ESendKeyError);<br><br> TKeyDef = record<br> Key : String;<br> Code: UINT;<br> end;<br><br> TMessageList = class(TList)<br> public<br> destructor Destroy; override;<br> end;<br><br>const<br> MaxKeys = 43;<br> ShiftKey = '+';<br> ControlKey = '^';<br> AltKey = '%';<br> EnterKey = '~';<br><br> KeyGroupOpen = '{';<br> KeyGroupClose = '}';<br><br> KeyTokens = '{}~%^+';<br><br> KeyDefs : array[1..MaxKeys] of TKeyDef = (<br> (Key: 'BACKSPACE' ; Code: VK_BACK),<br> (Key: 'BKSP' ; Code: VK_BACK),<br> (Key: 'BS' ; Code: VK_BACK),<br> (Key: 'CAPS' ; Code: VK_CAPITAL),<br> (Key: 'CAPSLOCK' ; Code: VK_CAPITAL),<br> (KEy: 'CLEAR' ; Code: VK_CLEAR),<br> (Key: 'DEL' ; Code: VK_DELETE),<br> (Key: 'DELETE' ; Code: VK_DELETE),<br> (Key: 'DOWN' ; Code: VK_DOWN),<br> (Key: 'END' ; Code: VK_END),<br> (Key: 'ENTER' ; Code: VK_RETURN),<br> (Key: 'ESC' ; Code: VK_ESCAPE),<br> (Key: 'ESCAPE' ; Code: VK_ESCAPE),<br> (Key: 'HOME' ; Code: VK_HOME),<br> (Key: 'INS' ; Code: VK_INSERT),<br> (Key: 'INSERT' ; Code: VK_INSERT),<br> (Key: 'LEFT' ; Code: VK_LEFT),<br> (Key: 'NUM' ; Code: VK_NUMLOCK),<br> (Key: 'NUMLOCK' ; Code: VK_NUMLOCK),<br> (Key: 'DOWN' ; Code: VK_DOWN),<br> (Key: 'PAGEDOWN' ; Code: VK_NEXT),<br> (Key: 'PGDN' ; Code: VK_NEXT),<br> (Key: 'PAGEUP' ; Code: VK_PRIOR),<br> (Key: 'PGUP' ; Code: VK_PRIOR),<br> (Key: 'RIGHT' ; Code: VK_RIGHT),<br> (Key: 'SCROLL' ; Code: VK_SCROLL),<br> (Key: 'SCROLLLOCK' ; Code: VK_SCROLL),<br> (Key: 'PRINTSCREEN'; Code: VK_SNAPSHOT),<br> (Key: 'PRTSC' ; Code: VK_SNAPSHOT),<br> (Key: 'TAB' ; Code: VK_TAB),<br> (Key: 'UP' ; Code: VK_UP),<br> (Key: 'F1' ; Code: VK_F1),<br> (Key: 'F2' ; Code: VK_F2),<br> (Key: 'F3' ; Code: VK_F3),<br> (Key: 'F4' ; Code: VK_F4),<br> (Key: 'F5' ; Code: VK_F5),<br> (Key: 'F6' ; Code: VK_F6),<br> (Key: 'F7' ; Code: VK_F7),<br> (Key: 'F8' ; Code: VK_F8),<br> (Key: 'F9' ; Code: VK_F9),<br> (Key: 'F10' ; Code: VK_F10),<br> (Key: 'F11' ; Code: VK_F11),<br> (Key: 'F12' ; Code: VK_F12));<br><br>var<br> bPlaying,<br> bAltPressed,<br> bControlPressed,<br> bShiftPressed : Boolean;<br> Delay, CurDelay : Integer;<br> Event : TEventMsg;<br> MessageList : TMessageList;<br> iMsgCount : Integer;<br> HookHandle : hHook;<br><br>destructor TMessageList.Destroy;<br>var<br> i : Integer;<br>begin<br> for i:=0 to Count-1 do<br> Dispose(PEventMsg(Items));<br> inherited;<br>end;<br><br>procedure StopPlayback;<br>begin<br> if bPlaying then UnhookWindowsHookEx(HookHandle);<br> MessageList.Free;<br> bPlaying := False;<br>end;<br><br>function Playback(nCode: Integer; wp: wParam; lp: lParam): Longint; stdcall; export;<br>begin<br> Result := 0;<br> case nCode of<br> HC_SKIP:<br> begin<br> inc(iMsgCount);<br> if iMsgCount>=MessageList.Count then<br> StopPlayback<br> else<br> begin<br> Event := TEventMsg(MessageList.Items[iMsgCount]^);<br> CurDelay := Delay;<br> end;<br> end;<br> HC_GETNEXT:<br> begin<br> with PEventMsg(lp)^ do<br> begin<br> Message := Event.Message;<br> ParamL := Event.ParamL;<br> ParamH := Event.ParamH;<br> Time := Event.Time;<br> hWnd := Event.hWnd;<br> end;<br> Result := CurDelay;<br> CurDelay := 0;<br> end;<br> else<br> begin<br> Result := CallNextHookEx(HookHandle, nCode, wp, lp);<br> end;<br> end;<br>end;<br><br>procedure StartPlayback;<br>begin<br> Event := TEventMsg(MessageList.Items[0]^);<br> iMsgCount := 0;<br> HookHandle := SetWindowsHookEx(WH_JOURNALPLAYBACK, @Playback, hInstance, 0);<br> if HookHandle=0 then<br> raise ESetHookError.Create('Could not set hook')<br> else<br> bPlaying := True;<br>end;<br><br>procedure MakeMessage(vKey, M: UINT);<br>var<br> E: PEventMsg;<br>begin<br> New(E);<br> with E^ do<br> begin<br> Message := M;<br> ParamL := vKey;<br> ParamH := MapVirtualKey(vKey, 0);<br> Time := GetTickCount;<br> hWnd := 0;<br> end;<br> MessageList.Add(E);<br>end;<br><br>function FindKeyInArray(Key: String; var Code: UINT): Boolean;<br>var<br> i : Integer;<br>begin<br> Result := False;<br> for i:=Low(KeyDefs) to High(KeyDefs) do<br> if UpperCase(Key)=KeyDefs.Key then<br> begin<br> Code := KeyDefs.Code;<br> Result := True;<br> Exit;<br> end;<br>end;<br><br>const<br> vkKeySet = [VK_SPACE, Ord('A')..Ord('Z'), VK_MENU, VK_F1..VK_F12];<br><br>procedure SimulateKey(Code: UINT; Down: Boolean);<br>const<br> KeyMsg: array[Boolean] of UINT = (WM_KEYUP, WM_KEYDOWN);<br> SysMsg: array[Boolean] of UINT = (WM_SYSKEYUP, WM_SYSKEYDOWN);<br>begin<br> if bAltPressed and (not bControlPressed) and (Code in vkKeySet) then<br> MakeMessage(Code, SysMsg[Down])<br> else<br> MakeMessage(Code, KeyMsg[Down])<br>end;<br><br>procedure SimulateKeyPress(Code: UINT);<br>begin<br> if bAltPressed then SimulateKey(VK_MENU, True);<br> if bControlPressed then SimulateKey(VK_CONTROL, True);<br> if bShiftPressed and not bControlPressed then SimulateKey(VK_SHIFT, True);<br> SimulateKey(Code, True);<br> SimulateKey(Code, False);<br> if bShiftPressed and not bControlPressed then<br> begin<br> SimulateKey(VK_SHIFT, False);<br> bShiftPressed := False;<br> end;<br> if bControlPressed then<br> begin<br> SimulateKey(VK_CONTROL, False);<br> bControlPressed := False;<br> end;<br> if bAltPressed then<br> begin<br> SimulateKey(VK_MENU, False);<br> bAltPressed := False;<br> end;<br>end;<br><br>procedure NormalKeyPress(C: Char);<br>var<br> KeyCode,<br> Shift : UINT;<br>begin<br> KeyCode := vkKeyScan(C);<br> Shift := HiByte(KeyCode);<br> if (Shift and 1)=1 then bShiftPressed := True;<br> if (Shift and 2)=2 then bControlPressed := True;<br> if (Shift and 4)=4 then bAltPressed := True;<br> SimulateKeyPress(LoByte(KeyCode))<br>end;<br><br>function CheckDelay(Token: String): Boolean;<br>begin<br> Token := UpperCase(Token);<br> Result := Pos('DELAY', Token)=1;<br> if Result then<br> begin<br> Delete(Token, 1, 5);<br> if (Length(Token)>0) and (Token[1]='=') then Delete(Token, 1, 1);<br> Delay := StrToIntDef(Token, 0);<br> end;<br>end;<br><br>procedure ProcessKey(S: String);<br>var<br> Index : Integer;<br> Token : String;<br> KeyCode: UINT;<br>begin<br> Index := 1;<br> repeat<br> case S[Index] of<br> KeyGroupOpen:<br> begin<br> Token := '';<br> inc(Index);<br> while (Index<Length(S)) and (S[Index]<>KeyGroupClose) do<br> begin<br> Token := Token + S[Index];<br> inc(Index);<br> if (Length(Token)=12) and (S[Index]<>KeyGroupClose) then<br> raise EInvalidToken.Create('No closing brace')<br> end;<br> if (Length(Token)=1) and (Pos(Token, KeyTokens)>0) then<br> NormalKeyPress(Token[1])<br> else if FindKeyInArray(Token, KeyCode) then<br> SimulateKeyPress(KeyCode)<br> else if not CheckDelay(Token) then<br> raise EInvalidToken.Create('Invalid token');<br> end;<br> AltKey:<br> bAltPressed := True;<br> ControlKey:<br> bControlPressed := True;<br> ShiftKey:<br> bShiftPressed := True;<br> EnterKey:<br> SimulateKeyPress(VK_RETURN);<br> else<br> NormalKeyPress(S[Index]);<br> end;<br> inc(Index);<br> until Index > Length(S);<br>end;<br><br>function SendKeys(S: String; Wait: Boolean): TSendKeyError;<br>begin<br> bAltPressed := False;<br> bControlPressed := False;<br> bShiftPressed := False;<br> Result := skNone;<br> Delay := 0;<br> if bPlaying or (S='') then Exit;<br> try<br> MessageList := TMessageList.Create;<br> ProcessKey(S);<br> StartPlayback;<br> if Wait then<br> repeat<br> Application.ProcessMessages;<br> until bPlaying = False;<br> except<br> on E:ESendKeyError do<br> begin<br> MessageList.Free;<br> if E is ESetHookError then<br> Result := skFailSetHook<br> else if E is EInvalidToken then<br> Result := skInvalidToken;<br> end<br> else<br> Result := skUnknownError;<br> end;<br>end;<br><br>end.