0

I have very little experience in C++, and I'm completely unfamiliar with the SendInput method. I've setup my laptop (with UK keyboard) via a registry modification, to create a crash dump whenever the right control key is held down and scroll lock is twice pressed. I am trying to achieve this programmatically via a c++ executable, compiled in Visual C++ 2010 Express.

Using this post: how to use sendinput function C++ as my inspiration, I have created the code snippet hereunder. Aside from multiple Cannot find or open the PDB debug outputs, which from reading this post: Error Message : Cannot find or open the PDB file can apparently be ignored, the code compiles and runs. However no BSOD transpires. I have manually "forced" the BSOD, so I know it works.

Bearing in mind I'm a novice, please explain what changes must be made for this to work?

#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;
}
Community
  • 1
  • 1
Albert F D
  • 529
  • 2
  • 13
  • 33
  • there is a program called NotMyFault you can use to trigger a BSOD. It is part of the Windows Internals book but seems to be available for free download separately. – David Ching Apr 04 '15 at 18:03
  • Thanks David, I will look into this... struggling on a different path toward programming enlightenment at the moment, but will return to this micro-project sooner or later. :D regards! – Albert F D Apr 05 '15 at 22:37

3 Answers3

1

Following is the relevant code for a simple application I wrote to display the virtual key, scan code, flags, etc. of keys that are typed into the application. (The code demonstrates creating a listbox and handling the WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN, and WM_SYSKEYUP messages, then displaying the parameters:

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);
}

When the Right Control key is pressed, then released, while this app has the keyboard focus, it displays:

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

Curiously, virtual key "17" is 0x11, which according to this chart is VK_CONTROL, not VK_RCONTROL! And the Extended flag is true.

When the Left Control key is pressed and released, the output is:

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

So it seems Windows never sees a VK_RCONTROL, instead it sees a VK_CONTROL with Extended = true.

So try to call SendInput() with that:

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: Specify ip.ki.wScan due to the comment

not using KEYEVENTF_SCANCODE doesn't mean the wScan value will be ignored. It won't and some applications (e.g. RDP-Client) may behave different/wrong if you set wScan to 0.

EDIT 2: I don't think it matters here, but it is better to call SendInput just once, and pass it an array of INPUT structures to execute as a transaction, so all the keystrokes are replayed as a unit (and user can't intersperse his own keys during the middle of yours, for example).

EDIT 3: You can download the application that shows the keys typed into it.

David Ching
  • 1,903
  • 17
  • 21
  • Thanks very much, very informative, and a very useful app you wrote there. Its is indeed odd that VK_RCONTROL is seen as 0x11 (like VK_CONTROL) with an extended flag, seeing as the MSDN constant for VK_RCONTROL is explicitly 0xA3. I edited my code in the OP per your suggested course of action, but unfortunately still nothing. I tried my script to check to see if other key inputs (alpha-numeric) work using notepad - and to make sure my code was correctly structured - all were successful. – Albert F D Mar 19 '15 at 18:58
  • On my laptop a `FN` key has to be pressed for `SCROLL LOCK` to work, this shouldn't affect the SendInput method right? – Albert F D Mar 19 '15 at 18:58
  • @Chris: Sure, please make sure you change your code from `ip.ki.wVk = VK_RCONTROL` to `ip.ki.wVk = VK_CONTROL;`. Also see my new edit about specifying the scan code. I don't think the `Fn` key would make any difference, as the laptop hardware should generate the same normal ScrollLock key. But you might want to change your key combination from RCtrl+ScrollLock+ScrollLock to some other more easily reproduced combination for testing, e.g. Ctrl+F10. – David Ching Mar 19 '15 at 19:03
  • Apologies, it was supposed to be `VK_CONTROL` that I tried... will revise my code according to your new edit on scan code. – Albert F D Mar 19 '15 at 20:12
0

@David Ching... this is frustrating me no end... I've read all the relevant documentation (and tried to make sense of it, remember I'm a novice at this), I've tried umpteen permutations of the code taking into account your advice and what I've read. The code below, according to your last suggestion, is the last I've tried with no success. I'm trying to determine what other factors can have a bearing on the issue - would hardware (Laptop is Toshiba Satellite L670D-10N), or OS (Windows 7 Ultimate - Spanish version with English Language Pack) make a difference? - I can't imagine so. I really, really appreciate your help, please don't give up helping! - btw, thanks for the app link.

#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;
}

UPDATE - THE SUCCESSFUL SENDINPUT TEST CODE

#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;
}

UPDATE - THE UNSUCCESSFUL REVISED CTRL((SCROLL LOCK)X2) CODE

