1

We have some Linux/macOS application, which can communicate with outer world by passing file descriptor and reading data from it. Usually this is done to pass stdin/stdout descriptors, however we use pipe() and this works pretty well. Except MinGW/Windows. What would be the recommended way of doing the same job under the Windows? Pass the whole file handle, or there are good ways to simulate small-int-like descriptor?

6
  • Your C++ application uses pipe and fdopen? If so, in Windows you can use CreateProcess and initialize the STARTUPINFO struct with file handles for stdin, stdout and stderr. Commented Nov 6, 2019 at 18:34
  • @TedLyngmo actually C++ application uses fdopen() and Python script feeds it with file descriptor number, obtained by os.pipe() call. So stdin/stdout/sterr approach will not work the desired way. Commented Nov 6, 2019 at 20:15
  • Ah... assuming the python side is ok, would the Windows _fdopen work? Or, you don't even get access to the file descriptor? Commented Nov 6, 2019 at 20:21
  • TedLyngmo: I receive fd = 3 from the Python, and attempt to open it fails with EBADF. @ErykSun thanks for the details! We use Python 2.7. Commented Nov 11, 2019 at 15:59
  • @ErykSun thanks again. Got a direction to search for explanation and got over this, which tells a lot: bugs.python.org/issue32865 Commented Nov 12, 2019 at 18:11

1 Answer 1

1

In Windows, C file descriptors are inherited in the process STARTUPINFO record in the reserved fields cbReserved2 and lpReserved2. The protocol is undocumented, but the source is distributed with Visual C++. C functions that use this feature include the _[w]spawn family of functions and [_w]system. (The [_w]system function, however, is not generally useful in this regard because only the immediate cmd.exe process inherits the parent's file descriptors. CMD does not pass them on to child processes.)

In Python 2.7, os.pipe is implemented by calling CreatePipe, which returns non-inheritable file handles for the read and write ends of the pipe. These handles are then manually wrapped with inheritable file descriptors via _open_osfhandle, but the underlying OS handle is still non-inheritable. To work around this, duplicate the file descriptor via os.dup, which internally duplicates an inheritable file handle, and then close the source file descriptor. For example:

pread, pwrite = os.pipe()
pwrite_child = os.dup(pwrite)
os.close(pwrite)

Python's subprocess module is usually the preferred way to create a child process. However, we can't use subprocess in this case because it doesn't support inheriting file descriptors via STARTUPINFO (*). Here's an example that uses os.spawnv instead:

rc = os.spawnv(os.P_WAIT, 'path/to/spam.exe', ['spam', 'arg1', 'arg2'])

(*) It's an awkward situation that Windows Python internally uses the C runtime file API (e.g. _wopen, _read, _write), but in places fails to support C file descriptors. It should bite the bullet and use the Windows file API directly with OS file handles, then it would at least be consistent.

Sign up to request clarification or add additional context in comments.

1 Comment

Managed code to work using this approach. Additional part of problem was Python, compiled with MSYS/MinGW - it didn't work at all, while Windows Python installation worked well. lpReserved2 of MSYS/MinGW version had different data.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.