Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.140.198.173] |
|
Сообщ.
#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. Потому что таков штатный механизм прерывания. Неважно, как конкретно организован рабочий цикл с дочерним процессом. У меня вот так. Тебе, думаю, достаточно взять за основу вон ту подготовку пары событий по каждой кнопке и циклом фигачить эти пары туда не знаю куда. Как? Я не знаю всего твоего кода, обрамление уж на твоей совести. Поэтому на всякий случай и привёл часть моего обрамления, где есть... ну, важные моменты организации диалога. |
Сообщ.
#16
,
|
|
|
Прани, сорри конечно ... но я совсем не вкурил, как производится запуск?
Через сиспемный пайп? Типа: proramm-1 | proramm-2 Или как-то иначе?? |
Сообщ.
#17
,
|
|
|
Цитата JoeUser @ Нет, сугубо программно. Но смысл ровно таков же с той лишь разницей, что пишуемое в пайп заранее неизвестно и формируется по ходу дела. Или как-то иначе?? |
Сообщ.
#18
,
|
|
|
Цитата Qraizer @ Нет, сугубо программно. Qraizer, стоп. Есть две программы. Пайп - это решение обмена, стандартизованное ОС. Если это не оно - то что оно? Добавлено АДД: да, есть еще атавизмы в виде "мэйл-слотов", но это явно не они. |
Сообщ.
#19
,
|
|
|
Цитата vlad2 @ Вообще говоря, за такие вещи нужно по рукам и розгами. Вот что в голове у этих иксоидов творится, что они с пеной у рта рассказывают, какая у них вся из себя унифицированная архитектура, всё суть файлы, даже внешние устройства, даже процессор и память, а на деле чекают, чё им подсунули и меняют алгоритмы. Какая тебе нафик разница, что у тебя с той стороны stdin-а? Зла не хватает, половина кроссплатформенного оперсорса более кроссплатформенен на винде, чем в позиксе... Прочитал про особенность Питона с предложением, как обойти проблему. Добавлено Цитата JoeUser @ Это оно. Только оно для юзера. Оно программно как раз так и реализуется, через CreatePipe() etc. Только делает это оболочка. Ну, в винде это cmd. И ничего не надо программить. Но это решение статично. Если нужно динамически общаться, юзер не справится, ему придётся что-нить наскриптовать хотя бы. Пайп - это решение обмена, стандартизованное ОС. Если это не оно - то что оно? |
Сообщ.
#20
,
|
|
|
Цитата Qraizer @ Но оно статично. Ой ли ... ой лю ли ... echo "Oi li, oi lu li" | program cat myfile | program ls /usr/local/bin | grep YO | program & etc ... !!! Все наоборот! Изначальная обработка может быть любая, но вывод в STDOUT будет одинаков. А вот как его отработает прога в STDIN - вопрос. ИМХО, И ящетаю - привязываться к API Венды (именно в этом случае) - это кощунство и сатанинство!!! И издевательство над православными! |
Сообщ.
#21
,
|
|
|
И где тут динамика, JoeUser? Наладь между grep и program двусторонний диалог.
|
Сообщ.
#22
,
|
|
|
Цитата Qraizer @ Наладь между grep и program двусторонний диалог. Ну я так понимаю, "двусторонность" не равна "динамичности"? Как я понимаю, динамичность может просто подразумевать какую-то зависимость исходящих величин - от входящих (читай функцию). А если обобщить - рассматриваем не только способ обработки, но и его построение, читай - получаем что-то типа "функтора". Хотя может я и загоняюсь |
Сообщ.
#23
,
|
|
|
В результате получилась запись, но не получается чтение. Вот рабочий код:
UnicodeString Cons_App = L"Python -i"; 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_USESHOWWINDOW | STARTF_USESTDHANDLES; si.wShowWindow = SW_HIDE; si.hStdInput = hReadIn; si.hStdOutput = hWriteOut; CreateProcess(NULL, Cons_App.c_str(), NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi); DWORD count = 0; char buf[1024]; strcpy(buf,"f= open(\"D:\\__aa.txt\",\"w+\")\r\n"); WriteFile(hWriteInDup, buf, strlen(buf), &count, NULL); // создание файла strcpy(buf,"f.write(\"1\n\")\r\n"); WriteFile(hWriteInDup, buf, strlen(buf), &count, NULL); // запись в файл "1\n" DWORD cread = 0; // чтение bool ret = true; for(int i=0; i < 10; i++) { ret = PeekNamedPipe(hWriteOutDup, NULL, 0, NULL, &count, NULL); ret = ret && (count > 0); if (ret) break; Sleep(1000); } if (ret) { ret = ReadFile(hWriteOutDup, buf, count, &cread, NULL); } // while (ret) // { // ret = ReadFile(hWriteOutDup, buf, strlen(buf), &count, NULL); // read from the pipe // if (!ret && GetLastError() != ERROR_MORE_DATA) // break; // } strcpy(buf,"f.close()\r\n"); WriteFile(hWriteInDup, buf, strlen(buf), &count, NULL); // закрытие файла CloseHandle(hWriteInDup); CloseHandle(hReadOut); CloseHandle(hWriteIn); CloseHandle(pi.hThread); CloseHandle(pi.hProcess); Результат PeekNamedPipe - всегда false. Те же действия в командной строке нормально работают - см. вложение. Как программно реализовать чтение? Прикреплённая картинка
При этом команда записи в файл не проходит. |
Сообщ.
#24
,
|
|
|
А с чего ты уверен, что команды туда приходят? Я ради такого случая поставил себе Python, не факт, что твоей версии, правда, и ничего работающего не обнаружил. Написал свою простейшую аппликуху, без этого дурацкого ветвления по типу tty, и в ней всё работает как часы, в обе стороны.
|
Сообщ.
#25
,
|
|
|
Цитата Qraizer @ А с чего ты уверен, что команды туда приходят? Файл образуется (f=open) и закрывается (f.close). Добавлено Цитата vlad2 @ без этого дурацкого ветвления по типу tty Что имеется в виду? Добавлено В сообщении #8 давал ссылку на Питон. Запускать его надо с опцией -i. |
Сообщ.
#26
,
|
|
|
У меня 3.8.1. Поведение отличается.
Короче, долби саппорт питона. Боюсь, без их помощи не обойтись. Тебе вот надо, чтобы твой код под каждую версию работал по-разному? Думаю, такое веселье никому не понравится. |
Сообщ.
#27
,
|
|
|
Цитата Qraizer @ Это точно(. Думаю, такое веселье никому не понравится |
Сообщ.
#28
,
|
|
|
Цитата vlad2 @ Цитата Qraizer @ Это точно(.Думаю, такое веселье никому не понравится А у меня всё получилось - после "python -i". Только я делал для x86. Где то у тебя ошибка. --- А вот этого я не делал никогда. ... DuplicateHandle(GetCurrentProcess(), hReadOut, GetCurrentProcess(), &hWriteOutDup, 0, FALSE, DUPLICATE_SAME_ACCESS); ... DuplicateHandle(GetCurrentProcess(), hWriteIn, GetCurrentProcess(), &hWriteInDup, 0, FALSE, DUPLICATE_SAME_ACCESS); ... |
Сообщ.
#29
,
|
|
|
Цитата ЫукпШ @ А у меня всё получилось Здорово! Я упростил код, убрал DuplicateHandle: UnicodeString Cons_App = L"Python -i"; HANDLE hReadIn, hWriteIn, hReadOut, hWriteOut, hWriteInDup, hWriteOutDup; SECURITY_ATTRIBUTES sa; PROCESS_INFORMATION pi; STARTUPINFO si; try { ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.bInheritHandle = TRUE; sa.lpSecurityDescriptor = NULL; CreatePipe(&hReadOut, &hWriteOut, &sa, NULL); CreatePipe(&hReadIn, &hWriteIn, &sa, NULL); ZeroMemory(&si, sizeof(STARTUPINFO)); si.cb=sizeof(STARTUPINFO); si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; si.wShowWindow = SW_HIDE; si.hStdInput = hReadIn; si.hStdOutput = hWriteOut; si.hStdError = hWriteOut; CreateProcess(NULL, Cons_App.c_str(), NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi); DWORD count = 0; char buf[1024]; strcpy(buf,"f= open(\"D:\\__aa.txt\",\"w+\")\r\n"); WriteFile(hWriteIn, buf, strlen(buf), &count, NULL); // создание файла strcpy(buf,"f.write(\"1\n\")\r\n"); WriteFile(hWriteIn, buf, strlen(buf), &count, NULL); // запись в файл "1\n" DWORD cread = 0; // чтение bool ret = true; for(int i=0; i < 10; i++) { ret = PeekNamedPipe(hWriteOut, NULL, 0, NULL, &count, NULL); ret = ret && (count > 0); if (ret) break; Sleep(1000); } if (ret) { if (count > strlen(buf)) count = strlen(buf); ret = ReadFile(hWriteOut, buf, count, &cread, NULL); } strcpy(buf,"f.close()\r\n"); WriteFile(hWriteIn, buf, strlen(buf), &count, NULL); // закрытие файла strcpy(buf,"exit()\r\n"); WriteFile(hWriteIn, buf, strlen(buf), &count, NULL); // выход из Питона CloseHandle(hReadIn); CloseHandle(hWriteOut); CloseHandle(hReadOut); CloseHandle(hWriteIn); CloseHandle(pi.hThread); CloseHandle(pi.hProcess); Всё равно не записывает в файл и не читает. Остальные команды проходят. Можешь посмотреть мой код на предмет ошибки или дать свой, который работает? |
Сообщ.
#30
,
|
|
|
Цитата vlad2 @ Можешь посмотреть мой код на предмет ошибки или дать свой, который работает? Да, но только вечером или ночью. --- Но я делал не так, как ты. Чтобы мастерить такие приложения на постоянной основе, я сделал некоторые классы и процедуры, которые занёс в библиотеку. Поэтому исходников получается не один. Но зато растёт производительность труда. 1. Делаем класс - Hahdle. Это весьма нужный класс. 2. Делаем класс - Pipe. 3. Делаем класс - Обёртку процесса для консольных приложений и .bat файлов. 4. Для удобства - обёртку вокруг памяти. 5. Класс - конвертор форматов 6. Делаем класс - обёртку для управления консольным приложением На основе сделанного набора И ещё там чего-то в виде библиотечных процедур. В результате получился такой исходник: #include "cmdShell.h" bool WriteToPython(cmdShell& cSh, const char* pStr) { bool bRetC=false; size_t sizeWr = strlen(pStr); if(sizeWr) { // ConvTchar2Char conv; // DbgStr::TypeDebugString(_T("%s %s"),_T(__FUNCTION__), conv.Char2Tchar(pStr)); if(!cSh.WriteToPipe (pStr,sizeWr)) return false; } MEMT<char> buf; bRetC = cSh.ReadFromPipe (buf); size_t size = buf.GetDataSize(); // _tprintf(_T("bRetC(ReadFromPipe)=%d size=%d\n"),bRetC,size); if((bRetC)&&(size)) { for(size_t i=0;i<size;++i) { _tprintf(_T("%c"),buf[i]); } } _tprintf(_T("\n")); return bRetC; } // -------------------------------------------------------------------------- int __cdecl _tmain(int argc, char **argv) { _tprintf(_T("\tHello, world!\n")); cmdShell cSh; bool bRetC = cSh.Install("python -i"); if(bRetC) { WriteToPython(cSh, ""); // создаём файл aa.txt в папке d:\0 WriteToPython(cSh, "f=open(\"d:\\\\0\\\\aa.txt\",\"w+\")\n"); WriteToPython(cSh, "f.write(\"223\")\n"); WriteToPython(cSh, "f.close()\n"); WriteToPython(cSh, "exit()\n"); } else { _tprintf(_T("bRetC(install)=%d\n"),bRetC); } return 0; } Приаттаченные исходники я надёргал из библиотеки, что-то мог и забыть. Прикреплённый файлForPython.zip (10,04 Кбайт, скачиваний: 337) |
Сообщ.
#31
,
|
|
|
Цитата ЫукпШ @ Спасибо, отлично. Да, но только вечером или ночью. Максимально упростил свой код, чтобы оценить возможность. Чтобы написать оперативную прогу, буду ориентироваться на твой подход, который считаю совершенно верным. |
Сообщ.
#32
,
|
|
|
Цитата vlad2 @ Как выполнять команды и читать из командной строки консольного приложения без закрытия самого приложения? Спасибо. Начнем с того, что термин "конвеер" (pipe) - это однонаправленный канал межпроцессного взаимодействия. Таким образом, одна программа пишет в STDOUT, вторая программа читает из STDIN, и что-то делает. Что делать чтобы вторая программа не закрывалась? Ответ простой - читать STDIN в бесконечном цикле, с возможностью выхода по условию. Увы, но не к сожалению, я Питона не знаю, приведу пример на Перле: first.pl #/usr/local/bin/perl $| = 1; # flush сразу после очередной операции I/O print "print 1;\n"; print "print 2;\n"; sleep(2); print "print 3;\n"; sleep(4); print "print 4;\n"; print "EXIT\n"; next.pl #/usr/local/bin/perl $| = 1; # flush сразу после очередной операции I/O while(1) { $in = <STDIN>; last if ($in =~ /^EXIT\s*$/); eval($in); } Тест запускаем так: perl first.pl | perl next.pl В результате первые две команды будут прочитаны и сразу выполнены - выведутся два числа 1 и 2. Потом задержка. Потом вывод числа 3. Потом еще одна задержка. Потом вывод числа 4 и вторая программа "ловит" условие выхода "EXIT", и выполнение завершается. Сценарий интерпретаторов, что Перла, что Питона - одинаков. Выполнить скрипт и завершится. Вот Перлу приходит на помощь функция eval(), которая выполняет как код переданную ей строку в качестве аргумента. Может ли такое Питон - я не знаю. Далее. Если нужен двусторонний обмен, вангую - нужно определить два именованных пайпа. Для одной программы один канал для записи, второй для чтения. Для второй программы каналы меняются местами. Тогда взаимодействие программ не будет зависеть на каком из терминалов запущены программы. |
Сообщ.
#33
,
|
|
|
Спасибо, JoeUser. Питона тоже не знаю. Мне нужно из внешней программы периодически (не одним скриптом) давать команды Питону и читать его ответы, если они есть. Питон во время всех этих действий должен находиться в памяти. Тоже создаю два пайпа, как написано во многих примерах, правда анонимных...
|
Сообщ.
#34
,
|
|
|
Цитата vlad2 @ Питона тоже не знаю. Ну сверскоростное гугление говорит, что eval() в Питоне есть. Так что "принимающий скрипт" можешь писать по образу и подобию моего next.pl, и Питон будет висеть в памяти и читать что ему определишь. А вот для ответов делай именованный пайп. |
Сообщ.
#35
,
|
|
|
Кстати да, а попробуй именованные.
|
Сообщ.
#36
,
|
|
|
Цитата JoeUser @ Вроде, для чтения использую PeekNamedPipe, как во многих примерах. для ответов делай именованный пайп |
Сообщ.
#37
,
|
|
|
я несколько подправил класс "cmdShell", и максимально возможно выполнил
его внутренности в стиле твоей программы. В итоге получилось так: #include "cmdShell_b.h" bool WriteToPython(cmdShell_b& cSh, const char* pStr) { bool bRetC=false; size_t sizeWr = strlen(pStr); if(sizeWr) { if(!cSh.WriteToPipe (pStr,sizeWr)) return false; } MEMT<char> buf; bRetC = cSh.ReadFromPipe (buf); size_t size = buf.GetDataSize(); if((bRetC)&&(size)) { for(size_t i=0;i<size;++i) { _tprintf(_T("%c"),buf[i]); } } _tprintf(_T("\n")); return bRetC; } // -------------------------------------------------------------------------- int __cdecl _tmain(int argc, char **argv) { _tprintf(_T("\tHello, world!\n")); cmdShell_b cSh; bool bRetC = cSh.Install(_T("python -i")); if(bRetC) { WriteToPython(cSh, ""); // пишем файл aa.txt в папку d:\0 WriteToPython(cSh, "f=open(\"d:\\\\0\\\\aa.txt\",\"w+\")\n"); WriteToPython(cSh, "f.write(\"223\")\n"); WriteToPython(cSh, "f.close()\n"); WriteToPython(cSh, "exit()\n"); } else { _tprintf(_T("bRetC(install)=%d\n"),bRetC); } return 0; } Где: Скрытый текст Файл cmdShell_b.h : // --------------------------------------------------------------------------------- // file cmdShell_b.h 2020.02.06 // --------------------------------------------------------------------------------- #ifndef __cmdShell_b_H__ #define __cmdShell_b_H__ // --------------------------------------------------------------------------------- //#include "TypeDebugString.h" #include "memt.h" #include "mpipe.h" // --------------------------------------------------------------------------------- // DbgStr::TypeDebugString(_T("%s bConnected=%d"),_T(__FUNCTION__), bConnected); // DbgStr::TypeDebugString(_T("%s buf.GetDataSize()=%d"),_T(__FUNCTION__), buf.GetDataSize()); // --------------------------------------------------------------------------------- class cmdShell_b { public: static const size_t TOUT_DEF_ms = 3000; static const size_t TOUT_DEF_GET_ms = 250; static const size_t SIZE_IN = 128*1024; static const size_t SIZE_PROXY_IN = SIZE_IN; static const size_t MAX_COMMLINE_BUFF = 32768; private: protected: bool bInstalled; MEMT<char> proxyIn; MPIPE In,Out; STARTUPINFO si; PROCESS_INFORMATION pi; TCHAR commLineBuff [MAX_COMMLINE_BUFF]; DWORD dwSizeIn; DWORD dwSizeOut; size_t sizePipeInBuf; virtual bool WINAPI WaitPipeData (size_t dwToutWait=TOUT_DEF_ms,DWORD* pdwSize=NULL); virtual bool WINAPI GetPipeData (MEMT<char>& buf, DWORD dwGot=0, size_t dwToutGet=TOUT_DEF_GET_ms); virtual bool WINAPI ReadPipe (bool RetBefore, MEMT<char>& buf, DWORD dwSize); virtual bool WINAPI AddData (MEMT<char>& tar, MEMT<char>& sou); bool WINAPI CreatePipes (void); bool WINAPI PipesExist (void); bool WINAPI Wait__Process (DWORD tout); bool WINAPI Check__Pipe (DWORD *pAvail); bool WINAPI Read__Pipe (char *pData,DWORD size,DWORD *pGot); bool WINAPI Write__Pipe (char *pData,DWORD size,DWORD *pOut); public: WINAPI cmdShell_b (void); virtual WINAPI ~cmdShell_b (void); void WINAPI PreSetPipeInSize (size_t size); virtual bool WINAPI Install (const TCHAR* pCommLine); virtual bool WINAPI WriteToPipe (const void* pB, size_t size=0); virtual bool WINAPI WriteToPipe (MEMT<char>& buf); virtual bool WINAPI ReadFromPipe (MEMT<char>& buf, size_t dwToutWait=TOUT_DEF_ms, size_t dwToutGet=TOUT_DEF_GET_ms); virtual bool WINAPI WriteWaitRead ( const void* pB, size_t size, MEMT<char>& buf, size_t dwToutWait = TOUT_DEF_ms, size_t dwToutGet = TOUT_DEF_GET_ms ); bool WINAPI UnInstall (void); bool WINAPI Terminate (UINT uiCode); }; // --------------------------------------------------------------------------------- #endif Файл cmdShell_b.cpp : // --------------------------------------------------------------------------------- // FILE cmdShell_b.cpp 2020.02.05 // --------------------------------------------------------------------------------- #include "stdafx.h" #include "cmdShell_b.h" // --------------------------------------------------------------------------------- WINAPI cmdShell_b::cmdShell_b () : dwSizeIn (0), dwSizeOut (0), bInstalled (false), sizePipeInBuf (SIZE_IN) { ::ZeroMemory(&si, sizeof(si)); ::ZeroMemory(&pi, sizeof(pi)); } // --------------------------------------------------------------------------------- WINAPI cmdShell_b::~cmdShell_b () { UnInstall(); if(pi.hProcess) {::CloseHandle(pi.hProcess);} if(pi.hThread) {::CloseHandle(pi.hThread);} } // --------------------------------------------------------------------------------- void WINAPI cmdShell_b::PreSetPipeInSize (size_t size) { if(size < SIZE_IN) size = SIZE_IN; sizePipeInBuf = size; } // --------------------------------------------------------------------------------- bool WINAPI cmdShell_b::Install(const TCHAR* pCommLine) { if(!pCommLine) return false; if(bInstalled) return false; if(!proxyIn.Alloc(SIZE_PROXY_IN)) return false; dwSizeIn = (DWORD)sizePipeInBuf; if(!CreatePipes ()) return false; ::ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); ::ZeroMemory(&pi, sizeof(pi)); ::GetStartupInfo(&si); si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; si.hStdOutput = In.GetWriteHandle(); si.hStdError = In.GetWriteHandle(); //подменяем дескрипторы для si.hStdInput = Out.GetReadHandle(); // дочернего процесса _sntprintf_s(commLineBuff,ARRAYSIZE(commLineBuff),_TRUNCATE,_T("%s"),pCommLine); // DbgStr::TypeDebugString(_T("%s %s"),_T(__FUNCTION__), commLineBuff); if ( !::CreateProcess ( NULL, commLineBuff, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi ) ) return false; bInstalled = true; return true; } // --------------------------------------------------------------------------------- bool WINAPI cmdShell_b::WriteToPipe(const void* pB, size_t size) { if(!bInstalled) return false; if(!pB) return false; const char* pBuf = (const char*) pB; if(size==0) size=strlen(pBuf); DWORD out=0; bool RetC = false; RetC = Write__Pipe ((char*)pBuf, (DWORD)size, &out); return RetC; } // --------------------------------------------------------------------------------- bool WINAPI cmdShell_b::WriteToPipe(MEMT<char>& buf) { if(buf.GetDataSize()==0) return false; return WriteToPipe(buf.GetPointer(), buf.GetDataSize()); } // --------------------------------------------------------------------------------- bool WINAPI cmdShell_b::ReadFromPipe(MEMT<char>& buf, size_t dwToutWait, size_t dwToutGet) { if(!bInstalled) return false; bool RetC = false; DWORD dwSize = 0; buf.SetDataSize(0); for(int ii=0;ii==0;++ii,RetC=true) { if(!WaitPipeData(dwToutWait, &dwSize)) break; if(!GetPipeData(buf, dwSize, dwToutGet)) break; } return RetC; } // --------------------------------------------------------------------------------- bool WINAPI cmdShell_b::WriteWaitRead(const void* pB, size_t size, MEMT<char>& buf, size_t dwToutWait, size_t dwToutGet) { bool RetC = false; for(int ii=0;ii==0;++ii,RetC=true) { if(pB) { if(!WriteToPipe(pB, size)) break; } if(!ReadFromPipe(buf, dwToutWait, dwToutGet)) break; } return RetC; } // --------------------------------------------------------------------------------- bool WINAPI cmdShell_b::UnInstall(void) { if(!bInstalled) return false; bool RetC=false; RetC = Terminate (0); bInstalled = false; return RetC; } // --------------------------------------------------------------------------------- // --------------------------------------------------------------------------------- // bRetC = exb.ExecBatFile(_T("plink -ssh hostOrIp -P port -l user -pw password")); // --------------------------------------------------------------------------------- bool WINAPI cmdShell_b::WaitPipeData(size_t dwToutWait, DWORD* pdwSize) { bool RetC=false; DWORD dwSize=0; if(pdwSize){*pdwSize=0;} for(size_t i=0;i<dwToutWait;++i) { ::Sleep(1); if(Wait__Process(0)) break; if(Check__Pipe (&dwSize)) { if(dwSize==0) continue; else { RetC=true; if(pdwSize){*pdwSize=dwSize;} break; } } else break; } return RetC; } // --------------------------------------------------------------------------------- bool WINAPI cmdShell_b::GetPipeData(MEMT<char>& buf, DWORD dwGot_, size_t dwToutGet) { bool RetC = false; DWORD dwSize = 0; DWORD dwGot = dwGot_; if(dwGot) RetC = true; for(size_t i=0;i<dwToutGet;++i) { ::Sleep(1); if(Wait__Process(0)) break; if(Check__Pipe (&dwSize)) { if(dwSize==0) {RetC=false; break;} else { if(dwSize > dwGot) { RetC=true; dwGot=dwSize; i=0; } } } else {RetC=false;break;} } RetC = ReadPipe(RetC, buf, dwGot); return RetC; } // --------------------------------------------------------------------------------- bool WINAPI cmdShell_b::ReadPipe(bool RetBefore, MEMT<char>& buf, DWORD dwSize) { if(!RetBefore) return false; if(!buf.Alloc((size_t)dwSize*2)) return false; buf.MemSet(0); if(!buf.SetDataSize(0)) return false; DWORD dwGot=0; if(!Read__Pipe (buf.GetPointer(), (DWORD)buf.GetSize(), &dwGot)) return false; if(!buf.SetDataSize((size_t)dwGot)) return false; return true; } // --------------------------------------------------------------------------------- bool WINAPI cmdShell_b::AddData(MEMT<char>& tar, MEMT<char>& sou) { size_t sizetar = tar.GetDataSize(); size_t sizesou = sou.GetDataSize(); size_t sizeres = sizetar+sizesou; if(sizeres==0) {tar.SetDataSize(0); return true;} MEMT<char> tmp; if(!tmp.Alloc(sizeres)) return false; size_t ind = 0; for(size_t i=0;i<sizetar;++i){ tmp[ind++]=tar[i]; } for(size_t i=0;i<sizesou;++i){ tmp[ind++]=sou[i]; } tmp.SetDataSize(sizeres); tar = tmp; return true; } //---------------------------------------------------------------------------------- bool WINAPI cmdShell_b::CreatePipes (void) { if(PipesExist ()) return true; if(!In. Create(dwSizeIn)) return false; if(!Out.Create(dwSizeOut))return false; return true; } //---------------------------------------------------------------------------------- bool WINAPI cmdShell_b::PipesExist (void) { bool rIn = In.PipeExist(); bool rOut = Out.PipeExist(); return (rIn && rOut); } //---------------------------------------------------------------------------------- bool WINAPI cmdShell_b::Wait__Process (DWORD tout) { DWORD RetC = ::WaitForSingleObject(pi.hProcess, tout); if(RetC==WAIT_OBJECT_0) return true; return false; } //---------------------------------------------------------------------------------- bool WINAPI cmdShell_b::Check__Pipe (DWORD *pAvail) { return In.CheckPipe(pAvail); } //---------------------------------------------------------------------------------- bool WINAPI cmdShell_b::Read__Pipe (char *pData,DWORD size,DWORD *pGot) { return In.ReadPipe(pData,size,pGot); } //---------------------------------------------------------------------------------- bool WINAPI cmdShell_b::Write__Pipe (char *pData,DWORD size,DWORD *pOut) { return Out.WritePipe(pData,size,pOut); } //---------------------------------------------------------------------------------- bool WINAPI cmdShell_b::Terminate (UINT uiCode) { if(!bInstalled) return false; if(!pi.hProcess) return false; if(::TerminateProcess(pi.hProcess, uiCode)) return true; return false; } //---------------------------------------------------------------------------------- // DbgStr::TypeDebugString(_T("%s WaitProcess bRet=%d"),_T(__FUNCTION__),bRet); // --------------------------------------------------------------------------------- Пробовал только для x86, но сборка x64 у меня тоже есть. |
Сообщ.
#38
,
|
|
|
TerminateProcess()... абажаю. В чём проблема была послать ему "quit()"?
|
Сообщ.
#39
,
|
|
|
Цитата Qraizer @ TerminateProcess()... абажаю. |
Сообщ.
#40
,
|
|
|
Цитата Qraizer @ TerminateProcess()... абажаю. В чём проблема была послать ему "quit()"? Допустим, произошла ошибка при пользовании Питоном. Юзер забыл послать ему "exit()". Ещё интереснее получится, если вообще произойдут какие-то ошибки (в процессе отладки нашего приложения) и у нас останется висеть запущенный процесс Питона. А мы завершим работу своего приложения. И задумаем его перезапустить. А это не получится - дочерний процесс уже запущен. "Это фиаско, братан" © Или получится, но мы не сразу обнаружим вышеупомянутую ошибку и начнём плодить "потерянные" процессы массово. Terminate - это просто предохранитель. Если Питон заканчивает работу штатным образом посредством "exit()", Terminate фактически ничего и не делает. --- Terminate - не так уж редко нужно. --- На самом деле это решение можно критиковать с другой стороны. Просто для упрощения примера я использовал такую реализацию. Предположим, что Питон сам начал запускать различные приложения для своей работы. Много различных процессов. Так вот, нужно перечислить и грохнуть их все. Иначе в результате любой аварии получится неудобная проблема. Более корректный вариант у меня такой: // --------------------------------------------------------------------------------- bool WINAPI cmdShell::UnInstall(size_t dwTout) { bool RetC=false; if((bInstalled)&&(pExit)) { RetC = WriteToPipe(pExit); if(!WaitProcess((DWORD)dwTout)) { BOOL bRKPEX = KillChildProcessesEx(); if(!bRKPEX) RetC = false; } } else { if(!KillChildProcessesEx()) RetC = false; } bInstalled = false; return RetC; } // --------------------------------------------------------------------------------- |
Сообщ.
#41
,
|
|
|
Цитата ЫукпШ @ Спасибо большое, буду разбираться. я несколько подправил класс "cmdShell" |
Сообщ.
#42
,
|
|
|
ЫукпШ, ты комп тоже reset-ом ребутаешь? Ненуачё, а вдруг какая-то аппликуха откажется отвечать, какой-то драйвер не отпустит свою шелезяку...
Добавлено Любое нормально работающее нормальное приложение имеет штатный режим завершения работы. Пользователь ими вполне пользуется. Ты же наверняка не запускаешь таск-менеджер и прибиваешь аппликуху из списка процессов каждый раз? Почему тогда советуешь другим программно делать именно так? Давно Рихтера открывал? Добавлено Цитата ЫукпШ @ Никогда не нужно. Ею должен пользоваться только пользователь только из таск-менеджера. Terminate - не так уж редко нужно. |
Сообщ.
#43
,
|
|
|
Цитата Qraizer @ ЫукпШ, ты комп тоже reset-ом ребутаешь? Ненуачё, а вдруг какая-то аппликуха откажется отвечать, какой-то драйвер не отпустит свою шелезяку... Qraizer, ниочём. ----- А вообще у меня есть такая функция - в софте у микроконтроллеров - аппаратный reset. Можно даже дать по сети. "И чо ?". Никаких комплексов - если надо, значит сделаем. ----- Есть даже такой характерный приём программирования системы. Хорошо известный в узких кругах. Аппаратный сброс на микроконтроллер подаётся от генератора постоянно. Во временном промежутками между сбросами контроллер и выполняет всю работу. Такая вычислительная система не может зависнуть... ----- Ты преподавателем работаешь ? Добавлено Цитата Qraizer @ Любое нормально работающее нормальное приложение имеет штатный режим завершения работы. А я тебе и ответил - что в штатном режиме Terminate и не используется. Это предохранитель на не предвиденную ситуацию и не штатный режим. Который всегда может возникнуть в любой момент. В том числе и по прямой ошибке программиста. |
Сообщ.
#44
,
|
|
|
Цитата ЫукпШ @ Это пять.Аппаратный сброс на микроконтроллер подаётся от генератора постоянно. Во временном промежутками между сбросами контроллер и выполняет всю работу. Такая вычислительная система не может зависнуть... Вэлкам ту Холиварс, ЫукпШ. Создавай тему, тут заканчиваем, бо B.V. счас взбесится. Перед созданием рекомендую ознакомиться с такими типами систем, как hosted и freestanding, пригодится. |
Сообщ.
#45
,
|
|
|
Цитата Qraizer @ Цитата ЫукпШ @ Вчера, 23:56 Terminate - не так уж редко нужно. Никогда не нужно. Ею должен пользоваться только пользователь только из таск-менеджера. Бывает, нужно: сам применяю, когда идёт очередь выполнения задач, и задержка цикла гораздо критичнее принудительного завершения процесса. Приходится прибивать. |
Сообщ.
#46
,
|
|
|
vlad2, в отладке юзай, что хочешь и как хочешь. В продакшн не переноси. Будешь сильно недоволен, например, тем, что очень важные отладочные логи не записались. Или там лицензия на сервере лицензий залипла на сутки. Или ещё чего.
|
Сообщ.
#47
,
|
|
|
Цитата Qraizer @ Согласен. Только в крайнем случае). в отладке юзай, что хочешь и как хочешь. В продакшн не переноси. ЫукпШ, спасибо за помощь. Только я немного разобрал твой код, что-то объединил и адаптировал к привычному для меня стилю. Вместо класса cmdShell_b сделал несколько функций. В результате не сразу, но всё заработало. Но всё-таки сделаю класс - так удобнее и элегантнее: мне понравился твой подход. На этом тему пока закрою. Если опять возникнут вопросы при эксплуатации, задам снова. Всем спасибо. |