使用 pipe() 和 fdopen() 将数据从 Python 脚本传递到 Windows 中的 C++ 应用程序

Posted

技术标签:

【中文标题】使用 pipe() 和 fdopen() 将数据从 Python 脚本传递到 Windows 中的 C++ 应用程序【英文标题】:Use pipe() and fdopen() to pass data from Python script to C++ application in Windows 【发布时间】:2019-11-06 18:04:37 【问题描述】:

我们有一些 Linux/macOS 应用程序,它可以通过传递文件描述符并从中读取数据来与外部世界通信。通常这样做是为了传递标准输入/标准输出描述符,但是我们使用 pipe() 并且效果很好。除了 MinGW/Windows。 在 Windows 下做同样工作的推荐方法是什么?传递整个文件句柄,或者有什么好方法可以模拟 small-int-like 描述符?

【问题讨论】:

您的 C++ 应用程序使用 pipefdopen?如果是这样,在 Windows 中,您可以使用 CreateProcess 并使用标准输入、标准输出和标准错误的文件句柄初始化 STARTUPINFO 结构。 @TedLyngmo 实际上 C++ 应用程序使用 fdopen() 并且 Python 脚本向它提供文件描述符编号,通过 os.pipe() 调用获得。所以 stdin/stdout/sterr 方法不会按预期方式工作。 啊...假设 python 端没问题,Windows _fdopen 可以工作吗?或者,您甚至无法访问文件描述符? TedLyngmo:我从 Python 收到 fd = 3,并尝试使用 EBADF 打开它失败。 @ErykSun 感谢您提供详细信息!我们使用 Python 2.7。 @ErykSun 再次感谢。找到了寻找解释的方向并克服了这一点,这说明了很多:bugs.python.org/issue32865 【参考方案1】:

在 Windows 中,C 文件描述符在进程STARTUPINFO 中被继承,记录在保留字段cbReserved2lpReserved2 中。该协议未记录,但源代码与 Visual C++ 一起分发。使用此功能的 C 函数包括 _[w]spawn 系列函数和 [_w]system。 (不过,[_w]system 函数在这方面通常没有用处,因为只有直接的 cmd.exe 进程会继承父进程的文件描述符。CMD 不会将它们传递给子进程。)

在 Python 2.7 中,os.pipe 是通过调用 CreatePipe 实现的,它为管道的读取和写入端返回不可继承的文件句柄。然后通过_open_osfhandle 使用可继承的文件描述符手动包装这些句柄,但底层操作系统句柄仍然是不可继承的。要解决此问题,请通过 os.dup 复制文件描述符,这会在内部复制可继承的文件句柄,然后关闭源文件描述符。例如:

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

Python 的subprocess 模块通常是创建子进程的首选方式。但是,在这种情况下我们不能使用subprocess,因为它不支持通过STARTUPINFO (*) 继承文件描述符。下面是一个使用os.spawnv 的示例:

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

(*) Windows Python 内部使用 C 运行时文件 API(例如 _wopen_read_write)是一个尴尬的情况,但在某些地方不支持 C 文件描述符。它应该咬紧牙关,直接使用 Windows 文件 API 与 OS 文件句柄,那么它至少会是一致的。

【讨论】:

使用这种方法工作的托管代码。问题的另一部分是 Python,使用 MSYS/MinGW 编译 - 它根本不起作用,而 Windows Python 安装运行良好。 MSYS/MinGW 版本的 lpReserved2 有不同的数据。

以上是关于使用 pipe() 和 fdopen() 将数据从 Python 脚本传递到 Windows 中的 C++ 应用程序的主要内容,如果未能解决你的问题,请参考以下文章

11NIO--管道Pipe

使用 io.Pipe 从 exec.command 获取输出以进行 http post

Angular管道PIPE介绍

5.管道 Pipe

NIO之管道 (Pipe)

Java NIO 管道 (Pipe)