禁用重定向 stdout 管道上的缓冲(Win32 API、C++)

发布于 2024-09-12 02:02:56 字数 526 浏览 5 评论 0原文

我使用 CreateProcess 从 Win32 生成一个进程,将 STARTUPINFOhStdOutputhStdError 属性设置为管道句柄使用CreatePipe创建。我有两个线程正在读取管道,等待数据可用(或进程完成,此时它会检查在终止线程之前是否没有剩余数据)。
当数据可用时,我将输出有效地写到一个大文本框中。

发生的情况是输出正在缓冲,因此运行缓慢的进程只会获取在文本框中抛出的数据块,而不是“当它发生时”。

我不确定是否是管道在进行缓冲,或者与重定向有关。

有没有办法将管道设置为无缓冲,或者以尽快发送标准输出的方式启动进程?

我正在使用一个测试应用程序进行测试,该应用程序每隔一秒打印一行

Here is line one
(waits one second)
Here is line two
(waits one second)
... etc

I'm spawning a process from Win32 using CreateProcess, setting the hStdOutput and hStdError properties of STARTUPINFO to pipe handles created with CreatePipe. I've got two threads reading the pipes, waiting for data to become available (or the process to complete, at which point it checks that there is no data left before terminating the thread).
As data becomes available, I write the output out to effectively a big textbox.

What's happening is the output is being buffered, so a slow running process just gets chunks of data thrown at the text box, but not "as it happens".

I'm not sure if it's the pipe that's doing the buffering, or something to do with the redirection.

Is there any way to either set the pipe to be unbuffered, or start the process in such a way that the stdout is sent as soon as possible?

I'm testing with a test app that prints lines one second apart

Here is line one
(waits one second)
Here is line two
(waits one second)
... etc

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

鹤仙姿 2024-09-19 02:02:56

缓冲可能在 C 运行时(printf 等)中,您对此无能为力(IIRC 它会执行 isatty() 检查以确定缓冲策略)

The buffering is probably in the C runtime (printf etc) and there is not much you can do about it (IIRC it does a isatty() check to determine a buffering strategy)

原来是傀儡 2024-09-19 02:02:56

就我而言,缓冲位于客户端的输出中(如 @Anders 所写),它使用普通的 printf。也许这也取决于 C 运行时 (Visual Studio 2019) 的实现,也许运行时检测到“不是控制台”并启用缓冲。

所以我通过这个调用禁用了缓冲
setvbuf(stdout, (char*)NULL, _IONBF, 0); 在我的客户端中,现在我立即在服务器的管道中获得输出。

只是为了完整性:这是我读取服务器中管道的方式

HANDLE in;
CreatePipe(&in, &startup.hStdOutput, &sa, 0);            // Pipe for stdout of the child

...

char buffer[16384];
DWORD read, total;
while (PeekNamedPipe(in, NULL, 0, &read, &total, NULL))
{
    if (total == 0)
    {
        if (WaitForSingleObject(info.hProcess, 0) == WAIT_OBJECT_0)
        {
            if (PeekNamedPipe(in, NULL, 0, &read, &total, NULL) && total == 0)
                break;
            continue;
        }
        Sleep(10);
        continue;
    }

    if (total > sizeof(buffer))
        total = sizeof(buffer);
    ReadFile(in, buffer, total, &read, NULL);

    ...
}

In my case the buffering was in the output of the client (as @Anders wrote), which uses normal printf. Maybe this also depends on the implementation of the C runtime (Visual Studio 2019), maybe the runtime detects 'not a console' and enables buffering.

So I disabled the buffering with this call
setvbuf(stdout, (char*)NULL, _IONBF, 0); in my client, now I get the output immediately in the pipe in the server.

Just for completeness: Here's how I read the pipe in the server

HANDLE in;
CreatePipe(&in, &startup.hStdOutput, &sa, 0);            // Pipe for stdout of the child

...

char buffer[16384];
DWORD read, total;
while (PeekNamedPipe(in, NULL, 0, &read, &total, NULL))
{
    if (total == 0)
    {
        if (WaitForSingleObject(info.hProcess, 0) == WAIT_OBJECT_0)
        {
            if (PeekNamedPipe(in, NULL, 0, &read, &total, NULL) && total == 0)
                break;
            continue;
        }
        Sleep(10);
        continue;
    }

    if (total > sizeof(buffer))
        total = sizeof(buffer);
    ReadFile(in, buffer, total, &read, NULL);

    ...
}

稚气少女 2024-09-19 02:02:56

SetNamedPipeHandleState,但它只控制远程管道的缓冲,而不是当两端都在同一台计算机上。

There's SetNamedPipeHandleState, but it only controls buffering for remote pipes, not when both ends are on the same computer.

時窥 2024-09-19 02:02:56

在我看来,如果将 STARTUPINFOhStdOutputhStdError 设置为管道句柄,就可以解决问题使用 CreatePipe 创建,而不是创建一个命名管道(使用 CallNamedPipe 函数,与之前使用的 CallNamedPipe 函数完全相同,如果之前还使用 SECURITY_ATTRIBUTES 和 bInheritHandle = TRUE,请参阅 http://msdn.microsoft。 com/en-us/library/aa365782.aspx),然后使用 FILE_FLAG_WRITE_THROUGH 标志根据 CreateFile 的名称打开那里。就像您可以在 MSDN (http://msdn.microsoft.com/ en-us/library/aa365592.aspx):

管道客户端可以使用CreateFile来
通过指定启用重叠模式
FILE_FLAG_OVERLAPPED 或启用
通过指定直写模式
FILE_FLAG_WRITE_THROUGH。

因此,只需使用 FILE_FLAG_WRITE_THROUGH 标志重新打开与 CreateFile 相关的管道,并将句柄设置为 hStdOutputhStdError STARTUPINFO

It seems to me you can solve the problem if you set the hStdOutput and hStdError of STARTUPINFO not to pipe handles created with CreatePipe, but instead of that you create a named pipes (with CallNamedPipe function exactly like you used if before also using SECURITY_ATTRIBUTES with bInheritHandle = TRUE, see http://msdn.microsoft.com/en-us/library/aa365782.aspx) and then open there by name with respect of CreateFile using FILE_FLAG_WRITE_THROUGH flag. Like you can read on the MSDN (http://msdn.microsoft.com/en-us/library/aa365592.aspx):

The pipe client can use CreateFile to
enable overlapped mode by specifying
FILE_FLAG_OVERLAPPED or to enable
write-through mode by specifying
FILE_FLAG_WRITE_THROUGH.

So just reopen the pipe with respect of CreateFile using FILE_FLAG_WRITE_THROUGH flag and set the handle/handles to hStdOutput and hStdError of STARTUPINFO.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文