Click here to Skip to main content
15,885,757 members
Articles / Programming Languages / C++
Article

ProcessTime - A tool to get start and end time of any process

Rate me:
Please Sign up or sign in to vote.
4.85/5 (15 votes)
9 Dec 20042 min read 80.5K   3.5K   53   11
This tool uses PSAPI to list kernel and user processes. Also, it can log start and end times of user level processes.

Sample Image - ProcessTime.jpg

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 )
{
    //
    // Adds the process name and ID to the ListCtrl
    //

    // first update the process time 
    UpdateProcessTime();

    CListCtrl *pList = (CListCtrl*)GetDlgItem(IDC_LSTPROCESS);

    int nCount = pList->GetItemCount();
    
    ST_PROCESSINFO *pstProcessInfo;
    char szBuff[MAX_PATH];
    char szProcessName[MAX_PATH] = "unknown";
    // in case EnumProcessModules fails

    char szItemString[MAX_PATH+64];

    // open the process to query the time information
    //   this handle will remain open until ClearProcessList call
    //   This should remain open to get the process terminated time 
    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 the passed process id is same
        // as the already updated process in the ListCtrl
        //    then check whether it is a terminated process
        //     if not then return immediately
        //        without updating (to avoid flicker)
        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);

    // fill the structure and store the info for later updates
    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).

// this will update the process start time and end time
// (end time, only if the process has terminated)
void CProcessTimeDlg::UpdateProcessTime()
{
    CListCtrl *pList = (CListCtrl*)GetDlgItem(IDC_LSTPROCESS);
    FILETIME ftCreate, ftExit, ftKernel, ftUser;
    
    int nCount = pList->GetItemCount();

    // loop all the process in the list box
    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))
        {
            // Horrible, disgusting hack!
            // The two lines below basically grab the
            // contents of a FILETIME structure
            // and store it in a 64 bit integer.
            LONGLONG tUser64 = *(LONGLONG *)&ftUser;
            LONGLONG tKernel64 = *(LONGLONG *)&ftKernel;
    
            DWORD tUser, tKernel;

            // The LONGLONGs contain the time in 100 nanosecond intervals (now
            // there's a useful unit of measurement...).  Divide each of them by
            // 10000 to convert into milliseconds, and store the results in a
            // DWORD.  This means that the max time before overflowing is around
            // 4 Million seconds (about 49 days)
            tUser = (DWORD)(tUser64 / 10000);
            tKernel = (DWORD)(tKernel64 / 10000);
            
            // Format the user and kernel times, and add to the process node
            char szItem[128];

            char szFileDate[32] = { 0 };
            char szFileTime[32] = { 0 };

            if(!ftCreate.dwHighDateTime&&!ftCreate.dwLowDateTime)
            {
                strcpy(szFileDate,"");
                strcpy(szFileTime,"");
            }
            else
            {    // formatting the date & time
                GetFileDateAsString(&ftCreate, szFileDate, sizeof(szFileDate));
                GetFileTimeAsString(&ftCreate, szFileTime, sizeof(szFileTime));
            }

            wsprintf(szItem, "%s %s", szFileDate, szFileTime);

            CString cszText = pList->GetItemText(i,3);

            // if already exists then don't update, this will reduce the flicker
            if(cszText != szItem)
                pList->SetItemText(i,3,szItem);

            if(!ftExit.dwHighDateTime&&!ftExit.dwLowDateTime)
            {
                strcpy(szFileDate,"");
                strcpy(szFileTime,"");
            }
            else
            {    // formatting the date & time
                GetFileDateAsString(&ftExit, szFileDate, sizeof(szFileDate));
                GetFileTimeAsString(&ftExit, szFileTime, sizeof(szFileTime));
            }

            wsprintf(szItem, "%s %s", szFileDate, szFileTime);

            cszText = pList->GetItemText(i,4);

            // if already exists then don't update, this will reduce the flicker
            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

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer (Senior)
Australia Australia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
ahonly4u21-Feb-12 3:54
ahonly4u21-Feb-12 3:54 
Questioncrash in the application Pin
jayeshgsv11-Dec-05 19:16
jayeshgsv11-Dec-05 19:16 
QuestionA way to make it less processor intensive? Pin
DavidJonson2-Dec-05 10:02
DavidJonson2-Dec-05 10:02 
GeneralGreat Job man Pin
gamitech24-Dec-04 11:37
gamitech24-Dec-04 11:37 
GeneralRe: Great Job man Pin
Ramanan.T24-Dec-04 23:55
Ramanan.T24-Dec-04 23:55 
QuestionProcess Port? Pin
sides_dale10-Dec-04 16:04
sides_dale10-Dec-04 16:04 
AnswerRe: Process Port? Pin
Ramanan.T10-Dec-04 19:04
Ramanan.T10-Dec-04 19:04 
GeneralRe: Process Port? Pin
sides_dale11-Dec-04 15:59
sides_dale11-Dec-04 15:59 
GeneralRe: Process Port? Pin
Ramanan.T12-Dec-04 17:25
Ramanan.T12-Dec-04 17:25 
GeneralRe: Process Port? Pin
Anonymous14-Dec-04 9:39
Anonymous14-Dec-04 9:39 
GeneralGetting Open File Information Pin
Gerard Nicol10-Dec-04 10:12
Gerard Nicol10-Dec-04 10:12 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.