locked
Problem with using events to synchronize two Windows processes RRS feed

  • Question

  • Hello, I hope that I am posting in the correct forum. My apologies if I am posting in the wrong place.

    I am trying to create an event that can be shared by two different Windows processes, Process1 and Process2.

    I have two different test cases:

    Test Case 1:
    ===========
    (Case "A")
    1. Process 1 creates a named event, starts Process2, and waits for Process2 to signal the event.
    2. Process 2 also creates the named event but expects it to have already been created by process 1.
       Process 2 sets the event, closes its handle to the event, then exits.
    3. Process 1 will time out if the event is not signaled within 2 seconds time.
       Process 1 closes its handle to the event, then exits

    (Case "B")
    1. Process 1 creates a named event, starts Process2, and waits for Process2 to signal the event.
    2. Process 2 opens the named event.
       Process 2 sets the event, closes its handle to the event, then exits.
    3. Process 1 will time out if the event is not signaled within 2 seconds time.
       Process 1 closes its handle to the event, then exits

    Test Case 2:
    ===========
    1. Process 1 creates a named event, but sets the event attributes so it can be shared
       Process 1 starts Process2 and passes the named event handle on the command line, and waits for Process2 to signal the event.
    2. Process 2 sets the event it received from the command line, then exits.
    3. Process 1 will time out if the event is not signaled within 2 seconds time.
       Process 1 closes its handle to the event, then exits


    Problems Observed (in test case 1 only):
    =======================================
    (Case "A")
    1. In process 2, I expect the GetLastError() function to return "ERROR_ALREADY_EXISTS" when 
       CreateEvent() is called, because Process 1 created it already.

       When I see it fail, GetLastError() returns either "0" or "183".

    (Case "B")
    2. In process 2, I expect OpenEvent() to not return NULL, because Process1 already created the event.

       When I see it fail, GetLastError() returns either "0" or "183".

    I have only seen this failure occur once for each test case. Based on my understanding of events and their
    usage between processes, I "think" I am setting things up correctly.

    I've attached example code to illustrate what I am trying to do. Is there something here in Test Case 1 that I am doing wrong?

    Thanks very much in advance.

    ===== Example Code Case Number 1 =====

    =====================================================
    Process 1: 
    1. Creates a named event, non-signaled
    2. Creates/starts Process2.exe
    3. Waits for event to be signaled from Process 2
    4. Closes the event handle 
    5. Esit
    =====================================================
    int main(int argc, char *argv[], char *envp[])
    {
        char   SysStr[1024];
        PROCESS_INFORMATION piProcInfo; 
        STARTUPINFO siStartInfo;
        BOOL bFuncRetn = FALSE; 
        DWORD lastErr  = 0;

        HANDLE  testEvent = NULL;

        ZeroMemory( SysStr, 1024 );
        ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
        ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
        siStartInfo.cb = sizeof(STARTUPINFO); 

        testEvent = CreateEvent( NULL, FALSE, FALSE, TEXT("TEST_EVENT") );
        if ( testEvent == NULL )
        {
            printf("Error: Failed to create test event. Error = %d\n", GetLastError());
            return (-1);
        }

        sprintf(SysStr, "Process2.exe");

        bFuncRetn = CreateProcess(NULL,          // NULL = Use the module name in SysStr below
                                  SysStr,        // command line 
                                  NULL,          // process security attributes, cannot be inherited 
                                  NULL,          // primary thread security attributes, cannot be inherited 
                                  FALSE,         // handles are not inherited 
                                  0,             //  creation flags, normal priority 
                                  NULL,          // use parent's environment 
                                  NULL,          // use parent's current directory 
                                  &siStartInfo,  // STARTUPINFO pointer 
                                  &piProcInfo);  // receives PROCESS_INFORMATION 


        if ( bFuncRetn == FALSE ) 
        {
            printf("Error: CreateProcess Process2.exe failed. Exiting. Error = %d\n", GetLastError());
            CloseHandle( testEvent );
            return (-1);
        } 
        else
        {
            CloseHandle(piProcInfo.hThread);
        }

        //
        // Wait for 2 seconds to give Process2.exe time to signal Process1.exe here
        //
        switch ( WaitForSingleObject( testEvent, 2000 ) )
        {
            case WAIT_ABANDONED:
                printf("Error: WaitForSingleObject() returned WAIT_ABANDONDED status for testEvent event.\n");
                CloseHandle( testEvent );
                return (-1);
                break;

            case WAIT_TIMEOUT:
                printf("Error: WaitForSingleObject() returned WAIT_TIMEOUT status for testEvent event.\n");
                CloseHandle( testEvent );
                return (-1);
                break;

            case WAIT_FAILED:
                printf("Error: WaitForSingleObject() returned WAIT_FAILED status for testEvent event.\n");
                CloseHandle( testEvent );
                return (-1);
                break;

            case WAIT_OBJECT_0: 
                printf("WaitForSingleObject() returned WAIT_OBJECT_0 status for testEvent event.\n");
                CloseHandle( testEvent );
                break;

            default:
                break;
        }

    return 0;
    }


    =====================================================
    Process 2 (variant A):
    1. Creates a handle to the named event created by Process 1
    2. Sets the event
    3. Closes the event handle
    4. Exit
    =====================================================
    int main(int argc, char *argv[], char *envp[])
    {
        HANDLE  testEvent = NULL; 
        WORD    lastErr   = 0;


        testEvent = CreateEvent( NULL, FALSE, FALSE, TEXT("TEST_EVENT") );
        if ( testEvent == NULL )
        {
            printf("Error, testEvent event was not created. GetLastError() = %d\n", GetLastError());
            return(-1);
        }
        else if ( (lastErr = GetLastError()) != ERROR_ALREADY_EXISTS )
        {
            printf("Error, testEvent event was not created by Process1.exe. GetLastError() = %d\n", lastErr);
            CloseHandle( testEvent );
            return(-1);
        }
        else
        {
            if ( !SetEvent( testEvent ) )
            {
                printf( "SetEvent() failed. GetLastError() = %d\n", GetLastError());
                CloseHandle( testEvent );
                return(-1);
            }
            else
            {
                CloseHandle( testEvent );
            }
        }
        return(0);
    }

    =====================================================
    Process 2 (variant B):
    1. Opens a handle to the named event created by Process 1
    2. Sets the event
    3. Closes the event handle
    4. Exit
    =====================================================
    int main(int argc, char *argv[], char *envp[])
    {
        HANDLE testEvent = NULL; 

        testEvent = OpenEvent( EVENT_MODIFY_STATE, FALSE, TEXT("TEST_EVENT") );
        if ( testEvent == NULL )
        {
            printf("Error, testEvent event was not opened. GetLastError() = %d\n", GetLastError());
            return(-1);
        }
        else
        {
            if ( !SetEvent( testEvent ) )
            {
                printf( "SetEvent() failed. GetLastError() = %d\n", GetLastError());
                CloseHandle( testEvent );
                return(-1);
            }
            else
            {
                CloseHandle( testEvent );
            }
        }
        return(0);
    }



    =============================

    ===== Example Code Case Number 2 =====

    =====================================================
    Process 1: 
    1. Creates a named event, non-signaled, with sharable handle attribute
    2. Creates/starts Process2.exe passing the handle on the command line
    3. Waits for event to be signaled from Process 2
    4. Closes the event handle 
    5. Esit
    =====================================================
    int main(int argc, char *argv[], char *envp[])
    {
        char   SysStr[1024];
        PROCESS_INFORMATION piProcInfo; 
        STARTUPINFO siStartInfo;
        SECURITY_ATTRIBUTES eventSecAttribs;
        BOOL bFuncRetn = FALSE; 
        DWORD lastErr  = 0;

        HANDLE  testEvent = NULL;

        ZeroMemory( SysStr, 1024 );
        ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
        ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
        siStartInfo.cb = sizeof(STARTUPINFO); 

        //
        // See: https://docs.microsoft.com/en-us/windows/win32/procthread/inheritance
        // 
        // We are going to make the handle to this event "inheritable"
        // 
        eventSecAttribs.nLength              = sizeof(SECURITY_ATTRIBUTES);
        eventSecAttribs.lpSecurityDescriptor = NULL;
        eventSecAttribs.bInheritHandle       = TRUE;

        testEvent = CreateEvent( &eventSecAttribs, FALSE, FALSE, TEXT("TEST_EVENT") );
        if ( testEvent == NULL )
        {
            printf("Error: Failed to create test event. Error = %d\n", GetLastError());
            return (-1);
        }

        sprintf(SysStr, "Process2.exe %d", (int)testEvent);

        bFuncRetn = CreateProcess(NULL,          // NULL = Use the module name in SysStr below
                                  SysStr,        // command line 
                                  NULL,          // process security attributes, cannot be inherited 
                                  NULL,          // primary thread security attributes, cannot be inherited 
                                  TRUE,          // handles ARE inherited, needed so the event we created can be inherited! 
                                  0,             // creation flags, normal priority (default) 
                                  NULL,          // use parent's environment 
                                  NULL,          // use parent's current directory 
                                  &siStartInfo,  // STARTUPINFO pointer 
                                  &piProcInfo);  // receives PROCESS_INFORMATION 
                                                
        if ( bFuncRetn == FALSE ) 
        {
            printf("Error: CreateProcess Process2.exe failed. Exiting. Error = %d\n", GetLastError());
            CloseHandle( testEvent );
            return (-1);
        } 
        else
        {
            CloseHandle(piProcInfo.hThread);
        }

        //
        // Wait for 2 seconds to give Process2.exe time to signal Process1.exe here
        //
        switch ( WaitForSingleObject( testEvent, 2000 ) )
        {
            case WAIT_ABANDONED:
                printf("Error: WaitForSingleObject() returned WAIT_ABANDONDED status for testEvent event.\n");
                CloseHandle( testEvent );
                return (-1);
                break;

            case WAIT_TIMEOUT:
                printf("Error: WaitForSingleObject() returned WAIT_TIMEOUT status for testEvent event.\n");
                CloseHandle( testEvent );
                return (-1);
                break;

            case WAIT_FAILED:
                printf("Error: WaitForSingleObject() returned WAIT_FAILED status for testEvent event.\n");
                CloseHandle( testEvent );
                return (-1);
                break;

            case WAIT_OBJECT_0: 
                printf("WaitForSingleObject() returned WAIT_OBJECT_0 status for testEvent event.\n");
                CloseHandle( testEvent );
                break;

            default:
                break;
        }

    return 0;
    }


    =====================================================
    Process 2:
    1. Retrieve the event handle value from the command line
    2. Sets the event
    3. Closes the event handle
    4. Exit
    =====================================================
    int main(int argc, char *argv[], char *envp[])
    {
        HANDLE  testEvent = NULL; 
        WORD    lastErr   = 0;
        char    procName[1024];
        LPTSTR  cmd;

        cmd  = GetCommandLine();
        if ( sscanf(cmd, "%1023s %d", &ProcName[0], (int *)&testEvent) != 2 )
        {
            WriteToErrorFile("Error, testEvent event was not provided, exiting\n");
            return(-1);
        }
      
        if ( !SetEvent( testEvent ) )
        {
            printf( "SetEvent() failed. GetLastError() = %d\n", GetLastError());
            return(-1);
        }

        return(0);
    }

    =============================

    Monday, November 23, 2020 5:24 PM