Ожидание завершения другой программы.

Программирование на Visual Basic, главный форум. Обсуждение тем программирования на VB 1—6.
Даже если вы плохо разбираетесь в VB и программировании вообще — тут вам помогут. В разумных пределах, конечно.
Правила форума
Темы, в которых будет сначала написано «что нужно сделать», а затем просьба «помогите», будут закрыты.
Читайте требования к создаваемым темам.
Kardinalli
Новичок
Новичок
 
Сообщения: 41
Зарегистрирован: 01.04.2009 (Ср) 1:34

Ожидание завершения другой программы.

Сообщение Kardinalli » 09.06.2015 (Вт) 19:10

Есть такой несложный код, опубликованный на винграде еще лет десять назад:

Код: Выделить всё
Private Sub Form_Load()
Dim v As Variant
Set v = CreateObject("Wscript.Shell")
v.run "c:\windows\notepad.exe", 1, True
set v=nothing
Beep 'пикнет только ПОСЛЕ того, как запущенная прога будет закрыта. Если не пикает, включи колонки.)
MsgBox "qq"
End Sub


Проблема в том, что выполнение оного замерзает на "v.run "c:\windows\notepad.exe", 1, True "
Как можно реализовать такой вариант, чтобы вместе с ожиданием завершения другой запущенной программы, основная могла обрабатывать свои другие задачи?

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Ожидание завершения другой программы.

Сообщение Хакер » 09.06.2015 (Вт) 20:13

—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Kardinalli
Новичок
Новичок
 
Сообщения: 41
Зарегистрирован: 01.04.2009 (Ср) 1:34

Re: Ожидание завершения другой программы.

Сообщение Kardinalli » 09.06.2015 (Вт) 20:33

Спасибо. Но этот кирпичик весьма громоздкий, у меня остальная часть запускающей программы с еще тремя процедурами, меньше.
Есть еще вот такой вариант
Точно помню, что много лет назад попадался код, вытекающий из первого поста, но там иначе запускалась сторонняя программа, без run и ожидание осуществлялось в цикле do loop, откуда можно было запускать нужные процедуры в режиме ожидания... Жаль, не сохранил ни ссылку, ни кода.

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Ожидание завершения другой программы.

Сообщение Хакер » 09.06.2015 (Вт) 20:44

Ну, если не критично узнавать о завершении процесса в то же мгновение, когда он завершился, можешь использовать подход с таймером, опрашивающем состояние объекта, олицетворяющего процесс, порождённый с помощью Wscript.Shell::Exec.
Вложения
WshRunDemo.zip
(1.76 Кб) Скачиваний: 114
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

The trick
Постоялец
Постоялец
 
Сообщения: 781
Зарегистрирован: 26.06.2010 (Сб) 23:08

Re: Ожидание завершения другой программы.

Сообщение The trick » 09.06.2015 (Вт) 22:47

UA6527P

Kardinalli
Новичок
Новичок
 
Сообщения: 41
Зарегистрирован: 01.04.2009 (Ср) 1:34

Re: Ожидание завершения другой программы.

Сообщение Kardinalli » 10.06.2015 (Ср) 12:05

Хакер писал(а):Ну, если не критично узнавать о завершении процесса в то же мгновение, когда он завершился, можешь использовать подход с таймером, опрашивающем состояние объекта, олицетворяющего процесс, порождённый с помощью

Вот это отлично подходит, мне не нужна мгновенная реакция, достаточно проверять с небольшим интервалом, спасибо! Зато не ест ресурсы.

Нашел еще один отлично работающий вариант не вешающий запускаемую программу, для для коллекции. Если вставить в цикл задержку в пару десятков мс, вообще не потребляет ресурсов.
Спасибо друзья!

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Ожидание завершения другой программы.

Сообщение Хакер » 10.06.2015 (Ср) 12:20

Kardinalli писал(а):Нашел еще один отлично работающий вариант не вешающий запускаемую программу

Запускаемую не вешает ни один способ. Запускающую этот способ «вешает».
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Kardinalli
Новичок
Новичок
 
Сообщения: 41
Зарегистрирован: 01.04.2009 (Ср) 1:34

Re: Ожидание завершения другой программы.

Сообщение Kardinalli » 10.06.2015 (Ср) 15:18

