我正在尝试做一些我认为应该简单的事情:从标准输入执行阻塞读取,但如果没有数据可用,则在指定的时间间隔后超时。
在Unix世界中,这对select()
来说很简单,但在Windows中不起作用,因为stdin
不是套接字。如果不创建额外的线程等,下一个最简单的选择是什么?
我正在使用面向Win32环境的visual C ++。
到目前为止,我已尝试过:
使用select
(如果输入不是套接字则不起作用)
使用WaitForSingleObject(GetStdHandle(STD_INPUT_HANDLE))
。 - 雷米的第一个建议。如果标准输入是控制台(其他人报告了同样的问题),当你调用它时,它总是会立即返回
使用重叠IO并执行WaitForSingleObject
(Remy的第三个建议)。在这种情况下,当输入来自控制台时,读取似乎总是阻塞 - 似乎stdin
不支持异步I / O.
目前我认为我唯一剩下的选择是创建一个线程,它将执行阻塞读取然后发出事件信号,然后让另一个线程等待事件超时。
答案 0 :(得分:5)
我必须解决类似的问题。在Windows上,它不像Linux那么容易或明显。但是,这是可能的。诀窍是Windows将控制台事件放在控制台输入事件队列中。您必须过滤掉您不关心的事件,并且只处理您关心的事件(如按键)。
进一步阅读:see the Win32 console documentation
以下是一些基于我正在研究的套接字和stdin多路复用器的主要调试示例代码:
void ProcessStdin(void)
{
INPUT_RECORD record;
DWORD numRead;
if(!ReadConsoleInput(GetStdHandle(STD_INPUT_HANDLE), &record, 1, &numRead)) {
// hmm handle this error somehow...
return;
}
if(record.EventType != KEY_EVENT) {
// don't care about other console events
return;
}
if(!record.Event.KeyEvent.bKeyDown) {
// really only care about keydown
return;
}
// if you're setup for ASCII, process this:
//record.Event.KeyEvent.uChar.AsciiChar
} // end ProcessStdin
int main(char argc, char* argv[])
{
HANDLE eventHandles[] = {
GetStdHandle(STD_INPUT_HANDLE)
// ... add more handles and/or sockets here
};
DWORD result = WSAWaitForMultipleEvents(sizeof(eventHandles)/sizeof(eventHandle[0]),
&eventHandles[0],
FALSE,
1000,
TRUE
);
switch(result) {
case WSA_WAIT_TIMEOUT: // no I/O going on right now
break;
case WSA_WAIT_EVENT_0 + 0: // stdin at array index 0
ProcessStdin();
break;
case WSA_WAIT_EVENT_0 + 1: // handle/socket at array index 1
break;
case WSA_WAIT_EVENT_0 + 2: // ... and so on
break;
default: // handle the other possible conditions
break;
} // end switch result
}
答案 1 :(得分:3)
这应该这样做:
int main()
{
static HANDLE stdinHandle;
// Get the IO handles
// getc(stdin);
stdinHandle = GetStdHandle(STD_INPUT_HANDLE);
while( 1 )
{
switch( WaitForSingleObject( stdinHandle, 1000 ) )
{
case( WAIT_TIMEOUT ):
cerr << "timeout" << endl;
break; // return from this function to allow thread to terminate
case( WAIT_OBJECT_0 ):
if( _kbhit() ) // _kbhit() always returns immediately
{
int i = _getch();
cerr << "key: " << i << endl;
}
else // some sort of other events , we need to clear it from the queue
{
// clear events
INPUT_RECORD r[512];
DWORD read;
ReadConsoleInput( stdinHandle, r, 512, &read );
cerr << "mouse event" << endl;
}
break;
case( WAIT_FAILED ):
cerr << "WAIT_FAILED" << endl;
break;
case( WAIT_ABANDONED ):
cerr << "WAIT_ABANDONED" << endl;
break;
default:
cerr << "Someting's unexpected was returned.";
}
}
return 0;
}
答案 2 :(得分:1)
使用GetStdHandle
+ WaitForSingleObject
可以正常使用。但是在进入循环之前,一定要设置approriate标志并刷新控制台缓冲区。
简而言之(无错误检查)
std::string inStr;
DWORD fdwMode, fdwOldMode;
HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
GetConsoleMode(hStdIn, &fdwOldMode);
// disable mouse and window input
fdwMode = fdwOldMode ^ ENABLE_MOUSE_INPUT ^ ENABLE_WINDOW_INPUT;
SetConsoleMode(hStdIn, fdwMode);
// flush to remove existing events
FlushConsoleInputBuffer(hStdIn);
while (!abort)
{
if (WaitForSingleObject(hStdIn, 100) == WAIT_OBJECT_0)
{
std::getline(std::cin, inStr);
}
}
// restore console mode when exit
SetConsoleMode(hStdIn, fdwOldMode);
答案 3 :(得分:0)
您需要GetStdHandle
函数来获取控制台的句柄,然后您可以使用WaitForSingleObject
等待事件在该句柄上发生,并超时。
答案 4 :(得分:0)
使用GetStdHandle()
获取stdin句柄。然后你可以:
使用WaitForSingleObject()
我的stdin句柄本身来检测何时有可供阅读的控制台输入,然后根据需要从中读取。
在循环中的stdin句柄上使用GetNumberOfConsoleInputEvents()
或PeekConsoleInput()
来确定何时有可供读取的数据,然后根据需要从中读取。
将ReadFile()
与包含事件句柄的OVERLAPPED
结构一起使用,然后使用带WaitForSingleObject()
的事件句柄来检测读取是否超时。
在任何情况下,如果重定向了stdin,请小心。如果将其重定向到非控制台I / O的某个位置,则不能将GetStdHandle()
句柄与控制台功能一起使用。如果将其重定向到文件,则必须使用ReadFile()
。
答案 5 :(得分:0)
如果有人正在编写chrome本机消息传递主机并且正在寻找解决方案来检查stdin上是否有任何输入而没有阻塞,那么这很有效:
HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
int timer = GetTickCount();
while(timer+10000 > GetTickCount())
{
unsigned int length = 0;
DWORD bytesAvailable = 0;
PeekNamedPipe(hStdin,NULL,0,NULL,&bytesAvailable,NULL);
if(bytesAvailable > 0)
{
for (int i = 0; i < 4; i++)
{
unsigned int read_char = getchar();
length = length | (read_char << i*8);
}
for (int i = 0; i < length; i++)
{
msg += getchar();
}
timer = GetTickCount();
}
else
{
// nothing to read, stdin empty
Sleep(10);
}
}