Start a process and get its PID

Microsoft Windows
Post Reply
User avatar
MigrationUser
Posts: 336
Joined: 2021-Jul-12, 1:37 pm
Contact:

Start a process and get its PID

Post by MigrationUser »

05 Jul 2012 21:51
npocmaka

Which would be the easiest way within batch file?
At the moment I'm using PsList -t and parsing the result knowing that what I'm looking for will be under cmd.exe.
Any other ideas? External tools?

----------------------------

#2 05 Jul 2012 22:28
RG


See
START /?
and
TASKLIST /?
Just execute TASKLIST to see an example of its output.

Last edited by RG (05 Jul 2012 22:28)

Windows Shell Scripting and InstallShield

----------------------------

#3 05 Jul 2012 22:39
npocmaka


I'm familiar with START and TASKLIST.But they won't help me if I want the PID that I've just started.
But what if i want to start example.exe with already running instances of example.exe.And later I want to kill only mine instance.Or watch memory usage of my instance and so on.
Even powershell/vbscript does not help much here :-)

----------------------------

#4 05 Jul 2012 23:07

npocmaka

wmic process call create "notepad.exe" | find "ProcessId"
:D

Now If I find out how to use other properties of process query it will be great.

----------------------------

#5 06 Jul 2012 00:20
npocmaka


and a small script :


When I find more about wmic process will try to improve this.

Last edited by npocmaka (08 Jul 2012 19:52)

----------------------------

#6 09 Jul 2012 20:18
npocmaka


here's a little bit improved script (I've called mine startpid ,but the name does not really matter):

Code: Select all

@echo off
setlocal

set commandline=%1
set workdir=%~dpf2

call :DeQuote commandline
call :DeQuote workdir


if [%1] EQU [] (
	call  :EchoHelp
	goto :eof
)

if [%1] EQU [-help] (
	call  :EchoHelp
	goto :eof 
)

if [%2] NEQ [] (
	if not exist "%workdir%\" ( 
		echo "directory %workdir% does not exist"
		exit
	) 
) else (
	set workdir=%~dp0	
)

::this does not work good enough. If there are brackets in the parameters for loop has problems.
::for /f "usebackq tokens=2 delims=;= " %%G IN (`wmic process call create '"%commandline%","%workdir%",null'  ^|find "ProcessId"`)  do set /A PID=%%G


pushd %workdir%
	for /f "tokens=2 delims=;= " %%G IN ('wmic process call create "%commandline%"  ^|find "ProcessId"')  do set /A PID=%%G
popd


if [%PID%] EQU [] (
	echo process didn't start
	goto :eof
) else (
	echo %PID%
	exit /b %pid% 
)

 ::BEGIN FUNCTION::::::::::::::::::::::::::::::::::::::::
 @ECHO OFF
 
 :DeQuote
 SET DeQuote.Variable=%1
 CALL Set DeQuote.Contents=%%%DeQuote.Variable%%%
 Echo.%DeQuote.Contents%|FindStr/brv ""^">NUL:&&Goto :EOF
 Echo.%DeQuote.Contents%|FindStr/erv ""^">NUL:&&Goto :EOF
 
 Set DeQuote.Contents=####%DeQuote.Contents%####
 Set DeQuote.Contents=%DeQuote.Contents:####"=%
 Set DeQuote.Contents=%DeQuote.Contents:"####=%
 Set %DeQuote.Variable%=%DeQuote.Contents%
 
 Set DeQuote.Variable=
 Set DeQuote.Contents=
 Goto :EOF
 :: Written by Frank P. Westlake, 2001.09.22, 2001.09.24
 :: Modified by Simon Sheppard 2002.06.09
 :::::::::::::::::::::::::::::::::::::::::::::::::::::::
 
 :EchoHelp
 
 	echo starts a process and return PID
	echo     %~n0 command_line  [work_directory]
	echo     %~n0 -help        -will print this message
	echo  PID of the process will be echoed and set to the errorlevel
 
 goto :EOF
  
 endlocal
I've stole dequote function from here:
https://ss64.com/nt/syntax-dequote.html

