Starting, Managing and Switching Processes

Environment: The source is UNICODE compliant and was tested with both Visual C++ 5 SP3 and Visual C++ 6 SP2.

The demo is a Visual C++ 6 project.

In a few of my projects I have to deal with processes. To protect me from implementing all of the hard bits again and again, I developed a set of classes. These are mainly a process manager class and a process enumerator class.

The Process Enumerator Class CProcessList

This class makes it easy to take a snapshot of all currently running processes and iterate them. There is no need for you to determine the type of system your application is running on. The enumerator selects the best enumeration method available. Because of this fact, you cannot simply instantiate an object of the process enumerator. You have to "Create()" one instead:

CProcessList * pProcessList = CProcessList::Create();

Now that you have a valid object, you can take a snapshot of the process list:

pProcessList->SnapShot();

After having done this, you can iterate the processes:

CProcess * pProcess;
while((pProcess = pProcessList->GetNextProcess()) != 0) {
    TRACE("process' filename: \"%s\"  PID: %lu\n", LPCTSTR(pProcess->GetFilename()), pProcess->GetPID());
}

You can reuse the enumerator by taking another snapshot.
If you don't need the process enumerator any longer, you have to delete it:

delete pProcessList;

A detailed class documentation is inside the source archives.

The Process Manager Class CProcessMgr

CProcessMgr has the following member functions (description follows below):

// split a command string
BOOL    ParseCommand(
                        const CString & CmdLine,
                        CString & Directory,
                        CString & Cmd,
                        CString & Args
                );   // execute a command
DWORD   Execute(
                        const CString & strCmd,
                        const CString & strArgs,
                        const CString & strDir = "",
                        BOOL bWait = FALSE
);
DWORD   Execute(
                        const CString & strCmdLine,
                        BOOL bWait = FALSE
                );   // wait for another process
DWORD   Wait( DWORD PID ) const;   // check wether a program is running
DWORD   IsProgramRunning(const CString & FileName) const;
DWORD   IsProgramRunning(DWORD PID) const;   // switch to another running application
BOOL    SwitchProcessIntoForeground(DWORD PID) const;
BOOL    SwitchProcessIntoForeground( const CString & FileName ) const;   // get informations about a shell-link (known as "shortcut")
BOOL    GetLinkInfo(const CString & LinkName, CString & Path) const;   // handle an error
virtual void OnExecError( int nErrorCode, const CString & strCommand );

One of the main goals of this class is, that it understands complex command strings. This is very useful since one can store such a command string in the registry (or in a file or database or whatever) at once. The syntax of such a command string is as follows (BNF-notation):

CmdLine : command
        | command arguments
        | 'cd' directory ';' command
        | 'cd' directory ';' command arguments
command : string
arguments: string
        | arguments string
directory: string               /* shall consist of a full path ! */
string  : '[^ \t]+'             /* string without blanks/tabs */
        | '"' '[^"]*' '"'       /* quoted string can 
                                   contain any character
                                   except " itself */

OK - not all of you are familiar with BNF, so let me give some examples:

cmd
cmd /c dir
cd C:\windows ; cmd /c dir
cd "C:\Program Files\Informix" ; cmd /c dir
cd "C:\Program Files\Pictures" ; "\\picard\Software\Graphic Utilities\PSP\psp.exe" Title.jpg

All of these are valid command strings. Note that these samples are not in C/C++ notation !

The Parse() method splits such a command string into its components "directory", "command" and "arguments". Any special tokens are stripped off, so if you parse the last example, it would give you C:\Program Files\Pictures as the directory, \\picard\Software\Graphic Utilities\PSP\psp.exe as the command and Title.jpg as the argument to the command. You can pass a complete command string to the Execute() method. If the command part of the string is not an executable file, then Execute() searches for the associated executable. The return value of Execute() depends on the <bWait> parameter. If <bWait> is TRUE, then it returns the exit code of the execution, otherwise a process-ID (PID) will return.
With the Wait() method you can explicitly wait for the end of a running process; just give Wait() the process id of that process.

To check whether a special program is currently running, you can use the IsProgramRunning() method. If you pass the name of an executable file, this method checks the process table and gives you the PID of the process or 0 (zero) if that program is currently not running. If you're developing an application, that does not provide a window, but this program must run only once per machine, you cannot use FindWindow() to determine, whether the application is already running. The use of mutexes or similar resources might be an inacceptable overhead too. In such a case, one can simply use the following code:

    TCHAR szName[1024];
    GetModuleFileName(0, szName, 1023);
    if( CProcessMgr().IsProgramRunning(szName) != DWORD(getpid()) ) {
         // the application is already running on this machine
    }

The code above works correctly, because a newly created process appears after a previously created process in the process table, so the method finds the other application first.

At last: the SwitchProcessIntoForeground() method switches a process into the foreground; i.e. makes that process the active process. If the main window of that process is minimized, then it will be restored to its previous state. If IsProgramRunning() indicates that a certain Process is running and SwitchProcessIntoForeground() says "I cannot switch to that process", then the process is possibly a DDE/OLE server that has currently hidden windows only or it is a process without a window (an NT service for instance).

There is a detailed class documentation in either the source- and the sample archive (in the htmldocs subdirectory; start with index.html).

Download

The process manager/enumerator package consists of four files
ProcessMgr.[h|cpp]
Processes.[h|cpp]

Note that ProcessMgr.* depends on Processes.* ; but not vice versa (thus you can use the enumerator "stand alone").

Download demo project - 63 KB

Download source - 52 KB

Date Last Updated: April 18, 1999



