VB6 - Process Exit Event?


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?
Who is Participating?
AzraSoundConnect With a Mentor Commented:
No, remember, .NET is what has introduced this framework of classes/objects for we developers to tap into.  Prior to .NET, everything was done manually via intrinsic VB functions and the Windows API.

In VB, you can watch a process you started with the Shell command or CreateProcess API, because once you have the process id, you can call another API to wait for it to complete:

However, if you are not launching this app, then unless you can grab a handle to the process somehow, this API function is useless to you.  It's been so long since I've done anything even remotely related to enumerating/searching for a process, so, if you are getting a handle to this process you are looking for, then you should be able to call the WaitForSingleObject API as demonstrated in the above link to determine when that process has ended.
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.
TLevin10Author Commented:
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?

Cloud Class® Course: Microsoft Azure 2017

Azure has a changed a lot since it was originally introduce by adding new services and features. Do you know everything you need to about Azure? This course will teach you about the Azure App Service, monitoring and application insights, DevOps, and Team Services.

TLevin10Author Commented:
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?


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.
TLevin10Author Commented:
Alright - well, I guess I will have to deal :) [The problem with working backwards is you miss all the new features!]

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)
    lBusy = MsgWaitForMultipleObjects(1, hProcess, False, INFINITE, QS_ALLINPUT&)
    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.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.