Link to home
Start Free TrialLog in
Avatar of TLevin10
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(ProcessToWatch)
        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?
Avatar of AzraSound
AzraSound
Flag of United States of America image

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.
Avatar of TLevin10
TLevin10

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(processToWatch);

      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
Avatar of AzraSound
AzraSound
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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
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.
Alright - well, I guess I will have to deal :) [The problem with working backwards is you miss all the new features!]

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.
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_ACCESS, 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
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,DispatchMessage.

http://blogs.msdn.com/oldnewthing/archive/2005/02/17/375307.aspx