Friday, August 2, 2013

Launch and monitor PID in DOS

After all of the below, you still have the problem of the launched werfault.exe window asking the user if they want to kill pid. Even after your script has cleaned it up the werfault window remains. To prevent the accumulation of werfault pids you have to disable problem reporting in windows. Changing the control panel settings and disabling the service did not prevent werfault for my crashed pid.

serverfault.com has the following answer, which worked for me on win2008r2.

To completely disable WerFault.exe on win2008, add the following registry keys:
 HKEY_CURRENT_USER\Software\Microsoft\Windows\Windows Error Reporting
  Disabled   = dword:00000001
  DontShowUI = dword:00000001
 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting
  Disabled   = dword:00000001
  DontShowUI = dword:00000001

Hilariously, the removal of werfault.exe allows the crashed program to terminate thus making all this process monitor/kill logic no longer required.

@echo off

REM  The status of a pid can be queried using the TASKLIST command.
REM
REM  TASKLIST /FI "PID eq 8536"
REM
REM  -- Example output when PID is running:
REM  Image Name                     PID Session Name        Session#    Mem Usage
REM  ========================= ======== ================ =========== ============
REM  cmd.exe                       8536 Console                    1      3,472 K
REM
REM  --Example output when PID is not running:
REM  INFO: No tasks are running which match the specified criteria.
REM
REM  When pid is not running pipe to find causes no output so the 
REM  body of the for loop isn't executed.
REM
REM  Usage:
REM   CALL :CHECK_PID_STATUS %PID%
REM   ECHO %PID_STATUS%
REM
GOTO SKIP_PID_STATUS
:CHECK_PID_STATUS
  SET PID_STATUS=dead
  FOR /F "usebackq delims=" %%G IN (`TASKLIST /FI "PID eq %1" ^| FIND "PID"`) DO (
    SET PID_STATUS=alive
  )
  GOTO :EOF
:SKIP_PID_STATUS

REM  http://ss64.org/viewtopic.php?id=1495
REM  http://technet.microsoft.com/en-us/library/bb742610.aspx#EEAA
REM
REM  WMIC PROCESS CALL CREATE arg1 arg2
REM  launches arg1 from location (and thus working dir) arg2 and returns output like the following
REM
REM  Executing (Win32_Process)->Create()
REM  Method execution successful.
REM  Out Parameters:
REM  instance of __PARAMETERS
REM  {
REM          ProcessId = 10904;
REM          ReturnValue = 0;
REM  };
REM
REM  CMD /C arg3
REM  executes arg3 in a command console then terminates
REM
REM  FIND "arg4"
REM  displays all lines that contain arg4
REM
REM  Thus
REM  WMIC PROCESS CALL CREATE "CMD /C your.exe >> C:\your.log", "C:\yourExeLocation" | FIND "ProcessId"
REM
REM  might output
REM          ProcessId = 12844;
REM
REM  But the goal is to get the pid as a value so we can kill it if it hangs.
REM  This can be achieved with the FOR construct, although it is convoluted.
REM  http://www.computerhope.com/forhlp.htm
REM
REM  SET PID=0
REM  FOR /F "tokens=2 delims==;" %%G IN ("        ProcessId ^= 12844;") DO (
REM    SET /A PID=%%G
REM  )
REM  ECHO %PID%
REM  
REM  Note that SET /A evaluates the right-hand-side as a numerical expression.
REM  So the result of the above is that %PID% holds the integer 12844
REM
REM  Bringing it all together, note the usebackq used to change the FOR() from 
REM  as string to the result of an exectued value. Also note the hat escapes
REM  for the comma and pipe used within the back quotes.

SET PROGANDARGS=mySlow.bat arg1 arg2
SET LOGFILE=D:\Temp\out.log
SET WORKDIR=D:\Temp

SET PID=0
FOR /F "usebackq tokens=2 delims==;" %%G IN (`WMIC PROCESS CALL CREATE "CMD /C %PROGANDARGS% >> %LOGFILE%"^, "%WORKDIR%" ^| FIND "ProcessId"`) DO (
  SET /A PID=%%G
)
ECHO Launched %PROGANDARGS% as %PID%

REM  Finally, having the PID you may want to monitor it and kill it if it
REM  doesn't complete in time.

SET /A COUNT=600
:WAIT_LOOP
SET /A COUNT=COUNT-1
TIMEOUT /t 1 /nobreak 1> NUL
CALL :CHECK_PID_STATUS %PID%
IF "%PID_STATUS%" == "alive" (
  IF "%COUNT%" == "0" (
    TASKKILL /pid %PID%
    ECHO Killed %PID% because it ran overtime.
  ) ELSE (
    GOTO WAIT_LOOP
  )
) ELSE (
  ECHO %PID% terminated normally.
)
{ "loggedin": false, "owner": false, "avatar": "", "render": "nothing", "trackingID": "UA-36983794-1", "description": "Launch a command as a seperate PID in DOS and terminate it if it doesn\u0027t complete within a certain time limit.", "page": { "blogIds": [ 435 ] }, "domain": "holtstrom.com", "base": "\/michael", "url": "https:\/\/holtstrom.com\/michael\/", "frameworkFiles": "https:\/\/holtstrom.com\/michael\/_framework\/_files.4\/", "commonFiles": "https:\/\/holtstrom.com\/michael\/_common\/_files.3\/", "mediaFiles": "https:\/\/holtstrom.com\/michael\/media\/_files.3\/", "tmdbUrl": "http:\/\/www.themoviedb.org\/", "tmdbPoster": "http:\/\/image.tmdb.org\/t\/p\/w342" }