#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;
}
Albert F D
  • 529
  • 2
  • 13
  • 33
  • Should I define: `ip.ki.time` `ip.ki.dwExtraInfo` `ip.ki.wScan` for each array member? – Albert F D Mar 20 '15 at 00:42
  • You can initialize to all zero members: `INPUT ip[6] = {0}`. Yes, please do try setting all `ip.ki.wScan = MapVirtualKey(...);`. The `ip[5].ki.wVk = VK_RCONTROL;` needs to be `VK_CONTROL`. The [SendInput doc](https://msdn.microsoft.com/en-us/library/windows/desktop/ms646310(v=vs.85).aspx) says "This function is subject to UIPI. Applications are permitted to inject input only into applications that are at an equal or lesser integrity level.". So you may need to make your program Run As Administrator, depending on the program receiving the keystrokes. – David Ching Mar 20 '15 at 02:02
  • I suggest you put your code into a program that sets a timer for about 5 seconds, then executes your code. Start the program running, then click and activate my ScanCode application (since keystrokes are sent to the active application). When your timer fires, it will `SendInput` the keystrokes into my Scancode app, which will display them. Make sure they are the correct ones. – David Ching Mar 20 '15 at 02:21
  • Hello David Ching... so I've started afresh, from the ground up as they say. First, I downloaded your app, I-like-it-a-lot, very impressive! I tested it a bit on my machine, all keys are recognised (except the `FN` key on its own) `FN` combined presses are captured, as are shortcut inputs... so all-in-all great. Next I wrote a simple script based on my code in the OP, which essentially did the following: opened `notepad` `sleep` (3 secs) then typed `chris` `space` `CHRIS` `ctrl+a` `ctrl+c` `→` `ctrl+v` `messagebox`. This was debugged within VS C++ 2010 Express, and ran without issue... – Albert F D Mar 20 '15 at 16:19
  • Next I intercepted the script with your app, it captured everything perfectly! ...Now this is where it gets weird. When I try the various methods attempted in this thread to get my OP code to work (using both debug with VS and by building an .exe), NOTHING appears in the scancode app. This is bizarre to say the least. I refuse to give up the ghost with this... what could I be doing wrong? ...I've read the MSDN information re. triggering the BSOD https://msdn.microsoft.com/en-us/library/windows/hardware/ff545499(v=vs.85).aspx - I can't see anything here which may explain this... – Albert F D Mar 20 '15 at 16:29
  • Good morning! How does your script get chars into my app, but not your C++ code? Does your script use your C++ code? Alter your C++ code to stuff the same chars e.g. `chris` `space` `CHRIS`; at the very least, your C++ code should succeed in putting characters into my app. (Did you make sure your C++ code is running As Administrator, if my app is also running As Administrator?) – David Ching Mar 20 '15 at 17:20
  • But even if your C++ code succeeds in putting characters into my app, perhaps SendInput() is too high-level to cause the BSOD. Your link implies Windows listens for the keystroke sequence at a very low level (kernel keyboard driver), lower than SendInput() works. Is your purpose to get a dump file? Because there are [easier ways of getting that](http://www.wintellect.com/devcenter/jrobbins/how-to-capture-a-minidump-let-me-count-the-ways). – David Ching Mar 20 '15 at 17:23
  • Sorry for the confusion, by script I meant code (exactly the same code method as in my OP). I updated the answer above to contain the codes I tested, and hopefully clarify what I've done. If what you interpret the link about BSOD to be is true, then is there no other way in C++ to get a low level acceptance of the input sequence? The thing is I wrote a batch, which essentially configures any computer on a network via the registry changes in the link, to receive a BSOD using this sequence. I was hoping to create an executable which would be called by the batch to reproduce this sequence. – Albert F D Mar 20 '15 at 19:15
  • I couldn't write this sequence in batch, because I didn't have the option to specify the `Right Control` key. Being somewhat acquainted with C++ I saw `SendInput` gave me that option... However **if** it's still not possible to trigger a BSOD in this way, I will reluctantly abandon this project. If you can provide another short answer reiterating the reasons for this OP not having an solution, I will happily mark it as the answer. – Albert F D Mar 20 '15 at 19:32
0

This Code successfully stuffs RControl+ScrollLock+ScrollLock into the ScanCode app, however, sorry, the computer does not reboot like when it does when these keys are manually typed.

#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 %d\n", numSuccessful, i, GetLastError());
    }

    return 0;
}

I believe the reason is that SendInput() injects the keys into the layer above the keyboard driver, and it is the keyboard driver that is monitored for these keystrokes to initiate the BSOD.

David Ching
  • 1,903
  • 17
  • 21
  • All I can say is, thanks so, so much for all your time, effort and good nature... and thanks for the link to your very useful app, I'm sure I will be putting it to good use in my code developing forays. I think I will shelve this project for the time being, perhaps somewhere, someday I will stumble across a method to simulate this sequence successfully... until then thanks again, you sir are a scholar and a gentleman. – Albert F D Mar 20 '15 at 22:17
  • @Chris - thanks for your kind words and sorry we didn't get it to work. Good luck in your next work! – David Ching Mar 21 '15 at 00:12