Comments

  • Compilation under VC++ 2005

    Posted by alexadvance on 01/30/2007 10:16am

    I can't compile it under Visual C++ 2005 : error C2664: 'std::list<_Ty>::_Iterator<_Secure_validation>::_Iterator(const std::list<_Ty>::_Iterator<_Secure_validation> &)' : impossible de convertir le paramC(tre 1 de 'int' en 'const std::list<_Ty>::_Iterator<_Secure_validation> &' Is there available up-to-date version ?

    Reply
  • How to check if an application in a DLL is running ?

    Posted by lulacapixaba on 03/08/2005 11:53am

    The code is very interesting but I think it would not work in some circumstances. Suppose you want to detect if the "Windows Picture and Fax Viewer" is running on your machine. This application is launched from a DLL in a command line similar to this one: rundll32.exe c:\windows\system32\shimgvw.dll,ImageView_Fullscreen C:\temp\logo.gif If you use your code to check if the application is running, it will not be abble to detect it. It would detect that RUNDLL32.EXE is running... but what about the "Windows Picture and Fax Viewer"? The problem I have at the moment is that I have a process handle and I want to know if I have an existing instance of this process runnig. I am not interested in the RUN32DLL.exe process but in the actual application that is inside the DLL. If I have multiple applications running from inside DLLs, how can I detect if they are running?

    Reply
  • CProcessMgr under Win2000

    Posted by Legacy on 07/10/2001 12:00am

    Originally posted by: Markus Schroth

    What do I have to Do that this very nice Code runs under Win2000 ( No Access ) ?

    Reply
  • If all you want to do is spawn and wait for completion...

    Posted by Legacy on 05/05/1999 12:00am

    Originally posted by: LALeonard

    If all you want to do is spawn and wait for completion (which is all I needed), look at ::CreateProcess() and
    ::GetExitCodeProcess(); they will do that for you.
    
    

    // Handles the messy details of unZIPing a file.
    bool FilePtf::Zip(FileZip& fz) const
    {
    bool bReturn(false);

    // Load the command format from the Registry.
    CString sFmt(GetApp()->m_MyEngineConfig.m_sZipCommand);
    CString sZipPath(fz.GetPathName());

    // Build the command.
    CString sCmd;
    sCmd.Format(sFmt, sZipPath, GetPathName());
    char* pszCmd = sCmd.GetBuffer(sCmd.GetLength());

    // Create the structures.
    STARTUPINFO si;
    ::ZeroMemory(&si, sizeof si);
    si.cb = sizeof si;
    si.dwFlags = STARTF_USESHOWWINDOW;
    si.wShowWindow = SW_HIDE;

    PROCESS_INFORMATION pi;
    ::ZeroMemory(&pi, sizeof pi);

    // Spawn the process (this replaces the old WinExec() call).
    bReturn = ::CreateProcess(
    NULL, // App name - use first token in command line.
    pszCmd, // Command line string.
    NULL, // LPSECURITY_ATTRIBUTES for process.
    NULL, // LPSECURITY_ATTRIBUTES for thread.
    false, // Handle inheritance flag.
    IDLE_PRIORITY_CLASS, // Creation flags.
    NULL, // Pointer to new environment block.
    NULL, // Pointer to current directory name.
    &si, // Pointer to STARTUPINFO.
    &pi // Pointer to PROCESS_INFORMATION.
    );

    sCmd.ReleaseBuffer();

    // Log the results of the spawn (not of the unZip process).
    if (bReturn) {

    MY_LOG(ENGLISH_DEBUG_ONLY, STSEV_Debug,
    "Starting ZIP of file " + GetPathName() + " to " + fz.GetPathName());

    // Wait around for the ZIPing to stop before continuing, but only for
    // an hour. How long can it take to Zip a file? We check once per
    // second, and log a message every minute.
    DWORD dwExitCode(STILL_ACTIVE);

    for (int nSeconds = 0;
    STILL_ACTIVE == dwExitCode && nSeconds < 3600;
    ++nSeconds) {

    if (::GetExitCodeProcess(pi.hProcess, &dwExitCode)) {
    ;
    }
    else {
    MY_LOG(API_FAILURE, STSEV_Error,
    "::GetExitCodeProcess" & (int) ::GetLastError() & Generic::GetErrorString());
    }

    if (59 == (nSeconds % 60)) {
    MY_LOG(ENGLISH_DEBUG_ONLY, STSEV_Debug,
    "Waiting for ZIP process to finish...");
    }

    ::Sleep(1000);
    }

    // Check the result of the ZIPing.
    if (0 == dwExitCode) {
    bReturn = true;
    MY_LOG(ZIP_SUCCESS, STSEV_Audit, GetPathName() & fz.GetPathName());
    }
    else {
    // The unZIP process returned an error, or we timed out; something must be
    // dreadfully wrong.
    bReturn = false;

    MY_LOG(ZIP_FAILURE, STSEV_Error,
    GetPathName() & fz.GetPathName() & (int) dwExitCode);
    }
    }
    else {
    // The spawn failed.
    MY_LOG(API_FAILURE, STSEV_Error,
    "::CreateProcess" & (int) ::GetLastError() & Generic::GetErrorString());
    }

    return bReturn;
    }

    Reply
  • "IsProgramRunning" hangs on WindowsNT

    Posted by Legacy on 03/09/1999 12:00am

    Originally posted by: jijon

    "IsProgramRunning"

    function hangs on WindowsNT..

    Any soldutions?

    Thanks in advance.

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • IBM Worklight is a mobile application development platform that lets you extend your business to mobile devices. It is designed to provide an open, comprehensive platform to build, run and manage HTML5, hybrid and native mobile apps.

  • New IT trends to support worker mobility — such as VDI and BYOD — are quickly gaining interest and adoption. But just as with any new trend, there are concerns and pitfalls to avoid.  Download this paper to learn the most important considerations to keep in mind for your VDI project.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds