Имя: Пароль:
IT
 
Как послать консольному процессу сообщение о корректном завершении?
,
0 Chai Nic
 
15.12.13
18:33
Краткая суть проблемы. Есть сторонняя консольная программа, которая корректно закрывается при нажатии комбинации клавиш Ctrl+C. Из моей программы на c++ (тоже консольной) я запускаю эту программу через CreateProcess (соответственно знаю её pid), и в определенный момент мне её надо корректно завершить. Под линуксом решение на поверхности - kill(pid,SIGINT), а под виндой такой функции нет. Есть TerminateProcess - но она убивает процесс "жестко", не давая ему возможности отработать необходимые действия, выполняемые при корректном завершении (по удалению временных файлов, удалению служебных процессов и тому подобному).
Может кто подскажет идейку?
1 oleg_km
 
15.12.13
18:42
Можно как-то послать консольному приложению событие Ctrl+C. У него же тоже есть очередь сообщений, только более упрощенная
2 oleg_km
 
15.12.13
18:44
http://pcwarez.mybb.ru/viewtopic.php?id=57

По умолчанию такие события, как нажатие Ctrl+C, Ctrl+Break интерпретируются как системные, вызывающие немедленное завершение консольной программы. Аналогично действует, например, простой клик на "крестике" окна.
Можно переназначить эти события вызовом: SetConsoleCtrlHandler(@Proc,True);

  Тогда при нажатии указанных комбинаций будет выполняться определенная в программе процедура Proc (имя может быть любое). Даже если эта процедура пустая - умолчание все равно уже не будет действовать. То же относится и к попытке просто закрыть окно Windows. Между прочим, можно вообще отменить особую обработку сочетания Ctrl+C - см. об этом дальше.

  Если мы хотим, чтобы после завершения Proc были все же выполнены действия по умолчанию, оформляем ее как функцию, возвращающую False.

  В Proc может быть произведен анализ типа системного события, чтобы реагировать на каждое по-своему. Например, так:
procedure Proc(SysEv: Cardinal);
begin
  case SysEv of
    CTRL_BREAK_EVENT: ... // если нажали Ctrl+Break
    CTRL_CLOSE_EVENT: ... // если пытаются закрыть окно
    CTRL_LOGOFF_EVENT: ... // если пользователь завершает сеанс
    CTRL_SHUTDOWN_EVENT: ... // если система выгружается
  end;
end;
3 spectre1978
 
15.12.13
18:45
HWND окна знаешь? Ну и пошли через SendMessage или PostMessage WM_QUIT - типа щелчок на крестике окна
4 Chai Nic
 
15.12.13
18:46
(2) Та программа, которую я запускаю - "черный ящик", я не могу её менять.
(3) Какое окно? Программа консольная, запущена в консольной же.
5 spectre1978
 
15.12.13
18:47
(4) окно есть все равно. Через FindWindow можно найти по заголовку
6 Chai Nic
 
15.12.13
18:47
Я так понимаю, нужно как-то подцепиться к stdin этого процесса, а вот как?
7 Chai Nic
 
15.12.13
18:48
(5) Своего окна у этой программы нет, она запущена "в окне" вызывающей.
8 spectre1978
 
15.12.13
18:49
(7) тогда хуже. В принципе, такой вариант как ты написал тоже возможен, хотя я не делал никогда. Суть там такая: создаешь трубу (CreatePipe) и подцепляешь ее в качестве параметра для CreateProcess как входной поток. Соответственно далее в эту трубу можно гадить и программа будет воспринимать данные, выходящие с другого конца трубы, как вводимые с клавиатуры.
9 spectre1978
 
15.12.13
18:51
10 Chai Nic
 
15.12.13
18:53
(9) Спасибо, попробую
11 Chai Nic
 
15.12.13
20:33
Не работает что-то. Вот примерчик, сделанный по мотивам (9) с безобидным бесконечным пингом в качестве запускаемой программы - нифига не прерывается. Хотя интерактивно Ctrl+C прерывает отлично. Что я не так делаю?

---

#include <stdio.h>
#include <stdlib.h>
#include <winsock.h>

