keybd_event
bScan: 定義該鍵的硬體掃描碼。
dwFlags:定義函式操作的各個方面的一個標誌位集。應用程式可使用如下一些預定義常數的組合設定標誌位。
KEYEVENTF_EXTENDEDKEY:若指定該值,則掃描碼前一個值為OXEO(224)的字首位元組。
KEYEVENTF_KEYUP:若指定該值,該鍵將被釋放;若未指定該值,該鍵將被按下。
dwExtralnfo:定義與擊鍵相關的附加的32位值。
返回值:該函式無返回值。
#include
void main()
{
Sleep(3000);
keybd_event(16,0,0,0); //按下Shift鍵
keybd_event('A',0,0,0); //按下a鍵
keybd_event('A',0,KEYEVENTF_KEYUP,0); //鬆開a鍵
keybd_event(16,0,KEYEVENTF_KEYUP,0); //鬆開Shift鍵
//構成組合鍵---->按下Shift的同時按下a,形成 A
}
SendInput模擬鍵盤輸入問題
最近接觸到這個函式,因此瞭解了一下,總結一下列在這。
我瞭解它的出發點是如何通過它向活動視窗輸入字元,這是很多程式都有的功能(我猜Visual Assist X就用了這個功能)。
根據MSDN,此函式模擬按鍵操作,將一些訊息插入鍵盤或滑鼠的輸入流中,Windows對它進行處理,生成相應的WM_KEYDOWN或 WM_KEYUP事件,這些事件與普通鍵盤輸入一起進入應用程式的訊息迴圈,它們不僅可以轉換為WM_CHAR訊息,還可以轉換為其它(諸如加速鍵)等訊息。
使用它來傳送字元訊息,並沒有看起來那麼簡單。這有兩個需要考慮的問題:
1. 輸入法的轉換。例如需要向活動視窗傳送一些英文字元,我們可能想象這樣來實現:獲取對應鍵盤字元的虛擬鍵碼,傳送一個SendInput。但是如果活動視窗正在使用一個輸入法,那麼我們傳送出去的訊息,會進入輸入法的Composition視窗,最終被轉換為象形文字或被丟棄。只有當輸入法關閉時,程式執行的效果才會像我們期望的那樣,在活動視窗中顯示出英文字元。
2. 對於中文字元,應該怎麼傳送給活動視窗?由於SendInput模擬的是WM_KEYDOWN和WM_KEYUP事件,按照一般的思路,我們是否應該獲取中文字元的輸入法編碼(拼音或五筆碼),然後向活動視窗傳送編碼相關的SendInput?那這不僅要求活動視窗開啟輸入法,甚至還要獲知它的編碼方式。
如上所述,若直接如想象中那樣使用SendInput來輸入字元,則必須分析活動視窗的輸入法狀態。而且輸入英文時,要求關閉輸入法,輸入中文時,又要求開啟輸入法。若真要以這樣的思路來實現,則必定是難以成功的。
那麼,有沒有不依賴活動視窗輸入法狀態的方式呢?
其實是有的,使用SendInput模擬鍵盤輸入時,其引數是KEYBDINPUT結構,通過將其dwFlags成員設定 KEYEVENTF_UNICODE就可以了。使用此方式,只需將KEYBDINPUT.wScan設定為字元的Unicode編碼即可。對於英文字元,不需要關閉活動視窗的輸入法;對於中文字元,也不要求活動視窗開啟輸入法和將字元轉換為輸入法編碼。
MSDN對此方式的說明為:INPUT_KEYBOARD支援非鍵盤的輸入方式,例如手寫識別或語音識別,通過KEYEVENTF_UNICODE 標識,這些方式與鍵盤(文字)輸入別無二致。如果指定了KEYEVENTF_UNICODE,SendInput傳送一個WM_KEYDOWN或 WM_KEYUP訊息給活動視窗的執行緒訊息佇列,訊息的wParam引數為VK_PACKET。GetMessage或PeedMessage一旦獲得此訊息,就把它傳遞給TranslateMessage,TranslateMessage根據wScan中指定的Unicode字元產生一個 WM_CHAR訊息。若視窗是ANSI視窗,則Unicode字元會自動轉換為相應的ANSI字元。
任何需要向活動視窗輸入字元(包括英文)的功能均應使用這種方式來實現。事實上,鍵盤訊息轉換為字元訊息的過程是很複雜的,這可能與鍵盤佈局、區域、換檔狀態等諸多因素有關,這也是Windows要使用TranslateMessage來轉換訊息的原因。因此,不應該試圖通過擊鍵事件來意圖向活動視窗輸入特定的字元。
經測試,SendInput還有兩個值得注意的地方:
1. 沒有為KEYBDINPUT.dwFlags指定KEYEVENTF_KEYUP標識時,SendInput將生成WM_KEYDOWN訊息,否則生成 WM_KEYUP訊息,由於只有WM_KEYDOWN會轉換為字元訊息,因此,若以輸入字元為目標,則不應指定KEYEVENTF_KEYUP標識。
2. 如果我們想達到實際做一次擊鍵所產生的效果:順序產生一個WM_KEYDOWN和一個WM_KEYUP事件。則必須分別以不指定 KEYEVENTF_KEYUP和指定KEYEVENTF_KEYUP的方式執行一次SendInput操作。SendInput允許在一次呼叫中傳送多個模擬訊息:
INPUT input[2];
memset(input, 0, 2 * sizeof(INPUT));
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk =
data;
input[1].type = INPUT_KEYBOARD;
input[1].ki.wVk =
data;
input[1].ki.dwFlags = KEYEVENTF_KEYUP;
SendInput(2, input, sizeof(INPUT));
但實際上,這將導致不產生任何訊息。這兩個訊息必須分開傳送,如下所示:
INPUT input[2];
memset(input, 0, 2 * sizeof(INPUT));
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk =
data;
SendInput(1, input, sizeof(INPUT));
input[1].type = INPUT_KEYBOARD;
input[1].ki.wVk =
data;
input[1].ki.dwFlags = KEYEVENTF_KEYUP;
SendInput(1, input + 1, sizeof(INPUT));
關於第二點內容,我很有疑問。因為之前有人在網上帖的程式碼是合併傳送的,想必有人這麼做過並且成功了。我不清楚是否與系統或其它因素有關。我也曾試圖嘗試解決此問題,但沒有成功:
1. 根據MSDN,KEYBDINPUT.time是一個時間戳,如果為零,系統將使用它自己的時間戳。因此我懷疑兩個一起傳送的事件,是不是因為其時間戳相同,而被忽略掉了。於是我在上述程式碼中顯式設定了該屬性,再合併傳送,結果依然是沒有產生任何訊息。
2. 我分別嘗試了兩種情況:合併傳送的兩條訊息都沒有指定KEYEVENTF_KEYUP(期望得到兩個相同的字元輸入);合併傳送的兩條訊息具有不同的虛擬鍵碼且都不指定KEYEVENTF_KEYUP(期望獲得兩個不同的字元輸入)。結果依然失敗,沒有產生任何訊息。
我不清楚這是否意味著:對於鍵盤輸入,不允許將訊息合併傳送。
相關知識:
1. 輸入法也可以處理SendInput傳送的Unicode訊息,具體方式不詳。見MSDN中ImmGetProperty方法的參考:當dwIndex引數為IGP_PROPERTY時,IME_PROP_ACCEPT_WIDE_VKEY是一個可能的返回值,它表示IME會處理SendInput函式以VK_PACKET注入的Unicode字元,若返回值無該標識,則Unicode字元會直接傳送給應用程式。
在VC中使用SendInput函式實現中文的自動輸入
首先是,標頭檔案必須包含以下兩個:#include
#include
前者是SendInput函式要用到,後者是字串轉換的時候要用到。
void SendAscii(wchar_t data, BOOL
shift)
{
INPUT input[2];
memset(input, 0, 2 *
sizeof(INPUT));
if (shift)
{
input[0].type =
INPUT_KEYBOARD;
input[0].ki.wVk =
VK_SHIFT;
SendInput(1, input,
sizeof(INPUT));
}
input[0].type =
INPUT_KEYBOARD;
input[0].ki.wVk = data;
input[1].type =
INPUT_KEYBOARD;
input[1].ki.wVk = data;
input[1].ki.dwFlags = KEYEVENTF_KEYUP;
SendInput(2, input, sizeof(INPUT));
if (shift)
{
input[0].type =
INPUT_KEYBOARD;
input[0].ki.wVk =
VK_SHIFT;
input[0].ki.dwFlags =
KEYEVENTF_KEYUP;
SendInput(1, input,
sizeof(INPUT));
}
}
void SendUnicode(wchar_t
data)
{
INPUT input[2];
memset(input, 0, 2 *
sizeof(INPUT));
input[0].type =
INPUT_KEYBOARD;
input[0].ki.wVk = 0;
input[0].ki.wScan = data;
input[0].ki.dwFlags =
0x4;//KEYEVENTF_UNICODE;
input[1].type =
INPUT_KEYBOARD;
input[1].ki.wVk = 0;
input[1].ki.wScan = data;
input[1].ki.dwFlags = KEYEVENTF_KEYUP |
0x4;//KEYEVENTF_UNICODE;
SendInput(2, input,
sizeof(INPUT));
}
//為方便使用,下面這個函式包裝了前兩個函式。
void SendKeys(CString
msg)
{
short vk;
BOOL shift;
USES_CONVERSION;
wchar_t*
data = T2W(msg.GetBuffer(0));
int len = wcslen(data);
for(int i=0;i
{
if (data[i]>=0 &&
data[i]<256) //ascii字元
{
vk =
VkKeyScanW(data[i]);
if (vk == -1)
{
SendUnicode(data[i]);
}
else
{
if (vk <
0)
{
vk = ~vk +
0x1;
}
shift = vk >> 8 &
0x1;
if (GetKeyState(VK_CAPITAL) &
0x1)
{
if (data[i]>='a' && data[i]<='z' || data[i]>='A' &&
data[i]<='Z')
{
shift =
!shift;
}
}
SendAscii(vk & 0xFF,
shift);
}
}
else //unicode字元
{
SendUnicode(data[i]);
}
}
}
直接呼叫SendKeys函式就可以在當前游標的位置自動輸入指定的字串,下面的例子演示瞭如何自動開啟記事本程式並輸入一段話:
void
CSendInputDlg::OnTest()
{
ShellExecute(NULL, NULL,
"notepad.exe", NULL, NULL, SW_SHOWNORMAL);
Sleep(500); //為了確保記事本程式開啟完畢,稍等片刻
CWnd *pWnd = FindWindow(NULL, "無標題 -
記事本");
if (pWnd)
{
pWnd->SetForegroundWindow();
SendKeys("我是sway,我愛中國!\nI love China!\nEmail: xmujava@163.com\t\n2010-05-21
\b\b");
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
SendInput模擬鍵盤和滑鼠事件
INPUT kbinput[5];
ZeroMemory(
&kbinput, sizeof(INPUT)*5 );
kbinput[0].type = INPUT_KEYBOARD;
kbinput[0].ki.wVk = 'Z';
kbinput[1].type = INPUT_KEYBOARD;
kbinput[1].ki.wVk = 'W';
kbinput[2].type = INPUT_KEYBOARD;
kbinput[2].ki.wVk = 'J';
//kbinput[2].ki.dwFlags =
KEYEVENTF_KEYUP;
kbinput[3].type=INPUT_MOUSE;
kbinput[3].mi.dx=100;
kbinput[3].mi.dy=100;
kbinput[3].mi.mouseData=0;
kbinput[3].mi.dwFlags=MOUSEEVENTF_RIGHTDOWN;
kbinput[4].type=INPUT_MOUSE;
kbinput[4].mi.dx=100;
kbinput[4].mi.dy=100;
kbinput[4].mi.mouseData=0;
kbinput[4].mi.dwFlags=MOUSEEVENTF_RIGHTUP;
UINT uRet = SendInput( 5, kbinput, sizeof(INPUT) );