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: %lun", 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 FilesInformix" ; cmd /c dir
cd "C:Program FilesPictures" ; "\picardSoftwareGraphic UtilitiesPSPpsp.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 FilesPictures as the directory,
\picardSoftwareGraphic UtilitiesPSPpsp.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

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read