Click here to Skip to main content
Email Password   helpLost your password?

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:

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...

Other related articles

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
Questioncrash in the application
jayeshgsv
20:16 11 Dec '05  
when i tried to run this program it is getting crashed during
EnumDeviceDriver method.
Can you explain me why and is there any way that i can handle this crash.
I am using same function in one of my application and i m getting crash in that as well
Please help me if you can or give some information where i can look in.
Thanking you in advance

-- modified at 1:18 Monday 12th December, 2005
GeneralA way to make it less processor intensive?
DavidJonson
11:02 2 Dec '05  
CPU usage jumps about 10% as long as the application is running, and more if the thread's sleep time is reduced. Does anyone know if there a way to update the dialog using some kind of notification instead of polling every half second?
GeneralGreat Job man
euacela
12:37 24 Dec '04  
I like your article.
good job keep on doing it

gabby
GeneralRe: Great Job man
T.YogaRamanan
0:55 25 Dec '04  
THanks for your appreciation. This encourages me a lot.

- Ramanan
GeneralProcess Port?
sides_dale
17:04 10 Dec '04  
I am a cross platform developer/admin and for several years now I have really enjoyed a tool on the Unix/Linux platform called LSOF. It will give a lot of information on processes and open files. I am curious if this api (or if you know) if this API will also list the TCP Port that a process is listening on or connected to? Most Unix utilities have been ported to windows through the SFU kit with windows, but this is the one tool that it is lacking and I would be very interested in expanding on this tool to generate some of the more detailed information that lsof gives.
GeneralRe: Process Port?
T.YogaRamanan
20:04 10 Dec '04  
That is a quite interesting IDEA. That will be very useful one.
For this purpose I'm using TCPView & TDIMon tool from sysinternal.

-Ramanan
GeneralRe: Process Port?
sides_dale
16:59 11 Dec '04  
I have used those as well, last night I also discovered a tool on CodeProject called HSSniffer. This tool needs a lot of work, but it has much of the functionality if combined with a product such as yours to give a lot of the lsof type functionality. Although the tool is mainly a sniffer it does map ports to pid. I am curious as to how that could be implemented into a product like yours.
GeneralRe: Process Port?
T.YogaRamanan
18:25 12 Dec '04  
I saw that tool, it is a C# tool and also it is using some 3rd party dll (IpHlpApi.net.dll). I don't think, that tool will give a good idea to incorporate here unless you find out a specific api.
Or do the other way, PSAPI is common one so you can find out how to use in C# and write a new tool by your self.

-Ramanan
GeneralRe: Process Port?
Anonymous
10:39 14 Dec '04  
Also available from http://www.sysinternals.com is procexp (Process Explorer) which will list (like TcpView) to what process listening / connected sockets belong, as well as the per process list of open files and loaded DLLs.

Like Linux' qps tool, it allows one to view the process hierarchy and point-n-shoot to kill processes and services. It can list thread stacks for a process, together with symbols (what's that thread doing that's eating all my CPU ??).

A Satisfied User
Jon
GeneralGetting Open File Information
Gerard Nicol
11:12 10 Dec '04  
Can you use this API to find the total number of open file handles each process has?


Last Updated 10 Dec 2004 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010