C++-使用SendInput方法模拟按键,无法区分右键

C++ - Simulating keystrokes using SendInput method, failure to distinguish rightctrl key

本文关键字:法区 右键 使用 SendInput 方法 模拟 C++-      更新时间:2023-10-16

我对C++的经验很少,对SendInput方法也完全不熟悉。我已经通过注册表修改设置了我的笔记本电脑(带有英国键盘),每当按下right control键并按下scroll lock两次时,就会创建一个崩溃转储。我正试图通过在Visual c++2010学习版中编译的c++可执行文件以编程方式实现这一点。

利用这篇文章:如何使用sendinput函数C++作为我的灵感,我创建了下面的代码片段。除了多个Cannot find or open the PDB调试输出之外,从阅读这篇文章中可以看出:错误消息:找不到或打开PDB文件显然可以被忽略,代码编译并运行。然而,没有BSOD发生。我已经手动"强制"了BSOD,所以我知道它是有效的。

考虑到我是一个新手,请解释一下必须做出哪些改变才能工作?

#define WINVER 0x500
#include <windows.h>
int main()
{
INPUT ip;
ip.type = INPUT_KEYBOARD;
ip.ki.wScan = 0;
ip.ki.time = 0;
ip.ki.dwExtraInfo = 0;
ip.ki.wVk = VK_RCONTROL;
ip.ki.dwFlags = 0; 
SendInput(1, &ip, sizeof(INPUT));
ip.ki.wVk = VK_SCROLL;
ip.ki.dwFlags = 0; 
SendInput(1, &ip, sizeof(INPUT));
ip.ki.wVk = VK_SCROLL;
ip.ki.dwFlags = KEYEVENTF_KEYUP;
SendInput(1, &ip, sizeof(INPUT));
ip.ki.wVk = VK_SCROLL;
ip.ki.dwFlags = 0;
SendInput(1, &ip, sizeof(INPUT));
ip.ki.wVk = VK_SCROLL;
ip.ki.dwFlags = KEYEVENTF_KEYUP;
SendInput(1, &ip, sizeof(INPUT));
ip.ki.wVk = VK_RCONTROL;
ip.ki.dwFlags = KEYEVENTF_KEYUP;
SendInput(1, &ip, sizeof(INPUT));
return 0;
}

以下是我编写的一个简单应用程序的相关代码,用于显示键入到应用程序中的键的虚拟键、扫描代码、标志等。(该代码演示创建一个列表框并处理WM_KEYDOWNWM_KEYUPWM_SYSKEYDOWNWM_SYSKEYUP消息,然后显示参数:

void CChildView::ReportKey (UINT nChar, UINT nRepCnt, UINT nFlags)
{
    CString str;
    str.Format ( "%s Virtual key=%d; Scan code=%d Extended=%d AltDown=%d",
             (nFlags & 0x8000) ? "Up" : "DOWN",
             nChar, (nFlags & 0xFF), !!(nFlags & 0x0100), !!(nFlags & 0x2000) );
    AddString (str);
}

void CChildView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    ReportKey (nChar, nRepCnt, nFlags);
    CListBox::OnKeyDown(nChar, nRepCnt, nFlags);
}
void CChildView::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    ReportKey (nChar, nRepCnt, nFlags);
    CListBox::OnKeyUp(nChar, nRepCnt, nFlags);
}
void CChildView::OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    ReportKey (nChar, nRepCnt, nFlags);
    CListBox::OnSysKeyDown(nChar, nRepCnt, nFlags);
}
void CChildView::OnSysKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    ReportKey (nChar, nRepCnt, nFlags);
    CListBox::OnSysKeyUp(nChar, nRepCnt, nFlags);
}

当按下Right Control键,然后释放时,当该应用程序具有键盘焦点时,它显示:

DOWN Virtual key=17; Scan code=29 Extended=1 AltDown=0
Up Virtual key=17; Scan code=29 Extended=1 AltDown=0

奇怪的是,虚拟密钥"17"是0x11,根据此图表,它是VK_CONTROL,而不是VK_RCONTROL!并且Extended标志为真。

按下并松开Left Control键时,输出为:

DOWN Virtual key=17; Scan code=29 Extended=0 AltDown=0
Up Virtual key=17; Scan code=29 Extended=0 AltDown=0

因此,Windows似乎从未看到VK_RCONTROL,而是看到了带有Extended = trueVK_CONTROL

所以试着用以下代码调用SendInput():

INPUT ip[6];
...
ip[0].ki.wVk = VK_CONTROL;
ip[0].ki.wScan = MapVirtualKey(VK_CONTROL, 0);
ip[0].ki.dwFlags = KEYEVENTF_EXTENDEDKEY; 
....
ip[5].ki.wVk = VK_CONTROL;
ip[5].ki.wScan = MapVirtualKey(VK_CONTROL, 0);
ip[5].ki.dwFlags = KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY; 
SendInput(_countof(ip), &ip[0], sizeof(INPUT));

EDIT:由于注释而指定ip.ki.wScan

