Как дождаться завершения процесса оболочки перед выполнением дальнейшего кода в VB6
у меня есть небольшое приложение VB6, в котором я использую Shell
команда для выполнения программы. Я сохраняю выходные данные программы в файле. Затем я читаю этот файл и помещаю вывод на экран с помощью msgbox в VB6.
вот как выглядит мой код сейчас:
sCommand = "evaluate.exe<test.txt "
Shell ("cmd.exe /c" & App.Path & sCommand)
MsgBox Text2String(App.Path & "experiments" & genname & "freq")
проблема в том, что вывод, который программа VB печатает с помощью msgbox, является старым состоянием файла. Есть ли способ удержать выполнение кода VB до моей оболочки командная программа завершается, чтобы я получил правильное состояние выходного файла, а не предыдущее состояние?
6 ответов
секретный соус нужно делать это WaitForSingleObject
функции, который блокирует выполнение процесса вашего приложения до завершения указанного процесса (или тайм-аута). Это часть API Windows, легко вызывается из приложения VB 6 после добавления соответствующего объявления в код.
эта декларация будет выглядеть так:
Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle _
As Long, ByVal dwMilliseconds As Long) As Long
он принимает два параметра: дескриптор процесса, который вы хотите ждать, и интервал времени ожидания (в миллисекундах), который указывает максимальное время ожидания. Если не указать интервал тайм-аута (нулевое значение), функция не будет ждать и немедленно вернется. Если задан бесконечный интервал времени ожидания, функция возвращает только когда процесс сигнализирует, что он завершен.
вооруженный этим знанием, единственная задача, которая остается выяснить, как получить ручку к процессу, который вы начали. Это оказывается довольно просто, и может быть достигнуто несколькими различными способами:
-
одна из возможностей (и способ, которым я бы это сделал) - использовать
ShellExecuteEx
функции, также из API Windows, в качестве замены дляShell
функция, встроенная в VB 6. Эта версия гораздо более универсальна и мощна, но так же легко вызывается с помощью соответствующего объявления.она возвращает дескриптор процесса создает. Все, что вам нужно сделать, это передать, что дескриптор
нет необходимости прибегать к дополнительным усилиям вызова CreateProcess () и т. д. Это более или менее дублирует старый код Рэнди Берча, хотя он не был основан на его примере. Существует так много способов освежевать кошку.
здесь у нас есть предварительно упакованная функция для удобного использования, которая также возвращает код выхода. Поместите его в статическое (.Bas) модуль или включить его в форму или класс.
Option Explicit
Private Const INFINITE = &HFFFFFFFF&
Private Const SYNCHRONIZE = &H100000
Private Const PROCESS_QUERY_INFORMATION = &H400&
Private Declare Function CloseHandle Lib "kernel32" ( _
ByVal hObject As Long) As Long
Private Declare Function GetExitCodeProcess Lib "kernel32" ( _
ByVal hProcess As Long, _
lpExitCode As Long) As Long
Private Declare Function OpenProcess Lib "kernel32" ( _
ByVal dwDesiredAccess As Long, _
ByVal bInheritHandle As Long, _
ByVal dwProcessId As Long) As Long
Private Declare Function WaitForSingleObject Lib "kernel32" ( _
ByVal hHandle As Long, _
ByVal dwMilliseconds As Long) As Long
Public Function ShellSync( _
ByVal PathName As String, _
ByVal WindowStyle As VbAppWinStyle) As Long
'Shell and wait. Return exit code result, raise an
'exception on any error.
Dim lngPid As Long
Dim lngHandle As Long
Dim lngExitCode As Long
lngPid = Shell(PathName, WindowStyle)
If lngPid <> 0 Then
lngHandle = OpenProcess(SYNCHRONIZE _
Or PROCESS_QUERY_INFORMATION, 0, lngPid)
If lngHandle <> 0 Then
WaitForSingleObject lngHandle, INFINITE
If GetExitCodeProcess(lngHandle, lngExitCode) <> 0 Then
ShellSync = lngExitCode
CloseHandle lngHandle
Else
CloseHandle lngHandle
Err.Raise &H8004AA00, "ShellSync", _
"Failed to retrieve exit code, error " _
& CStr(Err.LastDllError)
End If
Else
Err.Raise &H8004AA01, "ShellSync", _
"Failed to open child process"
End If
Else
Err.Raise &H8004AA02, "ShellSync", _
"Failed to Shell child process"
End If
End Function
Я знаю, что это старый нить, но...
Как насчет использования метода запуска узла сценария Windows? Он имеет параметр bWaitOnReturn.
делаю так :
Private Type STARTUPINFO
cb As Long
lpReserved As String
lpDesktop As String
lpTitle As String
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 Long
hStdInput As Long
hStdOutput As Long
hStdError As Long
End Type
Private Type PROCESS_INFORMATION
hProcess As Long
hThread As Long
dwProcessID As Long
dwThreadID As Long
End Type
Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal _
hHandle As Long, ByVal dwMilliseconds As Long) As Long
Private Declare Function CreateProcessA Lib "kernel32" (ByVal _
lpApplicationName As String, ByVal lpCommandLine As String, ByVal _
lpProcessAttributes As Long, ByVal lpThreadAttributes As Long, _
ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, _
ByVal lpEnvironment As Long, ByVal lpCurrentDirectory As String, _
lpStartupInfo As STARTUPINFO, lpProcessInformation As _
PROCESS_INFORMATION) As Long
Private Declare Function CloseHandle Lib "kernel32" _
(ByVal hObject As Long) As Long
Private Declare Function GetExitCodeProcess Lib "kernel32" _
(ByVal hProcess As Long, lpExitCode As Long) As Long
Private Const NORMAL_PRIORITY_CLASS = &H20&
Private Const INFINITE = -1&
Public Function ExecCmd(cmdline$)
Dim proc As PROCESS_INFORMATION
Dim start As STARTUPINFO
' Initialize the STARTUPINFO structure:
start.cb = Len(start)
' Start the shelled application:
ret& = CreateProcessA(vbNullString, cmdline$, 0&, 0&, 1&, _
NORMAL_PRIORITY_CLASS, 0&, vbNullString, start, proc)
' Wait for the shelled application to finish:
ret& = WaitForSingleObject(proc.hProcess, INFINITE)
Call GetExitCodeProcess(proc.hProcess, ret&)
Call CloseHandle(proc.hThread)
Call CloseHandle(proc.hProcess)
ExecCmd = ret&
End Function
Sub Form_Click()
Dim retval As Long
retval = ExecCmd("notepad.exe")
MsgBox "Process Finished, Exit Code " & retval
End Sub
большой код. Только одна маленькая проблема: вы должны объявить в ExecCmd (после тусклого запуска как STARTUPINFO):
Дим рет как долго
вы получите ошибку при попытке компиляции в VB6, если вы этого не сделаете. Но он отлично работает :)
с уважением
в моих руках решение csaba зависает с intWindowStyle = 0 и никогда не передает управление обратно в VB. Единственный выход-завершить процесс в taskmanager.
установка intWindowStyle = 3 и закрытие окна вручную передает управление назад