Сорри, я имел ввиду запускающую - и нет, не вешает, если использовать небольшое дополнение предложенное Vlanib-ом чуть ниже там же. Для этого DoEvents и используется.
Я разместил на форме запускающей программы несколько контролов со своими задачами, после запуска второй программы первая не зависает, можно с ней спокойно работать. Когда запускаемая программа закрывается, отрабатывает нужный вам код за Do...Loop (в моем случае закрывается и запускающая программа). Так что все в порядке.

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Ожидание завершения другой программы.

Сообщение Хакер » 10.06.2015 (Ср) 15:22

Подход с Do...Loop и DoEvents — криминальный, так делать не надо.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Kardinalli
Новичок
Новичок
 
Сообщения: 41
Зарегистрирован: 01.04.2009 (Ср) 1:34

Re: Ожидание завершения другой программы.

Сообщение Kardinalli » 10.06.2015 (Ср) 16:17

А почему? Я действительно хочу выбрать оптимальный вариант. В принципе, ваш вариант тоже отлично работает и компактнее.

И еще вопрос, корректно ли закрывать принудительно вторую программу такой конструкцией:
Код: Выделить всё
shell "taskkill /pid "ID процесса" /f"
shell "taskkill /pid "ID процесса" /t /f"
shell "taskkill /im explorer.exe /f"

/im - Имя процесса
/pid - ИД процесса
/t - завершение указанного процесса и всех его дочерних процессов
/f - принудительное завершение процесса

Работает без проблем, закрывает. Нет ли подводных камней?

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Ожидание завершения другой программы.

Сообщение Хакер » 10.06.2015 (Ср) 21:54

Kardinalli писал(а):А почему? Я действительно хочу выбрать оптимальный вариант.

А потому, что DoEvents слишком тяжеловесная функция, и помимо одиночной прокачки очереди сообщений выполняет ряд лишних тяжелых действий, но, что ещё хуже — DoEvents не обеспечивает замораживании потока до прихода ближайшего сообщения, а выполнив тяжеловесные задачи, возвращает управление сразу же. Будучи засунутой в цикл, она обеспечивает загрузку ЦП под 100%.

С идеологической точки зрения это также неверно, потому что один цикл уже есть (под капотом), и нет никакого смысла городить ещё и второй, выполняющийся в рамках итерации внешнего. Кроме того, если понадобится ожидать несколько процессов, в твоём случае это обернётся несколькими циклами, что крайней бессмысленно — должен быть один, и очень хорошо, чтобы это был тот цикл, который уже есть изначально.

Идеальный вариант, это вариант, когда есть основной поток (GUI-поток), крутящий цикл прокачки сообщений (как обычно), и есть ждущий поток, который сигналит первому при помощи сообщений. В моём случае вместо сигнализации сообщениями используется флаговый подход. Авторы Wscript.Shell непонятно чем думали: могли бы сделать событие изменения статуса процесса, на которое можно подписаться.

Kardinalli писал(а):Работает без проблем, закрывает. Нет ли подводных камней?

Ну да, работает, но я бы так делать не стал бы (если бы писал что-то отличное от временной утилитки), а использовал бы соответствующую API-функцию. Подводные камни, конечно же, есть. И не важно, на сколько маловероятно то, что я сейчас опишу, ибо по сравнению с вероятностью, что что-то не так будет с API-функцией, эти вероятности выглядят огромными.

Во-первых, набор API-функцией — более-менее солидная вещь. Не может API-функция просто так внезапно исчезнуть, их набор поддерживают в работоспособном виде для разработчиков, а если какую-то API-функцию хотят упразднить — заранее уведомляют об этом в соответствующей странице документации для разработчиков. API-функции позиционируются как «вещь для разработчиков», и соответствующие люди над этим трясутся. Набор утилит не позиционируется как инструментарий для разработчиков — скорее как инструмент для администрирования. Что-то я не припомню, чтобы где-то MS задокументировали (на правах документа для разработчиков, а не хелпа для юзеров, относящегося к конкретной версии продукта), что есть такой-то набор утилит, что они обещают сохранять по возможности неизменным, что смысл ключиков командной строки гарантированно не поменяется в какой-то следующей версией. Поэтому API-функции намного лучше.

