Почему нельзя прервать вызов ReadConsole?

Представьте, что где-то в коде есть такой кусок:

BOOL Res =
    ReadConsole(
        GetStdHandle(STD_INPUT_HANDLE),
        Buffer,
        sizeof(Buffer),
        &ReadChars,
        NULL);

Теперь, скажем, нам в какой-то момент нужно корректно прервать вызов ReadConsole() (из другого потока). Как это сделать?

Как выясняется, ни CancelIoEx(), ни CancelSynchronousIo() не работают в этом случае. CancelIoEx() возвращает ошибку ERROR_INVALID_HANDLE, а CancelSynchronousIo() - ERROR_OBJECT_NOT_FOUND. Также интересно, то GetStdHandle() возвращает значение “3”, что не очень-то похоже на описатель (handle) ядерного объекта.

Проблема заключается в том, что консольная подсистема обслуживается системным процессом Csrss (в Windows 7 - Conhost). Консольные функции вроде ReadConsole() на самом деле выполняют RPC вызовы в Csrss, вместо обращения в ядро. Соответственно, прервать текущую операцию можно было бы вызвав CancelIoEx() с описателем LPC порта, поверх которого «ходит» RPC. Правда добраться до этого описателя не очень реально. Да и RPC соединение после такого финта ушами может быть потеряно.

Остаются всякие окольные методы. Во-первых, можно насильно завершить поток, читающий консоль. Во-вторых, можно имитировать консольный ввод с помощью WriteConsoleInput(), разблокировав тем самым ReadConsole(). В некоторых случаях можно отказаться от построчного ввода и реализовать ReadConsole() в виде надстройки над ReadConsoleInput(). Хотя этот путь только для настоящих джедаев. В общем, не просто это все…

comments powered by Disqus