int main()
{
    char command[]="ping 127.0.0.1 -t";

    SECURITY_ATTRIBUTES secattr;
    ZeroMemory(&secattr,sizeof(secattr));
    secattr.nLength = sizeof(secattr);
    secattr.bInheritHandle = TRUE;

    HANDLE rPipe, wPipe;

    CreatePipe(&rPipe,&wPipe,&secattr,0);

    STARTUPINFO sInfo;
    ZeroMemory(&sInfo,sizeof(sInfo));
    sInfo.cb=sizeof(sInfo);
    sInfo.dwFlags=STARTF_USESTDHANDLES;
    sInfo.hStdInput=rPipe;
    sInfo.hStdOutput=stdout;
    sInfo.hStdError=GetStdHandle(STD_ERROR_HANDLE);    
    
    PROCESS_INFORMATION pInfo;
    ZeroMemory(&pInfo,sizeof(pInfo));

    
    CreateProcess(NULL,command,NULL,NULL,TRUE,NORMAL_PRIORITY_CLASS|CREATE_NO_WINDOW,NULL,NULL,&sInfo,&pInfo);
    CloseHandle(rPipe);
    
    puts("Press Enter to continue"); getchar();
    char str[]="\3"; // символ с кодом 3 (ctrl+c)
    DWORD flag;
    WriteFile (wPipe, str, 1, &flag, NULL);
    CloseHandle (wPipe);
    
}
12 spectre1978
 
15.12.13
20:52
ну вообще для начала у тебя процесс не создается. CreateProcess false возвращает. Почему - пока не знаю.
13 Chai Nic
 
15.12.13
21:17
(12) Всё создается.. в процессах он виден. А окно его мне и не нужно.
14 spectre1978
 
15.12.13
21:35
15 spectre1978
 
15.12.13
21:55
16 Torquader
 
16.12.13
01:28
Насколько я помню, есть WriteConsole, но нужно как-то получить указатель на консоль, в которой работает процесс.
Нажатие "крестика" на окне тоже можно сделать, но это сложнее.
Также можно выделить консоль и "засунуть" в буфер клавиатуры сообщение Ctrl+C - наверное, наиболее простой вариант. (смотрим keybd_event)
17 Torquader
 
16.12.13
01:30
И ещё - программа на Си++ исполняется в той же или другой консоли ? Если в той же, то она будет ждать, пока не завершится тот, кого она запустила.
18 spectre1978
 
16.12.13
08:10
(17) Да, похоже консоль нужна отдельная. http://www.wasm.ru/forum/viewtopic.php?id=6810
19 Chai Nic
 
16.12.13
08:12
(18) Категорически не приемлемо
20 spectre1978
 
16.12.13
08:16
(19) а почему? Как вариант - спрятать сразу консоль
21 spectre1978
 
16.12.13
08:26
22 Chai Nic
 
16.12.13
09:59
(20) Предполагается работа в виде сервиса, без взаимодействия с пользователем и без какого-либо отображения чего-либо.
23 spectre1978
 
16.12.13
10:13
(22) и какая в данном случае разница, в отдельной консоли стартует порожденный тобой процесс или в той же?
24 Torquader
 
16.12.13
10:50
Если service, то тут намного всё сложнее - там консоли, как таковой нет. Есть некоторый DeskTop, который доступен только для самой службы.
Я бы, на самом деле, посмотрел бы чем отличается TerminateProcess и Cltr+C - есть подозрение, что Ctrl+C просто вызывает ExitProcess - тогда разницы никакой.
25 spectre1978
 
16.12.13
10:54
(24) Так и есть
http://msdn.microsoft.com/en-us/library/windows/desktop/ms686016(v=vs.85).aspx
Each console process has its own list of application-defined HandlerRoutine functions that handle CTRL+C and CTRL+BREAK signals. The handler functions also handle signals generated by the system when the user closes the console, logs off, or shuts down the system. Initially, the handler list for each process contains only a default handler function that calls the ExitProcess function.
То бишь обработчик Ctrl-C по умолчанию именно ExitProcess и вызывает.
26 Torquader
 
16.12.13
10:56
(25) И чем это тогда от TerminateProcess отличается ?
27 spectre1978
 
