dcsimg
Results 1 to 11 of 11

Thread: [RESOLVED] Run CMD and wait

  1. #1

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    5,446

    Resolved [RESOLVED] Run CMD and wait

    Ok, here's another one I haven't done before. And again, I'm sure I could figure something out, and I'm sure there are prior threads that discuss this.

    However, I'm working on other stuff, and I thought I'd throw it out here for the latest advice.

    Primarily, I'm operating in my VB6 program. However, I'll be building files that are processed by another program. This other program will accept my input files from a command line, and then write its own output files (to the folder I direct). This other program is a command line utility, which spits a bunch of stuff to the CMD console (which I don't care about) and also writes an output file (which I do care about).

    Here's my question: I need to run this CMD (with my custom command line), and then pause until it's done. For testing, I'm currently doing it with a batch (BAT) file, and that works perfectly. Here's the single line in that batch file:

    Code:
    C:\PROGRA~1\OPENSI~1.0\bin\Analyze.exe -S C:\Users\Elroy\Desktop\MuscleLengths\ExampleSubject\MuscleLength_Setup_MuscleAnalysis.xml

    That XML file has all the other directives in it telling the Analyze.exe what to do. But that's beyond the scope of this question. I just need to know when it's done, freezing (or looping) VB6 until it is.

    And just a bit more information ... I'll find this Analyze.exe program, and warn the user if VB6 can't find it. That's not a problem. Also, it could be a 32-bit version of this Analyze.exe or it could be a 64-bit version. OpenSim has both versions. And, I won't be running on any OSs earlier than Windows 7. But it could be 7, 8, or 10.

    For the curious, this is about using the OpenSim program, or more specifically, their Analyze.exe utility, to calculate relative muscle lengths from lower extremity kinematics. Once the data are returned from OpenSim, I will further process and plot it.

    Thanks,
    Elroy
    Any software I post in these forums written by me is provided “AS IS” without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. Please understand that I’ve been programming since the mid-1970s and still have some of that code. My contemporary VB6 project is approaching 1,000 modules. In addition, I have a “VB6 random code folder” that is overflowing. I’ve been at this long enough to truly not know with absolute certainty from whence every single line of my code has come, with much of it coming from programmers under my employ who signed intellectual property transfers. I have not deliberately attempted to remove any licenses and/or attributions from any software. If someone finds that I have inadvertently done so, I sincerely apologize, and, upon notice and reasonable proof, will re-attach those licenses and/or attributions. To all, peace and happiness.

  2. #2
    PowerPoster
    Join Date
    Feb 2006
    Posts
    20,002

    Re: Run CMD and wait

    The basics:

    Shell() the external process, keeping the returned process ID. Call OpenProcess() against the process ID to get the process handle.

    Then you can use a Timer, etc. to poll using WaitForSingleObject() passing the process handle and a 0 timeout delay.


    There should be lots of examples posted in other threads.

  3. #3

  4. #4
    Frenzied Member
    Join Date
    Mar 2008
    Posts
    1,139

    Re: Run CMD and wait

    It looks like the old ShellandWait routine pointed to above by dill that most of us have a version of in our toolboxes. eg.

    Code:
    Sub ShellAndWait(sAppPath$, Optional iWindowStyle As VbAppWinStyle = vbMinimizedFocus, Optional lmsTimeOut As Long = INFINITE)
        
        'ref BasLan #732415 from Tom Esh
        
        Dim lPid As Long, hProc As Long, lTimeout As Long
        
        lPid = Shell(sAppPath, iWindowStyle)
        hProc = OpenProcess(SYNCHRONIZE, 0&, lPid)
        If hProc <> 0& Then
            WaitForInputIdle hProc, INFINITE
            WaitForSingleObject hProc, lmsTimeOut
            CloseHandle hProc
        End If
        
    End Sub
    … remember Baslan!?

    If you want to run a multi-line batch file command then something like
    Code:
    Sub RunBatchFileString(ByVal BatchFileString$, Optional ByVal Path$ = "", Optional Show As VbAppWinStyle = vbHide)
    
            Dim F&, q$
            
            q$ = """"
    
            If Len(Path$) And Right$(Path$, 1) <> "\" Then Path$ = Path$ & "\"
            Path$ = Path$ & "temp.bat"
            
             F = FreeFile
            Open Path$ For Output As F
                Print #F, BatchFileString$
            Close F
            
            ShellAndWait Environ$("COMSPEC") & " /C " & q$ & Path$ & q$, Show
            Kill Path$
    
    End Sub
    … can be handy.

  5. #5
    PowerPoster
    Join Date
    Feb 2006
    Posts
    20,002

    Re: Run CMD and wait

    I mention using a Timer to poll because you normally don't want to freeze the GUI thread. That can cause a lot of users to terminate your program because it appears hung.

  6. #6

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    5,446

    Re: Run CMD and wait

    Ok, thanks guys.

    I guess I thought it was going to be a bit trickier because it was a command line utility. I'm not sure why, but I didn't think about just using Shell. I've got the old ShellAndWait procedure. In fact, here's what I've got:

    Code:
    
    
    Public Sub WaitForTermination(lProcessIdFromShell As Long)
        ' This procedure should be used with some caution.
        ' The program will not be able to refresh its forms and controls
        '   while waiting.  The best way to use this is to make sure that
        '   the program has nothing visible before calling this procedure.
        '
        ' The following command is the best way to use this procedure:
        '            WaitForTermination Shell(  sTheShellCommandString  )
        Const SYNCHRONIZE As Long = &H100000
        Const INFINITE As Long = &HFFFF
        Dim lProcessHwnd As Long
        '
        If lProcessIdFromShell = 0 Then Exit Sub    ' Make sure there is a process to wait on.
        lProcessHwnd = OpenProcess(SYNCHRONIZE, 0, lProcessIdFromShell)
        If lProcessHwnd = 0 Then Exit Sub           ' Make sure we can open the process.
        WaitForSingleObject lProcessHwnd, INFINITE  ' Wait for process to terminate.
        CloseHandle lProcessHwnd
    End Sub
    
    

    I searched my code and found a couple of places where it had been used in the past, but they were commented out, and I haven't used it in quite a while. I guess I'll try it out and see how it goes.

    Thanks,
    Elroy
    Any software I post in these forums written by me is provided “AS IS” without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. Please understand that I’ve been programming since the mid-1970s and still have some of that code. My contemporary VB6 project is approaching 1,000 modules. In addition, I have a “VB6 random code folder” that is overflowing. I’ve been at this long enough to truly not know with absolute certainty from whence every single line of my code has come, with much of it coming from programmers under my employ who signed intellectual property transfers. I have not deliberately attempted to remove any licenses and/or attributions from any software. If someone finds that I have inadvertently done so, I sincerely apologize, and, upon notice and reasonable proof, will re-attach those licenses and/or attributions. To all, peace and happiness.

  7. #7

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    5,446

    Re: Run CMD and wait

    Okey dokey ... my WaitForTermination seems to work perfectly. This one was resolved before it was started.
    Any software I post in these forums written by me is provided “AS IS” without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. Please understand that I’ve been programming since the mid-1970s and still have some of that code. My contemporary VB6 project is approaching 1,000 modules. In addition, I have a “VB6 random code folder” that is overflowing. I’ve been at this long enough to truly not know with absolute certainty from whence every single line of my code has come, with much of it coming from programmers under my employ who signed intellectual property transfers. I have not deliberately attempted to remove any licenses and/or attributions from any software. If someone finds that I have inadvertently done so, I sincerely apologize, and, upon notice and reasonable proof, will re-attach those licenses and/or attributions. To all, peace and happiness.

  8. #8
    Addicted Member
    Join Date
    Aug 2017
    Posts
    183

    Re: Run CMD and wait

    Quote Originally Posted by The trick View Post
    If you don't care about Unicode you can use Shell function with the subsequent OpenProcess/WaitForSingleObject.
    Apparently, the intrinsic Shell function became Unicode-aware starting with Vista:

    Quote Originally Posted by Bonnie West View Post
    BTW, as hinted above, the Shell function is actually Unicode-aware on Windows Vista & 7 (and possibly on newer OSs as well; haven't checked yet). Here's some test code for those who would like to verify it for themselves: Read more...
    Quote Originally Posted by Magic Ink View Post
    If you want to run a multi-line batch file command then something like ...
    The temporary batch file can be avoided by stringing multiple commands together using an &:

    Code:
    ShellAndWait Environ$("COMSPEC") & " /C Title Hi %UserName%! & Ping 127.0.0.1 & Echo. & Pause & Exit 1000", Show
    Quote Originally Posted by dilettante View Post
    ... you normally don't want to freeze the GUI thread. That can cause a lot of users to terminate your program because it appears hung.
    FWIW, here's yet another Shell & Wait implementation that doesn't make the shelling program unresponsive:

    Code:
    Public g_ExitDoLoops As Boolean  'Remember to set this to True just before program termination to ensure all
                                     'Do...Loops in this module exits normally in case they are still running
    
    Private m_Busy1      As Boolean  'Busy flag for Shell_n_Wait
    
    'Extends the native Shell function by waiting for the shelled program's termination without blocking other events.
    
    Public Function Shell_n_Wait(ByRef PathName As String, Optional ByVal WindowStyle As VbAppWinStyle = vbNormalFocus) As Long
        Const PROCESS_QUERY_INFORMATION = &H400&, QS_ALLINPUT = &H4FF&, SYNCHRONIZE = &H100000
        Dim hProcess As Long, sPath As String
    
        If Not m_Busy1 Then m_Busy1 = True Else Exit Function   'Only 1 instance of this function at a time is allowed
    
        If InStr(PathName, "%") = 0& Then   'Check if there are environment variables that needs to be expanded
            sPath = PathName
        Else
            SysReAllocStringLen VarPtr(sPath), , ExpandEnvironmentStringsW(StrPtr(PathName)) - 1&
            ExpandEnvironmentStringsW StrPtr(PathName), StrPtr(sPath), Len(sPath) + 1&
        End If
    
        On Error GoTo 1       'Shell the specified executable file and get a handle to its process
        hProcess = OpenProcess(PROCESS_QUERY_INFORMATION Or SYNCHRONIZE, FALSE_, Shell(sPath, WindowStyle))
        On Error GoTo 0
    
        If hProcess Then
            sPath = vbNullString
            g_ExitDoLoops = False
    
            Do While MsgWaitForMultipleObjects(1&, hProcess, FALSE_, INFINITE, QS_ALLINPUT)
                DoEvents                        'MWFMO returns when either the process ends or an input arrives.
                If g_ExitDoLoops Then Exit Do   'It returns 1& (WAIT_OBJECT_0 + nCount) for an input
            Loop                                'and 0& (WAIT_OBJECT_0 + nCount - 1&) for a process.
    
           'Return the exit code of the terminated program (usually 0; STILL_ACTIVE (259) if still running)
            WindowStyle = GetExitCodeProcess(hProcess, Shell_n_Wait):   Debug.Assert WindowStyle   'If code stops here, the
            hProcess = CloseHandle(hProcess):                           Debug.Assert hProcess      'handle(s) weren't closed
        End If
    
        m_Busy1 = False
        Exit Function
    
    1   m_Busy1 = False 'Always reset the busy flag
        Err.Raise Err   'If Shell failed, propagate the error to the caller to
    End Function        'help distinguish its failure from a return value of 0

  9. #9

  10. #10
    PowerPoster
    Join Date
    Feb 2006
    Posts
    20,002

    Re: [RESOLVED] Run CMD and wait

    DoEvents calls seem clever until they bring the house of cards down.

    They cause events to be handled, which means that your program can be pushed right off the rails. People often try to disable controls or Forms hoping to pave over that too, but if you have any external events (MSComm, Winsock, etc.) - kaboom!

    DoEvents calls should only be used in desperate situations and then only after some deep thought.

    Just use a Timer and be done with it. After all, this is exactly the sort of thing we have them for.
    Last edited by dilettante; Feb 12th, 2019 at 07:52 AM.

  11. #11
    Addicted Member
    Join Date
    Aug 2017
    Posts
    183

    Re: [RESOLVED] Run CMD and wait

    I'm well aware of the pitfalls of DoEvents, hence why the m_Busy1 and g_ExitDoLoops variables exists. m_Busy1 ensures only one instance of the function is running at any given time while g_ExitDoLoops guarantees the loop has been exited before the app ends (thus preventing the process from remaining in memory even when all Forms have been closed).

    Polling isn't really ideal either, especially when the polling interval is too small. If polling is too frequent, the CPU cannot go to sleep and a mobile device's battery will drain faster.

    Quote Originally Posted by MSDN
    Best Practices for Energy Efficiency

    • Avoid polling and spinning in tight loops

    Heavy processor usage reduces the effectiveness of processor power-management technologies such as processor idle states and processor performance states.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Featured


Click Here to Expand Forum to Full Width