Во-вторых, помимо ситуации, когда taskkill отсутствует, потому что его убрали из штатной поставки ОС вообще, может оказаться, что taskkill отсутствует на конкретной машине. Это может случиться по разным причинам: какая-либо кастомная сборка ОС, например минималистическая, которая загружается с CD, либо потому что кто-то (что-то) удалил эту программу непреднамеренно, а SFP отключён, либо потому, что её удалили намеренно (не важно, в силу каких причин). С API-функцией такого произойти не может.

В-третьих, этот способ не сработает, если в папке с программой окажется другой taskkill. В этом случае будет вызван не системный taskkill, а тот, который лежит рядом (который может либо делать что-то совершенно другое, либо вообще не работать). Конечно, если программа живёт в своём собственном каталоге, и устанавливается в Program Files, вероятность такого случая — мала (но остаётся), но есть такой популярный формат распространения программ как «portable», когда программа не требует установки, когда она может жить в любой папке, когда она распространяется в виде одного EXE-файла, не зависящего от каких-либо рядом лежящих файлов. В этом случае вероятность такого отказа сильно увеличивается: если твой EXE-файл может лежать где угодно (и при этом будет работать), может запросто оказаться, что рядом лежит какой-то левый taskkill. Например, я часто прошу мне скинуть какую-нибудь программку из другой версии ОС, чтобы что-то проверить/поисследовать. И вот будет у меня лежать на рабочем столе taskkill из 64-битной Win7, и кину я на рабочий стол твой exe-шник, и случится облом. А с API-функций такие сценарии исключены.

В-четвёртвых, очень легко может быть, что твоё программа будет запущено в изменённом окружении или сама изменит своё окружение. Если более конкретно, то речь идёт о переменных окружения. Если ещё более конкретно — о переменной окружения PATH. Именно эта переменная окружения используется, когда запускается процесс, полный путь к которому не указан. Windows будет искать исполняемый файл в местах, перечисленных в переменной окружения PATH. Если модифицировать значение этой переменной, твой запуск taskkill обломается — система не сможет его найти.

Вот я в командной строке вызываю taskkill (он вызывается и выводит свой краткий хелп), затем порчу PATH и пытаюсь второй раз вызвать taskkill — система уже не может его найти:
running_taskkill_with_spoiled_path_envvar.png
running_taskkill_with_spoiled_path_envvar.png (8.8 Кб) Просмотров: 3836

Если из этого окна командной строки запустить твою программу, она таким же образом не сможет запустить taskkill. И, естественно, испортить переменную окружения PATH можно не только из командной строки: это может сделать любой процесс, который запустит твой процесс, или это может сделать сам же твой процесс, или это может быть сделано глобально на уровне пользователя ОС или на уровне всей ОС. А с API-функцией подобные сценарии отменяются в принципе.

В-пятых, запуск taskkill — это всё таки создание (порождение) нового процесса. Ситуация, когда для завершения одного процесса нужно сначала породить новый другой, попахивает дебилизмом. В условиях недостатка ресурсов (например памяти ядра или свободных страниц в файле подкачки) может случиться так, что запуск нового процесса обломится. И мы получим ситуацию, когда из-за нехватки памяти мы не можем уничтожить процесс, хотя как раз убийство процесса освободило бы память и сильно улучшило бы положение дел. Это всё равно, что умереть от жажды, потому что не было сил открыть рот (а не потому, что не было воды). И такая ситуация (когда не хватает системных ресурсов на запуск taskkill) вовсе не надуманная и не такая нереалистичная, как могло бы показаться, — я буквально месяц назад с ней сталкивался. Я рендерил видео к этому посту в Sony Vegas-е, и он никак не мог его отрендерить — говорил «недостаточно памяти». При том, что места в файлах подкачки было явно навалом, было понятно, что не хватает не памяти как таковой, а свободного региона в адресном пространстве. Погуглив по точной строке с сообщением об ошибке, я наткнулся на то, что люди решают эту проблему, добавляя EXE-файлу атрибут LargeAddressAware (с помощью editbin). Но мне-то было совершенно очевидно, что одно лишь добавление этого атрибута вообще никак не влияет на ситуацию: наличие этого атрибута сыграет свою роль только в том случае, если система была загружена с ключом /3GB в boot.ini. С этим ключом ядерная часть АП всех процессов будет ужата до 1 GB, а пользовательская часть — расширена до 3 GB. Впрочем, без этого атрибута система всё равно будет выделять процессу память только из нижних двух гигабайт, и только с наличием атрибута LargeAddressAware процесс сможет воспользоваться 3-х гигабайтным АП.

