Search code examples
c++named-pipesunreal-engine4

Named pipe sometimes doesn't work


So I wrote a C++ application that connects to an Unreal Engine 4 game through a named pipe. 95% of the time it works perfectly, but sometimes it doesn't seem to connect properly. It is very random so it is hard for me to find the problem that is causing this. The Server is created in my application and the Client is created in the UE4 game. And sometimes the UE4 game doesn't connect and displays error 121:

//
// MessageId: ERROR_SEM_TIMEOUT
//
// MessageText:
//
// The semaphore timeout period has expired.
//
#define ERROR_SEM_TIMEOUT                121L

As I said the problem occurs very randomly and I can't seem to find a specific reason that could cause the problem. The pipe is successfully created, I can see this in windows powershell (get-childitem \.\pipe).

I was thinking maybe it has something todo with the pipe settings I use?

This is my code for creating the pipe server:

    DWORD erPipeServer::CreatePipeServer() {

    // create a SECURITY_ATTRIBUTES structure.
    if (!CreatePipeSecurity(&pSa))
    {
        dwError = GetLastError();
        //wprintf(L"CreatePipeSecurity failed w/err 0x%08lx\n", dwError);
        Cleanup();
        return dwError;
    }

    // Create the named pipe.
    hNamedPipe = CreateNamedPipe(
        pipename,             // Pipe name.
        PIPE_ACCESS_DUPLEX,         // The pipe is duplex; both server and 
                                    // client processes can read from and 
                                    // write to the pipe
        PIPE_TYPE_MESSAGE |         // Message type pipe 
        PIPE_READMODE_MESSAGE |     // Message-read mode 
        PIPE_NOWAIT,                  // Blocking mode is enabled
        PIPE_UNLIMITED_INSTANCES,   // Max. instances
        BUFFER_SIZE,                // Output buffer size in bytes
        BUFFER_SIZE,                // Input buffer size in bytes
        NMPWAIT_WAIT_FOREVER,   // Time-out interval
        pSa                         // Security attributes
        );

    if (hNamedPipe == INVALID_HANDLE_VALUE)
    {
        dwError = GetLastError();
        //wprintf(L"Unable to create named pipe w/err 0x%08lx\n", dwError);
        Cleanup();
        return dwError;
    }

    //wprintf(L"The named pipe (%s) is created.\n", pipename);
    return dwError;
}

And this is my code for creating the client in Unreal Engine 4:

    // Try to open the named pipe identified by the pipe name.
        while (true)
        {
            hPipe = CreateFile(
                FULL_PIPE_NAME,                 // Pipe name 
                GENERIC_READ | GENERIC_WRITE,   // Read and write access
                0,                              // No sharing 
                NULL,                           // Default security attributes
                OPEN_ALWAYS,                  // Opens existing pipe
                0,                              // Default attributes
                NULL                            // No template file
                );

            // If the pipe handle is opened successfully ...
            if (hPipe != INVALID_HANDLE_VALUE)
            {
                GEngine->AddOnScreenDebugMessage(-1, 3.f, FColor::Green, FString::Printf(TEXT("The named pipe %d is connected."), FULL_PIPE_NAME));
                break;
            }
            dwError = GetLastError();

            // Exit if an error other than ERROR_PIPE_BUSY occurs.
            if (ERROR_PIPE_BUSY != dwError)
            {
                GEngine->AddOnScreenDebugMessage(-1, 3.f, FColor::Red, FString::Printf(TEXT("Unable to open named pipe ------ %d"),dwError));
                goto Cleanup;
            }

            // All pipe instances are busy, so wait for 5 seconds.
            if (!WaitNamedPipe(FULL_PIPE_NAME, 5000))
            {
                dwError = GetLastError();
                GEngine->AddOnScreenDebugMessage(-1, 3.f, FColor::Red, FString::Printf(TEXT("Could not open pipe: 5 second wait timed out. %d"),dwError));
**THE 121 ERROR OCCURED HERE^^**
                goto Cleanup;
            }
        }

Could it be a problem with the pipe settings or something? I do not understand why it works almost all the time, but sometimes not with no clear reason why or when...

Thanks for any help in advance!


Solution

  • Ok, so I think I fixed the problem, maybe not in the best way but it seems to work good enough for my purpose.

    After managing to reproduce the problem, I started working with the idea that the Handle could be open or busy ( Thanks to Karsten!!). I could bug the game into showing the 121 error by using windows powershell and running \.\pipe\name (where name is the pipes name). This would open the pipe and the game could not connect anymore, displaying error 121.

    How I "fixed" it: Recreating the pipe when no connection is made after a second. Normally in my application when the server is connected the client is already ready. So connection should be immediately. When it isn't, now the pipe is recreated after a second and then it will work. Off-course this is not a clean fix, but I have no idea how the handle normally can open, because normally the only application trying to connect to the pipe is the game...

    But anyway it is a decent work-around for the 1 in 30 times the problem occured for (still) some strange reason...

    Any other ideas would be appreciated but this is working for now I think :)