【 tulaoshi.com - 编程语言 】
VB中,常以Shell指令来执行外部程式,然而它在Create该外部process後,立刻
就会回到vb的下一行程式,无法做到等待该Process结束时,才执行下一行指令,
或是说,无法得知该Process是否已结束,甚者,该Process执行到一半,又该如何
中止其执行等等,这些都不是Shell指令所能控制的,因此我们需使API的帮助来完
成。
第一个问题,如何等待shell所Create的process结束後才往後执行vb的程式。
首先要知道的是,每个Process有唯一的一个ProcessID,这是OS给定的,用来
区别每个Process,这个ProcessID(PID)主要可用来取得该Process相对应的一些
资讯,然而要对该Process的控制,却大多透过ProcessHandle(hProcess)。VB
Shell指令的传回值是PID,而非hProcess,所以我们需透过OpenProcess这个API来
取得hProcess而OpenProcess()的第一个叁数,指的是所取得的hProcess所具有的
能力,像PROCESS_QUERY_INFORMATION便是让GetExitCode()可取得hProcess所指
的process之状态,而PROCESS_TERMINATE,便是让TerminateProcess(hProcess..)
的指令能够生效,也就是说,不同叁数设定,使hProcess所具有的权限、能力有所
不同。取得hProcess後便可以使用WaitForSingleObject()来等待hProcess状态的
改变,也就是说,它会等待hProcess所指的process执行完,这个指令才结束,它
第二个叁数所指的是WaitForSingleObject()所要等待的时间(inmilliseconds)
,如果超过所指的时间,就TimeOut而结束WaitForSingleObject()的等待。若要它
无限的等下去,就设定为INFINITE。
pid=Shell("C:oolsspe3pe2.exe",vbNormalFocus)
hProcess=OpenProcess(PROCESS_QUERY_INFORMATION,0,pid)
ExitEvent=WaitForSingleObject(hProcess,INFINITE)
CallCloseHandle(hProcess)
上例会无限等待shell指令create之process结束後,才再做後面的vb指令。有
时觉得那会等太久,所以有第二个解决方式:等process结束时再通知vb就好,即
:设定一个公用变数(isDone),当它变成True时代表Shell所Create的Process已结
束。当Process还在执行时,GetExitCodeProcess会传&H103给其第二个叁数,直到
结束时才传另外的数值,如果程式正常结束,那Exitcode=0,否则就得看它如何
结束了。或许有人在其他地方看到loop的地方是LoopwhileExitcode<>0,那
有一点危险,如果以这程子来看,您不是用F4来离开pe2而是用右上方X的结束
doswindow那麽,会因为ExitCode的值永远不会是0,而进入无穷的回圈。
DimpidAsLong
pid=Shell("C:oolsspe3pe2.exe",vbNormalFocus)
hProcess=OpenProcess(PROCESS_QUERY_INFORMATION,0,pid)
isDone=False
Do
CallGetExitCodeProcess(hProcess,ExitCode)
Debug.PrintExitCode
DoEvents
LoopWhileExitCode=STILL_ALIVE
CallCloseHandle(hProcess)
isDone=True
另外,如果您的shell所Create的程式,有视窗且为立刻Focus者,可另外用以
下的方式DimpidAsLong
Dimhwnd5AsLong
pid=Shell("c:oolsspe3pe2.exe",vbNormalFocus)
hwnd5=GetForegroundWindow()
isDone=False
DoWhileIsWindow(hwnd5)
DoEvents
Loop
isDone=True
而如何强迫shell所Create的process结束呢,那便是
DimaaAsLong
IfhProcess0Then
aa=TerminateProcess(hProcess,3838)
EndIf
hProcess便是先前的例子中所取得的那个ProcessHandle,3838所指的是传给
GetExitCodeProcess()中的第二叁数,这是我们任意给的,但最好不要是0,因为
0一般是代表正常结束,当然这样设也不会有错。当然不可设&H103,以这个例子来
看,如果程式正处於以下的LOOP
Do
CallGetExitCodeProcess(hProcess,ExitCode)
Debug.PrintExitCode
DoEvents
LoopWhileExitCode=STILL_ALIVE
Debug.printExitCode
而执行了TerminateProcess(hProcess,3838)那会看到ExitCode=3838。然
而,这个方式在win95没问题,在NT中,可能您要在OpenProcess()的第一个叁数要
更改成PROCESS_QUERY_INFORMATIONOrPROCESS_TERMINATE这样才能Work。不过
良心的建议,非到最後关头,不要使用TerminateProcess(),因不正常的结束,往
往许多程式结束前所要做的事都没有做,可能造成Resource的浪费,甚者,下次再
执行某些程式时会有问题,例如:本人常使用MS-dosShellLink的方式执行一程
式,透过Comport与大电脑的联结,如果Ms-dosShellLink不正常结束,下次再
想Link时,会发现tooManyOpens,这便是一例。
另外,有人使用Shell来执行.bat档,即:
pid=Shell("c:aa.bat",vbNormalFocus)
可是却遇上aa.bat结束了,但ms-dos的Window却仍活着,那可以用以下的方式来做
pid=Shell("c:command.com/cc:aa.bat",vbNormalFocus)
那是执行Command.com,而Command.com指定执行c:aa.bat而且结束时自动Close
所有程式如下:
PrivateDeclareFunctionOpenProcessLib"kernel32"_
(ByValdwDesiredAccessAsLong,ByValbInheritHandleAsLong,_
ByValdwProcessIdAsLong)AsLong
PrivateDeclareFunctionWaitForSingleObjectLib"kernel32"_
(ByValhHandleAsLong,ByValdwMillisecondsAsLong)AsLong
PrivateDeclareFunctionCloseHandleLib"kernel32"_
(ByValhObjectAsLong)AsLong
PrivateDeclareFunctionGetExitCodeProcessLib"kernel32"_
(ByValhProcessAsLong,lpExitCodeAsLong)AsLong
PrivateDeclareFunctionTerminateProcessLib"kernel32"_
(ByValhProcessAsLong,ByValuExitCodeAsLong)AsLong
PrivateDeclareFunctionGetForegroundWindowLib"user32"()AsLong
PrivateDeclareFunctionIsWindowLib"user32"_
(ByValhwndAsLong)AsLong
ConstPROCESS_QUERY_INFORMATION=&H400
ConstSTILL_ALIVE=&H103
ConstINFINITE=&HFFFF
PrivateExitCodeAsLong
PrivatehProcessAsLong
PrivateisDoneAsLong
PrivateSubCommand1_Click()
DimpidAsLong
pid=Shell("C:oolsspe3pe2.exe",vbNormalFocus)
hProcess=OpenProcess(PROCESS_QUERY_INFORMATION,0,pid)
isDone=False
Do
CallGetExitCodeProcess(hProcess,ExitCode)
Debug.PrintExitCode
DoEvents
LoopWhileExitCode=STILL_ALIVE
CallCloseHandle(hProcess)
isDone=True
EndSub
PrivateSubCommand2_Click()
DimpidAsLong
DimExitEventAsLong
pid=Shell("C:oolsspe3pe2.exe",vbNormalFocus)
hProcess=OpenProcess(PROCESS_QUERY_INFORMATION,0,pid)
ExitEvent=WaitForSingleObject(hProcess,INFINITE)
CallCloseHandle(hProcess)
EndSub
PrivateSubCommand3_Click()
DimaaAsLong
IfhProcess0Then
aa=TerminateProcess(hProcess,3838)
EndIf
EndSub
PrivateSubCommand4_Click()
DimpidAsLong
Dimhwnd5AsLong
pid=Shell("c:oolsspe3pe2.exe",vbNormalFocus)
hwnd5=GetForegroundWindow()
isDone=False
DoWhileIsWindow(hwnd5)
DoEvents
Loop
isDone=True
EndSub
PrivateSubCommand5_Click()
DimpidAsLong
'pid=Shell("c:windowscommandxcopyc:aa.bata:",vbHide)
pid=Shell("c:command.com/cc:aa.bat",vbNormalFocus)
EndSub->