正常结束由Shell所调用的Window 程序

来源:cww

我们曾在使"Shell指令具Wait功能"的文章中
提过,使用TerminateProcess()来结束一个由Shell所调用的Process,但也说过,这可
能会有一些问题,如果说,所调用的是一般正常且单一的Window程序(如NotePad),那
是有办法令之正常结束的,那便是使用PostMessage(hWnd, WM_CLOSE,0,0),令该Window
结束。然而,Shell的传回值是Process ID而不是hWnd,所以要加一些动作来取得hWnd。
我们可以用 GetForegroundWindow来做(如果该Shell是指定vbNormalFocus),另也可以使
用FindWindow来做,但是,如果有两个NotePad在时,会取到那一个,实在不知。另外使
EnumWindows来做,该Function用来巡行Top Level的Window,我们传入ProcessID当做
EnumWindows的第二个叁数,於是EnumWindowProcedure中的lParam便是该ProcessID,我
们另外用tid = GetWindowThreadProcessId(hwnd, pid)来取得hWnd所属的ProcessID
与我们传入的ProcessID(lParam)做比较,若相同,代表我们已找到所要的hWnd了。
EnumWindows的用法请叁考"尝试寻找电脑中执行的程序"

当然,这个程序的做法不是万能的,如果产生的Process又产生有好多个Window,我们结束 的,可能只是其中之一,那程序可能要改一下,变成只要在EnumWindow Procedure中找到 一个PID与Shell传回值的PID相同者,就使用PostMessage(hwnd, WM_CLOSE, 0,0)来结束 之,但这也不一定就全然可行,如果产生的不是一个有Window的程序,那使用WM_CLOSE 是没有用的,唯一能做的,就是使用TerminateProcess来强迫中断程序。 以下程序在.BAS
Declare Function GetParent Lib "user32" (ByVal hwnd As Long) As Long
Declare Function EnumWindows Lib "user32" (ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long
Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As Long, lpdwProcessId As Long) As Long
Declare Function SetForegroundWindow Lib "user32" Alias "SetForegroundWindow" (ByVal hwnd As Long) As Long
Declare Function OpenProcess Lib "kernel32" _
    (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, _
     ByVal dwProcessId As Long) As Long
Declare Function WaitForSingleObject Lib "kernel32" _
    (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
Declare Function CloseHandle Lib "kernel32" _
    (ByVal hObject As Long) As Long
Declare Function GetExitCodeProcess Lib "kernel32" _
    (ByVal hProcess As Long, lpExitCode As Long) As Long
Declare Function TerminateProcess Lib "kernel32" _
    (ByVal hProcess As Long, ByVal uExitCode As Long) As Long
Declare Function PostMessage Lib "user32" Alias "PostMessageA" _
    (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, _
     ByVal lParam As Long) As Long

Public Const SYNCHRONIZE = &H100000
Public Const STILL_ALIVE = &H103
Public Const INFINITE = &HFFFF
Public Const WM_CLOSE = &H10
Public hWnd5 As Long
Function EnumWindowsProc(ByVal hwnd As Long, ByVal lParam As Long) As Boolean
    Dim S As String
    If GetParent(hwnd) = 0 Then
       Dim tid As Long, pid As Long
       tid = GetWindowThreadProcessId(hwnd, pid)
       If pid = lParam Then
	  hWnd5 = hwnd
	  EnumWindowsProc = False
       End If
    End If
    EnumWindowsProc = True ' 表示继续列举 hWnd
End Function
以下程序在Form
Option Explicit
 Private ExitCode As Long
 Private hProcess As Long
 Private isDone As Long
 Private Sub Command1_Click()
 Dim pid As Long
 pid = Shell("notepad.exe", vbNormalFocus)
 Call EnumWindows(AddressOf EnumWindowsProc, pid) '设定hWnd5的值
 hProcess = OpenProcess(SYNCHRONIZE , 0, pid)
 isDone = False
 Do
   Call GetExitCodeProcess(hProcess, ExitCode)
   DoEvents
 Loop While ExitCode = STILL_ALIVE Or isDone
 Call CloseHandle(hProcess)
 isDone = True
 Label1.Caption = "Over"
 End Sub

 Private Sub Command2_Click()
 Dim i As Long
 Call SetForegroundWindow(hWnd5)
 Call PostMessage(hWnd5, WM_CLOSE, 0, 0)
 End Sub

 Private Sub Form_Unload(Cancel As Integer)
 isDone = True
 End Sub