Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Named Pipe CreateFile() returns INVALID_HANDLE_VALUE, and GetLastError() returns ERROR_PIPE_BUSY

I have written a class to handle named pipe connections, and if I create an instance, close it, and then try to create another instance the call to CreateFile() returns INVALID_HANDLE_VALUE, and GetLastError() returns ERROR_PIPE_BUSY. What's going on here? What can I do to insure the call to Connect() succeeds?

PipeAsync A, B;

A.Connect("\\\\.\\pipe\\test",5000);
A.Close();

cout << GetLastError(); // some random value
B.Connect("\\\\.\\pipe\\test",5000);
cout << GetLastError(); // 231 (ERROR_PIPE_BUSY)
B.Close(); 

Here are my implementations of Connect() and Close()

BOOL PipeAsync::Connect(LPCSTR pszPipeName, DWORD dwTimeout)
{
    this->pszPipeName = pszPipeName;
    this->fExisting = TRUE;
    DWORD dwMode = this->fMessageMode ? PIPE_READMODE_MESSAGE : PIPE_READMODE_BYTE;

    hPipe = CreateFile(
        this->pszPipeName,
        GENERIC_READ | GENERIC_WRITE,
        0,
        NULL,
        OPEN_EXISTING,
        FILE_FLAG_OVERLAPPED,
        NULL);

    if( INVALID_HANDLE_VALUE == hPipe )
        return FALSE; /* set break point here ; breaks here on second call to Connect() */

    if( GetLastError() == ERROR_PIPE_BUSY )
        if(!WaitNamedPipe( this->pszPipeName, dwTimeout ))
            return FALSE; /* set break point here */

    if( !SetNamedPipeHandleState( hPipe, &dwMode, NULL, NULL ) )
        return FALSE; /* set break point here */

    return TRUE;

}

VOID PipeAsync::Close()
{

    if( fExisting )
        DisconnectNamedPipe( hPipe );

    CloseHandle( hPipe );
}



EDIT: I forgot to tell you how I concluded this... I set break points indicated in the comments. When run, it stops on the first break point.

EDIT: This is my updated code

if( INVALID_HANDLE_VALUE == hPipe )
    if( GetLastError() == ERROR_PIPE_BUSY )
    {
        if(!WaitNamedPipe( this->pszPipeName, dwTimeout ))
            return FALSE; /* break-point: breaks here on second call */
    }
    else
        return FALSE; /* break-point /*

Now, WaitNamedPipe() is returning false on the second call to Connect() and GetLastError() is returning 2, or ERROR_FILE_NOT_FOUND ?

like image 815
Drew Chapin Avatar asked Dec 16 '11 02:12

Drew Chapin


1 Answers

From Named Pipe Client:

If the pipe exists but all of its instances are busy, CreateFile returns INVALID_HANDLE_VALUE and the GetLastError function returns ERROR_PIPE_BUSY. When this happens, the named pipe client uses the WaitNamedPipe function to wait for an instance of the named pipe to become available.

The link has example code on coping with ERROR_PIPE_BUSY.

EDIT:

Small compilable example that demonstrates accepting and connecting on a named pipe:

const char* const PIPE_NAME = "\\\\.\\pipe\\test";
const int MAX_CONNECTIONS   = 10;

void client_main()
{
    DWORD last_error;
    unsigned int elapsed_seconds       = 0;
    const unsigned int timeout_seconds = 5;

    HANDLE handle = CreateFile(PIPE_NAME,
                               GENERIC_READ | GENERIC_WRITE,
                               0,
                               0,
                               OPEN_EXISTING,
                               FILE_ATTRIBUTE_NORMAL,
                               0);

    while (INVALID_HANDLE_VALUE == handle &&
           elapsed_seconds < timeout_seconds)
    {
        last_error = GetLastError();

        if (last_error != ERROR_PIPE_BUSY)
        {
            break;
        }

        Sleep(1 * 1000);
        elapsed_seconds++;

        handle = CreateFile(PIPE_NAME,
                            GENERIC_READ | GENERIC_WRITE,
                            0,
                            0,
                            OPEN_EXISTING,
                            FILE_ATTRIBUTE_NORMAL,
                            0);
    }

    if (INVALID_HANDLE_VALUE == handle)
    {
        std::cerr << "Failed to connect to pipe " << PIPE_NAME <<
            ": last_error=" << last_error << "\n";
    }
    else
    {
        std::cout << "Connected to pipe " << PIPE_NAME << "\n";
        CloseHandle(handle);
    }
}

HANDLE _get_server_handle()
{
    // Error handling omitted for security descriptor creation.
    SECURITY_DESCRIPTOR sd;
    InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
    SetSecurityDescriptorDacl(&sd, TRUE, static_cast<PACL>(0), FALSE);

    SECURITY_ATTRIBUTES sa;
    sa.nLength              = sizeof(sa);
    sa.lpSecurityDescriptor = &sd;
    sa.bInheritHandle       = FALSE;

    // Create a bi-directional message pipe.
    HANDLE handle = CreateNamedPipe(PIPE_NAME,
                                    PIPE_ACCESS_DUPLEX,
                                    PIPE_TYPE_MESSAGE       |
                                      PIPE_READMODE_MESSAGE |
                                      PIPE_NOWAIT,
                                    PIPE_UNLIMITED_INSTANCES,
                                    4096,
                                    4096,
                                    0,
                                    &sa);

    if (INVALID_HANDLE_VALUE == handle)
    {
        std::cerr << "Failed to create named pipe handle: last_error=" <<
            GetLastError() << "\n";
    }

    return handle;
}

void server_main()
{
    HANDLE handle = _get_server_handle();

    if (INVALID_HANDLE_VALUE != handle)
    {
        int count = 0;
        while (count < MAX_CONNECTIONS)
        {
            BOOL result = ConnectNamedPipe(handle, 0);

            const DWORD last_error = GetLastError();

            if (ERROR_NO_DATA == last_error)
            {
                count++;
                std::cout << "A client connected and disconnected: count=" <<
                    count << "\n";
                CloseHandle(handle);
                handle = _get_server_handle();
            }
            else if (ERROR_PIPE_CONNECTED == last_error)
            {
                count++;
                std::cout << "A client connected before call to " <<
                    "ConnectNamedPipe(): count=" << count << "\n";
                CloseHandle(handle);
                handle = _get_server_handle();
            }
            else if (ERROR_PIPE_LISTENING != last_error)
            {
                std::cerr << "Failed to wait for connection: last_error=" <<
                    GetLastError() << "\n";
                CloseHandle(handle);
                break;
            }
            Sleep(100);
        }
    }
}

int main(int a_argc, char** a_argv)
{
    if (2 == a_argc)
    {
        if (std::string("client") == *(a_argv + 1))
        {
            for (int i = 0; i < MAX_CONNECTIONS; i++)
            {
                client_main();
            }
        }
        else if (std::string("server") == *(a_argv + 1))
        {
            server_main();
        }
    }
    return 0;
}

Execute server-side first:

pipetest.exe server

Then execute client-side:

pipetest.exe client

I could not tell what the problem was from the posted code. Hopefully this small example will assist you in finding the issue.

like image 80
hmjd Avatar answered Oct 05 '22 11:10

hmjd