I have this application which runs multiple threads and one of those threads uses ReadFile () to read console input from STD_INPUT_HANDLE.
What I do is something like.
local buf[BUFSIZ]:BYTE
local dwRead:DWORD
invoke GetStdHandle, STD_INPUT_HANDLE
xchg eax, ebx
wait_for_signal:
invoke WaitForSingleObject, ebx, INFINITE
sub eax, WAIT_OBJECT_0
cmp eax, 0 ; STD_INPUT_HANDLE
jnz exit_loop
invoke ReadFile, ebx, addr buf, BUFSIZ, addr dwRead, 0
; process contents of buf here
; .....
jmp wait_for_signal
exit_loop:
The problem is that ReadFile() blocks and using OVERLAPPED structure doesn't matter.
Even if you open STD_INPUT_HANDLE with CreateFile() specifying FILE_FLAG_OVERLAPPED, windows ignores the flag and opens as normal.
The ReadFile() blocking means I can't signal the thread to terminate in a safe manner and so what I've decided to do is use CloseHandle()
Very crude I know but since the application is ending anyway, it's the only way I can unblock ReadFile() and exit the thread safely.
TerminateThread would be no different to ExitProcess in this case, I need some way to end the thread safely but because of this ReadFile() problem, all there is right now is CloseHandle()
CloseHandle (GetStdHandle(STD_INPUT_HANDLE))
This is really bad, what's worse is that there doesn't appear to be any solution for it until Vista and later.
CancelIo() would work if in the same thread but since ReadFile() blocks on STDIN, there's only CancelIoEx available on Vista if I'm not mistaken.
Of course, CancelIoEx() may have been undocumented on XP but I haven't checked.
Has anyone ever had that problem with ReadFile() blocking STDIN before and if so, how did you solve it?
Quote from: peter_asm on November 03, 2013, 11:16:51 PMstructure doesn't matter.
Even if you open STD_INPUT_HANDLE with CreateFile() specifying FILE_FLAG_OVERLAPPED, windows ignores the flag and opens as normal.
AFAIK the console does not support asynchronous IO. You might consider to use the low level console IO functions (http://msdn.microsoft.com/en-us/library/windows/desktop/ms684199%28v=vs.85%29.aspx) Peek/ReadConsoleInput() and GetNumberOfConsoleInputEvents() to avoid blocking.
Quote from: peter_asm on November 03, 2013, 11:16:51 PM
Has anyone ever had that problem with ReadFile() blocking STDIN before and if so, how did you solve it?
You may consider to replace WaitForSingleObject() by WaitForMultipleObjects() and supply 2 objects as arguments. The second object will be an "owned" mutex. By releasing the mutex your thread will be unblocked, AFAIK.
if you are looking at "normal" keys, you might consider using crt__kbhit and crt__getch
do away with the thread altogether, and put the rest of the program in a round-robin loop :P
;***********************************************************************************************
InKyb PROC
;Polled Keyboard Input - DednDave 8, 2010
;
;This function returns a keystroke in EAX if there is one in the buffer.
;If the buffer is empty, the function returns immediately.
;
;If the keyboard buffer is empty, AH = 0, AL = 0, ZF = 1.
;If the stroke is a regular key, AH = 0, AL = key char, ZF = 0.
;If the stroke is an extended key, AH = extended key, AL = E0h, ZF = 0.
;If the stroke is a function key, AH = function key, AL = 0, ZF = 0.
;
;ECX, EDX are not preserved.
call crt__kbhit
or eax,eax
jz InKyb1
call crt__getch
and eax,0FFh
jz InKyb0
cmp al,0E0h
jnz InKyb1
InKyb0: push eax
call crt__getch
pop edx
shl eax,8
or eax,edx
InKyb1: retn
InKyb ENDP
;***********************************************************************************************
alternatively, you could just use crt__kbhit to detect keystrokes
use ReadConsoleInput to read individual keys
that would allow you to test the semaphore for thread exit before reading a key
(if no key and no exit semaphore, Sleep,40 and loop back to crt__kbhit)
Quote from: japheth on November 04, 2013, 12:26:24 AM
You may consider to replace WaitForSingleObject() by WaitForMultipleObjects() and supply 2 objects as arguments. The second object will be an "owned" mutex. By releasing the mutex your thread will be unblocked, AFAIK.
I had something like this initially but what i noticed was STD_INPUT_HANDLE can receive all kinds of console events which isn't normal behavior you see on other operating systems that read console input.
For example, if the console window gains or loses focus, WaitForMultipleObjects() or WaitForSingleObject() will return because of FOCUS_EVENT.
PeekConsoleInput() will return one or more INPUT_RECORD structures and you can use FlushConsoleInputBuffer() to ignore these and continue waiting but then when user presses a key or releases it, that signals a KEY_EVENT.
alternatively, you could just use crt__kbhit to detect keystrokes
use ReadConsoleInput to read individual keys
that would allow you to test the semaphore for thread exit before reading a key
(if no key and no exit semaphore, Sleep,40 and loop back to crt__kbhit)
This might be worth looking at again, just hoped there'd be simpler way.
I suppose if after using PeekConsoleInput() was somehow able to check what key was pressed or released and it was carriage return, ReadFile() might return immediately then.
Disabling ENABLE_LINE_INPUT with SetConsoleMode() would probably mean having to buffer each character until carriage return and take into account backspaces..
Came across one solution to this in putty / plink.c
static DWORD WINAPI stdin_read_thread(void *param)
{
struct input_data *idata = (struct input_data *) param;
HANDLE inhandle;
inhandle = GetStdHandle(STD_INPUT_HANDLE);
while (ReadFile(inhandle, idata->buffer, sizeof(idata->buffer),
&idata->len, NULL) && idata->len > 0) {
SetEvent(idata->event);
WaitForSingleObject(idata->eventback, INFINITE);
}
idata->len = 0;
SetEvent(idata->event);
return 0;
}
A separate thread handles reading data and triggers event which WaitForMultipleObjects() waits on in another thread.
This seems to work well too but since I'm ending the application anyway, I decided to stick with CloseHandle(STD_INPUT_HANDLE) for now.
as qWord suggested, you can use GetNumberOfConsoleInputEvents, then PeekConsoleInput
what you wind up with with those 2 functions is somehwhat similar to crt__kbhit:
1) use GetNumberOfConsoleInputEvents to see how many input records are in the buffer
2) allocate stack space for that many INPUT_RECORD structures
3) use PeekConsoleInput to fill those structures
4) parse through the records to see if any are keyboard events with key down
4a) you'd probably ignore Shift, Ctrl, Alt keys
5) release the stack allocation
after that, you'd know whether there are any key strokes of interest to be read
the problem with any of these methods is that you have to buffer the results and echo to the display
by itself, that doesn't sound too bad - but, you have to support edit keys like arrow and backspace :(
I wrote something in C just to test out and it works okay.
Sorry this isn't ASM but it was quicker this way ;)
DWORD ReadStdIn (HANDLE hMutex, BYTE buf[], DWORD dwSize)
{
INPUT_RECORD ir;
DWORD recCnt, dwLen, evt;
HANDLE h[2];
HANDLE hin = GetStdHandle (STD_INPUT_HANDLE);
h[0] = hin;
h[1] = hMutex;
// read characters until we reach buffer size
for (dwLen = 0; dwLen < dwSize;) {
// wait for input or until mutex is released
evt = WaitForMultipleObjects (2, h, FALSE, INFINITE) - WAIT_OBJECT_0;
// if not STD_INPUT_HANDLE, exit
if (evt != 0) break;
if (ReadConsoleInput (hin, &ir, sizeof (INPUT_RECORD), &recCnt)) {
if (ir.EventType == KEY_EVENT) {
KEY_EVENT_RECORD *pKey = (KEY_EVENT_RECORD*)&ir.Event;
if (!pKey->bKeyDown) {
continue;
}
if (pKey->wVirtualKeyCode == VK_BACK) {
dwLen = (dwLen > 0) ? dwLen-1 : dwLen;
continue;
}
if (pKey->uChar.AsciiChar != 0) {
buf[dwLen++] = pKey->uChar.AsciiChar;
}
if (pKey->wVirtualKeyCode == VK_RETURN) break;
}
}
}
buf[dwLen] = 0;
return dwLen;
}
It's just a rough idea that would have to be integrated with thread code I have..
Still have to wait on additional events but the idea is to create a mutex with main thread as owner and then should the thread need to terminate, that function would end too once hMutex is released.
Quote from: peter_asm on November 03, 2013, 11:16:51 PM
I have this application which runs multiple threads and one of those threads uses ReadFile () to read console input from STD_INPUT_HANDLE.
The input you are reading, is it output from another console app, or is it really a user typing?
i was curious if he could open the "CON" device, then use SetCommTimeouts or something like that
i have never tried that :P
i use that for serial comm, though
i know it says the console doesn't support asynchronous communications
but, it also says it doesn't support UNICODE, then offers about 50 console functions in A or W flavours - lol
Quote from: jj2007 on November 05, 2013, 12:36:38 AM
The input you are reading, is it output from another console app, or is it really a user typing?
It's a console application that reads user input but also has to display information simultaneously, like a terminal.
ReadFile() works fine as a separate thread, but if I need to end for some reason, it's stuck waiting for input...solved with hitting RETURN but I wanted something that ended straight away
without TerminateThread() or just ExitProcess().
It's not really a big deal I suppose.
I thought console input would work with OVERLAPPED structure but apparently not unless it was created with CreateProcess() ... a named pipe would work.
Was thinking there may be a way with SetStdHandle (STD_INPUT_HANDLE, hNamedPipe) and what dedndave suggests might work too.
Doesn't seem to be any simple way to do it..why did Microsoft send events to hStdInput anyway? :biggrin:
i seem to recall Mike (SlugSnack) using pipes for console i/o in the old forum
http://www.masmforum.com/board/index.php?topic=11880.0 (http://www.masmforum.com/board/index.php?topic=11880.0)
if you use the old forum advanced search, "pipe", and username SlugSnack, there are several threads to browse