不使用KEYEVENTF_SCACODE并不意味着wScan值将被忽略。它不会,并且如果将wScan设置为0,某些应用程序(例如RDP客户端)的行为可能会不同/错误。

编辑2:我认为这在这里并不重要,但最好只调用SendInput一次,并将INPUT结构的数组作为事务传递给它执行,这样所有的击键都会作为一个单元进行重放(例如,用户不能将自己的键穿插在击键的中间)。

编辑3:您可以下载显示键入密钥的应用程序。

此代码成功地将RControl+ScrollLock+ScrollLock填充到ScanCode应用程序中,但是,很抱歉,计算机不会像手动键入这些键时那样重新启动。

#define WINVER 0x0500
#include <windows.h>
int main()
{
    // Must specify INPUT_KEYBOARD for all INPUT structs
    INPUT ip[6] = { 
            { INPUT_KEYBOARD },
            { INPUT_KEYBOARD },
            { INPUT_KEYBOARD },
            { INPUT_KEYBOARD },
            { INPUT_KEYBOARD },
            { INPUT_KEYBOARD },
    };
    Sleep(3000);

    // Specify keys by scancode.  For the VK_SCROLL, it was necessary
    // to instead specify the wVK, otherwise VK==3 was received by ScanCode, instead
    // of VK_SCROLL == 145!
    //ip[0].ki.wVk = VK_CONTROL;
    ip[0].ki.wScan = MapVirtualKey(VK_RCONTROL, MAPVK_VK_TO_VSC);
    ip[0].ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_EXTENDEDKEY;
    ip[1].ki.wVk = VK_SCROLL;
    ip[1].ki.wScan = MapVirtualKey(VK_SCROLL, MAPVK_VK_TO_VSC);
    ip[1].ki.dwFlags = /*KEYEVENTF_SCANCODE*/ 0;
    ip[2].ki.wVk = VK_SCROLL;
    ip[2].ki.wScan = MapVirtualKey(VK_SCROLL, MAPVK_VK_TO_VSC);
    ip[2].ki.dwFlags = /*KEYEVENTF_SCANCODE |*/ KEYEVENTF_KEYUP;
    ip[3].ki.wVk = VK_SCROLL;
    ip[3].ki.wScan = MapVirtualKey(VK_SCROLL, MAPVK_VK_TO_VSC);
    ip[3].ki.dwFlags = /*KEYEVENTF_SCANCODE*/ 0;
    ip[4].ki.wVk = VK_SCROLL;
    ip[4].ki.wScan = MapVirtualKey(VK_SCROLL, MAPVK_VK_TO_VSC);
    ip[4].ki.dwFlags = /*KEYEVENTF_SCANCODE |*/ KEYEVENTF_KEYUP;
    //ip[5].ki.wVk = VK_CONTROL;
    ip[5].ki.wScan = MapVirtualKey(VK_RCONTROL, MAPVK_VK_TO_VSC);
    ip[5].ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY;
    int i = _countof(ip);
    int numSuccessful = SendInput(i, ip, sizeof(INPUT));
    if (numSuccessful == i)
        printf("Stuffed successful.n");
    else
    {
        printf("Succeeded with %d of %d; error %dn", numSuccessful, i, GetLastError());
    }
    return 0;
}

我相信原因是SendInput()将键注入到键盘驱动程序之上的层中,而正是键盘驱动程序监视这些按键来启动BSOD。

@David Ching。。。这让我沮丧不已。。。我已经阅读了所有相关的文档(并试图理解它,记住我是这方面的新手),考虑到你的建议和我所读到的内容,我尝试了无数种代码排列。根据你最后的建议,下面的代码是我最后一次尝试,但没有成功。我正在努力确定还有什么其他因素会对这个问题产生影响-硬件(笔记本电脑是东芝卫星L670D-10N)或操作系统(Windows 7 Ultimate-带英语语言包的西班牙语版本)会产生影响吗?-我无法想象。我真的,真的很感谢你的帮助,请不要放弃帮助!-顺便说一句,谢谢你的应用链接。

#define WINVER 0x0500
#include <windows.h>
int main()
{
INPUT ip[6];
ip[0].ki.wVk = VK_CONTROL;
ip[0].ki.wScan = MapVirtualKey(VK_CONTROL, 0);
ip[0].ki.dwFlags = KEYEVENTF_EXTENDEDKEY;
ip[1].ki.wVk = VK_SCROLL;
ip[1].ki.dwFlags = 0;
ip[2].ki.wVk = VK_SCROLL;
ip[2].ki.dwFlags = KEYEVENTF_KEYUP;
ip[3].ki.wVk = VK_SCROLL;
ip[3].ki.dwFlags = 0;
ip[4].ki.wVk = VK_SCROLL;
ip[4].ki.dwFlags = KEYEVENTF_KEYUP;
ip[5].ki.wVk = VK_CONTROL;
ip[5].ki.wScan = MapVirtualKey(VK_CONTROL, 0);
ip[5].ki.dwFlags = KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY;
SendInput(_countof(ip), &ip[0], sizeof(INPUT));
return 0;
}

更新-成功发送测试代码

