Delphi的SendKeys函数

2012-10-07  金城  6414

(*
	VB中有SendKeys函数
	DELPHI中一般用WinAPI (keybd_event,SendInput,PostMessage,SendMessage 等)		

	Converts a string of characters and key names to keyboard events and
	passes them to Windows.
	将一组由或字符或键名组成的字符串转换成按键事件交给Windows系统去处理。
	
	Example syntax:用法:
	SendKeys( 'abcdefg^{ENTER}', True); // 键入abcdefg[CTRL+回车]
*)

Function SendKeys(SendKeysString: PChar; Wait: Boolean): Boolean;
type
	WBytes = array[0..pred(SizeOf(Word))] of Byte;

	TSendKey = record
		Name: ShortString;
		VKey: Byte;
	end;

const
	{Array of keys that SendKeys recognizes.

	If you add to this list, you must be sure to keep it sorted alphabetically
	by Name because a binary search routine is used to scan it.}

	MaxSendKeyRecs = 41;
	SendKeyRecs: array[1..MaxSendKeyRecs] of TSendKey = (
	  (Name:'BKSP ';	VKey:VK_BACK),
	  (Name:'BS ';	VKey:VK_BACK),
	  (Name:'BACKSPACE ';	VKey:VK_BACK),
	  (Name:'BREAK ';	VKey:VK_CANCEL),
	  (Name:'CAPSLOCK ';	VKey:VK_CAPITAL),
	  (Name:'CLEAR ';	VKey:VK_CLEAR),
	  (Name:'DEL ';	VKey:VK_DELETE),
	  (Name:'DELETE ';	VKey:VK_DELETE),
	  (Name:'DOWN ';	VKey:VK_DOWN),
	  (Name:'END ';	VKey:VK_END),
	  (Name:'ENTER ';	VKey:VK_RETURN),
	  (Name:'ESC ';	VKey:VK_ESCAPE),
	  (Name:'ESCAPE ';	VKey:VK_ESCAPE),
	  (Name:'F1 ';	VKey:VK_F1),
	  (Name:'F10 ';	VKey:VK_F10),
	  (Name:'F11 ';	VKey:VK_F11),
	  (Name:'F12 ';	VKey:VK_F12),
	  (Name:'F13 ';	VKey:VK_F13),
	  (Name:'F14 ';	VKey:VK_F14),
	  (Name:'F15 ';	VKey:VK_F15),
	  (Name:'F16 ';	VKey:VK_F16),
	  (Name:'F2 ';	VKey:VK_F2),
	  (Name:'F3 ';	VKey:VK_F3),
	  (Name:'F4 ';	VKey:VK_F4),
	  (Name:'F5 ';	VKey:VK_F5),
	  (Name:'F6 ';	VKey:VK_F6),
	  (Name:'F7 ';	VKey:VK_F7),
	  (Name:'F8 ';	VKey:VK_F8),
	  (Name:'F9 ';	VKey:VK_F9),
	  (Name:'HELP ';	VKey:VK_HELP),
	  (Name:'HOME ';	VKey:VK_HOME),
	  (Name:'INS ';	VKey:VK_INSERT),
	  (Name:'LEFT ';	VKey:VK_LEFT),
	  (Name:'NUMLOCK ';	VKey:VK_NUMLOCK),
	  (Name:'PGDN ';	VKey:VK_NEXT),
	  (Name:'PGUP ';	VKey:VK_PRIOR),
	  (Name:'PRTSC ';	VKey:VK_PRINT),
	  (Name:'RIGHT ';	VKey:VK_RIGHT),
	  (Name:'SCROLLLOCK ';	VKey:VK_SCROLL),
	  (Name:'TAB ';	VKey:VK_TAB),
	  (Name:'UP ';	VKey:VK_UP)
	);

	{Extra VK constants missing from Delphi 's Windows API interface}
	VK_NULL=0;
	VK_SemiColon=186;
	VK_Equal=187;
	VK_Comma=188;
	VK_Minus=189;
	VK_Period=190;
	VK_Slash=191;
	VK_BackQuote=192;
	VK_LeftBracket=219;
	VK_BackSlash=220;
	VK_RightBracket=221;
	VK_Quote=222;
	VK_Last=VK_Quote;

	ExtendedVKeys: set of byte =
	[VK_Up,
	  VK_Down,
	  VK_Left,
	  VK_Right,
	  VK_Home,
	  VK_End,
	  VK_Prior,	 {PgUp}
	  VK_Next,	 {PgDn}
	  VK_Insert,
	  VK_Delete];

const
	INVALIDKEY = $FFFF {Unsigned -1};
	VKKEYSCANSHIFTON = $01;
	VKKEYSCANCTRLON = $02;
	VKKEYSCANALTON = $04;
	UNITNAME = 'SendKeys ';
var
	UsingParens, ShiftDown, ControlDown, AltDown, FoundClose: Boolean;
	PosSpace: Byte;
	I, L: Integer;
	NumTimes, MKey: Word;
	KeyString: String[20];
	AllocationSize:Integer;

procedure DisplayMessage(Message: PChar);
begin
	MessageBox(0,Message,UNITNAME,0);
end;

function BitSet(BitTable, BitMask: Byte): Boolean;
begin
	Result:=ByteBool(BitTable and BitMask);
end;

procedure SetBit(var BitTable: Byte; BitMask: Byte);
begin
	BitTable:=BitTable or Bitmask;
end;

Procedure KeyboardEvent(VKey, ScanCode: Byte; Flags: Longint);
var
	KeyboardMsg: TMsg;
begin
	keybd_event(VKey, ScanCode, Flags,0);
	If (Wait) then While (PeekMessage(KeyboardMsg,0,WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)) do begin
		TranslateMessage(KeyboardMsg);
		DispatchMessage(KeyboardMsg);
	end;
end;

Procedure SendKeyDown(VKey:Byte; NumTimes: Word; GenUpMsg: Boolean);
var
	Cnt: Word;
	ScanCode: Byte;
	NumState: Boolean;
	KeyBoardState: TKeyboardState;
begin
	If (VKey=VK_NUMLOCK) then begin
		NumState:=ByteBool(GetKeyState(VK_NUMLOCK) and 1);
		GetKeyBoardState(KeyBoardState);
		If NumState then KeyBoardState[VK_NUMLOCK]:=(KeyBoardState[VK_NUMLOCK] and not 1)
		else KeyBoardState[VK_NUMLOCK]:=(KeyBoardState[VK_NUMLOCK] or 1);
		SetKeyBoardState(KeyBoardState);
		exit;
	end;

	ScanCode:=Lo(MapVirtualKey(VKey,0));
	For Cnt:=1 to NumTimes do
		If (VKey in ExtendedVKeys)then begin
			KeyboardEvent(VKey, ScanCode, KEYEVENTF_EXTENDEDKEY);
			If (GenUpMsg) then
				KeyboardEvent(VKey, ScanCode, KEYEVENTF_EXTENDEDKEY or KEYEVENTF_KEYUP)
		end else begin
			KeyboardEvent(VKey, ScanCode, 0);
			If (GenUpMsg) then KeyboardEvent(VKey, ScanCode, KEYEVENTF_KEYUP);
		end;
end;

Procedure SendKeyUp(VKey:Byte);
var
	ScanCode: Byte;
begin
	ScanCode:=Lo(MapVirtualKey(VKey,0));
	If (VKey in ExtendedVKeys)then
		KeyboardEvent(VKey, ScanCode, KEYEVENTF_EXTENDEDKEY and KEYEVENTF_KEYUP)
	else KeyboardEvent(VKey, ScanCode, KEYEVENTF_KEYUP);
end;

Procedure SendKey(MKey:Word; NumTimes: Word; GenDownMsg: Boolean);
begin
	If (BitSet(Hi(MKey),VKKEYSCANSHIFTON)) then SendKeyDown(VK_SHIFT,1,False);
	If (BitSet(Hi(MKey),VKKEYSCANCTRLON)) then SendKeyDown(VK_CONTROL,1,False);
	If (BitSet(Hi(MKey),VKKEYSCANALTON)) then SendKeyDown(VK_MENU,1,False);
	SendKeyDown(Lo(MKey), NumTimes, GenDownMsg);
	If (BitSet(Hi(MKey),VKKEYSCANSHIFTON)) then SendKeyUp(VK_SHIFT);
	If (BitSet(Hi(MKey),VKKEYSCANCTRLON)) then SendKeyUp(VK_CONTROL);
	If (BitSet(Hi(MKey),VKKEYSCANALTON)) then SendKeyUp(VK_MENU);
end;

{Implements a simple binary search to locate special key name strings}

Function StringToVKey(KeyString: ShortString): Word;
var
	Found, Collided: Boolean;
	Bottom, Top, Middle: Byte;
begin
	Result:=INVALIDKEY;
	Bottom:=1;
	Top:=MaxSendKeyRecs;
	Found:=false;
	Middle:=(Bottom+Top) div 2;
	Repeat
		Collided:=((Bottom=Middle) or (Top=Middle));
		If (KeyString=SendKeyRecs[Middle].Name) then begin
			  Found:=True;
			  Result:=SendKeyRecs[Middle].VKey;
		end else begin
			  If (KeyString> SendKeyRecs[Middle].Name) then Bottom:=Middle
			  else Top:=Middle;
			  Middle:=(Succ(Bottom+Top)) div 2;
		end;
	Until (Found or Collided);
	If (Result=INVALIDKEY) then DisplayMessage( 'Invalid Key Name ');
end;

procedure PopUpShiftKeys;
begin
	If (not UsingParens) then begin
		If ShiftDown then SendKeyUp(VK_SHIFT);
		If ControlDown then SendKeyUp(VK_CONTROL);
		If AltDown then SendKeyUp(VK_MENU);
		ShiftDown:=false;
		ControlDown:=false;
		AltDown:=false;
	end;
end;

begin
	AllocationSize:=MaxInt;
	Result:=false;
	UsingParens:=false;
	ShiftDown:=false;
	ControlDown:=false;
	AltDown:=false;
	I:=0;
	L:=StrLen(SendKeysString);
	If (L> AllocationSize) then L:=AllocationSize;
	If (L=0) then Exit;

	While(I<L) do begin
		case SendKeysString[I] of
		'( ' : begin
				UsingParens:=True;
				Inc(I);
			end;
		') ' : begin
				UsingParens:=False;
				PopUpShiftKeys;
				Inc(I);
			end;
		'% ' : begin
				AltDown:=True;
				SendKeyDown(VK_MENU,1,False);
				Inc(I);
			end;
		'+ ' : begin
				ShiftDown:=True;
				SendKeyDown(VK_SHIFT,1,False);
				Inc(I);
			end;
		'^ ' : begin
				ControlDown:=True;
				SendKeyDown(VK_CONTROL,1,False);
				Inc(I);
			end;
		'{ ' : begin
				NumTimes:=1;
				If (SendKeysString[Succ(I)]= '{ ') then begin
					MKey:=VK_LEFTBRACKET;
					SetBit(Wbytes(MKey)[1],VKKEYSCANSHIFTON);
					SendKey(MKey,1,True);
					PopUpShiftKeys;
					Inc(I,3);
					Continue;
				end;
				KeyString:= ' ';
				FoundClose:=False;
				While (I <=L) do begin
					Inc(I);
					If (SendKeysString[I]= '} ') then begin
						FoundClose:=True;
						Inc(I);
						Break;
					end;
					KeyString:=KeyString+Upcase(SendKeysString[I]);
				end;
				If (Not FoundClose) then begin
					DisplayMessage( 'No Close ');
					Exit;
				end;
				If (SendKeysString[I]= '} ') then begin
					MKey:=VK_RIGHTBRACKET;
					SetBit(Wbytes(MKey)[1],VKKEYSCANSHIFTON);
					SendKey(MKey,1,True);
					PopUpShiftKeys;
					Inc(I);
					Continue;
				end;
				PosSpace:=Pos( '   ',KeyString);
				If (PosSpace<>0)   then   begin
					NumTimes:=StrToInt(Copy(KeyString,Succ(PosSpace),Length(KeyString)-PosSpace));
					KeyString:=Copy(KeyString,1,Pred(PosSpace));
				end;
				If (Length(KeyString)=1) then MKey:=vkKeyScan(KeyString[1])
				else MKey:=StringToVKey(KeyString);
				If (MKey<>INVALIDKEY)   then   begin
					SendKey(MKey,NumTimes,True);
					PopUpShiftKeys;
					Continue;
				end;
        end;
		'~ ' : begin
				SendKeyDown(VK_RETURN,1,True);
				PopUpShiftKeys;
				Inc(I);
			end;
		else begin
				MKey:=vkKeyScan(SendKeysString[I]);
				If (MKey <> INVALIDKEY) then begin
					SendKey(MKey,1,True);
					PopUpShiftKeys;
				end else DisplayMessage( 'Invalid   KeyName ');
				Inc(I);
			end;
		end;
	end;
	Result:=true;
	PopUpShiftKeys;
end;