-
Feb 11th, 2019, 01:44 PM
#1
[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. To all, peace and happiness.
-
Feb 11th, 2019, 02:08 PM
#2
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.
-
Feb 11th, 2019, 02:22 PM
#3
Re: Run CMD and wait
If you don't care about Unicode you can use Shell function with the subsequent OpenProcess/WaitForSingleObject.
The another option is WSHShell.Run with the WaitOnReturn flag.
You can use also CreateProcess/ShellExecute(Ex).
-
Feb 11th, 2019, 02:45 PM
#4
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.
-
Feb 11th, 2019, 02:53 PM
#5
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.
-
Feb 11th, 2019, 03:01 PM
#6
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. To all, peace and happiness.
-
Feb 11th, 2019, 04:13 PM
#7
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. To all, peace and happiness.
-
Feb 12th, 2019, 12:09 AM
#8
Re: Run CMD and wait
Originally Posted by The trick
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:
Originally Posted by Bonnie West
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...
Originally Posted by Magic Ink
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
Originally Posted by dilettante
... 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
-
Feb 12th, 2019, 02:10 AM
#9
Re: [RESOLVED] Run CMD and wait
Alternatively you can use that class which generates the event when the a process has terminated.
-
Feb 12th, 2019, 07:36 AM
#10
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.
-
Feb 12th, 2019, 11:25 AM
#11
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.
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
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|