0

I have a function in which I call _open a few times.

If _popen returns NULL do I need to call _pclose before the function returns?

I've marked 3 locations where I think _pclose might need to be called.

At which of these locations must I call _pclose?

bool theFunction()
{
    FILE* pPipe;
    char buffer[1000];
    if( (pPipe = _popen("dir", "rt")) == NULL )
    {
        //location 1
        _pclose(pPipe);
        return false;
    }

    while(fgets(pipeBuffer, maxBufferSize, pPipe))
    {
        printf(pipeBuffer);
    }

    if( (pPipe = _popen("cls", "rt")) == NULL )
    {
        //location 2
        _pclose(pPipe);
        return false;
    }

    //location 3
    _pclose(pPipe);

    return true;
}
user3731622
  • 4,844
  • 8
  • 45
  • 84

2 Answers2

1

Simple: close the pipe if you could open it but no longer need it. So:

bool theFunction()
{
    FILE* pPipe;
    char buffer[1000];
    if( (pPipe = _popen("dir", "rt")) == NULL )
    {
        return false;
    }

    while(fgets(pipeBuffer, maxBufferSize, pPipe))
    {
        printf(pipeBuffer);
    }

    // The fact that you have to close it here in the middle of nowhere
    // should ring a bell that you need to think about separation of concern 
    _pclose(pPipe);

    if( (pPipe = _popen("cls", "rt")) == NULL )
    {
        return false;
    }

    _pclose(pPipe);
    return true;
}
huysentruitw
  • 27,376
  • 9
  • 90
  • 133
1

If you successfully create a pipe with popen but do not call pclose, then the memory occupied by the FILE object is not released. Worse, there are externally visible consequences. The child process which was created by popen may linger around. When you popen, a process is created with fork. The corresponding waitpid might not happen until pclose is called. (I believe that that is a typical, obvious implementation, and it is how I have implemented popen-like functions for other programming languages.)

Although Win32 doesn't have fork and wait, there is likely a similar resource issue in Microsoft C Library's _popen. The FILE pipe handle probably has an internal Win32 handle to a process, which is not subject to a CloseHandle until _pclose is invoked. Plus other resources like Win32 pipes which communicate with that process. If you don't close the pipe, then you leak these resources.

About passing a null pointer. This is a no-no with the original POSIX function. The behavior is not defined if pclose is invoked on a null pointer. POSIX says that "[i]f the argument stream to pclose() is not a pointer to a stream created by popen(), the result of pclose() is undefined." (A null pointer isn't a pointer to a stream, even if it was returned by popen).

Microsoft allows _pclose to be called with a null pointer. This is documented in MSDN and the behavior is that _pclose returns -1 and sets the errno pseudo-variable to EINVAL.

That's something to consider if you ever want to port code that is based on these functions.

Kaz
  • 55,781
  • 9
  • 100
  • 149