天天看點

SendInput模拟滑鼠鍵盤

一、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);   //放開滑鼠左鍵