tags:

views:

82

answers:

3

Quoted from here:

BOOL WINAPI CreateProcess(
  __in_opt     LPCTSTR lpApplicationName,
  __inout_opt  LPTSTR lpCommandLine,
  __in_opt     LPSECURITY_ATTRIBUTES lpProcessAttributes,
  __in_opt     LPSECURITY_ATTRIBUTES lpThreadAttributes,
  __in         BOOL bInheritHandles,
  __in         DWORD dwCreationFlags,
  __in_opt     LPVOID lpEnvironment,
  __in_opt     LPCTSTR lpCurrentDirectory,
  __in         LPSTARTUPINFO lpStartupInfo,
  __out        LPPROCESS_INFORMATION lpProcessInformation
);

I have two independant programe that creates exactly the same process, how can I ensure that if one of them has already created the process, the other won't create it twice?

A: 

You can use this function

BOOL WINAPI EnumProcesses( __out DWORD *pProcessIds, __in DWORD cb, __out DWORD *pBytesReturned );

to get a list of all the pids of all currently running processes and check if the process is running?

MEURSAULT
What if the two programes are run at the same time? In this extreme case,will still create two process.
No, don't do this. Use a mutex.
erikkallen
+4  A: 

The most simple way is if you create a named object after the start of the program. For example CreateEvent, CreateMutex and so on. To verify existance of the application you can just use OpenEvent, OpenMutex and so on before creating of the object. You can choose (if desired) the name of the object with the the "Global\" prefix (see http://msdn.microsoft.com/en-us/library/aa382954.aspx) to allow only one process for all terminal server session.

UPDATED: Because how I can see there are different opinions about my suggestion I try to explain it more exactly and add the corresponding test example.

The main idea is that the application which are started create any named object is the object with the same name not yet exist. This only reserve the name in the Kernel Object Namespaces. No real usage of the object are needed. The advantaged of this way compared with creating of a file on the disk is that named objects are temporary and are owned by a application. So if the application are ended, be killed or be terminated in any other way (because of unhanded exception for example) the named object will be automatically deleted by the operation system. In the following example I don't use CloseHandle at all. How you can test the application can successfully determine whether it runs as the first instance or not.

#include <windows.h>
//#include <Sddl.h>

LPCTSTR g_pszEventName = TEXT("MyTestEvent"); // TEXT("Global\\MyTestEvent")

void DisplayFirstInstanceStartedMessage()
{
    TCHAR szText[1024];
    wsprintf (szText,
        TEXT("The first instance are started.\nThe event with the name \"%s\" is created."),
        g_pszEventName);

    MessageBox (NULL,
        szText,
        TEXT("CreateEventTest"), MB_OK);
}

void DisplayAlreadyRunningMessage ()
{
    TCHAR szText[1024];
    wsprintf (szText,
        TEXT("The first instance of the aplication is already running.\nThe event with the name \"%s\" already exist."),
        g_pszEventName);

    MessageBox (NULL,
        szText,
        TEXT("CreateEventTest"), MB_ICONWARNING | MB_OK);
}

