Дождаться завершения работы процесса

Ответы на вопросы, чаще всего задаваемые в форумах VBStreets. Для тех, кому лень искать.
GSerg
Шаман
Шаман
 
Сообщения: 14286
Зарегистрирован: 14.12.2002 (Сб) 5:25
Откуда: Магадан

Дождаться завершения работы процесса

Сообщение GSerg » 27.02.2007 (Вт) 11:16

Дождаться окончания работы процесса можно при помощи API-функции WaitForSingleObject. Ей нужно передать хэндл к процессу, завершение работы которого нуждо дождаться.
Получить этот хэндл можно двумя способами:
  1. С помощью функции Shell
    Эта функция, запустив указанный exe, возвращает не хэндл, а идентификатор (ID) процесса. Разница между ними существенна: ID - всего лишь номер, тогда как с хэндлом связана различная нужная информация (к примеру, список того, чего конкретно вам разрешается делать с процессом - разрешается ли вам читать его память, завершать его и т.п.). Для получения хэндла из ID используется функция OpenProcess, которой передаётся ID процесса, а также желаемые права доступа. Нам нужно только одно - дождаться окончания, поэтому запрашиваем лишь право SYNCHRONIZE, указав соответствующую константу.
    Например, для запуска Блокнота следует следует использовать (объявив предварительно константу SYNCHRONIZE, разумеется):
    Код: Выделить всё
    Dim hProcess As Long
    hProcess = OpenProcess(SYNCHRONIZE, 0, Shell("notepad.exe", vbNormalFocus))


    После того, как ожидание завершено, полученный хэндл нужно будет закрыть функцией CloseHandle (потому что больше он нам ни для чего не нужен).
    Код: Выделить всё
    Option Explicit

    Private Declare Function OpenProcess Lib "kernel32.dll" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
    Private Declare Function WaitForSingleObject Lib "kernel32.dll" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
    Private Declare Function CloseHandle Lib "kernel32.dll" (ByVal hObject As Long) As Long

    Private Const SYNCHRONIZE As Long = &H100000
    Private Const INFINITE As Long = &HFFFFFFFF

    Private Sub Form_Load()
      Dim hProcess As Long
     
      hProcess = OpenProcess(SYNCHRONIZE, 0, Shell("notepad.exe", vbNormalFocus))
     
      WaitForSingleObject hProcess, INFINITE
     
      MsgBox "Вы закрыли Блокнот!", vbInformation
     
      CloseHandle hProcess
    End Sub

    Константа INFINITE, передаваемая в WaitForSingleObject, означает, что мы готовы ждать завершения процесса, сколько потребуется, а не лишь определённое время.
  2. Запустив процесс функцией CreateProcess
    Функция Shell создаёт как бы "чужой" процесс: она внутри себя получает, но не передаёт нам хэндлы к созданному процессу, закрывая их - а это готовые хэндлы с правом полного доступа. Теоретически возможно (хотя и практически невероятно, настолько, что можно пренебречь), что система откажет в праве открыть процесс по ID с правом SYNCHRONIZE. Поэтому для полноты рассмотрим другой вариант запуска процесса с получением хэндлов, уже гарантированный:
    Код: Выделить всё
    Option Explicit

    Private Declare Function WaitForSingleObject Lib "kernel32.dll" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
    Private Declare Function CloseHandle Lib "kernel32.dll" (ByVal hObject As Long) As Long
    Private Declare Function CreateProcess Lib "kernel32.dll" Alias "CreateProcessA" (ByVal lpApplicationName As String, ByVal lpCommandLine As String, ByRef lpProcessAttributes As Any, ByRef lpThreadAttributes As Any, ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, ByRef lpEnvironment As Any, ByVal lpCurrentDriectory As String, ByRef lpStartupInfo As STARTUPINFO, ByRef lpProcessInformation As PROCESS_INFORMATION) As Long

    Private Type PROCESS_INFORMATION
      hProcess As Long
      hThread As Long
      dwProcessId As Long
      dwThreadId As Long
    End Type

    Private Type STARTUPINFO
      cb As Long
      lpReserved As Long
      lpDesktop As Long
      lpTitle As Long
      dwX As Long
      dwY As Long
      dwXSize As Long
      dwYSize As Long
      dwXCountChars As Long
      dwYCountChars As Long
      dwFillAttribute As Long
      dwFlags As Long
      wShowWindow As Integer
      cbReserved2 As Integer
      lpReserved2 As Byte
      hStdInput As Long
      hStdOutput As Long
      hStdError As Long
    End Type

    Private Const STARTF_USESHOWWINDOW As Long = &H1
    Private Const SW_NORMAL As Long = 1
    Private Const NORMAL_PRIORITY_CLASS As Long = &H20

    Private Const INFINITE As Long = &HFFFFFFFF

    Private Sub Form_Load()
      Dim si As STARTUPINFO, pi As PROCESS_INFORMATION
     
      si.cb = Len(si)
      si.dwFlags = STARTF_USESHOWWINDOW
      si.wShowWindow = SW_NORMAL
     
      'Хэндлы процесса и к потока будут позвращены в структуре pi, последний параметр.
      CreateProcess vbNullString, "notepad.exe", ByVal 0&, ByVal 0&, 0, NORMAL_PRIORITY_CLASS, ByVal 0&, vbNullString, si, pi

     
      'Нам не нужен хэндл потока, закроем его сразу.
      CloseHandle pi.hThread
     
      'А хэндл процесса - нужен, будем его ждать.
      WaitForSingleObject pi.hProcess, INFINITE
     
      MsgBox "Вы закрыли Блокнот!", vbInformation
     
      CloseHandle pi.hProcess
    End Sub




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


Для удобного копирования в будущем в код объявлений API-функций рекомендуется скачать API-Viewer, ссылка на который лежит тут.

Вернуться в Популярные вопросы

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

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

    TopList