The main difference is the added workdir where the process will be started (I'm not sure how big improvement is this :-) )
the PID will be returned as a errorlevel (is it possible to overflow errorlevel's max number allowed?(no - pid is int errorlevel is long))

As this calling of creating a process with wmic wmic process call create "calc.exe" is more popular , but
with this wmic process call create "notepad.exe","c:\",null or this wmic process call create "notepad.exe","c:\"
you can create a process in a certain directory.

The third parameter is a Win32_ProcessStartup -> https://docs.microsoft.com/en-gb/window ... 32-process
but I don't know if it can be created in a WMIC query and how can be used.

It can add more useful options -> https://docs.microsoft.com/en-gb/window ... essstartup
Any idea how this can be used (may be in powershell script it will be possible)?

Creating a process on a remote host is also possible but the processing the parameters will became a mess smile

Last edited by npocmaka (11 Jul 2012 20:59)

----------------------------

#7 12 Jul 2012 21:20
npocmaka


There are a lot of improvements now :D

Code: Select all

@echo off
setlocal

if [%1] EQU [] (
	call :echoHelp
	goto :eof
)

if [%1] EQU [-help] (
	call :echoHelp
	goto :eof
)

set /A shifter=1
set workdir=.


:argParser
	if [%1] EQU [-exec] (
		set exec=%2
	)
	if [%1] EQU [-commandline] (
		set commandline=%2
	) 
	if [%1] EQU [-workdir] (
		set workdir=%2
	)
	if [%1] EQU [-host] (
		set host=%2
	)
	if [%1] EQU [-user] (
		set user=%2
	)
	if [%1] EQU [-pass] (
		set pass=%2
	)
	if [%1] EQU [-record] (
		set record=%2
	)
	shift
	shift
	set /A shifter=%shifter% + 1
	
	if %shifter% EQU 7 (
		
		goto :endArgParser
	)

goto :argParser
:endArgParser

call  :DeQuote exec
call  :DeQuote commandline
call  :DeQuote workdir
call  :DeQuote host
call  :DeQuote user 
rem call  :DeQuote pass



rem echo exec - %exec%
rem echo commandline - %commandline%
rem echo workdir - %workdir%
rem echo host - %host%
rem echo user - %user%
rem echo pass - %pass%
rem echo record - %record%

if [%exec%] EQU [] (
	echo executable not defined
	goto :eof
)

if [%host%] NEQ [] (
	set host_param=/NODE:%host%
	if [%user%] NEQ [] (
		set user_param=/USER:%user%
		if [%pass%] NEQ [] (
			set pass_param=/PASSWORD:%pass%
		)
	)
)

if [%record%] NEQ [] (
	set record_param=/RECORD:%record%
)

set global_params=%record_param% %host_param% %user_param% %pass_param%


for /f "usebackq tokens=*" %%G IN (`wmic  %global_params%  process call create "%exec% %commandline%"^,"%workdir%"`)  do ( 
	 rem echo %%G
	echo %%G | find "ProcessId" >nul
	IF %ERRORLEVEL% EQU 0 (
		for /f  "tokens=2 delims=;= " %%H in ('echo %%G ^| find  "ProcessId"') do (
			call set /A PID=%%H
			rem echo %%H
			goto :endLoop1
		)
	)
	
 	echo %%G | find "ReturnValue" >nul
 	IF %ERRORLEVEL% EQU 0 (
 		for /f  "tokens=2 delims=;= " %%I in ('echo %%G ^| find  "ReturnValue"') do (
 			call set /A RETCOD=%%I
 			rem echo %%I
 			goto :endLoop2
 		)
 	)
	
	call :concat "%%G"	
)
goto :endloop3

::successful execution
:endLoop1
if %PID% NEQ 0 (
	echo %PID%
	exit /B %PID%
)

::unsuccessful with code
::check the return code and give hints
:endLoop2

echo return code : %RETCOD%

if %RETCOD% EQU 2 (
	echo -Access Denied
)

if %RETCOD% EQU 3 (
	echo -Insufficient Privilege
)

if %RETCOD% EQU 8 (
	echo -Unknown failure  
	echo Hint: Check if the executable and workdit exists or if command line parameters are correct. 
)

if %RETCOD% EQU 9 (
	echo -Path Not Found
	echo Hint: check if  the work exists on the remote machine.
)

if %RETCOD% EQU 21 (
	echo -Invalid Parameter
	echo Hint: Check executable path.Check if  host and user are correct.
)

exit /b 0
goto :eof

::unsuccessful with no code
:endloop3
echo %output%
echo HINT :brackets,quotes or commas in the password may could break the script
goto :eof

endlocal
goto :eof


:echoHelp
	echo %~n0 -exec executubale [-commandline command_line] [ -workdir working_directory] [-host  remote_host [-user user [-pass password]]] [-record path_to_xml_output]
	echo\
	echo localhost cant' be used as in -host variable
	echo Examples:
	echo %~n0  -exec "notepad" -workdir "c:/"  -record "test.xml" -commandline "/A startpid.txt"
	echo %~n0  -exec "cmd" -workdir "c:/"  -record "test.xml" -host remoteHost -user User

goto :eof

:concat
	call set output=%output% %1

goto :eof

 :DeQuote
 SET DeQuote.Variable=%1
 CALL Set DeQuote.Contents=%%%DeQuote.Variable%%%
 Echo.%DeQuote.Contents%|FindStr/brv ""^">NUL:&&Goto :EOF
 Echo.%DeQuote.Contents%|FindStr/erv ""^">NUL:&&Goto :EOF
 
 Set DeQuote.Contents=####%DeQuote.Contents%####
 Set DeQuote.Contents=%DeQuote.Contents:####"=%
 Set DeQuote.Contents=%DeQuote.Contents:"####=%
 Set %DeQuote.Variable%=%DeQuote.Contents%
 
 Set DeQuote.Variable=
 Set DeQuote.Contents=
 Goto :EOF

 ::DeQuote ->
 :: Written by Frank P. Westlake, 2001.09.22, 2001.09.24
  :: and modified by Simon Sheppard 2002.06.09

I've added an options for execution on remote machine.Now I'm parsing also error codes and trying to give some hints.In some cases there's no error code - just a error message and it's also printed. The target executable and its parameters are now separated in two arguments.
I've added an option for recording the wmic command to xml file using wmic /RECORD swith (this was useful durring the testing).

here are the params:
-exec executable : path to executable or the executable if it's in the %PATH%
[-commandline command_line] : command line parameters
[ -workdir working_directory] : working directory.Now I'm not checking the existence of the directory because of the remote host execution.

[-host remote_host [-user user [-pass password]]] : not hard to guess
wmi has a limitation and localost can't be used with user and password.May be I should add check if the host is localhost.
[-record path_to_xml_output] : path for xml used in /RECORD switch - there you'll see the wmi command and full output

In the previous version working directory didn't worked in the wmic query because the FOR /F had eating the "," - now works fine with "^,"
(why the "," is problem for the for /f ? )
Still have a problems with brackets and double quotes in password parameter - it uses wmic /PASSWORD switch and is not parsed well.
Return codes are taken form here : https://docs.microsoft.com/en-gb/window ... 32-process
I still hadn't produced 2 and 3 . 9 is thrown only where the path does not exist on remote machine.

the PID is still stored in errorlevel

I'm still not sure if other global switches could be useful.

examples :
startpid -exec "notepad" -workdir "c:/" -record "test.xml" -commandline "/A startpid.txt"
startpid -exec "cmd" -workdir "c:/" -record "test.xml" -host remoteHost -user User (<-- without pass it will be prompted)

Last edited by npocmaka (12 Jul 2012 21:30)

----------------------------

#8 16 Jul 2012 15:29
smerch


Slightly beautified version (for procedural approach, more like programming style)

Code: Select all

@echo off
setlocal enabledelayedexpansion

if {%~1} NEQ {} (
	if {%~1} NEQ {-help} (
		call :proc %*
		exit /b %ERRORLEVEL%
	)
)

call :echoHelp
exit /b

:proc
	call :argParser %*

	if {%exec%} EQU {} (
		call :err 1000
		exit /b %ERRORLEVEL%
	)

	if {%host%} NEQ {} (
		set host=/NODE:%host%
		if {%user%} NEQ {} (
			set user=/USER:%user%
			if {%pass%} NEQ {} (
				set pass=/PASSWORD:%pass%
			)
		)
	)

	if {%record%} NEQ {} (
		set record=/RECORD:%record%
	)

	set global_params=%record% %host% %user% %pass%

	for /f "usebackq tokens=*" %%G IN (`wmic  %global_params%  process call create "%exec% %commandline%"^,"%workdir%"`) do ( 
		rem echo %%G
		echo %%G | find "ProcessId" > nul && (
			for /f  "tokens=2 delims=;= " %%H in ('echo %%G') do (
				call set /A PID=%%H
			)
		)
		echo %%G | find "ReturnValue" > nul && (
			for /f  "tokens=2 delims=;= " %%I in ('echo %%G') do (
				call set /A RETCOD=%%I
			)
		)
		call :concat "%%G"	
	)

	rem successful execution
	if %PID% NEQ 0 (
		echo %PID%
		exit /b
		rem exit /B %PID%
	) else (
		call :err %RETCOD%
	)
exit /b %ERRORLEVEL%


:argParser
	set comstr=-exec-commandline-workdir-host-user-pass-record
	for /L %%i in (1,1,7) do (
		echo %comstr% | find "%~1" > nul && (
			set _tmp=%~1
			set !_tmp:-=!=%~2
		)
		shift
		shift
	)
	set _tmp=
exit /b

:echoHelp

	echo %~n0 -exec executubale {-commandline command_line} { -workdir working_directory} 
	echo  {-host  remote_host {-user user {-pass password}}} {-record path_to_xml_output}
	echo\
	echo localhost cant' be used as in -host variable
	echo Examples:
	echo %~n0  -exec "notepad" -workdir "c:/"  -record "test.xml" -commandline "/A startpid.txt"
	echo %~n0  -exec "cmd" -workdir "c:/"  -record "test.xml" -host remoteHost -user User
exit /b

:concat

	call set output=%output% ^& echo %~1
exit /b

:err
	if %1 EQU 2          (set errmsg=Access Denied)
	if %1 EQU 3          (set errmsg=Insufficient Privilege)
	if %1 EQU 8          (set errmsg=Unknown failure ^& echo Hint: Check if the executable and workdit exists or if command line parameters are correct.)
	if %1 EQU 9          (set errmsg=Path Not Found ^& echo Hint: check if  the work exists on the remote machine.)
	if %1 EQU 21         (set errmsg=Invalid Parameter ^& echo Hint: Check executable path.Check if  host and user are corect.)
	if %1 EQU 1000       (set errmsg=Executable not defined.)
	if {%errmsg%} EQU {} (set errmsg=%output% ^& echo Hint: brackets,quotes or commas in the password may could breack the script.)

	echo %errmsg%
exit /b %1
----------------------------

#9 16 Jul 2012 17:12
npocmaka


Yeaaaah. :-)
I like the "&&" in the loop and argparser enhancements.
I've tested this but strangely the workdir parameter is never set (it's seems it should work).
And when script fails to create process it prints : "0 was unexpected at this time."
Also a mysterious empty Create() file is created.
I need some testing to see what's going on (for test reasons wmic /record:c:\test.xm %global_params% process call create "%exec% %commandline%"^,"%workdir% can be used")

Last edited by npocmaka (16 Jul 2012 17:12)

----------------------------

#10 17 Jul 2012 08:54
smerch


Ok. Here more working version:

Code: Select all

@echo off
setlocal enabledelayedexpansion

if {%~1} NEQ {} (
	if {%~1} NEQ {-help} (
		call :proc %*
		exit /b %ERRORLEVEL%
	)
)

call :echoHelp
exit /b

:proc
	call :argParser %*

	if {%exec%} EQU {} (
		call :err 1000
		exit /b %ERRORLEVEL%
	)

	if {%host%} NEQ {} (
		set host=/NODE:%host%
		if {%user%} NEQ {} (
			set user=/USER:%user%
			if {%pass%} NEQ {} (
				set pass=/PASSWORD:%pass%
			)
		)
	)

	if {%record%} NEQ {} (
		set record=/RECORD:%record%
	)

	set global_params=%record% %host% %user% %pass%

	for /f "usebackq tokens=*" %%G IN (`wmic  %global_params%  process call create "%exec% %commandline%"^,"%workdir%"`) do ( 
		rem echo %%G
		set _tmp=%%G
		set _tmp=!_tmp:^>=^^^>!
		echo !_tmp! | find "ProcessId" > nul && (
			for /f  "tokens=2 delims=;= " %%H in ('echo !_tmp!') do (
				call set /A PID=%%H
			)
		)
		echo !_tmp! | find "ReturnValue" > nul && (
			for /f  "tokens=2 delims=;= " %%I in ('echo !_tmp!') do (
				call set /A RETCOD=%%I
			)
		)
		call :concat
	)
	set _tmp=
	
	rem successful execution
	if {%PID%} NEQ {} (
		echo %PID%
		exit /b
		rem exit /B %PID%
	) else (
		call :err %RETCOD%
	)
exit /b %ERRORLEVEL%


:concat

	call set output=%output% ^& echo !_tmp:^>=^^^>!
exit /b

:argParser

	set comstr=-exec-commandline-workdir-host-user-pass-record
	:nextShift
	set /A shifter=shifter+1
	echo %comstr% | find "%~1" > nul && (
		set _tmp=%~1
		set !_tmp:-=!=%~2
	)
	shift &	shift
	if %shifter% LSS 7 goto :nextShift
	set _tmp=
	set shifter=
exit /b

:echoHelp

	echo %~n0 -exec executubale {-commandline command_line} { -workdir working_directory} 
	echo  {-host  remote_host {-user user {-pass password}}} {-record path_to_xml_output}
	echo\
	echo localhost cant' be used as in -host variable
	echo Examples:
	echo %~n0  -exec "notepad" -workdir "c:/"  -record "test.xml" -commandline "/A startpid.txt"
	echo %~n0  -exec "cmd" -workdir "c:/"  -record "test.xml" -host remoteHost -user User
exit /b

:err
	if %1 EQU 2          (set errmsg=Access Denied)
	if %1 EQU 3          (set errmsg=Insufficient Privilege)
	if %1 EQU 8          (set errmsg=Unknown failure ^& echo Hint: Check if the executable and workdir exists or if command line parameters are correct.)
	if %1 EQU 9          (set errmsg=Path Not Found ^& echo Hint: check if the workdir exists on the remote machine.)
	if %1 EQU 21         (set errmsg=Invalid Parameter ^& echo Hint: Check executable path.Check if  host and user are correct.)
	if %1 EQU 1000       (set errmsg=Executable not defined.)
	if {%errmsg:~0,1%} EQU {} (set errmsg=%output% ^& echo Hint: brackets,quotes or commas in the password may could break the script.)

	echo %errmsg%