void DisplayErrorMessage (DWORD dwErrorCode)
{
    if (dwErrorCode == ERROR_ALREADY_EXISTS)
        DisplayAlreadyRunningMessage();
    else {
        LPTSTR  pErrorString;
        if (FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM |    // Always search in system message table !!!
                            FORMAT_MESSAGE_ALLOCATE_BUFFER |
                            FORMAT_MESSAGE_IGNORE_INSERTS |
                            0, NULL,                // source of message definition
                            dwErrorCode,            // message ID
    //                        0,                      // language ID
    //                        GetUserDefaultLangID(), // language ID
    //                        GetSystemDefaultLangID(),
                            MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
                            (LPTSTR)&pErrorString,   // pointer for buffer to allocate
                            0,                      // min number of chars to allocate
                            NULL)) {
            MessageBox (NULL, pErrorString, TEXT("CreateEventTest"), MB_OK);
            LocalFree (pErrorString);
        }
        else {
            TCHAR szText[1024];
            wsprintf (szText, TEXT("Error %d in the CreateEvent(..., \"%s\")"), dwErrorCode, g_pszEventName);
            MessageBox (NULL, szText, TEXT("CreateEventTest"), MB_OK);
        }
    }
}

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
    //SECURITY_ATTRIBUTES sa;
    //BOOL bSuccess;
    HANDLE hEvent = OpenEvent (EVENT_MODIFY_STATE, FALSE, g_pszEventName);// EVENT_ALL_ACCESS
    if (hEvent == NULL) {
        DWORD dwErrorCode = GetLastError();
        if (dwErrorCode != ERROR_FILE_NOT_FOUND) {
            DisplayErrorMessage(dwErrorCode);
            return 1;
        }
    }
    else {
        DisplayAlreadyRunningMessage();
        return 0;
    }

    //sa.bInheritHandle = FALSE;
    //sa.nLength = sizeof(SECURITY_ATTRIBUTES);
    //bSuccess = ConvertStringSecurityDescriptorToSecurityDescriptor (
    //    TEXT("D:(A;OICI;GA;;;WD)"),    // Allow full control 
    //    SDDL_REVISION_1,
    //    &sa.lpSecurityDescriptor,
    //    NULL);
    hEvent = CreateEvent (NULL, // &sa
        TRUE, FALSE, g_pszEventName);
    //sa.lpSecurityDescriptor = LocalFree (sa.lpSecurityDescriptor);
    if (hEvent == NULL) {
        DWORD dwErrorCode = GetLastError();
        DisplayErrorMessage(dwErrorCode);
        return 1;
    }
    else
        DisplayFirstInstanceStartedMessage();

    return 0;
    UNREFERENCED_PARAMETER (hInstance);
    UNREFERENCED_PARAMETER (hPrevInstance);
    UNREFERENCED_PARAMETER (lpCmdLine);
    UNREFERENCED_PARAMETER (nShowCmd);
}

If one want support that different users from the same desktop or from the different desktops could start only one instance of the program, one can uncomment some parts of the commented code or replace the name MyTestEvent of the event to Global\MyTestEvent.

I hope after the example my position will be clear. In such kind of the event usage no call of WaitForSingleObject() are needed.

Oleg
You don't need to create and check for existence separately, just use `CreateMutex` and check if `GetLastError` returns `ERROR_ALREADY_EXISTS`
Hasturkun
Your sample has a potential race as both processes can clear `OpenEvent` and "succeed" to create the event (since `CreateEvent` will not return NULL if the event already exists)
Hasturkun
@Hasturkun: In general you should have right, but ... A simple test on Windows 7 shows that if you try to use directly `CreateEvent` or `CreateMutex` instead of probing with `OpenEvent` or `OpenMutex` you receive no time `ERROR_ALREADY_EXISTS` error. Instead of that you **open** the existing event/mutex with respect of `CreateEvent` or `CreateMutex` exactly like with `OpenEvent` or `OpenMutex`. You can just try with `PulseEvent` from one instance and see that you send signaled event another instance. So I recommend use combination of `OpenEvent` and `CreateEvent` like in my example.
Oleg
@Hasturkun: Sorry, I don't understand your second comment. Could you explain your test which you suggest more exactly.
Oleg
`CreateEvent` will both return a handle _and_ set the last error to `ERROR_ALREADY_EXISTS`. You should check `GetLastError` even when a non-NULL value was returned
Hasturkun
@Hasturkun: OK, now I understand what you mean. You are right. Your suggestion can a little simplify the code. But I never claimed that my code is the best one. It's only a suggestion to use **named objects** for checking of the existence of the previous one instance of the same application.
Oleg
+2  A: 

You cannot do this by letting the process you start creating a named object. That's an inherent race condition, it takes time for the process to get started. Both programs need to call CreateMutex at some point before trying to create the 3rd process with an agreed-upon name. Then they need to call WaitForSingleObject() with a zero wait time to try to acquire the mutex. Whomever gets it is the one that should call CreateProcess().

More work is needed after this to deal with this 3rd process terminating.

Hans Passant
No, this is wrong. All create/open/aquire operations on Mutexes are atomic, otherwise they would be useless.
erikkallen
You are missing the point completely.
Hans Passant
Agreed, you need to protect the _creation_ of the process with a mutex, this is a good answer
John Burton
That is to say Get the mutex, check if the process is already created, if needed created it, then release the mutex. Both processes need to do this to ensure only one will create it
John Burton
Please rephrase then. I read your answer as you'd have a race condition if the first lines in your main method in the child process say `CreateMutext(name); if (GetLastError() == ERROR_ALREADY_EXISTS) ExitProcess();`, and this is not the case. Sure, some work would be wasted by starting the process and then exiting it immediately, but you won't have a race condition.
erikkallen
What kind of work needed for terminating?
@Hans Passant,can you elaborate with a bit of code regards `WaitForSingleObject` and terminating?