#define WINVER 0x0500
#include <windows.h>
int main()
{
INPUT ip;
Sleep(3000);
ip.type = INPUT_KEYBOARD;
ip.ki.wScan = 0;
ip.ki.time = 0;
ip.ki.dwExtraInfo = 0;
ip.ki.wVk = 0x43;
ip.ki.dwFlags = 0; 
SendInput(1, &ip, sizeof(INPUT));
ip.ki.wVk = 0x43;
ip.ki.dwFlags = KEYEVENTF_KEYUP; 
SendInput(1, &ip, sizeof(INPUT));
ip.ki.wVk = 0x48;
ip.ki.dwFlags = 0; 
SendInput(1, &ip, sizeof(INPUT));
ip.ki.wVk = 0x48;
ip.ki.dwFlags = KEYEVENTF_KEYUP; 
SendInput(1, &ip, sizeof(INPUT));
ip.ki.wVk = 0x52;
ip.ki.dwFlags = 0; 
SendInput(1, &ip, sizeof(INPUT));
ip.ki.wVk = 0x52;
ip.ki.dwFlags = KEYEVENTF_KEYUP; 
SendInput(1, &ip, sizeof(INPUT));
ip.ki.wVk = 0x49;
ip.ki.dwFlags = 0; 
SendInput(1, &ip, sizeof(INPUT));
ip.ki.wVk = 0x49;
ip.ki.dwFlags = KEYEVENTF_KEYUP; 
SendInput(1, &ip, sizeof(INPUT));
ip.ki.wVk = 0x53;
ip.ki.dwFlags = 0; 
SendInput(1, &ip, sizeof(INPUT));
ip.ki.wVk = 0x53;
ip.ki.dwFlags = KEYEVENTF_KEYUP; 
SendInput(1, &ip, sizeof(INPUT));
ip.ki.wVk = 0x10;
ip.ki.dwFlags = 0; 
SendInput(1, &ip, sizeof(INPUT));
ip.ki.wVk = 0x43;
ip.ki.dwFlags = 0; 
SendInput(1, &ip, sizeof(INPUT));
ip.ki.wVk = 0x43;
ip.ki.dwFlags = KEYEVENTF_KEYUP; 
SendInput(1, &ip, sizeof(INPUT));
ip.ki.wVk = 0x48;
ip.ki.dwFlags = 0; 
SendInput(1, &ip, sizeof(INPUT));
ip.ki.wVk = 0x48;
ip.ki.dwFlags = KEYEVENTF_KEYUP; 
SendInput(1, &ip, sizeof(INPUT));
ip.ki.wVk = 0x52;
ip.ki.dwFlags = 0; 
SendInput(1, &ip, sizeof(INPUT));
ip.ki.wVk = 0x52;
ip.ki.dwFlags = KEYEVENTF_KEYUP; 
SendInput(1, &ip, sizeof(INPUT));
ip.ki.wVk = 0x49;
ip.ki.dwFlags = 0; 
SendInput(1, &ip, sizeof(INPUT));
ip.ki.wVk = 0x49;
ip.ki.dwFlags = KEYEVENTF_KEYUP; 
SendInput(1, &ip, sizeof(INPUT));
ip.ki.wVk = 0x53;
ip.ki.dwFlags = 0; 
SendInput(1, &ip, sizeof(INPUT));
ip.ki.wVk = 0x53;
ip.ki.dwFlags = KEYEVENTF_KEYUP; 
SendInput(1, &ip, sizeof(INPUT));
ip.ki.wVk = 0x10;
ip.ki.dwFlags = KEYEVENTF_KEYUP; 
SendInput(1, &ip, sizeof(INPUT));
return 0;
}

更新-未成功修订CTRL((滚动锁)X2)代码

#define WINVER 0x0500
#include <windows.h>
int main()
{
INPUT ip[6] = {0};
Sleep(3000);
ip[0].ki.wVk = VK_CONTROL;
ip[0].ki.wScan = MapVirtualKey(VK_CONTROL, 0);
ip[0].ki.dwFlags = KEYEVENTF_EXTENDEDKEY;
ip[1].ki.wVk = VK_SCROLL;
ip[1].ki.wScan = MapVirtualKey(VK_SCROLL, 0);
ip[1].ki.dwFlags = 0;
ip[2].ki.wVk = VK_SCROLL;
ip[2].ki.wScan = MapVirtualKey(VK_SCROLL, 0);
ip[2].ki.dwFlags = KEYEVENTF_KEYUP;
ip[3].ki.wVk = VK_SCROLL;
ip[3].ki.wScan = MapVirtualKey(VK_SCROLL, 0);
ip[3].ki.dwFlags = 0;
ip[4].ki.wVk = VK_SCROLL;
ip[4].ki.wScan = MapVirtualKey(VK_SCROLL, 0);
ip[4].ki.dwFlags = KEYEVENTF_KEYUP;
ip[5].ki.wVk = VK_CONTROL;
ip[5].ki.wScan = MapVirtualKey(VK_CONTROL, 0);
ip[5].ki.dwFlags = KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY;
SendInput(_countof(ip), &ip[0], sizeof(INPUT));
return 0;
}