模擬鍵盤按鍵 自動輸入文字
鍵盤對於每個操作電腦的人員來說是最熟悉不過的了。鍵盤上的按鍵可分爲兩類 按下後會在電腦的輸入窗口上出現對應字符的按鍵,如字母鍵和數字鍵等,我們稱之爲字符鍵;按下後雖然看不到字符但會產生控制作用的按鍵,如回車鍵、光標鍵等,我們稱之爲控制鍵。
對於程序員來說,鍵盤上的每個按鍵都一樣,無非是不同按鍵產生的鍵盤掃描碼不同。在不同的操作系統下,鍵盤掃描碼常常被轉換爲不同的編碼以方便應用程序調用,比如在DOS系統下的ASCII碼,在Windows系統下的虛擬鍵盤碼等等。
有時我們希望能以程序的方式模擬鍵盤按鍵,以達到自動輸入文字或者控制操作的目的。在DOS系統下通常使用中斷調用,產生鍵盤的掃描碼的方法來實現。在Windows 系統下,由於Windows本身的一些限制和特點,一般不直接使用中斷調用。
瞭解一點Windows編程的朋友應該知道, Windows系統是通過消息的傳遞(或稱事件的發生)來控制各個應用程序的執行和數據通信的。例如:應用程序打開和關閉會產生相應的窗口消息,鼠標的移動、點擊動作會產生相應的鼠標消息,同樣鍵盤的按下、彈起也會產生相應的鍵盤消息。那麼如果用程序產生鍵盤消息,也就達到了模擬鍵盤按鍵的目的。
有了這樣的思路,我們現在就來實驗一下。
首先要知道在Windows系統中與鍵盤按鍵相關的消息有:WM_KEYDOWN、WM_KEYUP、 WM_SYSKEYDOWN、WM_SYSKEYUP、WM_CHAR等。其中,WM_KEYDOWN爲鍵按下,WM_KEYUP爲鍵彈起,WM_SYSKEYDOWN爲系統鍵按下,WM_SYSKEYUP爲系統鍵彈起,WM_CHAR爲按鍵對應的字符。
要模擬鍵盤產生鍵盤消息,我們就發送一條鍵盤消息給指定窗口。比如要模擬一個字母鍵A,可以這樣PostMessage(hWnd, WM_CHAR, 'A', 0); 模擬按一個回車:PostMessage(hWnd, WM_KEYDOWN, VK_RETURN, 0)。這裏的關鍵問題是要確定窗口句柄(hWnd),使用GetFocus()函數可以得到鍵盤光標所在窗口句柄,但該函數只能得到當前進程內的窗口句柄。
如果要得到其他應用程序的鍵盤光標所在窗口句柄,需要調用 AttachThreadInput()函數。該函數的作用就是將其他窗口線程的輸入附加到本窗口線程的輸入操作中,這樣就可以調用GetFocus()函數得到其他窗口的句柄了。
與2001年第24期程序谷刊登的《如何寫一個聊天輔助程序》一文中作者使用ChildWindowFromPointEx的方法相比,使用AttachThreadInput似乎更能夠使鍵盤模擬具有通用性。
AttachThreadInput()函數的原形如下
BOOL AttachThreadInput(
DWORD idAttach, 需要附加的線程ID
DWORD idAttachTo, 附加到的線程ID
BOOL fAttach true 附加 false 取消
);
函數使用的過程大致如下
HWND hWnd;
hWnd = GetForegroundWindow(); 得到當前窗口
if (hWnd == Form1-Handle) return; 排除程序本身的窗口
DWORD FormThreadID = GetCurrentThreadId(); 本程序的線程ID
當前窗口的線程ID
DWORD CWndThreadID = GetWindowThreadProcessId(hWnd, NULL);
附加輸入線程
AttachThreadInput(CWndThreadID, FormThreadID, true);
得到當前鍵盤光標所在的窗口
hWnd = GetFocus();
取消附加的輸入線程
AttachThreadInput(CWndThreadID, FormThreadID, false);
hWnd就是當前鍵盤光標所在的窗口句柄。另外,經過測試發現,在Windows2000系統下發送字符消息(WM_CHAR)時,如果字符是一個漢字,則該字符對應的虛擬鍵盤碼高位不爲0,這樣得到的字符就不正確。解決辦法是做一個與運算 ch & 0xFF就可以了。
下面又到了給出例程的時間了。例程刷刷刷能夠在鍵盤光標所在的文本輸入框中自動輸入文字(中文、英文、數字),程序使用C++ Builder 5開發。首先運行C++ Builder並新建工程。接着,將窗體Form1的邊框樣式(BorderStyle)改爲對話框(bsDialog),並放置相應控件如圖所示,其中SS_Text是一個用於輸入文本的TComboBox控件,當然,你可以在設計階段預先向控件中輸入一些常用文本,以便程序運行後可以直接選用; txtTimes和txtDelay爲TEdit控件,分別用於控制發送文本的次數和間隔時間;chkAutoWrap和chkAutoNumber爲TCheckBox控件,決定是否在每一行發送文本後面自動回車或自動加記數編號; 以上控件包含在Panel1(TPanel控件)中; Timer1用於控制循環發送和時間間隔。
下面是程序清單
--------------------------------------------
#include
#pragma hdrstop
#include Unit1.h
--------------------------------------------
#pragma package(smart_init)
#pragma resource .dfm
int nTotalTimes, 發送本文的總次數
nTimes; 已經發送的次數
TForm1 Form1;
--------------------------------------------
__fastcall TForm1TForm1(TComponent Owner)
TForm(Owner)
{
}
--------------------------------------------
void __fastcall TForm1btnStartClick(TObject Sender) 開始刷屏
{ if (SS_Text-Text.IsEmpty())
{
文本不能爲空
ShowMessage(請輸入刷刷文本!);
SS_Text-SetFocus();
return;
}
__try
{
Timer1-Interval取值爲n秒(最小爲50毫秒)
int Interval = StrToInt(txtDelay-Text);
Timer1-Interval = (Interval 0) Interval 1000 50;
nTotalTimes取值爲n次(最小爲0次)
nTotalTimes = StrToInt(txtTimes-Text);
if (nTotalTimes 0)
nTotalTimes = 0;
nTimes = 0;
Timer1-Enabled = true;
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
ShowMessage(請輸入數值類型數據!);
return;
}
btnStart-Enabled = false;
btnStop-Enabled = true;
Panel1-Enabled = false;
Application-Minimize(); 最小化刷刷窗口
}
--------------------------------------------
void __fastcall TForm1btnStopClick(TObject Sender) 停止刷屏
{
Timer1-Enabled = false;
btnStart-Enabled = true;
btnStop-Enabled = false;
Panel1-Enabled = true;
}
--------------------------------------------
void __fastcall TForm1Timer1Timer(TObject Sender)
{
現刷屏nTimes次,到nTotalTimes次後完成。
if (nTimes == nTotalTimes)
{
btnStopClick(Sender);
return;
}
HWND hWnd;
hWnd = GetForegroundWindow(); 得到當前窗口
if (hWnd == Form1-Handle) return; 不需要程序本身的窗口
DWORD FormThreadID = GetCurrentThreadId();
DWORD CWndThreadID = GetWindowThreadProcessId(hWnd, NULL);
附加輸入線程
AttachThreadInput(CWndThreadID, FormThreadID, true);
hWnd = GetFocus(); 得到當前鍵盤光標所在的窗口
AttachThreadInput(CWndThreadID, FormThreadID, false); 取消
if (hWnd == NULL) return;
nTimes++;
for (int i = 1; i = SS_Text-Text.Length(); i++)
{ 模擬鍵盤按鍵輸入文本
PostMessage(hWnd, WM_CHAR, (WPARAM)(SS_Text-Text[i] & 0xFF), 0);
}
if (chkAutoNumber-Checked)
{ 自動編號
AnsiString Lines = IntToStr(nTimes);
for (int j = 1; j = Lines.Length(); j++)
PostMessage(hWnd, WM_CHAR, (WPARAM)(Lines[j]), 0);
}
if (chkAutoWrap-Checked) 自動回車
PostMessage(hWnd, WM_KEYDOWN, VK_RETURN, 0);
}
---------------------------------------------
本程序在Windows 2000 + C++ Builder 5下編譯通過。
作者:周世攀