Поэтому я выдал Vegas-у этот атрибут, а в boot.ini добавил ключ /3GB. Это имело вполне ожидаемый исход: система вообще не могла загрузиться. Ключ /3GB заставлял ядерную часть АП стиснутся с 2 Гб до 1 Гб (то есть уменьшиться вдвое), а у меня в то же самое время был включен режим PAE, включение которого сильно увеличивает размер таблиц PDE/PTE (размер структур PTE и PDE без PAE — 4 байта, в режиме PAE — в два раза больше — 8 байт). Режим PAE был задействован из-за того, что был включен аппаратный DEP. Поэтому я отредактировал boot.ini так, чтобы запуститься с /3GB но без DEP (а значит без использования PAE).

Загрузиться смог, но сильно это не помогло: большая часть драйверов не смогла загрузиться, работал только основной монитор, да и то в режиме 640×480 · 16 цветов, но что самое главное: я не могу запустить практически ничего, потому что ядру не хватало памяти. Было ясно, что о Вегасе не приходится и мечтать, и нужно было хотя бы вернуть boot.ini в исходное состояние. Но не тут-то было: не хватало ресурсов, чтобы запустить тот же Notepad. Нужно было позавершать разные фоновые процессы, но не хватало ресурсов даже на то, чтобы открыть Диспетчер задач по Ctrl+Alt+Del. Удавалось открыть cmd, но и оттуда taskkill не мог нормальо запуститься — не хватило памяти. Тем не менее, я тогда как-то выкрутился и сумел убить explorer, поубивать taskkill-ом все фоновые процессы, запустить блокнот и отремонтировать boot.ini. А видео в итоге пришлось 5 часов рендерить на виртуальной машине (которая легко загрузилась с ключом /3GB).

В-шестых, запуск taskkill может быть попросту запрещён административными настройками на целевой машине.

В-седьмых, уничтожение процесса с помощью так или иначе выполняется с помощью API-функции TerminateProcess — не важно, кто будет ею пользоваться (твоё приложение само или вызванный им taskkill). Этой функции нужен хендл процесса. Но не просто хендл, а хендл с правом на уничтожение процесса. При этом порождающий процесс при создании дочернего может получить хендл с полным набором прав. Сторонний же процесс (который не порождал убиваемый) вынужден использовать OpenProcess, чтобы из PID-а получить хендл. Есть вероятность, что при запросе хендла по PID-у, из-за особенностей административной настройки конкретной машины, процесс taskkill не сможет получить хендл с правом на уничтожение процесса жертвы, а значит и не сможет его убить. При этом процесс-родитель такой хендл может иметь запросто.

Ну и в конце-концов, то есть в-восьмых: есть некоторая комбинация 3-го и 4-го пунктов, которая выглядит даже более реалистичной, чем эти пункты в отдельности. Не обязательно, чтобы левый taskkill лежал в папке с нашей программой. Не обязательно портить переменную окружения PATH. Она может быть вполне законно изменена после установки какого-то софта так, что будет включать какую-то директорию, в которой будет лежать какая-то левая taskkill какого-то стороннего софта. Например у меня так после установки cygwin-а в PATH добавился каталог с cygwin-овскими бинарниками (ls, cat, grep, ps), при этом cygwin-овский бинарник link (создающий ссылки уровня файловой системы) перекрыл Microsoft-овский линкер link (из состава Visual Studio или Platform SDK), а cygwin-овский бинарник find перекрыл системный (штатный) бинарник find. Cygwin делали отнюдь не дураки, и, тем не менее, мы получили такой конфликт имён. А где гарантия, что какой-нибудь софт не притащит свой taskkill, убивающий какую-то свою задачу в терминах этого софта, на компьютер и не занесёт каталог, в котором эта программа лежит, в PATH? А с использованием API-функции такой сценарий исключён.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Kardinalli
Новичок
Новичок
 
