Batch file wait for another processes to finish before continuing - windows

I have a batch file that I use every time I start my work PC. Basically, it kills all the bloatware that the IT department puts on the PC that run as my user account that I never even use using taskkill, installs a small program, then loads all the programs I actually need at once. Problem is, one of the programs that I run, the IT department recently thought it would be helpful to install a "helper" program to automate some things within the program that I already know how to do and can do it fairly quickly manually. Problem is, the helper program actually has the reverse effect and slows the actual program down. We are of course complaining to IT about it but it'll probably take them months to fix it, if ever. In the meantime, I found out that the helper program runs as it's own process under my user account, which means I can kill it, and everything runs smoothly again. Problem is, how the program runs. I launch the program normally and this happens:
Process A loads. Process A loads process B. Process A kills itself. Process B loads process C. Process C loads process D, E, and F (the helper programs). Process B kills itself, while leaving C, D, E, and F in memory (program is fully loaded at this point)
How can I have the batch file just wait for process B to kill itself, before proceeding with the taskkill commands to kill processes D, E, and F? Since the command line only sees process A when directly calling the EXE, it resumes the batch file in under a second since A kills itself that quickly. I can't just use timeout or some other generic time waster because the load time of the program is too volatile, what with network issues, program updates, etc.

I built something exactly like this a few years ago, but unfortunately don't have the code with me and don't have access to a Windows machine at the moment.
However, it should be fairly simple, and will be along these lines (just tweak the code to get it work or let me know if there are any issues; I can't test it but should be able to fix them if I know what they are):
rem Usage: :killprocesswithwait [pid]
call :killprocesswithwait 1234
:killprocesswithwait
set pid=%1
taskkill /pid %pid%
:iloop
ping 127.0.0.1 -n 2 > nul
for /f "tokens=*" %%a in ('tasklist /fi "PID eq %pid%" ^| find /i %pid%') do (
if /i "%%a"=="" (
goto :eof
) else (
goto :iloop
)
)

This waits for a any and all programs to exit and checks if Notepad and restarts it if it is.
It surely would be better to wait for the three programs to start. Change Win32_ProcessStopTrace to Win32_ProcessStartTrace.
Set WshShell = WScript.CreateObject("WScript.Shell")
Set objWMIService = GetObject("winmgmts:\\.\root\CIMV2")
Set objEvents = objWMIService.ExecNotificationQuery _
("SELECT * FROM Win32_ProcessStopTrace")
Do
Set objReceivedEvent = objEvents.NextEvent
msgbox objReceivedEvent.ProcessName
If lcase(objReceivedEvent.ProcessName) = lcase("Notepad.exe") then
Msgbox "Process exited with exit code " & objReceivedEvent.ExitStatus
WshShell.Run "c:\Windows\notepad.exe", 1, false
End If
Loop
And this lists processes and terminate any called calculator.
Set objWMIService = GetObject("winmgmts:\\.\root\cimv2")
Set colItems = objWMIService.ExecQuery("Select * From Win32_Process")
For Each objItem in colItems
'msgbox objItem.ProcessID & " " & objItem.CommandLine
If objItem.name = "Calculator.exe" then objItem.terminate
Next

#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
SET "filename1=%sourcedir%\q36241667.txt"
SET "filename2=%sourcedir%\q36241667_2.txt"
:dloop
FOR /f "usebackqdelims=" %%a IN ("%filename1%") DO (
tasklist|FIND "%%a" >NUL
IF ERRORLEVEL 1 timeout /t 1 >nul&GOTO dloop
)
FOR /f "usebackqdelims=" %%a IN ("%filename2%") DO ECHO(%%a
GOTO :EOF
You would need to change the setting of sourcedir to suit your circumstances.
I used files named q36241667.txt & q36241667_2.txt containing some dummy data for my testing.
Suppose filename1 contains
processa.exe
processb.exe
processc.exe
processd.exe
processe.exe
then the for loop will wait until all of the processes are running before proceeding.
filename2 contains the kill commands required (echoed for demo purposes)
If this routine is simply started in your startup routine, then it could itself trigger start commands to kill any unwanted processes.
I use a similar routine to back up critical directories to a ramstick on start-up.

Thanks guys, I actually took Ruslan's answer and simplified and changed it a bit. This works great. Waits for exe3 to load since it's the last one to load, then kills all 3. Didn't need to set the variables so removed those and completely forgot about putting it in a loop.
:start
FOR /F %%x IN ('tasklist /NH /FI "IMAGENAME eq exe3.exe"') DO IF %%x == exe3.exe goto found
goto start
:found
taskkill /f /fi "blah1.exe"
taskkill /f /fi "blah2.exe"
taskkill /f /fi "blah3.exe"

Related

Is there a way to run a batch of asynchronous commands a few at a time? [duplicate]

I have a main batch file than calls 4 other batch files so we can run in parallel.
Example:
Main.bat
start call batch1.bat
start call batch2.bat
start call batch3.bat
start call batch4.bat
exit
I want the Main.bat to exit after all the batch1 to batch 4 has stopped executing. In this way, I can get the total run time of the batch file. Problem is Main.bat exits even before batch1 to batch4 finishes executing.
I tried to compute for %errorlevel% for each batch file, but it always return 0 even though the 4 .bat files are still running.
Hoping someone could help me!
Thank you! :)
I think this is the simplest and most efficient way:
#echo off
echo %time%
(
start call batch1.bat
start call batch2.bat
start call batch3.bat
start call batch4.bat
) | set /P "="
echo %time%
In this method the waiting state in the main file is event driven, so it does not consume any CPU time!
EDIT: Some explanations added
The set /P command would terminate when anyone of the commands in the ( block ) outputs a line, but start commands don't show any line in this cmd.exe. This way, set /P keeps waiting for input until all processes started by start commands ends. At that point the pipe line associated to the ( block ) is closed, so the set /P Stdin is closed and set /P command is terminated by the OS.
give a unique title string to the new processes, then check if any processes with this string in the title are running:
start "+++batch+++" batch1.bat
start "+++batch+++" batch2.bat
start "+++batch+++" batch3.bat
start "+++batch+++" batch4.bat
:loop
timeout /t 1 >nul
tasklist /fi "windowtitle eq +++batch+++*" |find "cmd.exe" >nul && goto :loop
echo all tasks finished
(find is used, because tasklist does not return a helpful errorlevel)
Give this a try.
#echo off
echo %time%
start "" /wait cmd /c bat1.bat |start "" /wait cmd /c bat2.bat |start "" /wait cmd /c bat3.bat
echo %time%
pause
You could have batch1..batchn4 create flag files when they finish running.
e.g echo y > flag1 in batch1.bat
Then in the main batch file check for the existence of the flag files before exiting. You would need some sort of sleep utility to do something like this at the end of the main batch file:
IF EXIST flag1 GOTO check2
sleep <for a short amount of time>
goto check1
:check2
IF EXIST flag2 GOTO check3
sleep <for a short amount of time>
goto check2
:check3
IF EXIST flag3 GOTO check4
sleep <for a short amount of time>
goto check3
:check4
IF EXIST flag4 GOTO xit
sleep <for a short amount of time>
goto check4
:xit
The downside of this technique is that your timing is going to be off a little because you're polling for the flag files instead of being event driven. This may or may not be a problem in your situation. Batch files are pretty limited in this way. You might be better off trying to do it in PowerShell or python or some other more capable scripting language.
No one mentioned this solution, which I find to be the best. Put this at the end of your script, modify your process name if needed and the search strings.
This includes modifications necessary to have this work remotely too which was a headache. Originally I used tasklist instead of WMIC and checked named window titles defined when START is called, but windowName is N/A in tasklist when using it remotely. The powershell command worked better than timeout in our circumstances, which required us to run from this from a Jenkins agent.
:WAIT_FOR_FINISH
ECHO Waiting...
powershell -command "Start-Sleep -Milliseconds 5000" > nul
WMIC PROCESS WHERE Name="cmd.exe" GET commandline | findstr "searchString1 searchString 2" > nul && goto :WAIT_FOR_FINISH
ECHO.
ECHO Processes are Complete!
ECHO.

Windows batch file - how to execute command after program close that's not started in the batch file

I'm having a hard time finding an answer to this question. I'm looking specifically to execute a command in a batch file only after a program is terminated - however, a program that wasn't launched by the batch file.
My problem is this - the program I am actually launching in the batch file I want to wait on is in turn launching another program, which in turn launches another. This probably doesn't make any sense - but it's because it's a game launcher. It is for Final Fantasy XIV. The normal program that is launched to start it is ffxivboot.exe, which in turn launches ffxivlauncher.exe. That is a login window, and once you login, it in turn launches ffxiv_dx11.exe. So while I originally wrote it to wait on ffxivboot.exe, that process doesn't stay running so I am unable to wait on it.
Here's my file (excluded paths for simplicity):
taskkill /im someprogram.exe
ffxivboot.exe
timeout /t 60 /nobreak
### ??? need to wait on ffxiv_dx11.exe to close before executing next command
someprogram.exe
I added a timer to wait so that it gives me plenty of time to login - because the ffxiv_dx11.exe process doesn't start until after logging in.
Is what I'm trying to do possible? It's hard to search for answers to this because I only get results regarding when you're waiting on a task to end that was started from the batch file. But like I said, that one launches another which in turn launches another - so the original process is no longer running.
Thanks for any help!
taskkill /im someprogram.exe
ffxivboot.exe
timeout /t 60 /nobreak
:repeat
::### ??? need to wait on ffxiv_dx11.exe to close before executing next command
tasklist /fi "imagename eq ffxiv_dx11.exe"|find /i "=========================" >nul 2>nul &&(
w32tm /stripchart /computer:localhost /period:10 /dataonly /samples:2 1>nul
goto :repeat
)
someprogram.exe
try this
Set WshShell = WScript.CreateObject("WScript.Shell")
Set objWMIService = GetObject("winmgmts:\\.\root\CIMV2")
Set objEvents = objWMIService.ExecNotificationQuery _
("SELECT * FROM Win32_ProcessStopTrace")
Do
Set objReceivedEvent = objEvents.NextEvent
msgbox objReceivedEvent.ProcessName
If lcase(objReceivedEvent.ProcessName) = lcase("Notepad.exe") then
Msgbox "Process exited with exit code " & objReceivedEvent.ExitStatus
WshShell.Run "c:\Windows\notepad.exe", 1, false
End If
Loop
This is a vbs file that needs to run elevated. It waits for any program to exit and checks if it's notepad and springs to life restarting it.
Type in a elevated command prompt taskkill /f /I'm wscript.exe to stop it.

i want to run a batch script into my pc when it is locked

i want to run a batch file on my pc for an overnight activity. The problem is that i can't do that because the pc is locked.
I am using this code:
#echo off
tasklist /FI "IMAGENAME eq \\Desktop\notepad.exe" | find /i "\\Desktop\notepad.exe"
IF ERRORLEVEL 1 GOTO LOOP1
IF ERRORLEVEL 0 GOTO EXIT
:LOOP1
start notepad.exe
goto EXIT
:EXIT
and it works only the pc is unlocked.
Any help will matters.
Create a new Scheduled Task. Set the task to run when user is logged in or not. Then set the interval of your task time.
On Windows 8 etc you are able to set triggers on when the task should be kicked of by using either a set time or when PC goes on idle, when an event occurs etc.
There is also an option for On Workstation Lock
If this is not your intention to use scheduler. Then right a script that runs in a permanent loop by adding some sleep time and only rerun the taks every now and again, something like this (untested, just used yours as example)
:START
#echo off
tasklist /FI "IMAGENAME eq \\Desktop\notepad.exe" |find /i "\\Desktop\notepad.exe"
IF ERRORLEVEL 1 GOTO LOOP1
IF ERRORLEVEL 0 GOTO EXIT
:LOOP1
start notepad.exe
timeout 300
goto START
timeout 300 is basically sleeping the script for 300 seconds and will start from START again. We can then run the batch file before locking the PC and it wil lrun in a continuous loop. Even though it might not be the right way, it is an option. Perhaps some more detail around how often the batch file should run?

Monitor a start of process in windows, then execute something (stop another process/service)

can someone think of a solution for something like this? :
Program/script logic: It would constantly monitor the windows OS for a process starting within it (***1.exe) (I guess it could constantly run via task scheduler to do the constant monitoring?) , while it sees that ***1.exe is running, it would kill/end another process ***2.exe, and once ***1.exe would go away, it would no longer be stopping the ***2.exe process.
I think it could be either a bash script, powershell script, or a windows service?
Thanks!!!
You can use the Register-CimIndicationEvent cmdlet to register for events raised by Win32_ProcessStartTrace WMI class:
# Define which events to listen for
$NewProcessQuery = "SELECT ProcessId,ProcessName FROM Win32_ProcessStartTrace WHERE ProcessName LIKE '%1.exe'"
# Define the code to run every time a new process is created
$ProcessAction = {
# See if any instances of *2.exe processes are running
if(($TargetProcess = Get-CimInstance -ClassName Win32_Process -Filter "Name LIKE '%2.exe'"))
{
# Terminate them
$TargetProcess |Invoke-CimMethod -MethodName Terminate
}
}
# Register for the event
Register-CimIndicationEvent -Query $NewProcessQuery -SourceIdentifier ProcessCreated
So since the solution above was for only windows 2012 and up, I decided to try another solution. This should work for regular processes, but I'll have to try something else rather than %ERRORLEVEL% because the process I'm monitoring is originally an msi installer and seems like it returns and errorlevel of 1 all the time (running or not) while regular processes return 0 or 1 depending on the status. The process I'm ending starts back up automatically, that's the reason there's no start service command included in here, timeout was set to 62 seconds because the service starts back up automatically every 60 seconds, a /NOBREAK can be added if wanted to eliminate the possibility of user input starting it (if this would be ran without a task scheduler,etc.)
:loop_check
TIMEOUT /T 62
TASKLIST /FI "IMAGENAME eq process.exe" 2>NUL | find /I /N "process.exe">NUL
IF "%ERRORLEVEL%"=="0" (
GOTO stop_process2
) ELSE (
GOTO loop_check
)
:stop_process2
ECHO killing task
TASKKILL /F /IM process2.exe
GOTO loop_check
Read my previous reply/comment before this one for more clarity. This is the final solution that worked for me. A star(*) is included at the end of the 'BeginningOfApplicationName' because the installer/msi I'm detecting has sometimes different names based on it's version, so it finds/finishes the ending (wildcard). Since the name of the process I'm monitoring can have different names, I couldn't compare it to a static string, so I'm comparing it to INFO: , seems thats what windows (2008 and 2012!) both print out when a process is not found.
#ECHO OFF
SETLOCAL EnableExtensions
:loop_check
TIMEOUT /T 62
FOR /F %%x IN ('tasklist /NH /FI "IMAGENAME eq BeginningOfApplicationName*"') DO IF %%x == INFO: (
GOTO loop_check
) ELSE (
GOTO stop_process
)
:stop_process
TASKKILL /F /IM process.exe
GOTO loop_check

Windows batch: analogue for `timeout` command

I'm trying to find out how to limit a program execution time within a Windows batch file. Is there something like Unix timeout command?
Please advise.
To limit the time a program has to run you could do something like this
start yourprogram.exe
timeout /t 10
taskkill /im yourprogram.exe /f
That starts yourprogram.exe, waits 10 seconds, then kills the program.
I just installed Cygwin and use unix-style timeout command from the distribution.
I don't think there is a timeout command. However, you can start executing a task in the background and sleep (using ping) for the timeout duration then kill the task.
This code waits 60 seconds, then checks to see if %ProgramName% is running.
To increase this time, change the value of WaitForMinutes.
To decrease the interval between checks, set WaitForSeconds for the number of seconds you want it to wait.
#echo off
set ProgramName=calc.exe
set EndInHours=2
:: How Many Minutes in between each check to see if %ProgramName% is Running
:: To change it to seconds, just set %WaitForSeconds% Manually
set WaitForMinutes=1
set /a WaitForSeconds=%WaitForMinutes%*60
:: How many times to loop
set /a MaxLoop=(%EndInHours%*60*60) / (%WaitForMinutes%*60)
REM Use a VBScript popup window asking to terminate %ProgramName%
echo set WshShell = WScript.CreateObject("WScript.Shell") > %tmp%\tmp.vbs
echo Wscript.Quit (WshShell.Popup( "Click 'OK' to terminate %ProgramName%." ,10 ,"Terminate %ProgramName%", 0)) >> %tmp%\tmp.vbs
start %ProgramName%
set running=True
:: Give time for %ProgramName% to launch.
timeout /t 5 /nobreak > nul
setlocal enabledelayedexpansion
for /l %%x in (1,1,%MaxLoop%) do (
if "!running!"=="True" for /l %%y in (1,1,%WaitForMinutes%) do (
if "!running!"=="True" (
set running=False
REM call Pop-Up
cscript /nologo %tmp%\tmp.vbs
if !errorlevel!==-1 (
for /f "skip=3" %%x in ('tasklist /fi "IMAGENAME EQ %ProgramName%"') do set running=True
) else (
taskkill /im %ProgramName%
)
)
)
)
if exist %tmp%\tmp.vbs del %tmp%\tmp.vbs
This code uses VBScript to make a pop-up box. Clicking OK will cause %ProgramName% to be killed via taskkill.
If you do not want to use a pop-up window, you can use timeout by removing...
REM Use a VBScript popup window asking to terminate %ProgramName%
echo set WshShell = WScript.CreateObject("WScript.Shell") > %tmp%\tmp.vbs
echo Wscript.Quit (WshShell.Popup( "Click 'OK' to terminate %ProgramName%." ,10 ,"Terminate %ProgramName%", 0)) >> %tmp%\tmp.vbs
...and replacing this...
REM call Pop-Up
cscript /nologo %tmp%\tmp.vbs
if !errorlevel!==-1 (
...with this:
REM Use CTRL+C to kill %ProgramName%
timeout /t %WaitForSeconds% /nobreak
if !errorlevel!==0 (
Using /nobreak is necessary because timeout does not distinguish between pressing a key or timing out. This will allow you to terminate %ProgramName% by pressing CTRL+C , but that causes your batch file to ask Terminate batch job (Y/N)? when you do. Sloppy/Messy/Nasty IMHO.
You could instead use CHOICE by replacing the above mentioned code with this:
REM Using choice, but choice can get stuck with a wrong keystroke
Echo [K]ill %ProgramName% or [S]imulate %WaitForSeconds% Seconds
Choice /n /c sk /t %WaitForSeconds% /d s
if !errorlevel!==1 (
But choice brings it's own set of limitations to the table. For one thing, it will stop it's countdown if a key that is not among it's choices (in this case s and k) has been pressed, essentially locking up until a correct choice is made. Second, the SPACEBAR cannot be a choice.

Resources