Лабораторные работы / Windows лаб 1-4 / lab2_var4_kod
.pdfВариант 4
Задание
Написать программу для обучения пользователя командной строки вежливости с использованием перенаправления потоков ввода и вывода через анонимные каналы.
1.Создать два анонимных канала функцией CreatePipe().
2.Запустить процесс cmd.exe, соединив один из созданных каналов с потоком ввода, а другой канал — с потоками вывода и ошибок дочернего процесса.
3.Считать из канала, связанного с потоком вывода дочернего процесса, все данные функцией ReadFile() и вывести их на экран.
Указание 1. Считать все данные можно, вызывая ReadFile() раз за разом и проверяя окончание считанных данных (их количество возвращается параметром lpNumberOfBytesRead). В условиях ЛР можно считать, что вывод окончен, если последний символ — '>'.
Указание 2. Функция ReadFile() работает с байтами, а не со строками, и не дописывает завершающий '\0' в конец считанных данных. Поэтому выводить данные нужно функцией fwrite(), cout.write() и т. п., но не printf() или оператором <<.
4.Запросить у пользователя полную строку-команду.
5.Если введенная строка не начинается со слова «please» (до первого пробела), уведомить об этом пользователя и перейти к пункту 4.
6.Если введена строка «thanks», остановить дочерний процесс функцией TerminateProcess(), закрыть анонимные каналы функцией CloseHandle() и завершить работу программы.
7.Записать в канал, связанный с потоком ввода дочернего процесса, оставшуюся
часть команды и символ перевода строки '\n'. 8. Перейти к пункту 3 (цикл).
Указание. Официальный пример перенаправления стандартных потоков дочернего процесса через анонимные каналы может быть полезен, но как основа для решения громоздок:
https://docs.microsoft.com/ru-ru/windows/win32/procthread/creating-a-child-process-with-redirected-input-and- output?redirectedfrom=MSDN
В примерах полужирным начертанием обозначен ввод пользователя.
Пример работы программы:
C:\> date /T
Please ask politely! > please date /T
01.09.2015
C:\> please do something nasty
'do' is not recognized as an internal or external command, operable program or batch file.
C:\> thanks
Примечание. Программа cmd.exe перед выводом каждой команды выводит также саму команду. Корректно было бы не печатать её, как в примере, но для простоты можно оставить.
Выполнение
#include <windows.h> #include <iostream> #include <string>
#define MAX_BUFFER_SIZE 256
int main()
1
{
SECURITY_ATTRIBUTES attr;
attr.nLength = sizeof(SECURITY_ATTRIBUTES); attr.bInheritHandle = TRUE; attr.lpSecurityDescriptor = nullptr;
//Создание канала для чтения дочерним процессом
HANDLE in_read = nullptr; HANDLE in_write = nullptr;
if (!CreatePipe(&in_read, &in_write, &attr, 0))
{
std::cout << "\nError. Error code: " << GetLastError() << '\n'; return EXIT_FAILURE;
}
SetHandleInformation(in_write, HANDLE_FLAG_INHERIT, 0);
//Создание канала для записи дочерним процессом
HANDLE out_read = nullptr; HANDLE out_write = nullptr;
if (!CreatePipe(&out_read, &out_write, &attr, 0))
{
std::cout << "\nError. Error code: " << GetLastError() << '\n'; return EXIT_FAILURE;
}
SetHandleInformation(out_read, HANDLE_FLAG_INHERIT, 0);
STARTUPINFO si;
ZeroMemory(&si, sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); si.hStdError = out_write;
si.hStdOutput = out_write; si.hStdInput = in_read;
si.dwFlags |= STARTF_USESTDHANDLES;
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
//Запуск cmd.exe
char cmd_name[] = "cmd.exe";
auto ok = CreateProcess(nullptr, cmd_name, nullptr, nullptr, TRUE, 0, nullptr, nullptr, &si, &pi); if (!ok)
{
std::cout << "\nError. Error code: " << GetLastError() << '\n'; return EXIT_FAILURE;
}
const char please[] = "please ";
const size_t please_size = sizeof(please) - 1; const char thanks[] = "thanks";
const size_t thanks_size = sizeof(thanks) - 1;
bool stop = false; std::string cmd; while (!stop)
{
2
//Пропуск вывода самой команды size_t skip_size = cmd.size();
if (skip_size)
skip_size -= please_size;
//Считывание данных из канала, связанного с потоком вывода дочернего процесса
DWORD read;
char buffer[MAX_BUFFER_SIZE];
while (ReadFile(out_read, buffer, sizeof(buffer), &read, nullptr))
{
if (skip_size < read)
{
char* buf_ptr = buffer + skip_size; std::cout.write(buf_ptr, read - skip_size); if (buffer[read - 1] == '>')
break;
}
else
{
skip_size -= read;
}
}
bool polite = false; while (!polite)
{
//Ввод строки-команды пользователя std::getline(std::cin, cmd);
//Остановка дочернего процесса, если введена строка thanks if(strncmp(cmd.c_str(), thanks, thanks_size) == 0)
{
stop = true; TerminateProcess(pi.hProcess, 0); break;
}
//Запрос на вежливый ввод, если строка не начинается с please if (strncmp(cmd.c_str(), please, please_size) != 0)
{
std::cout << "Please ask politely!\n>"; polite = false;
}
else
{
//Запись команды дочернему процессу, если введена вежливая команда
DWORD written; cmd += "\n";
WriteFile(in_write, cmd.c_str() + please_size, cmd.size() - please_size, &written, nullptr); polite = true;
}
}
}
3
//Закрытие открытых дескрипторов
CloseHandle(in_read);
CloseHandle(out_write);
CloseHandle(in_write);
CloseHandle(out_read);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess); return 0;
}
4