Сообщения: 41
Зарегистрирован: 01.04.2009 (Ср) 1:34

Re: Ожидание завершения другой программы.

Сообщение Kardinalli » 11.06.2015 (Чт) 21:26

Весьма развернутый ответ, спасибо.
По поводу DoEvents - было обсуждение на тему влияет ли ее применение на загрузку процессора, у меня никак не получалось загрузить его более чем на 1% обрабатывая внутри цикла несколько ссылок на подпрограммы. А если добавить таймер на пару десятков мс, то загрузка падает до 0.1% максимум.
Разумеется речь идет о конкретно моей программе, возможно в других случаях будет иначе. Впрочем, я использую вариант со скриптом.

Далее, по поводу надежности API и ненадежности taskkill-а, наверное это все же актуально для совсем уж диких сборок и изощренных издевательств над ОС.
Впрочем, раз я стартую программу скриптовым вариантом, логично и закрывать ее так же.

Код: Выделить всё
Sub killprog()
Dim wbemObjectSet
Dim wbemObject
Dim strComputer
Dim strProcsToKill
strComputer = "."
strProcsToKill = "1.exe"
Set wbemObjectSet = GetObject("winmgmts://" & strComputer).InstancesOf("Win32_Process")
For Each wbemObject In wbemObjectSet
If LCase(wbemObject.Name) = strProcsToKill Then wbemObject.Terminate
Next
End Sub

Поставил кнопку на закрытие - работает без проблем.

WSH часть системы, по моему еще с Win95 OSR, вряд ли кто то специально будет его останавливать, да и софт будет стоять на подконтрольных машинах.

Тут вот еще какой момент всплыл. Мне нужно чтобы запускаемая программа закрывалась, если закрыть лоадер принудительно. Для этого в Private Sub Form_Terminate() ссылка на код который выше. Если закрыть через диспетчер, или Process Explorer, то дочерняя программа остается работать. Как можно это побороть?

Kardinalli
Новичок
Новичок
 
Сообщения: 41
Зарегистрирован: 01.04.2009 (Ср) 1:34

Re: Ожидание завершения другой программы.

Сообщение Kardinalli » 14.06.2015 (Вс) 23:00

Что-то непонятное творится. Набросал несколько разных вариантов программки, везде для запуска используется тот скрипт который вы выше выложили. Сама часть отвечающая за запуск везде одинакова. По Вин 7 все работают одинаково.
Но под ХР конечный вариант как взбесился, выбрасывает ошибку о необрабатываемом исключении вот на этой строке
Set ExecObject = sh.Exec("calc.exe")
Файл и пути там разумеется другие. Причем средствами VB эта ошибка не обрабатывается, пришлось пошагово искать где именно вылетает, в чем проблема, не понимаю! В других вариантах эта часть точно такая же и работает!

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Ожидание завершения другой программы.

Сообщение Хакер » 14.06.2015 (Вс) 23:07

Я лично ни одного скрипты не выкладывал в этой теме.

Kardinalli писал(а):Причем средствами VB эта ошибка не обрабатывается,

Ну, при желании — обрабатывается.

Может какая-то библиотека, подгруженная в АП твоего процесса портит память WShell. Может быть на твоей XP-машине стоит глючная версия WShell и нужен поставить какой-нибудь хотфикс (SP-то хоть третий?).

У меня XP, можешь выложить падающей образец мне на тестирование.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Kardinalli
Новичок
Новичок
 
Сообщения: 41
Зарегистрирован: 01.04.2009 (Ср) 1:34

Re: Ожидание завершения другой программы.

Сообщение Kardinalli » 15.06.2015 (Пн) 0:14

Спасибо большое, я разобрался, проблема была именно в запускаемом файле, не хватает кое-каких библиотек. :oops:
Плохо, что в данном случае ошибка никак не идентифицируется, это уже другая программа и не имеет отношения к загрузчику. Просто вылетает и предложение отправить лог мелкомягким.
Насчет скриптов - я имел ввиду ваш архив выше, запуск с помощью сервера сценариев, Windows Script Host, для него стандартные языки VBScript и JScript.

Очень много полезного нашел тут, оказывается насколько проще решать множество задач используя WSH...


Вернуться в Visual Basic 1–6

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 58

    TopList