LRESULT CALLBACK ProcessMsgs(HWND hwnd, UINT msg, WPARAM param, LPARAM lparam) {
switch (msg) {
case WM_HOTKEY: {
WORD AUXKEY = LOWORD(lparam);
WORD MKEY = HIWORD(lparam);
INPUT ip;
ip.type = INPUT_KEYBOARD;
ip.ki.time = 0;
ip.ki.dwExtraInfo = 0;
ip.ki.dwFlags = KEYEVENTF_UNICODE;
ip.ki.wVk = 0; // 0 because of unicode
if (AUXKEY == MOD_ALT) {
switch (MKEY) { // Lowercase
case 0x41: // A
ip.ki.wScan = 0xE1; // lowercase a with accent
break;
case 0x4E: // N
ip.ki.wScan = 0xF1; // lowercase n with tilde accent
break;
case 0x4F: // O
ip.ki.wScan = 0xF3; // lowercase o with accent
break;
case 0x55: // U
ip.ki.wScan = 0xFA; // lowercase u with accent
break;
case 0x49: // I
ip.ki.wScan = 0xED; // lowercase i with accent
break;
case 0xBD: // DASH
ip.ki.wScan = 0x2014; // em dash
break;
}
SendInput(1, &ip, sizeof(INPUT)); // breakpoint here
}
if (AUXKEY == MOD_ALT MOD_SHIFT) {
switch (MKEY) { // Uppercase
case 0x41: // A
ip.ki.wScan = 0xC1; // uppercase a with accent
break;
case 0x4E: // N
ip.ki.wScan = 0xF1; // uppercase n with tilde accent
break;
case 0x4F: // O
ip.ki.wScan = 0xD2; // uppercase o with accent
break;
case 0x55: // U
ip.ki.wScan = 0xDA; // uppercase u with accent
break;
case 0x49: // I
ip.ki.wScan = 0xCD; // uppercase i with accent
break;
}
SendInput(1, &ip, sizeof(INPUT));
}
ip.ki.dwFlags = KEYEVENTF_KEYUP KEYEVENTF_UNICODE; // KEYEVENTF_KEYUP for key release
SendInput(1, &ip, sizeof(INPUT));
return 0;
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hwnd, msg, param, lparam);
}
}
當與 ALT 鍵結合使用時,此代碼鍵入帶有重音的元音。它WM_HOTKEY從注冊的熱鍵接收訊息,如下所示:
RegisterHotKey(
TheWindow,
NULL,
MOD_ALT,
0x41 // A
);
所有元音、字母 N 和破折號字符都有熱鍵。我遇到的問題是,當我在初始 SendInput 的行上使用斷點時代碼作業正常,但是當我正常運行它時,沒有輸入帶重音的字母。所有這些中斷都在那里,因為開關總是使用最后一種情況,我不知道為什么。
問題是當我運行我在上述代碼之前開發的這個非常 alpha 和原始版本的代碼時,它不需要斷點就可以正常作業(這個版本使用 CTRL 作為指示器,我將其更改為 ALT,因為有這么多的功能使用 CTRL 加一個字母作為用戶函式的程式... RegisterHotKey 函式使用MOD_CONTROL而不是MOD_ALT相應的):
switch (msg) {
case WM_HOTKEY:
INPUT ip;
ip.type = INPUT_KEYBOARD;
ip.ki.wScan = 0xE1; // lowercase a with accent
ip.ki.time = 0;
ip.ki.dwExtraInfo = 0;
ip.ki.dwFlags = KEYEVENTF_UNICODE;
ip.ki.wVk = 0; // 0 because of unicode
SendInput(1, &ip, sizeof(INPUT));
ip.ki.dwFlags = KEYEVENTF_KEYUP | KEYEVENTF_UNICODE; // KEYEVENTF_KEYUP for key release
SendInput(1, &ip, sizeof(INPUT));
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hwnd, msg, param, lparam);
}
我知道我可以使用識別符號來簡化其中的一些,但無論如何......代碼功能,只是帶有斷點......太奇怪了。
uj5u.com熱心網友回復:
問題
不幸的是,我手頭沒有要測驗的 Windows 系統,但我想我可以猜到你的問題是什么 - 但是很好的 heisenbug,我花了很長時間才弄清楚:D
請記住,即使您有一個注冊的熱鍵,目標視窗仍然會得到按鍵。
除此之外SendInput 不會重置鍵盤狀態:
此功能不會重置鍵盤的當前狀態。呼叫該函式時已按下的任何鍵都可能會干擾該函式生成的事件。為避免此問題,請使用 GetAsyncKeyState 函式檢查鍵盤狀態并根據需要進行更正。
因此,從目標視窗的視圖中,您會看到以下關鍵事件:
ALT向下A向下- (熱鍵激活)
á向下á向上- (用戶需要一些時間來釋放密鑰)
A向上ALT向上
而如果您正在除錯,您可能會在遇到斷點后釋放鍵,因此序列如下所示:
ALT向下A向下- (熱鍵激活)
- (斷點命中)
- (用戶釋放密鑰)
A向上ALT向上- (恢復程序)
á向下á向上
所以這就是它在除錯時作業的最可能原因 - 一旦斷點被??擊中,你就會釋放鍵。
ALT 鍵本身有點邪惡 - 主要是因為只要您按住 ALT 鍵,視窗就不會收到WM_KEYDOWN訊息,而只會收到WM_SYSKEYDOWN訊息:Key-Down 和 Key-Up 訊息
WM_SYSKEYDOWN 訊息指示系統鍵,它是呼叫系統命令的鍵擊。有兩種型別的系統密鑰:
- ALT 任意鍵
- F10
所有其他擊鍵都被視為非系統鍵并產生 WM_KEYDOWN 訊息。這包括 F10 以外的功能鍵。
所以基本上目標應用程式將完全忽略您的輸入,因為它只接收它作為 SYSKEY 訊息。
解決方案
您可以通過在呼叫熱鍵時檢查 ALT 鍵(或任何其他修飾鍵)是否仍然按下來解決此問題,例如通過GetAsyncKeyState()然后呼叫SendInput()該修飾鍵的釋放事件,然后發送您的特殊字符,然后恢復修飾鍵的狀態。
此外,我建議將所有發送的鍵事件集中到一個SendInput()中,以確保在您的輸入事件之間沒有用戶輸入(或其他編程輸入)交錯。
例如:
std::vector<INPUT> inputEvents;
short altKeyState = GetAsyncKeyState(VK_MENU);
// if we can't get the current input state
// we most likely aren't allowed to inject input
if(altKeyState == 0) return 0;
bool isAltDown = altKeyState < 0;
if(isAltDown) {
INPUT ip;
ip.type = INPUT_KEYBOARD;
ip.ki.time = 0;
ip.ki.dwExtraInfo = 0;
ip.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP;
ip.ki.wVk = VK_MENU;
inputEvents.push_back(ip);
}
// TODO: Add other modifier keys that might interfere with input
// Generate your char input
INPUT ips;
ips.type = INPUT_KEYBOARD;
ips.ki.wScan = 0xE1;
ips.ki.time = 0;
ips.ki.dwExtraInfo = 0;
ips.ki.dwFlags = KEYEVENTF_UNICODE;
ips.ki.wVk = 0;
inputEvents.push_back(ips);
if(isAltDown) {
INPUT ip;
ip.type = INPUT_KEYBOARD;
ip.ki.time = 0;
ip.ki.dwExtraInfo = 0;
ip.ki.dwFlags = KEYEVENTF_SCANCODE;
ip.ki.wVk = VK_MENU;
inputEvents.push_back(ip);
}
// TODO: Add other modifier keys that might interfere with input
// Submit all inputs to the queue
UINT result = SendInput(inputEvents.size(), &inputEvents[0], sizeof(INPUT));
if(result == inputEvents.size()) {
// SUCCESS! Injected all inputs
} else if (result > 0) {
// Partial success, something failed, not really much you can do here other than call GetLastError()
} else {
// blocked by other thread
}
請注意,您的程式仍然受制于 UIPI(用戶界面特權隔離),因此您不能使用SendInput()將符號注入以更高完整性級別運行的程式中。
如果您專注于具有更高完整性級別的程式視窗,它根本不會收到您的SendInput()訊息 - 但您也根本無法檢測到故障。
(除非它曾經ChangeWindowMessageFilter(Ex)允許來自低優先級行程的鍵盤輸入)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/410342.html
標籤:
