Introduction
ProcessTime is a simple tool that logs the processes created and terminated on your system. My colleague asked me for some type of test tool to check the end time of a Dialog application, which terminates itself after certain idle time. As a challenge, I searched the Internet but couldn't find any particular ones. So overnight I created this; hope this will be useful for some one out there. It is also very useful to catch any process that pops up in a sudden and terminates before you even notice it. It monitors user level processes and kernel level processes and lists them in two separate list controls. It displays the start time of user level processes and end time of processes that terminated only after this tool gets started. Also, it lists all the kernel processes (i.e., drivers loaded) on your system.
I don't know the way to pick up the driver loaded and unloaded time. It seems there is no way through PSAPI, but I think there are some other ways to do that. If any one knows how, please teach us. You are most welcome to modify this code or suggest to me any change.
Background
This doesn't have any complex code, it just demonstrates PSAPI basic usage. For more information about PSAPI. refer Platform SDK under MSDN or search PSAPI on MSDN. (Please don't criticize me for copying some part of code from MSDN sample ;) ) This tool uses the following APIs as basic:
EnumDeviceDrivers
to enumerate drivers.
EnumProcesses
to enumerate user mode processes.
How It Works
First, it gets all the processes' IDs using EnumProcesses
. Then, one by one it opens the processes and adds them to the list (see code below) with all information packed inside ST_PROCESSINFO
structure. This opened process will not be closed. It needs to remain open to gather end time after that process terminates. This opened process should be closed on some point, that will be on OnDestroy
or whenever user presses Clear button.
void CProcessTimeDlg::AddProcessToList(DWORD processID )
{
UpdateProcessTime();
CListCtrl *pList = (CListCtrl*)GetDlgItem(IDC_LSTPROCESS);
int nCount = pList->GetItemCount();
ST_PROCESSINFO *pstProcessInfo;
char szBuff[MAX_PATH];
char szProcessName[MAX_PATH] = "unknown";
char szItemString[MAX_PATH+64];
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ,
FALSE, processID);
if(!hProcess)
return;
for(int i=0;i<nCount; i++)
{
ST_PROCESSINFO *pstPrvProcessInfo =
(ST_PROCESSINFO *)pList->GetItemData(i);
if(pstPrvProcessInfo->dwProcessId == processID)
{
CString cszText = pList->GetItemText(i,4);
cszText.TrimRight();
if(cszText == "")
return;
}
}
HMODULE hMod;
DWORD cbNeeded;
if(EnumProcessModules( hProcess, &hMod, sizeof(hMod), &cbNeeded))
GetModuleBaseName( hProcess, hMod, szProcessName,
sizeof(szProcessName));
wsprintf(szItemString, "%u", processID);
wsprintf(szBuff,"%d", nCount);
pstProcessInfo = new ST_PROCESSINFO();
pstProcessInfo->dwProcessId = processID;
pstProcessInfo->hProcess = hProcess;
pList->InsertItem(nCount,szItemString);
pList->SetItemText(nCount,2,szProcessName);
pList->SetItemText(nCount,1,szBuff);
pList->SetItemData(nCount,(DWORD)pstProcessInfo);
}
UpdateProceTime
uses the API GetProcessTimes
to retrieve process times, particularly start and end times. End time will not be available until the process terminates. This function can be extended to display usage time. (See David Crow's article, check the link below under "Other related articles" heading).
void CProcessTimeDlg::UpdateProcessTime()
{
CListCtrl *pList = (CListCtrl*)GetDlgItem(IDC_LSTPROCESS);
FILETIME ftCreate, ftExit, ftKernel, ftUser;
int nCount = pList->GetItemCount();
for(int i=0;i<nCount;i++)
{
ST_PROCESSINFO *pstProcessInfo =
(ST_PROCESSINFO *)pList->GetItemData(i);
if(!pstProcessInfo->hProcess)
continue;
if(GetProcessTimes(pstProcessInfo->hProcess,
&ftCreate, &ftExit, &ftKernel, &ftUser))
{
LONGLONG tUser64 = *(LONGLONG *)&ftUser;
LONGLONG tKernel64 = *(LONGLONG *)&ftKernel;
DWORD tUser, tKernel;
tUser = (DWORD)(tUser64 / 10000);
tKernel = (DWORD)(tKernel64 / 10000);
char szItem[128];
char szFileDate[32] = { 0 };
char szFileTime[32] = { 0 };
if(!ftCreate.dwHighDateTime&&!ftCreate.dwLowDateTime)
{
strcpy(szFileDate,"");
strcpy(szFileTime,"");
}
else
{
GetFileDateAsString(&ftCreate, szFileDate, sizeof(szFileDate));
GetFileTimeAsString(&ftCreate, szFileTime, sizeof(szFileTime));
}
wsprintf(szItem, "%s %s", szFileDate, szFileTime);
CString cszText = pList->GetItemText(i,3);
if(cszText != szItem)
pList->SetItemText(i,3,szItem);
if(!ftExit.dwHighDateTime&&!ftExit.dwLowDateTime)
{
strcpy(szFileDate,"");
strcpy(szFileTime,"");
}
else
{
GetFileDateAsString(&ftExit, szFileDate, sizeof(szFileDate));
GetFileTimeAsString(&ftExit, szFileTime, sizeof(szFileTime));
}
wsprintf(szItem, "%s %s", szFileDate, szFileTime);
cszText = pList->GetItemText(i,4);
if(cszText != szItem)
pList->SetItemText(i,4,szItem);
}
}
}
When this tool starts, it starts a thread, which runs behind and executes the monitoring functions in particular intervals. This makes it possible to catch any newly appearing process and end time of existing processes.
Lacking Feature
I have some more enhancements to make but due to lack of time and to maintain simplicity, I haven't done those. These are some of those...
- Making this as service
- Logging to a file for permanent record
- To appear in system tray after minimize
- Sorting
Other related articles