win api readConsole()
c++, winapi, windows
Solution
I saw this question and assumed it would be trivial, but spent the last 30 minutes trying to figure it out and finally have something.
That `dwCtrlWakeupMask` is pretty poorly documented in `CONSOLE_READCONSOLE_CONTROL`. MSDN says "A user-defined control character used to signal that the read is complete.", but why is it called `mask`? Why is it a `ULONG` instead of a `TCHAR` or something like that? I tried feeding it chars and wchars and nothing happened, so there must be more to the story.
I took to the web searching for that particular variable and found this link: https://groups.google.com/forum/#!topic/golang-codereviews/KSp37ITmcUg It is a random Go library coder asking for help and the answer is that tab is `1 << '\t'`. I tried it, and it works!
So, for future web searchers, `dwCtrlWakeupMask` is a bitmask of ASCII control characters that will cause `ReadConsole` to return. You can `|` together as many `1 << ctrl_char`s as you like... but it cannot be arbitrary characters, since it is a bitmask in a 32 bit value, only the chars 1-31 (inclusive) are possible (this group btw is called control characters, it includes things like tab, backspace, bell; things that do not represent printable characters per se).
Thus, this mask:
cReadControl.dwCtrlWakeupMask = (1 << '\t') | (1 << 0x08);
Will cause `ReadConsole` to return when tab (`\t`) OR when backspace (`0x08`) is pressed.
The characters represented by `ctrl+ some_ascii_value` are the number of that letter in the english alphabet, starting at a == 1. So, `ctrl+d` is `4`, and `ctrl+z` is `26`.
Therefore, this will return when the user hits `ctrl+d` or `ctrl+z`:
cReadControl.dwCtrlWakeupMask = (1 << 4) | (1 << 26);
Note that the Linux terminal driver also returns on `read` when the user hits `ctrl+d` so this might be a nice compatibility thing.
I believe the point of this argument is to allow easier tab-completion in processed input mode; otherwise, you'd have to turn processed input off and process keys one by one to do that. Now you don't have to.... though tbh, I still prefer taking my input with `ReadConsoleInput` for interactive programs since you get much better control over it all.
But while there are a lot of other ways to do what you want - and using `.` as a delimiter is impossible here, since it has a value >= 32, so you need to do it yourself... understanding what this does is interesting to me anyway, and there's scarce resources on the web so I'm writing this up just for future reference.
Note that this does not appear to work in `wineconsole` so be sure you are on a real Windows box to test it out.
Now, `dwControlKeyState` is actually set BY the function. Your value passed in is ignored (at least as far as I can tell), but you can inspect it for the given flags when the function returns. So, for example, after calling `ReadConsole` and hitting the key, it will be 32 if your numlock was on. It will be 48 is numlock was on and you pressed shift+tab (and had numlock on). So you test it after the function returns.
I typically like MSDN docs but IMO they completely dropped the ball on explaining this parameter!
Problem
I am trying to use Win API `ReadConsole(...)` and I want to pass in a delimiter char to halt the input from the console. The below code works but it only stops reading the input on `\r\n`. I would like it to stop reading the console input on `'.'` for instance. ``` void read(char *cIn, char delim) { HANDLE hFile; DWORD charsRead; DWORD charsToRead = MAX_PATH; CONSOLE_READCONSOLE_CONTROL cReadControl; cReadControl.nLength = sizeof(CONSOLE_READCONSOLE_CONTROL); cReadControl.nInitialChars = 0; cReadControl.dwCtrlWakeupMask = delim; cReadControl.dwControlKeyState = NULL; DWORD lpMode; // char cIn[MAX_PATH]; //-- buffer to hold data from the console hFile = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); GetConsoleMode(hFile,&lpMode); // lpMode &= ~ENABLE_LINE_INPUT; //-- turns off this flag // SetConsoleMode(hFile, lpMode); //-- set the mode with the new flag off bool read = ReadConsole(hFile, cIn, charsToRead * sizeof(TCHAR), &charsRead, &cReadControl); cIn[charsRead - 2] = '\0'; } ``` I know there are other easy ways to do this but I am just trying to understand some of the win api functions and how to use them. Thank you.