2

I need to know how many instances of my process are running on a local Windows system. I need to be able to do it using C++/MFC/WinAPIs. So what is a reliable method to do this?

I was thinking to use process IDs for that, stored as a list in a shared memory array that can be accessed by the process. But the question is, when a process is closed or crashes how soon will its process ID be reused?

ahmd0
  • 16,633
  • 33
  • 137
  • 233

4 Answers4

1

You can snag the process handles by the name of the process using the method described in this question. It's called Process Walking. That'll be more reliable than process id's or file paths.

A variation of this answer is what you're looking for. Just loop through the processes with Process32Next, and look for processes with the same name using MatchProcessName. Unlike the example in the link I provided, you'll be looking to count or create a list of the processes with the same name, but that's a trivial addition.

Community
  • 1
  • 1
Logical Fallacy
  • 3,017
  • 5
  • 25
  • 41
  • Yeah, thanks. That's a variation of marceln's answer. The issue here is that we rely on my process to have the same name. Obviously if someone renames it this method will break. – ahmd0 Jun 16 '12 at 21:40
1

The process and thread identifiers may be reused any time after closure of all handles. See When does a process ID become available for reuse? for more information on this.

However if you are going to store a pair of { identifier, process start time } you can resolve these ambiguities and detect identifier reuse. You can create a named file mapping to share information between the processes, and use IPC to synchronize access to this shared data.

Roman R.
  • 68,205
  • 6
  • 94
  • 158
  • Very nice. Thank you. I didn't think about using process start time. So what API do I need to get it? – ahmd0 Jun 16 '12 at 21:37
  • At some point you will be initializing the array in shared memory with certain values (static "current time" initialized once), so if you see another entry on the array with the same identifier, but different time, you will clean it up as gone process. Well actually, doing this carefully you will be able to do event without time. It is just initialization code needs to clean up possibly existing previous process reference with the same id (if any). Also note PSAPI process walking answer, it might also be useful as an alternate option. – Roman R. Jun 16 '12 at 21:41
  • Also, `GetProcessTimes` http://msdn.microsoft.com/en-us/library/windows/desktop/ms683223%28v=vs.85%29.aspx gets you process creation time. – Roman R. Jun 16 '12 at 21:44
  • Yeah, I just found it too. So say, if I store pairs of PID and FILETIME ftCreationTime that I get from GetProcessTimes() in a shared memory array. Then say, to check if the process is still alive I do OpenProcess() on PID and call GetProcessTimes() with a process handle to get creation time and compare it to the value stored in a shared memory. Is that what you meant? I'm also assuming that the FILETIME ftCreationTime expressed in 100 nanosecond intervals does not change over time. – ahmd0 Jun 16 '12 at 21:48
  • 1
    This is possible, but I would prefer it a different way. Processes add themselves on the list *1, and remove also. Some external check also removes crashed processes by checking their identifiers against OpenProcess. *1 - on adding onto list, a process seeing its identifier just removed previous process as it obviously crashed. – Roman R. Jun 16 '12 at 21:57
1

If you are trying to limit the number of instances of your process to some number you can use a Semaphore.
You can read in detail here: http://msdn.microsoft.com/en-us/library/windows/desktop/ms686946(v=vs.85).aspx

In a nutshell, the semaphore is initialized with a current count and max count. Each instance of your process will decrement the count when it acquires the semaphore. When the nth process tries to acquire it but the count has reached zero that process will fail to acquire it and can terminate or take appropriate action.

The following code should give you the gist of what you have to do:

#include <windows.h>
#include <stdio.h>

// maximum number of instances of your process
#define MAX_INSTANCES 10

// name shared by all your processes.  See http://msdn.microsoft.com/en-us/library/windows/desktop/aa382954(v=vs.85).aspx
#define SEMAPHORE_NAME "Global\MyProcess"

// access rights for semaphore, see http://msdn.microsoft.com/en-us/library/windows/desktop/ms686670(v=vs.85).aspx
#define MY_SEMAPHORE_ACCESS SEMAPHORE_ALL_ACCESS

DWORD WINAPI ThreadProc( LPVOID );

int main( void )
{
        HANDLE semaphore;

    // Create a semaphore with initial and max counts of MAX_SEM_COUNT

    semaphore = CreateSemaphore( 
        NULL,           // default security attributes
        MAX_INSTANCES,  // initial count
        MAX_INSTANCES,  // maximum count
        SEMAPHORE_NAME ); 

    if (semaphore == NULL) 
    {
        semaphore = OpenSemaphore(
            MY_SEMAPHORE_ACCESS, 
        FALSE, // don't inherit the handle for child processes
        SEMAPHORE_NAME );

        if (semaphore == NULL)
        {
            printf("Error creating/opening semaphore: %d\n", GetLastError());
                return 1;           
        }
    }

    // acquire semaphore and decrement count
    DWORD acquireResult = 0;
        acquireResult = WaitForSingleObject( 
        semaphore,
        0L);  // timeout after 0 seconds trying to acquire

    if(acquireResult == WAIT_TIMEOUT)
    {
        printf("Too many processes have the semaphore.  Exiting.");
        CloseHandle(semaphore);
        return 1;
    }

    // do your application's business here

    // now that you're done release the semaphore
    LONG prevCount = 0;
    BOOL releaseResult = ReleaseSemaphore(
            semaphore,
            1, // increment count by 1
            &prevCount );

    if(!releaseResult)
    {
        printf("Error releasing semaphore");
        CloseHandle(semaphore);
        return 1;
    }

    printf("Semaphore released, prev count is %d", prevCount);

    CloseHandle(semaphore);
    return 0;
}
Pavel P
  • 15,789
  • 11
  • 79
  • 128
kennbrodhagen
  • 4,258
  • 2
  • 26
  • 21
  • Thanks. And no, I'm not limiting the number of instances of my process. This is needed for the single writer/multiple readers lock mechanism that should work between processes. This mechanism should count the number of open readers. – ahmd0 Jun 17 '12 at 00:18
  • 1
    Here is an example solution to the readers/writers problem using semaphores: http://www.codeproject.com/Articles/3023/A-solution-to-the-Readers-Writers-Problem-using-se – kennbrodhagen Jun 19 '12 at 17:58
0

Well, your solution is not very reliable. PIDs can be reused by the OS at any later time. I did it once by going through all the processes and comparing their command line string (the path of the executable) with the one for my process. Works pretty well.

Extra care should be taken for programs that are started via batch files (like some java apps/servers).

Other solutions involve IPC, maybe through named pipes, sockets, shared memory (as you mentioned). But none of them are that easy to implement and maintain.

Marcel N.
  • 13,726
  • 5
  • 47
  • 72
  • Thanks for sharing. Although I'd be very leery about comparing file paths because there's a ton of ways a process can be started. Example: short-file names, virtual folders, network shares, etc. – ahmd0 Jun 16 '12 at 21:28
  • I didn't mean you should hardcode the path in your app. However, you're right. If your app is self-contained and portable (can be copied to a net share, USB drive, etc) then indeed this is not reliable as well. I guess you're stuck with some form of IPC, whichever you find more convenient. – Marcel N. Jun 16 '12 at 21:31