WinBatch Tech Support Home

Database Search

If you can't find the information using the categories below, post a question over in our WinBatch Tech Support Forum.

TechHome

Process ID

Can't find the information you are looking for here? Then leave a message over on our WinBatch Tech Support Forum.

Get Process Times UDF


I was digging through some UDF libraries to day and I found one I wrote a while ago and completely forgot about that does obtain process creation times directly via an API function call using DllCall(). The reason that I had forgotten about it is that it uses some of the more obscure kernel mode API function calls on the NT platform family. Specifically, it uses the API function ZwQueryInformationProcess(), which is documented in the book "Windows NT/2000 Native API Reference" by Garry Nebbett. The underlying kernel API functions are used by the Win32 API functions, but the kernel mode functions often have a broader range of functionality that what the Win32 API functions expose.

Attached is a UDF library file with some example script code tagged on to the end of it that demonstrates how to get the process creation time, termination time [not really applicable for a running process], the kernel mode time and user mode time by simply passing in a PID value.

; GetProcessTimes_UDF.wbt
; 32-bit
;
; UDF library to obtain a process' times using a kernele mode API function.
;
; The process extender is used to open a handle to a process using a PID value,
; but we could easily eliminate that requirement by calling a Win32 API function
; via DllCall().  The shortcut of using the tOpenProc() function is used since it
; is likely that the process extender is already being used to get an enumerated
; list of PID values.
;
; Rigorous error handling has not been implemented in this UDF.
;
; The process termination time will be an empty string for a process that is still running
;
; The kernel & user mode times need to be calculated using the huge math extender since
; they are really unsigned 64 bit integer values.  The little arithmetic trick used to
; handle the separate Hi & Lo DWORD values that compose a file system time is introducing
; a slight bit of numerical error in the final results.  A file system time is measured in
; 100 nanosecond intervals, either as an absolute time using an epoch of 1/1/1601 00:00:00,
; or as a relative time simply measured in 100 ns units.


#DEFINEFUNCTION udfGetProcessTimes(nPID)

if (nPID == '')
  nPID = DllCall('KERNEL32.DLL',long:'GetCurrentProcessId')
endif

if (!IsInt(nPID)) then return 0

; Set up the constants & binary buffers needed to do the job.

nProcInfoBufSize = 4 * 8
hProcInfoBuf = BinaryAlloc(nProcInfoBufSize)

nFileTimeBufSize = 2 * 4
hFileTimeBuf = BinaryAlloc(nFileTimeBufSize)

nSystemTimeBufSize = 8 * 2
hSystemTimeBuf = BinaryAlloc(nSystemTimeBufSize)

; This is the particular information class value that we're using.

ProcessTimes = 4

; Open a handle to the process for query information access.

hProc = tOpenProc(nPID,2)

Result = DllCall('NTDLL.DLL',long:'ZwQueryInformationProcess',long:hProc,long:ProcessTimes,lpbinary:hProcInfoBuf,long:nProcInfoBufSize,lpnull)

BinaryEodSet(hProcInfoBuf,nProcInfoBufSize)

TimeList = ArrDimension(4)

tCloseProc(hProc)
hProc = 0

; Get the process creation time.

BinaryCopy(hFileTimeBuf,0,hProcInfoBuf,0,nFileTimeBufSize)
Result = DllCall('KERNEL32.DLL',long:'FileTimeToSystemTime',lpbinary:hFileTimeBuf,lpbinary:hSystemTimeBuf)
BinaryEodSet(hSystemTimeBuf,nSystemTimeBufSize)
if (Result != 0)
  TimeList[0] = ''
  TimeList[0] = StrCat(TimeList[0],StrFixLeft(BinaryPeek2(hSystemTimeBuf,0),'0',4))
  TimeList[0] = StrCat(TimeList[0],':',StrFixLeft(BinaryPeek2(hSystemTimeBuf,2),'0',2))
  TimeList[0] = StrCat(TimeList[0],':',StrFixLeft(BinaryPeek2(hSystemTimeBuf,6),'0',2))
  TimeList[0] = StrCat(TimeList[0],':',StrFixLeft(BinaryPeek2(hSystemTimeBuf,8),'0',2))
  TimeList[0] = StrCat(TimeList[0],':',StrFixLeft(BinaryPeek2(hSystemTimeBuf,10),'0',2))
  TimeList[0] = StrCat(TimeList[0],':',StrFixLeft(BinaryPeek2(hSystemTimeBuf,12),'0',2))
else
  TimeList[i] = ''
endif

; Get the process termination time.

BinaryCopy(hFileTimeBuf,0,hProcInfoBuf,8,nFileTimeBufSize)

if (BinaryPeek4(hFileTimeBuf,0) == 0 && BinaryPeek4(hFileTimeBuf,4) == 0)
  TimeList[1] = ''
else
  Result = DllCall('KERNEL32.DLL',long:'FileTimeToSystemTime',lpbinary:hFileTimeBuf,lpbinary:hSystemTimeBuf)
  BinaryEodSet(hSystemTimeBuf,nSystemTimeBufSize)
  if (Result != 0)
    TimeList[1] = ''
    TimeList[1] = StrCat(TimeList[1],StrFixLeft(BinaryPeek2(hSystemTimeBuf,0),'0',4))
    TimeList[1] = StrCat(TimeList[1],':',StrFixLeft(BinaryPeek2(hSystemTimeBuf,2),'0',2))
    TimeList[1] = StrCat(TimeList[1],':',StrFixLeft(BinaryPeek2(hSystemTimeBuf,6),'0',2))
    TimeList[1] = StrCat(TimeList[1],':',StrFixLeft(BinaryPeek2(hSystemTimeBuf,8),'0',2))
    TimeList[1] = StrCat(TimeList[1],':',StrFixLeft(BinaryPeek2(hSystemTimeBuf,10),'0',2))
    TimeList[1] = StrCat(TimeList[1],':',StrFixLeft(BinaryPeek2(hSystemTimeBuf,12),'0',2))
  else
    TimeList[1] = ''
  endif
endif

TimeLoDW = BinaryPeek4(hProcInfoBuf,16)
TimeHiDW = BinaryPeek4(hProcInfoBuf,20)

TimeHiDW = (TimeHiDW * 0.0000001) * 2.0**32
TimeLoDW = (TimeLoDW * 0.0000001)

TimeList[2] = TimeHiDW + TimeLoDW

TimeLoDW = BinaryPeek4(hProcInfoBuf,24)
TimeHiDW = BinaryPeek4(hProcInfoBuf,28)

TimeHiDW = (TimeHiDW * 0.0000001) * 2.0**32
TimeLoDW = (TimeLoDW * 0.0000001)

TimeList[3] = TimeHiDW + TimeLoDW

; Free up the buffers we used.

hProcInfoBuf = BinaryFree(hProcInfoBuf)
hFileTimeBuf = BinaryFree(hFileTimeBuf)
hSystemTimeBuf = BinaryFree(hSystemTimeBuf)


return TimeList

#ENDFUNCTION



; This UDF requires the process extender

;Addextender("wproc34i.dll")
Addextender("wwprc44i.dll")

Title01 = 'Get Process Times'

; the PID variable must be a valid PID [Process IDentifier] value.  In this case, we will be sneaky
; and rely on the default behavior of the UDF, which is coded to substitute our own PID value if
; the PID parameter is an empty string.

PID = ''

Result = udfGetProcessTimes(PID)

if (VarType(Result) == 256)
  TempMsg = StrCat('Process Create Time (UTC) = "',Result[0],'"')
  TempMsg = StrCat(TempMsg,@CRLF,'Process Exit Time (UTC) = "',Result[1],'"')
  TempMsg = StrCat(TempMsg,@CRLF,'Process Kernel Time (seconds) = ',Result[2])
  TempMsg = StrCat(TempMsg,@CRLF,'Process User Time (seconds) = ',Result[3])
  Message(Title01,TempMsg)
else
  Message(Title01,'Errror!  Result is not an array.')
endif



exit


Article ID:   W16851
File Created: 2017:07:28:13:58:39
Last Updated: 2013:04:01:09:09:31