16.12.13
10:59
(26) выходит, что по гамбургскому счету, ничем.
28 Torquader
 
16.12.13
10:59
Хотя, разница есть, если используются dll и обработка кодов запуска и завершения - при двоекратном вызове ExitProcess можно словить блокировку, а TerminateProcess просто не извещает dll о том, что состоялся выход.
С другой стороны, эти dll всё равно из адресного пространства процесса "выпнут", так что это не смертельно.
29 Torquader
 
16.12.13
11:04
В общем, если процесс сделал системный вызов, то ExitProcess будет дожидаться завершения этого вызова, а TerminateProcess не будет - просто ответ на вызов придёт в никуда (иногда это будет вызов синего экрана).
30 spectre1978
 
16.12.13
11:06
Выходит, что все-таки ExitProcess более цивилизованное завершение
31 spectre1978
 
16.12.13
11:10
http://msdn.microsoft.com/en-us/library/windows/desktop/ms682658(v=vs.85).aspx

Вот тут про DLL:
If one of the terminated threads in the process holds a lock and the DLL detach code in one of the loaded DLLs attempts to acquire the same lock, then calling ExitProcess results in a deadlock. In contrast, if a process terminates by calling TerminateProcess, the DLLs that the process is attached to are not notified of the process termination.

И далее интересно:

Therefore, if you do not know the state of all threads in your process, it is better to call TerminateProcess than ExitProcess. Note that returning from the main function of an application results in a call to ExitProcess.

Calling ExitProcess in a DLL can lead to unexpected application or system errors. Be sure to call ExitProcess from a DLL only if you know which applications or system components will load the DLL and that it is safe to call ExitProcess in this context.
32 Torquader
 
16.12.13
11:13
В том же MSDN предложено решение для управления дочерними процессами через регистрацию собственного сообщения и рассылки его через BroadCast, но для этого нужно вносить изменения в код приложений.
Консольный процесс обычно никаких Windows-объектов не создаёт, для которых было бы нужно удаление до конца завершения процесса поэтому ему будет всё равно, как его завершат.
33 spectre1978
 
16.12.13
11:14
Видимо, народ не лезет обычно в эти дебри и поэтому старается завершать как можно более "мягкими" функциями. Хотя судя по тому что написано, действительно, можно грохать TerminateProcess и не париться
34 Torquader
 
16.12.13
11:15
(31) Я про это в (28) и писал.
Теперь вопрос - сколько потоков в процессе ?
Если один, то ThreadLocalStorage не создаётся (а именно для неё нужны были вызовы подключения и отключения в dll).
35 spectre1978
 
16.12.13
11:18
(34) это уже вопрос к ТС, я не знаю что у него за процесс
36 Torquader
 
16.12.13
11:21
В общем, вопрос интересный, но сильно зависит от завершаемого процесса.
37 Chai Nic
 
16.12.13
14:38
(33) "можно грохать TerminateProcess и не париться"
В общем случае нельзя.. ибо терминируемый процесс не обязательно столь примитивен, как ping, он может и еще кучу мусора за собой оставить..
38 Chai Nic
 
16.12.13
14:41
(27) Он ничем не отличается лишь в том случае, если в приложении не задействован перехват Ctrl-C и не выполняются некие действия перед выходом..
39 Torquader
 
18.12.13
01:16
(38) Если процесс "взять на отладку", то можно узнать, есть ли у него обработчики Ctrl+C.
Ну и на самом деле, можно ему "подставить" вызов ExitProcess в его родной код.
40 spectre1978
 
19.12.13
08:25
ТС, куда пропал, расскажи - получилось?
Вот тут есть пример применения GenerateConsoleCtrlEvent: http://www.uhlib.ru/kompyutery_i_internet/sistemnoe_programmirovanie_v_srede_windows/p7.php#metkadoc18
41 Chai Nic
 
19.12.13
15:39
(40) Цель вопроса была в корректном закрытии процессов, которые порождает запускаемый процесс. То есть, "внучатых". Решил проблему получением списка процессов и поиском внучек по родителю.
Выдавать глобальные идеи — это удовольствие; искать сволочные маленькие ошибки — вот настоящая работа. Фредерик Брукс-младший