TLevin10
asked on
VB6 - Process Exit Event?
Hello,
Due to constraints within our company, we are rewriting a VB.NET component in VB6. Our component watches a process to see when it is closed by the user (or by other means). In VB.NET, we used the Process Class to attach to a running process and then caught the "Process.Exited" event thrown when the process ended. Is there a similar event in VB6?
Our program spawns a launcher, which then spawns another process. We want to wait for the 3rd process to exit, and then throw an event. For example, our Process (Process A) shell's off Process B (3rd party), which then creates Process C. When process C exits, we need to throw an event from Process A. Right now, we are using a loop to constantly check when the process exits, but this causes too much processor usage for no reason. Currently, our main method looks like the following:
Public Sub RunShell(FileName As String, ProcessToWatch As String)
Dim idProg As Long, iExit As Long, iProc As Long
idProg = Shell(FileName, 1)
iExit = WaitOnShell(idProg)
If Not iExit Then
iExit = WaitOnShellByName(ProcessT oWatch)
If Not iExit Then RaiseEvent OnExit
End If
End Sub
Is there something similar to "Process.Exited" in VB6, and how can I use it?
Due to constraints within our company, we are rewriting a VB.NET component in VB6. Our component watches a process to see when it is closed by the user (or by other means). In VB.NET, we used the Process Class to attach to a running process and then caught the "Process.Exited" event thrown when the process ended. Is there a similar event in VB6?
Our program spawns a launcher, which then spawns another process. We want to wait for the 3rd process to exit, and then throw an event. For example, our Process (Process A) shell's off Process B (3rd party), which then creates Process C. When process C exits, we need to throw an event from Process A. Right now, we are using a loop to constantly check when the process exits, but this causes too much processor usage for no reason. Currently, our main method looks like the following:
Public Sub RunShell(FileName As String, ProcessToWatch As String)
Dim idProg As Long, iExit As Long, iProc As Long
idProg = Shell(FileName, 1)
iExit = WaitOnShell(idProg)
If Not iExit Then
iExit = WaitOnShellByName(ProcessT
If Not iExit Then RaiseEvent OnExit
End If
End Sub
Is there something similar to "Process.Exited" in VB6, and how can I use it?
Not that I'm aware of, unless you are the one launching the process, in which case you can "watch" for the process to exit. If it's merely a processor issue, you may try putting the code checking into some type of timer event to check once a second and then notify you once the process has ended. It should be much less of a strain on the processor.
ASKER
Is there any way to attach to the process at all? I know that in VB.NET/C#, I can actually enable a process to raise events and it has an exit handler. I assume that these are operating system features, not specific to .NET, since I can do it for any running process (Not just .NET compatible ones). For instance, in C#:
public void WatchProcess(string processToWatch)
{
Process[] procCollection = Process.GetProcessesByName (processTo Watch);
foreach (Process p in procCollection)
{
p.EnableRaisingEvents = true;
p.Exited +=new EventHandler(p_Exited);
}
}
Is there a similar "Process" class in VB6, or some API calls to do the same thing?
public void WatchProcess(string processToWatch)
{
Process[] procCollection = Process.GetProcessesByName
foreach (Process p in procCollection)
{
p.EnableRaisingEvents = true;
p.Exited +=new EventHandler(p_Exited);
}
}
Is there a similar "Process" class in VB6, or some API calls to do the same thing?
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Hi Azra,
First off, thanks for the quick response. In fact, I do have the handle to the process, so calling WaitForSingleObject isn't such a problem.
Similar to looping, however, WaitForSingleObject() causes my application to hang until the spawned process is closed. This is not good in my application, since I expect my users to continue working while the new process is doing its job...
Is it possible to use "WaitForSingleObject()" in a non-blocking manner, so that my application can continue running while the new process is doing its thing?
Thanks,
TLevin10
First off, thanks for the quick response. In fact, I do have the handle to the process, so calling WaitForSingleObject isn't such a problem.
Similar to looping, however, WaitForSingleObject() causes my application to hang until the spawned process is closed. This is not good in my application, since I expect my users to continue working while the new process is doing its job...
Is it possible to use "WaitForSingleObject()" in a non-blocking manner, so that my application can continue running while the new process is doing its thing?
Thanks,
TLevin10
Unfortunately, that's just a drawback of VB and watching a process (not event driven). Add to the fact VB's lack of real ability to create separate threads, and you begin to run out of options.
With that said, I would say you have a couple of options:
1) As I noted before, run a simple check in some type of timer event to cut down the processor throttling
2) Do the best VB can do in multithreading. Have this process checking functionality enclosed within an ActiveX EXE project that you can spawn from your main application. You can shoot the component off to watch for the process and your main app can continue on as normal. In addition, you can have the ActiveX EXE raise an event when it determines the process has ended, giving you the closest, in terms of look and feel, to your familar .NET Process.Exited scenario.
With that said, I would say you have a couple of options:
1) As I noted before, run a simple check in some type of timer event to cut down the processor throttling
2) Do the best VB can do in multithreading. Have this process checking functionality enclosed within an ActiveX EXE project that you can spawn from your main application. You can shoot the component off to watch for the process and your main app can continue on as normal. In addition, you can have the ActiveX EXE raise an event when it determines the process has ended, giving you the closest, in terms of look and feel, to your familar .NET Process.Exited scenario.
ASKER
Alright - well, I guess I will have to deal :) [The problem with working backwards is you miss all the new features!]
Thanks
Thanks
No problem...good luck w/ everything
<< Is it possible to use "WaitForSingleObject()" in a non-blocking manner
Yes. You can use MsgWaitForMultipleObjects. This will allow your GUI to process all messages.
Yes. You can use MsgWaitForMultipleObjects.
Here is quick example to show how it works. You can spice it up as you please.
Option Explicit
Private Declare Function OpenProcess Lib "kernel32" _
(ByVal dwDesiredAccessas As Long, _
ByVal bInheritHandle As Long, _
ByVal dwProcId As Long) As Long
Private Declare Function MsgWaitForMultipleObjects Lib "user32" ( _
ByVal nCount As Long, _
pHandles As Long, _
ByVal fWaitAll As Long, _
ByVal dwMilliseconds As Long, _
ByVal dwWakeMask As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" ( _
ByVal hObject As Long) As Long
Private Const QS_HOTKEY& = &H80
Private Const QS_KEY& = &H1
Private Const QS_MOUSEBUTTON& = &H4
Private Const QS_MOUSEMOVE& = &H2
Private Const QS_PAINT& = &H20
Private Const QS_POSTMESSAGE& = &H8
Private Const QS_SENDMESSAGE& = &H40
Private Const QS_TIMER& = &H10
Private Const QS_ALLINPUT& = (QS_SENDMESSAGE _
Or QS_PAINT _
Or QS_TIMER _
Or QS_POSTMESSAGE _
Or QS_MOUSEBUTTON _
Or QS_MOUSEMOVE _
Or QS_HOTKEY _
Or QS_KEY)
Private Const WAIT_OBJECT_0& = 0
Private Const INFINITE = &HFFFF
Private Const PROCESS_ALL_ACCESS = &H1F0FFF
Dim hProcess As Long
Private Sub WaitOnProcess(ByVal arg As String)
Dim PID As Long
Dim lBusy As Long
PID = Shell(arg, vbNormalFocus)
hProcess = OpenProcess(PROCESS_ALL_AC CESS, 0&, PID)
Do
lBusy = MsgWaitForMultipleObjects( 1, hProcess, False, INFINITE, QS_ALLINPUT&)
DoEvents
Loop Until lBusy = WAIT_OBJECT_0&
CloseHandle hProcess
MsgBox arg & vbTab & "Exited", vbSystemModal
End Sub
Private Sub Command1_Click()
WaitOnProcess "c:\windows\system32\calc. exe"
End Sub
Option Explicit
Private Declare Function OpenProcess Lib "kernel32" _
(ByVal dwDesiredAccessas As Long, _
ByVal bInheritHandle As Long, _
ByVal dwProcId As Long) As Long
Private Declare Function MsgWaitForMultipleObjects Lib "user32" ( _
ByVal nCount As Long, _
pHandles As Long, _
ByVal fWaitAll As Long, _
ByVal dwMilliseconds As Long, _
ByVal dwWakeMask As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" ( _
ByVal hObject As Long) As Long
Private Const QS_HOTKEY& = &H80
Private Const QS_KEY& = &H1
Private Const QS_MOUSEBUTTON& = &H4
Private Const QS_MOUSEMOVE& = &H2
Private Const QS_PAINT& = &H20
Private Const QS_POSTMESSAGE& = &H8
Private Const QS_SENDMESSAGE& = &H40
Private Const QS_TIMER& = &H10
Private Const QS_ALLINPUT& = (QS_SENDMESSAGE _
Or QS_PAINT _
Or QS_TIMER _
Or QS_POSTMESSAGE _
Or QS_MOUSEBUTTON _
Or QS_MOUSEMOVE _
Or QS_HOTKEY _
Or QS_KEY)
Private Const WAIT_OBJECT_0& = 0
Private Const INFINITE = &HFFFF
Private Const PROCESS_ALL_ACCESS = &H1F0FFF
Dim hProcess As Long
Private Sub WaitOnProcess(ByVal arg As String)
Dim PID As Long
Dim lBusy As Long
PID = Shell(arg, vbNormalFocus)
hProcess = OpenProcess(PROCESS_ALL_AC
Do
lBusy = MsgWaitForMultipleObjects(
DoEvents
Loop Until lBusy = WAIT_OBJECT_0&
CloseHandle hProcess
MsgBox arg & vbTab & "Exited", vbSystemModal
End Sub
Private Sub Command1_Click()
WaitOnProcess "c:\windows\system32\calc.
End Sub
That, along with GetExitCodeProcess, though, both require a Do...Loop with the DoEvents keyword which results in 100% CPU usage, which is why I didn't suggest it (since processor usage was already an issue).
I agree with you that GetExitCodeProcess is high cpu usage but this is not the same when using MsgWaitForMultipleObjects
Really? That's very interesting as I don't think I've ever seen a Do...Loop, regardless of what was inside of it, as long as it had a DoEvents in it, it would eat up 100% of the CPU.
Have you looked at the CPU usage when you run the example
No, I'll take your word for it...I'm just saying its new to me.
Yeah its very nice for waiting on events because it pumps messages to your gui, and passes on the messages that are not of interest. This is what doevents is doing in the call simply passing along all messages. In C++ instead of doevents you would need to use PeekMessage,DispatchMessag e.
http://blogs.msdn.com/oldnewthing/archive/2005/02/17/375307.aspx
http://blogs.msdn.com/oldnewthing/archive/2005/02/17/375307.aspx