exit /b %1
First. It seems that putting some command in for loop or in "()" block enables immediate variables mode (i.e. using !variable! syntax) which is not implemented for parameters like %1 — so I made a workaround though goto (which I personally don't like to use) to make shift to work. Now argParser works.

Second. Create() file was created due to appearance of "Blah blah -> Create()" to echo command which was interpreted as attempt to write to Create() file — so I made some "circum masking" to prevent that.

Third. "0 was unexpected at this time." was caused by syntax error in if operator due to empty %PID% — curly braces solve this issue.

P.S. Have to use %errmsg:~0,1% construction in :err to prevent appearance of interpreted commands after if operator and syntax errors.

Last edited by smerch (17 Jul 2012 10:13)

----------------------------

#11 17 Jul 2012 13:25
npocmaka


:)
Fast initial testing:

1.When I try to set an executable with spaces in it's path it can't be found - still need dequote.At least it's easy for fix
2.Setting arguments is outside of setlocal enabledelayedexpansion scope and wmic global parameters are not cleaned ( EDIT: Aaahh with your last edit this is fixed)

Last edited by npocmaka (17 Jul 2012 13:25)

----------------------------

#12 17 Jul 2012 20:50
smerch


New even more working version.

Code: Select all

@echo off
setlocal enabledelayedexpansion

if "%~1" NEQ "" (
	if "%~1" NEQ "-help" (
		call :proc %*
		exit /b %ERRORLEVEL%
	)
)

