Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[52.14.87.152] |
|
Страницы: (4) [1] 2 3 ... Последняя » все ( Перейти к последнему сообщению ) |
Сообщ.
#1
,
|
|
|
Посылаю команду консольному приложению, запущенного CreateProcess, которая выполняется только после закрытия хэндла = закрытия этого приложения. Вот рабочий пример (один из нашедшихся в сети):
HANDLE hReadIn, hWriteIn, hReadOut, hWriteOut, hWriteInDup, hWriteOutDup; SECURITY_ATTRIBUTES sa; PROCESS_INFORMATION pi; STARTUPINFO si; ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.bInheritHandle = TRUE; sa.lpSecurityDescriptor = NULL; CreatePipe(&hReadOut, &hWriteOut, &sa, NULL); DuplicateHandle(GetCurrentProcess(), hReadOut, GetCurrentProcess(), &hWriteOutDup, 0, FALSE, DUPLICATE_SAME_ACCESS); CloseHandle(hReadOut); CreatePipe(&hReadIn, &hWriteIn, &sa, NULL); DuplicateHandle(GetCurrentProcess(), hWriteIn, GetCurrentProcess(), &hWriteInDup, 0, FALSE, DUPLICATE_SAME_ACCESS); CloseHandle(hWriteIn); ZeroMemory(&si, sizeof(STARTUPINFO)); si.cb=sizeof(STARTUPINFO); si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; si.hStdInput = hReadIn; si.hStdOutput = hWriteOut; CreateProcess(NULL, Cons_App.c_str(), NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi); WriteFile(hWriteInDup, buf, len, &count, NULL); // запись команды CloseHandle(hWriteInDup); // команда выполняется только после CloseHandle = закрытия консольного приложения CloseHandle(hReadOut); CloseHandle(hWriteIn); CloseHandle(pi.hThread); CloseHandle(pi.hProcess); Как выполнять команды и читать из командной строки консольного приложения без закрытия самого приложения? Спасибо. |
Сообщ.
#2
,
|
|
|
Цитата vlad2 @ Посылаю команду консольному приложению, запущенного ... А каким образом консольное приложение читает команду ? "Как выполнять команды и читать из командной строки консольного приложения без закрытия самого приложения?" Интересен также вопрос, по какой причине ты сделал такой вывод.. Если посмотреть твой исходник: ... ZeroMemory(&si, sizeof(STARTUPINFO)); si.cb=sizeof(STARTUPINFO); si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; si.hStdInput = hReadIn; si.hStdOutput = hWriteOut; CreateProcess(NULL, Cons_App.c_str(), NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi); WriteFile(hWriteInDup, buf, len, &count, NULL); // запись команды CloseHandle(hWriteInDup); // команда выполняется только после CloseHandle = закрытия консольного приложения ... то все операции непрерывно следуют одна за другой. После WriteFile данные консольным приложением не успевают приняться до CloseHandle(hWriteInDup); попробуй: WriteFile(hWriteInDup, buf, len, &count, NULL); // запись команды ::Sleep(10000);// 10 секунд CloseHandle(hWriteInDup); // команда выполняется только после CloseHandle = закрытия консольного приложения |
Сообщ.
#3
,
|
|
|
Цитата ЫукпШ @ А каким образом консольное приложение читает команду ? Первый раз делаю такое приложение, которое работает с консольным. Думаю, что команда должна выполниться после WriteFile. Однако, не получается - ни с ожиданием, никак. Выполняется только во время CloseHandle. Команда - создание файла, который появляется только после CloseHandle. |
Сообщ.
#4
,
|
|
|
Цитата vlad2 @ Цитата ЫукпШ @ А каким образом консольное приложение читает команду ? Первый раз делаю такое приложение, которое работает с консольным. я задал этот вопрос чтобы взглянуть на исходник. |
Сообщ.
#5
,
|
|
|
Закешировалось?
|
Сообщ.
#6
,
|
|
|
Цитата ЫукпШ @ я задал этот вопрос чтобы взглянуть на исходник. Вот конкретный работающий код: UnicodeString Cons_App = L"Python"; HANDLE hReadIn, hWriteIn, hReadOut, hWriteOut, hWriteInDup, hWriteOutDup; SECURITY_ATTRIBUTES sa; PROCESS_INFORMATION pi; STARTUPINFO si; try { SetCurrentDir(CurDir); ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.bInheritHandle = TRUE; sa.lpSecurityDescriptor = NULL; CreatePipe(&hReadOut, &hWriteOut, &sa, NULL); DuplicateHandle(GetCurrentProcess(), hReadOut, GetCurrentProcess(), &hWriteOutDup, 0, FALSE, DUPLICATE_SAME_ACCESS); CloseHandle(hReadOut); CreatePipe(&hReadIn, &hWriteIn, &sa, NULL); DuplicateHandle(GetCurrentProcess(), hWriteIn, GetCurrentProcess(), &hWriteInDup, 0, FALSE, DUPLICATE_SAME_ACCESS); CloseHandle(hWriteIn); ZeroMemory(&si, sizeof(STARTUPINFO)); si.cb=sizeof(STARTUPINFO); si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; si.hStdInput = hReadIn; si.hStdOutput = hWriteOut; CreateProcess(NULL, Cons_App.c_str(), NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi); DWORD count = 0; char buf[] = {"f= open(\"D:\\__aa.txt\",\"w+\")\r\n"}; WriteFile(hWriteInDup, buf, sizeof(buf), &count, NULL); // запись команды // Sleep(10000); // WriteFile(hWriteInDup, "f.close()\r\n", 11, &count, NULL); // запись команды CloseHandle(hWriteInDup); // команда выполняется только после CloseHandle = закрытия консольного приложения CloseHandle(hReadOut); CloseHandle(hWriteIn); CloseHandle(pi.hThread); CloseHandle(pi.hProcess); Файл появляется вместе с закрытием интерпретатора, а мне надо, чтобы файл образовался, но Питон остался, чтобы дать другие команды и что-то прочитвть из консоли (про чтение пока не думал, пока отлаживаю запись). |
Сообщ.
#7
,
|
|
|
Цитата ЫукпШ @ Цитата vlad2 @ Посылаю команду консольному приложению, запущенного ... А каким образом консольное приложение читает команду ? Хочется посмотреть исходник принимающей стороны. Твоего slave-приложения. Консольного, которым ты управляешь. --- Вызывает сомнения это: char buf[] = {"f= open(\"D:\\__aa.txt\",\"w+\")\r\n"}; Когда ты пайпом управляешь консольным приложением, ты фактически подменяешь ручной ввод действиями приложения - Мастера. Пайп подключает консольный ввод/вывод к другому приложению. Что будет, если ввести "\r\n" ? я не знаю. С клавиатуры мы вводим только '\r'. --- Запусти управляемое консольное приложение отдельно. И делай вручную все необходимые команды. Будет ли работать правильным образом ? --- А что консольное приложение шлёт назад ? Когда юзер жмёт на клавиши, он получает эхо на скрин. Твоё мастер-приложение тоже должно получить весь этот вывод. |
Сообщ.
#8
,
|
|
|
Цитата ЫукпШ @ И делай вручную все необходимые команды. Будет ли работать правильным образом ? Да, это первое, что проверил: ручками работает, как надо: выполнил команду и ждёт следующей, пока не закрою. Хочу сделать то же самое, но из программы. Цитата ЫукпШ @ Что будет, если ввести "\r\n" ? я не знаю. Любая комбинация \r и \n и её отсутствие никак не влияет (о чём это говорит?). Цитата ЫукпШ @ Хочется посмотреть исходник принимающей стороны. Консольное приложение - это интерпретатор Python. Вот установщик. |
Сообщ.
#9
,
|
|
|
Не всеми консольными приложениями можно управлять посредством pipe. Да, у меня такое было. Внутрь Питона не влезешь... Что можно попробовать - напиши консольного "кролика" и по-отлаживай твой комплекс с двух сторон. Просто чтобы убедиться, что "Мастер" работает правильно. Передавай "кролику" то же самое, что и Питону и изучай, как идут данные в обе стороны. --- Других идей пока нет. --- Качай ситуацию, придумывай эксперименты. Как юзер вводит информацию ? нажимает клавишу раз в секунду, каждый раз дожидаясь эха на экране. Сделай такой алгоритм - передай данные по-символьно с обязательным ожиданием эха перед вводом следующего символа. --- А, вот я нашёл кусочек своего исходника - pipe я инициализировал иначе: //---------------------------------------------------------------------------------- bool WINAPI MPIPE::Create(DWORD dwSize) { if(pipe) return false; SECURITY_DESCRIPTOR sd; ::ZeroMemory(&sd,sizeof(sd));//структура security для пайпов SECURITY_ATTRIBUTES sa; ::ZeroMemory(&sa,sizeof(sa)); sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = true; //разрешаем наследование дескрипторов if(WinIsNT()) { if(!::InitializeSecurityDescriptor(&sd,SECURITY_DESCRIPTOR_REVISION)) return false; if(!::SetSecurityDescriptorDacl(&sd, true, NULL, false)) return false; sa.lpSecurityDescriptor = &sd; } if(!::CreatePipe(hReadStd,hWriteStd,&sa,dwSize)) return false; pipe=true; return true; } //---------------------------------------------------------------------------------- |
Сообщ.
#10
,
|
|
|
Цитата ЫукпШ @ Не всеми консольными приложениями можно управлять Спасибо, ЫукпШ. Прочитал про особенность Питона с предложением, как обойти проблему. Но это помогло только для одной команды. При попытке дать другую (создать ещё один файл), Питон закрывается и всё, вторая команда не выполняется. Из консоли всё работает нормально. Теперь задача, как выполнить несколько команд в этом открытом Питоне. |
Сообщ.
#11
,
|
|
|
vlad2, а зачем тебе запускать python процессом? Там же есть готовые библиотеки для интеграции скриптов внутрь твоего exe на плюсах.
|
Сообщ.
#12
,
|
|
|
Цитата vlad2 @ Ах, воночто. Ок, пиши ему в консольный ввод. Прочитал про особенность Питона с предложением, как обойти проблему. Добавлено Нашёл у себя пример: if (CreateProcess(NULL, &commandLine.front(), NULL, NULL, TRUE, CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW | BELOW_NORMAL_PRIORITY_CLASS, NULL, NULL, &startInfo, &processInfo) == FALSE) logMsg("CreateProcess() failed", GetLastError()); else { // при успехе CloseHandle(processInfo.hThread); // HANDLE нитки не нужен while (WaitForSingleObject(processInfo.hProcess, 1000) == WAIT_TIMEOUT)// пока менеджер работает { if (fs::exists(stop)) continue; // досрочного стопа нет, продолжаем ждать // Ок, прекращаем прогон if (!AttachConsole(processInfo.dwProcessId)) // присоединяемся к консоли менеджера logMsg("AttachConsole() failed", GetLastError()); else { DWORD written; INPUT_RECORD ev[2]; // создаём два события ev[0].EventType = ev[1].EventType = KEY_EVENT; // кнопочные ev[0].Event.KeyEvent.bKeyDown = TRUE; // нажали ev[1].Event.KeyEvent.bKeyDown = FALSE; // отпустили ev[0].Event.KeyEvent.wRepeatCount = ev[1].Event.KeyEvent.wRepeatCount = 1;// ESC 1 раз ev[0].Event.KeyEvent.wVirtualKeyCode = ev[1].Event.KeyEvent.wVirtualKeyCode = VK_ESCAPE; ev[0].Event.KeyEvent.wVirtualScanCode= 0x01; // скан коды ESC ev[1].Event.KeyEvent.wVirtualScanCode= 0x81; ev[0].Event.KeyEvent.uChar.AsciiChar = ev[1].Event.KeyEvent.uChar.AsciiChar = 27; // ASCII ev[0].Event.KeyEvent.dwControlKeyState=ev[1].Event.KeyEvent.dwControlKeyState=0; // пишем в консоль события if (!WriteConsoleInput(GetStdHandle(STD_INPUT_HANDLE), ev, 2, &written)) logMsg("WriteConsoleInput() failed", GetLastError()); FreeConsole(); // отсоединяемся от консоли break; } } // Ждём завершения менеджера if (WaitForSingleObject(processInfo.hProcess, INFINITE) != WAIT_OBJECT_0) logMsg("UART manager failure", GetLastError()); DWORD exitCode = -1; // Получить код завершения менеджера GetExitCodeProcess(processInfo.hProcess, &exitCode); CloseHandle(processInfo.hProcess); return exitCode; } Добавлено Неочевидные вещи: Добавлено Цитата vlad2 @ И вообще, что тебе мешает сразу написать все команды скриптом? Но это помогло только для одной команды. |
Сообщ.
#13
,
|
|
|
Цитата Qraizer @ И вообще, что тебе мешает сразу написать все команды скриптом? Например, то, что следующая команда может быть определена не только результатом предыдущей, но вообще, работой других программ, и заранее неизвестной. Добавлено Цитата shm @ Пока ничего не знаю об этом, питоновцы живут отдельно. зачем тебе запускать python процессом? |
Сообщ.
#14
,
|
|
|
Цитата Qraizer @ Пока довольно трудно разобраться.Нашёл у себя пример Видимо, для строки будет что-то типа: INPUT_RECORD ev[2]; // создаём два события ev[0].EventType = ev[1].EventType = KEY_EVENT; // кнопочные ev[0].Event.KeyEvent.bKeyDown = TRUE; // нажали ev[1].Event.KeyEvent.bKeyDown = FALSE; // отпустили ev[0].Event.KeyEvent.wRepeatCount = ev[1].Event.KeyEvent.wRepeatCount = 1; // 1 раз AttachConsole(pi.dwProcessId); // присоединяемся к консоли менеджера while (WaitForSingleObject(pi.hProcess, 1000) == WAIT_TIMEOUT)// пока менеджер работает { if (fs::exists(stop)) continue; // досрочного стопа нет, продолжаем ждать ??? // Ок, прекращаем прогон // AttachConsole(pi.dwProcessId); // присоединяемся к консоли менеджера for (int k = 0; k < sizeof(buf); ++k) { ev[0].Event.KeyEvent.wVirtualKeyCode = ev[1].Event.KeyEvent.wVirtualKeyCode = BYTE(buf[k]); ev[0].Event.KeyEvent.wVirtualScanCode= VkKeyScan(buf[k]); // скан коды ev[1].Event.KeyEvent.wVirtualScanCode= 0x81; // ??? ev[0].Event.KeyEvent.uChar.AsciiChar = ev[1].Event.KeyEvent.uChar.AsciiChar = 27; // ASCII // ??? ev[0].Event.KeyEvent.dwControlKeyState=ev[1].Event.KeyEvent.dwControlKeyState=0; // ??? // пишем в консоль события WriteConsoleInput(GetStdHandle(STD_INPUT_HANDLE), ev, 2, &count); } FreeConsole(); // отсоединяемся от консоли break; } // Ждём завершения менеджера if (WaitForSingleObject(pi.hProcess, INFINITE) != WAIT_OBJECT_0) logMsg("UART manager failure", GetLastError()); |
Сообщ.
#15
,
|
|
|
Ну, в примере хватает не относящегося к записи в консоль, да. Просто это логически цельный фрагмент. Если из него что-то вырезать, то получится некорректно спроектированный код, хотя и решающий задачу.
Давай сокращу чуть. Но учти, что опущенное тоже важно. if (CreateProcess(NULL, &commandLine.front(), NULL, NULL, TRUE, CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW | BELOW_NORMAL_PRIORITY_CLASS, NULL, NULL, &startInfo, &processInfo) == FALSE) /* сообщить об ошибке запуска менеджера */; else { // при успехе запуска CloseHandle(processInfo.hThread); // HANDLE нитки не нужен while (/* проверяем, что менеджер не завершился сам по себе */ ) { if (/* проверяем, что не поступило сигнала о принудительном завершении менеджера */) continue; /* Ок, сигналим менеджеру, чтоб выходил. Он это понимает, если ему в консоли нажали ESC. Т.к. консольного окна мы его лишили, пишем ему туда ESC программно. */ if (!AttachConsole(processInfo.dwProcessId)) // присоединяемся к консоли менеджера /* сообщить об ошибке подачи сигнала менеджеру о выходе */; else { DWORD written; INPUT_RECORD ev[2]; // создаём два события ev[0].EventType = ev[1].EventType = KEY_EVENT; // кнопочные ev[0].Event.KeyEvent.bKeyDown = TRUE; // нажали ev[1].Event.KeyEvent.bKeyDown = FALSE; // отпустили ev[0].Event.KeyEvent.wRepeatCount = ev[1].Event.KeyEvent.wRepeatCount = 1;// ESC 1 раз ev[0].Event.KeyEvent.wVirtualKeyCode = ev[1].Event.KeyEvent.wVirtualKeyCode = VK_ESCAPE; ev[0].Event.KeyEvent.wVirtualScanCode= 0x01; // скан коды ESC ev[1].Event.KeyEvent.wVirtualScanCode= 0x81; ev[0].Event.KeyEvent.uChar.AsciiChar = ev[1].Event.KeyEvent.uChar.AsciiChar = 27; // ASCII ev[0].Event.KeyEvent.dwControlKeyState=ev[1].Event.KeyEvent.dwControlKeyState=0; // пишем в консоль два события if (!WriteConsoleInput(GetStdHandle(STD_INPUT_HANDLE), ev, 2, &written)) /* сообщить об ошибке подачи сигнала менеджеру о выходе */; FreeConsole(); // отсоединяемся от консоли break; } } // Ждём, пока менеджер не слопает ESC и выйдет if (WaitForSingleObject(processInfo.hProcess, INFINITE) != WAIT_OBJECT_0) /* вообще-то, тут баги быть не должно */; CloseHandle(processInfo.hProcess); // не забываем чистить ресурсы Добавлено Если хочется больше конкретики. Есть некий менеджер, который знает своё дело. Он является консольным приложением, его запустили, передав в командной строке параметры, остальные он берёт из окружения сам – и вперёд, на очередные несколько минут он занят работой. Потом завершается, положив в файлы и в консоль результаты и логи. Ещё он умеет по ESC прерываться досрочно. Я же представляю собой робота, который тихонько работает себе в фоне на этой же машине и никого не трогает. И его задача принимать сетевые запросы на обслуживание этим менеджером, формировать параметры командной строки для него, перенаправить его консольный вывод в файлы, усиленно перед этим процессом делая вид, что с ним работают как обычно через консоль. (Естественно, я привёл лишь малую часть кода.) Т.о. люди получают возможность работать на этой машине по сети, а не только непосредственно за ней или по RDP. Я запускаю дочерний процесс, который делает своё дело и выходит сам. Теоретически, я его и ждать-то не должен, запустил и хай работает себе. Но тут дело в том, что помимо сетевого запроса на запуск может прийти и запрос на прерывание ранее запущенного. Поэтому, во-первых, приходится ждать, пока менеджер не завершится сам, и если до этого времени пришёл запрос на его прерывание, передать ему эту команду. Как? Правильно, "нажав" ему ESC. Потому что таков штатный механизм прерывания. Неважно, как конкретно организован рабочий цикл с дочерним процессом. У меня вот так. Тебе, думаю, достаточно взять за основу вон ту подготовку пары событий по каждой кнопке и циклом фигачить эти пары туда не знаю куда. Как? Я не знаю всего твоего кода, обрамление уж на твоей совести. Поэтому на всякий случай и привёл часть моего обрамления, где есть... ну, важные моменты организации диалога. |