一、SendInput
SendInput可以将指定的滑鼠鍵盤消息插入到系統消息隊列,進而實作對滑鼠鍵盤的模拟。有很多程式對SendInput進行了屏蔽,但不是所有的。是以這裡介紹一下SendInput的使用。我已經将主要的模拟功能寫在了一個單元檔案中:SIMouseKeyboard.pas,調用該單元檔案中的相關函數就可以實作滑鼠鍵盤的模拟。該單元檔案的下載下傳見本樓末尾。SendInput的參數其實很簡單,在Windows.pas就有函數的聲明如下:
function SendInput(cInputs: UINT; var pInputs: TInput; cbSize: Integer): UINT; stdcall;
cInputs:定義pInputs中記錄數組的元素數目。pInputs:TInput類型記錄數組的第1個元素。每個元素代表插人到系統消息隊列的鍵盤或滑鼠事件。cbSize:定義TInput的大小,一般為SizeOf(TInput)。函數傳回成功插入系統消息隊列中事件的數目,失敗傳回0。調用SendInput關鍵的就是要搞清楚它的幾個記錄結構的意思,在Windows.pas中對TInput的聲明如下:
tagINPUT = packed record
Itype: DWORD;
case Integer of
0: (mi: TMouseInput);
1: (ki: TKeybdInput);
2: (hi: THardwareInput);
end;
TInput = tagINPUT;
其中mi、ki、hi是3個共用型的記錄結構,Itype指出記錄結構中所使用的類型,它有3個值。INPUT_MOUSE:表示使用mi記錄結構,忽略ki和hi;INPUT_KEYBOARD:表示使用ki記錄結構,忽略mi和hi。
二、鍵盤模拟
TKeybdInput記錄結構的聲明如下:
tagKEYBDINPUT = packed record
wVk: WORD;
wScan: WORD;
dwFlags: DWORD;
time: DWORD;
dwExtraInfo: DWORD;
TKeybdInput = tagKEYBDINPUT;
其中wVk是将要操作的按鍵的虛鍵碼。wScan是安全碼,一般不用。dwFlags指定鍵盤所進行的操作,為0時表示按下某鍵,KEYEVENTF_KEYUP表示放開某鍵。time是時間戳,可以使用API函數GetTickCount的傳回值。dwExtraInfo是擴充資訊,可以使用API函數GetMessageExtraInfo的傳回值。例如擊鍵“A”的程式如下:
procedure KeyPressA;
var
Inputs : array [0..1] of TInput;
begin
Inputs[0].Itype:=INPUT_KEYBOARD;
with Inputs[0].ki do
begin
wVk:=VK_A;
wScan:=0;
dwFlags:=0;
time:=GetTickCount;
dwExtraInfo:=GetMessageExtraInfo;
end;
Inputs[1].Itype:=INPUT_KEYBOARD;
with Inputs[1].ki do
dwFlags:=KEYEVENTF_KEYUP;
SendInput(2,Inputs[0],SizeOf(TInput));
end;
注意:在Windows.pas單元中并沒有字母和數字的虛鍵碼的聲明,在我寫的SIMouseKeyboard.pas單元檔案中對所有的虛鍵碼進行了重新聲明,包含了字母、數字和标點符号。
三、滑鼠模拟
TMouseInput記錄結構的聲明如下:
tagMOUSEINPUT = packed record
dx: Longint;
dy: Longint;
mouseData: DWORD;
TMouseInput = tagMOUSEINPUT;
其中dx、dy是滑鼠移動時的坐标差(不是象素機關),在滑鼠移動時有效。mouseData是滑鼠滾輪滾動值,在滾動滑鼠滾輪時有效。當mouseData小于0時向下滾動,當mouseData大于0時向上滾動,mouseData的絕對值一般設為120。dwFlags指定滑鼠所進行的操作,例如,MOUSEEVENTF_MOVE表示移動滑鼠,MOUSEEVENTF_LEFTDOWN表示按下滑鼠左鍵,MOUSEEVENTF_LEFTUP表示放開滑鼠左鍵。time是時間戳,可以使用API函數GetTickCount的傳回值。dwExtraInfo是擴充資訊,可以使用API函數GetMessageExtraInfo的傳回值。例如單擊滑鼠左鍵的程式如下:
procedure MouseClick;
Inputs[0].Itype:=INPUT_MOUSE;
with Inputs[0].mi do
dx:=0;
dy:=0;
mouseData:=0;
dwFlags:=MOUSEEVENTF_LEFTDOWN;
Inputs[1].Itype:=INPUT_MOUSE;
with Inputs[1].mi do
dwFlags:=MOUSEEVENTF_LEFTUP;
滑鼠的移動總是很麻煩,上面的dx、dy不是以象素為機關的,而是以滑鼠裝置移動量為機關的,它們之間的比值受滑鼠移動速度設定的影響。具體的解決方法我已經在《Delphi下利用WinIo模拟滑鼠鍵盤詳解》一文中進行了讨論,這裡不再重複。dwFlags可以設定一個MOUSEEVENTF_ABSOLUTE标志,這使得可以用另外一種方法移動滑鼠。當dwFlags設定了MOUSEEVENTF_ABSOLUTE标志,dx、dy為螢幕坐标值,表示将滑鼠移動到dx,dy的位置。但是這個坐标值也不是以象素為機關的。這個值的範圍是0到65535($FFFF),當dx等于0、dy等于0時表示螢幕的最左上角,當dx等于65535、dy等于65535時表示螢幕的最右下角,相當于将螢幕的寬和高分别65536等分。API函數GetSystemMetrics(SM_CXSCREEN)可以傳回螢幕的寬度,函數GetSystemMetrics(SM_CYSCREEN)可以傳回螢幕的高度,利用螢幕的寬度和高度就可以将象素坐标換算成相應的dx、dy。注意:這種換算最多會出現1象素的誤差。例如:将滑鼠指針移動到螢幕150,120坐标處的程式如下:
procedure MouseMove;
Input : TInput;
Input.Itype:=INPUT_MOUSE;
with Input.mi do
dx:=($FFFF div (GetSystemMetrics(SM_CXSCREEN)-1)) * 150;
dy:=($FFFF div (GetSystemMetrics(SM_CYSCREEN)-1)) * 120;
dwFlags:=MOUSEEVENTF_MOVE or MOUSEEVENTF_ABSOLUTE;
SendInput(1,Input,SizeOf(TInput));
四、SendInput與WInIo的對比
在《Delphi下利用WinIo模拟滑鼠鍵盤詳解》一文中我已經說了WinIo的很多缺點,SendInput幾乎沒有這些缺點。SendInput的模拟要比WinIo簡單的多。事件是被直接插入到系統消息隊列的,是以它的速度比WinIo要快。系統也會保證資料的完整性,不會出現資料包混亂的情況。利用“絕對移動”可以将滑鼠指針移動到準确的位置,同滑鼠的配置隔離不會出現相容性的問題。SendInput的缺點也是最要命的,它會被一些程式屏蔽。是以說在SendInput與WInIo都可以使用的情況下優先考慮SendInput。另外SendInput與WInIo可以接合使用,一些程式對滑鼠左鍵單擊敏感,可以使用WinIo模拟滑鼠左鍵單擊,其它操作由SendInput模拟。
五、SIMouseKeyboard.pas的使用
我在SIMouseKeyboard.pas單元檔案中對所有的虛鍵碼進行了重新聲明,包含了Windows.pas單元中沒有聲明的字母、數字和标點符号。在SIMouseKeyboard.pas單元檔案中共有9個函數,使用方法如下:
1、procedure SIKeyDown(Key : WORD);
按下指定的鍵。Key為虛鍵碼。
2、procedure SIKeyUp(Key : WORD);
放開指定的鍵。Key為虛鍵碼。
3、procedure SIKeyPress(Key : WORD; Interval : Cardinal);
按下并放開指定的鍵,Interval為按下和放開之間的時間間隔。注意:本函數不支援重複機打,即無論Interval設的多麼大都隻有一次按鍵。
4、procedure SIKeyInput(const Text : String; Interval : Cardinal);
模拟鍵盤輸入指定的文本,傳回是否成功。文本中隻能是單位元組字元(#32~#126)、Tab(#9)鍵和Enter鍵(#13),即可以從鍵盤上輸入的字元,不能是漢字,其它字元會被忽略。Interval為按下和放開鍵之間的時間間隔,機關毫秒。
示範程式,組合鍵Ctrl+A如下:
SIKeyDown(VK_CONTROL); //按下Ctrl
SIKeyPress(VK_A); //擊鍵A
SIKeyUp(VK_CONTROL); //放開Ctrl
5、procedure SIMouseDown(Key : WORD);
按下滑鼠的指定鍵。Key為虛鍵碼,滑鼠左鍵為VK_LBUTTON,右鍵為VK_RBUTTON,中鍵為VK_MBUTTON。
6、procedure SIMouseUp(Key : WORD);
放開滑鼠的指定鍵。Key為虛鍵碼,滑鼠左鍵為VK_LBUTTON,右鍵為VK_RBUTTON,中鍵為VK_MBUTTON。
7、procedure SIMouseClick(Key : WORD; Interval : Cardinal);
單擊滑鼠的指定鍵,Interval為按下和放開之間的時間間隔。過快的單擊可能會使一些程式無法識别,适當的調整Interval的值可以解決這個問題。
8、procedure SIMouseWheel(dZ : Integer);
滾動滑鼠的滾輪。當dZ小于0時向下滾動,當dZ大于0時向上滾動,dZ的絕對值一般設為120。
9、procedure SIMouseMoveTo(X,Y : Integer; MaxMove : Integer; Interval : Cardinal);
将滑鼠指針移動到指定位置,傳回是否成功。X和Y為象素值,X和Y的值的範圍不能超出螢幕,MaxMove為移動時的dX和dY的最大值,Interval為兩次移動之間的時間間隔,一些程式對滑鼠移動速度敏感,當滑鼠移動太快時無法對滑鼠做出反應,适當的設定MaxMove和Interval的值可以解決這個問題。
示範程式,拖放到指定位置如下:
SIMouseDown(VK_LBUTTON); //按下滑鼠左鍵
SIMouseMoveTo(780,300); //移動到指定位置
SIMouseUp(VK_LBUTTON); //放開滑鼠左鍵