Harry
Harry

Reputation: 1263

C Programatically send Enter to cmd.exe by PID

The goal is to programatically hit Enter to some cmd.exe (running in background, e.g. owned by SYSTEM user, started from a windows service) which would visually result in a new, empty promt line like "C:\dev\sendenter>".

All i found on the net concentrates on receiving the keys where you have the option to check if ascii code 10 or 13 was sent. But when sending those to a running cmd.exe, all that will happen is that you get a newline but no new command prompt. Even using VK_RETURN which should be an alias for Carriage Return and Line Feed does not end up in a new prompt.

I believe the problem could be that WM_KEYUP or such must follow the ASCII key 10 or 13 but i was not able to find out how to send this using below minimal example.

#include <Windows.h>
#include <stdio.h>

//argument: int PID of process
//compile with gcc, no options

//first and only argument: int PID
int main(int argc, char** argv){
    DWORD pid = atoi(argv[1]);
    printf ("Argument PID: %d\n",pid);

    if (!FreeConsole())
    {
        printf("Could not FreeConsole\n");
        exit(1);
    }
    if (!AttachConsole(pid)){
        printf("Error: could not attach console to specified PID\n");
        exit(2);
    }

    char s[] = {VK_RETURN};
    HANDLE stdoutt = GetStdHandle(STD_OUTPUT_HANDLE);
    unsigned long cChars;
    WriteConsole(stdoutt, s, lstrlen(s), &cChars, NULL);

    return 0;
    
}

Upvotes: 0

Views: 335

Answers (1)

tttony
tttony

Reputation: 5082

Tested on VS 2013

int main(int argc, char** argv){
    DWORD pid = atoi(argv[1]);

    if (!FreeConsole())
    {
        printf("Could not FreeConsole\n");
        exit(1);
    }
    if (!AttachConsole(pid)){
        printf("Error: could not attach console to specified PID\n");
        exit(2);
    }

    HANDLE hConsole = GetStdHandle(STD_INPUT_HANDLE);
    INPUT_RECORD ir[2];
    DWORD dwTmp = 0;
    
    ir[0].EventType = KEY_EVENT;
    ir[0].Event.KeyEvent.bKeyDown = TRUE;
    ir[0].Event.KeyEvent.dwControlKeyState = 0;
    ir[0].Event.KeyEvent.uChar.UnicodeChar = '\r';
    ir[0].Event.KeyEvent.wRepeatCount = 1;
    ir[0].Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
    ir[0].Event.KeyEvent.wVirtualScanCode = MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC);
    
    ir[1].EventType = KEY_EVENT;
    ir[1].Event.KeyEvent.bKeyDown = FALSE;
    ir[1].Event.KeyEvent.dwControlKeyState = 0;
    ir[1].Event.KeyEvent.uChar.UnicodeChar = '\r';
    ir[1].Event.KeyEvent.wRepeatCount = 1;
    ir[1].Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
    ir[1].Event.KeyEvent.wVirtualScanCode = MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC);
    
    WriteConsoleInput(hConsole, ir, 2, &dwTmp); // <-- 2 it's the number of inputs to send

    return 0;
}

I just tested with only cmd.exe opened and sending input to that process, at the beginning it only typed ? in the console, that's because you have to send two inputs, the first with the bKeyDown to TRUE and later FALSE and UnicodeChar is setted to \r instead of \n

Also I had to set wRepeatCount to 1 because it was triggering asserts

Microsoft says:

wRepeatCount

The repeat count, which indicates that a key is being held down. For example, when a key is held down, you might get five events with this member equal to 1, one event with this member equal to 5, or multiple events with this member greater than or equal to 1.

Remember that any printf will be sent to the attached console and can mess up the input

Upvotes: 1

Related Questions