c++写入stdin(在Windows中)

C++ write to stdin (in Windows)

本文关键字:Windows 写入 stdin c++      更新时间:2023-10-16

我有一个多线程的Windows控制台应用程序,它的控制线程运行一个用户输入循环,像这样:

char c;
do {
    cin >> c;
    // Alter activity based on c
} while(c != 'q')
// Tell other threads to close, .join(), and do cleanup

然而,在某个时候,我希望程序本身能够优雅地退出。最明显的方法是将"qn"放到标准输入流中。有什么合理的方法吗?

或者是一个很好的回调方法来强制退出主控制循环(在主线程上),以便程序进入后续的清理方法?

(到目前为止,我发现最接近的是这个,它需要生成一个子进程,看起来最好是笨拙的过度杀伤。)

您可以使用另一个标志作为所有线程的循环中断条件。线程间通信的问题可以通过c++ 11自带的同步对象(如原子或互斥锁)来解决。

我最终使用了@Captain Obvlious的建议,但即使这样也很棘手(感谢Windows!):一旦控制信号被处理,它就会导致各种未检查的char输入循环问题。然而,通过以下添加,任何线程都可以通过调用GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, 0);

优雅地结束程序
bool exiting = false;
bool cleanedUp = false;
void Cleanup() {
    if(!cleanedUp) cleanedUp = true;
    else return;
    // Tell other threads to close, .join(), and do cleanup
}
// https://msdn.microsoft.com/en-us/library/ms683242(v=vs.85).aspx
bool ConsoleControl(DWORD dwCtrlType) {
    exiting = true; // This will cause the stdin loop to break
    Cleanup();
    return false;
}
int main() {
    SetConsoleCtrlHandler((PHANDLER_ROUTINE) ConsoleControl, true);
    .
    .
    .
    // Main user control loop
    char c;
    do {
        cin >> c;
        if(exiting) break;
        if(c < 0) continue; // This must be a control character
        // Alter activity based on c
    } while(c != 'q')
    Cleanup();
    return 0;
}

这是一个老问题,但我偶然发现了它,最终解决了原来的问题。

我使用WriteConsoleInput函数来模拟用户向控制台输入按键。这个代码示例是基于这个问题的。

HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
DWORD dwTmp;
INPUT_RECORD ir[2];
ir[0].EventType = KEY_EVENT;
ir[0].Event.KeyEvent.bKeyDown = TRUE;
ir[0].Event.KeyEvent.dwControlKeyState = 0;
ir[0].Event.KeyEvent.uChar.UnicodeChar = 'q';
ir[0].Event.KeyEvent.wRepeatCount = 1;
ir[0].Event.KeyEvent.wVirtualKeyCode = 'Q';
ir[0].Event.KeyEvent.wVirtualScanCode = MapVirtualKey('Q', MAPVK_VK_TO_VSC);
ir[1].EventType = KEY_EVENT;
ir[1].Event.KeyEvent.bKeyDown = TRUE;
ir[1].Event.KeyEvent.dwControlKeyState = 0;
ir[1].Event.KeyEvent.uChar.UnicodeChar = VK_RETURN;
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(hStdin, ir, 2, &dwTmp);

我的系统没有定义MAPVK_VK_TO_VSC,所以我用0代替。

你应该在适当的地方添加错误检查。