call :echoHelp
exit /b

:proc
	call :argParser %*

	if "%exec%" EQU "" (
		call :err 1000
		exit /b %ERRORLEVEL%
	)

	if "%host%" NEQ "" (
		set host=/NODE:%host%
		if "%user%" NEQ "" (
			set user=/USER:%user%
			if "%pass%" NEQ "" (
				set pass=/PASSWORD:%pass%
			)
		)
	)

	if "%record%" NEQ "" (
		set record=/RECORD:%record%
	)

	set global_params=%record% %host% %user% %pass%

	for /f "usebackq tokens=*" %%G IN (`wmic  %global_params%  process call create "%exec% %commandline%"^,"%workdir%"`) do ( 
		rem echo %%G
		set _tmp=%%G
		set _tmp=!_tmp:^>=^^^>!
		echo !_tmp! | find "ProcessId" > nul && (
			for /f  "tokens=2 delims=;= " %%H in ('echo !_tmp!') do (
				call set /A PID=%%H
			)
		)
		echo !_tmp! | find "ReturnValue" > nul && (
			for /f  "tokens=2 delims=;= " %%I in ('echo !_tmp!') do (
				call set /A RETCOD=%%I
			)
		)
		call :concat
	)
	set _tmp=
	
	rem successful execution
	if "%PID%" NEQ "" (
		echo %PID%
		exit /b
		rem exit /B %PID%
	) else (
		call :err %RETCOD%
	)
exit /b %ERRORLEVEL%


:concat

	call set output=%output% ^& echo !_tmp:^>=^^^>!
exit /b

:argParser

	set comstr=-exec-commandline-workdir-host-user-pass-record
	:nextShift
	set /A shifter=shifter+1
	echo %comstr% | find "%~1" > nul && (
		set _tmp=%~1
		set !_tmp:-=!=%~2
	)
	shift &	shift
	if %shifter% LSS 7 goto :nextShift
	set _tmp=
	set shifter=
exit /b

:echoHelp

	echo %~n0 -exec executubale {-commandline command_line} { -workdir working_directory} 
	echo  {-host  remote_host {-user user {-pass password}}} {-record path_to_xml_output}
	echo\
	echo localhost cant' be used as in -host variable
	echo Examples:
	echo %~n0  -exec "notepad" -workdir "c:/"  -record "test.xml" -commandline "/A startpid.txt"
	echo %~n0  -exec "cmd" -workdir "c:/"  -record "test.xml" -host remoteHost -user User
exit /b

:err
	if %1 EQU 2          (set errmsg=Access Denied)
	if %1 EQU 3          (set errmsg=Insufficient Privilege)
	if %1 EQU 8          (set errmsg=Unknown failure ^& echo Hint: Check if the executable and workdit exists or if command line parameters are correct.)
	if %1 EQU 9          (set errmsg=Path Not Found ^& echo Hint: check if the workdir exists on the remote machine.)
	if %1 EQU 21         (set errmsg=Invalid Parameter ^& echo Hint: Check executable path. Check if host and user are corect.)
	if %1 EQU 1000       (set errmsg=Executable not defined.)
	if "%errmsg:~0,1%" EQU "" (set errmsg=%output% ^& echo Hint: brackets, quotes or commas in the password may could breack the script.)

	echo %errmsg%
exit /b %1
First. "Dequote" is not required since enclosing quotes are stripped off by %~1. However for if operator we (still) have to use quotes so not mess with syntax errors (since quoted text not interpreted). But a special treatment is required in case of quotes inside parameters (not implemented in current version).
Second. setlocal statement defines a local "scope of visibility" for variables, bounded by endlocal, exit or end of file; so any called subroutine will share same variables within same scope. Scopes can be nested, so new scope in subroutine (declared by setlocal) will have all variables of parent scope but any changes to variables will not be visible to parent scope. Conclusion: all shared variables must be defined within same scope.

Last edited by smerch (18 Jul 2012 09:36)

----------------------------

#13 18 Jul 2012 11:43
smerch


Btw, npocmaka, if you can give me a list of not permitted characters for password I can make changes to :argParser so it can deal with them wink

Last edited by smerch (18 Jul 2012 11:44)
Post Reply