问题描述
考虑将此小程序编译为 application.exe
Consider this little programm be compiled as application.exe
#include <stdio.h>
int main()
{
char str[100];
printf ("Hello, please type something\n");
scanf("%[^\n]s", &str);
printf("you typed: %s\n", str);
return 0;
}
现在,我使用此代码来启动 application.exe
并获取其输出.
Now I use this code to start application.exe
and fetch its output.
#include <stdio.h>
#include <iostream>
#include <stdexcept>
int main()
{
char buffer[128];
FILE* pipe = popen("application.exe", "r");
while (!feof(pipe)) {
if (fgets(buffer, 128, pipe) != NULL)
printf(buffer);
}
pclose(pipe);
return 0;
}
我的问题是,直到我完成输入后才输出.然后获取两条输出线.我可以通过在第一个 printf
语句之后添加此行来解决此问题.
My problem is that there is no output until I did my input. Then both output lines get fetched.I can workarround this problem by adding this line after the first printf
statement.
fflush(stdout);
然后在我按预期进行输入之前先获取第一行.
Then the first line is fetched before I do my input as expected.
但是但是,如何才能实时"(无法退出)表示获取无法修改并且不使用 fflush()
的应用程序的输出?.Windows cmd是如何做到的?
But how can I fetch output of applications that I cannot modify and that do not use fflush()
in "realtime" (means before they exit)? .And how does the windows cmd do it?
推荐答案
我的原始帖子中的问题已得到很好的解释在其他答案中.
控制台应用程序使用名为 isatty()
的函数来检测如果他们的 stdout
处理程序已连接到管道或真实控制台.如果是管道除非您直接调用 fflush()
,否则所有输出均按块进行缓冲和刷新.如果是真正的控制台,输出将不缓冲,并直接打印到控制台输出.
在Linux中,您可以使用 openpty()
创建一个伪终端并在其中创建您的进程.作为一个结果该进程将认为它在真实终端中运行并且使用无缓冲输出.
Windows似乎没有这样的选择.
在仔细阅读winapi文档后,我发现这是不正确.其实你可以创造您自己的控制台屏幕缓冲区,并将其用于您的进程的 stdout
,然后将对其进行缓冲.
遗憾的是,这不是一个非常舒适的解决方案,因为没有事件处理程序,我们需要轮询新数据.另外,目前我不确定在此屏幕缓冲区已满时如何处理滚动.
但是即使仍然存在一些问题离开后,我认为我为那些想获取无缓冲(且不受干扰)的人创建了一个非常有用(且有趣)的起点Windows控制台进程输出.
The problems of my question in my original post are already very good explainedin the other answers.
Console applications use a function named isatty()
to detectif their stdout
handler is connected to a pipe or a real console. In case of a pipeall output is buffered and flushed in chunks except if you directly call fflush()
.In case of a real console the output is unbuffered and gets directly printed to theconsole output.
In Linux you can use openpty()
to create a pseudoterminal and create your process in it. As aresult the process will think it runs in a real terminal and uses unbuffered output.
Windows seems not to havesuch an option.
After a lot of digging through winapi documentation I found that this is not true. Actually you can createyour own console screen buffer and use it for stdout
of your process that will be unbuffered then.
Sadly this is not a very comfortable solution because there are no event handler and we need to poll for new data.Also at the moment I'm not sure how to handle scrolling when this screen buffer is full.
But even if there are still some problemsleft I think I have created a very useful (and interesting) starting point for those of you who ever wanted to fetch unbuffered (and unflushed)windows console process output.
#include <windows.h>
#include <stdio.h>
int main(int argc, char* argv[])
{
char cmdline[] = "application.exe"; // process command
HANDLE scrBuff; // our virtual screen buffer
CONSOLE_SCREEN_BUFFER_INFO scrBuffInfo; // state of the screen buffer
// like actual cursor position
COORD scrBuffSize = {80, 25}; // size in chars of our screen buffer
SECURITY_ATTRIBUTES sa; // security attributes
PROCESS_INFORMATION procInfo; // process information
STARTUPINFO startInfo; // process start parameters
DWORD procExitCode; // state of process (still alive)
DWORD NumberOfCharsWritten; // output of fill screen buffer func
COORD pos = {0, 0}; // scr buff pos of data we have consumed
bool quit = false; // flag for reading loop
// 1) Create a screen buffer, set size and clear
sa.nLength = sizeof(sa);
scrBuff = CreateConsoleScreenBuffer( GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
&sa, CONSOLE_TEXTMODE_BUFFER, NULL);
SetConsoleScreenBufferSize(scrBuff, scrBuffSize);
// clear the screen buffer
FillConsoleOutputCharacter(scrBuff, '\0', scrBuffSize.X * scrBuffSize.Y,
pos, &NumberOfCharsWritten);
// 2) Create and start a process
// [using our screen buffer as stdout]
ZeroMemory(&procInfo, sizeof(PROCESS_INFORMATION));
ZeroMemory(&startInfo, sizeof(STARTUPINFO));
startInfo.cb = sizeof(STARTUPINFO);
startInfo.hStdOutput = scrBuff;
startInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
startInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
startInfo.dwFlags |= STARTF_USESTDHANDLES;
CreateProcess(NULL, cmdline, NULL, NULL, FALSE,
0, NULL, NULL, &startInfo, &procInfo);
CloseHandle(procInfo.hThread);
// 3) Read from our screen buffer while process is alive
while(!quit)
{
// check if process is still alive or we could quit reading
GetExitCodeProcess(procInfo.hProcess, &procExitCode);
if(procExitCode != STILL_ACTIVE) quit = true;
// get actual state of screen buffer
GetConsoleScreenBufferInfo(scrBuff, &scrBuffInfo);
// check if screen buffer cursor moved since
// last time means new output was written
if (pos.X != scrBuffInfo.dwCursorPosition.X ||
pos.Y != scrBuffInfo.dwCursorPosition.Y)
{
// Get new content of screen buffer
// [ calc len from pos to cursor pos:
// (curY - posY) * lineWidth + (curX - posX) ]
DWORD len = (scrBuffInfo.dwCursorPosition.Y - pos.Y)
* scrBuffInfo.dwSize.X
+(scrBuffInfo.dwCursorPosition.X - pos.X);
char buffer[len];
ReadConsoleOutputCharacter(scrBuff, buffer, len, pos, &len);
// Print new content
// [ there is no newline, unused space is filled with '\0'
// so we read char by char and if it is '\0' we do
// new line and forward to next real char ]
for(int i = 0; i < len; i++)
{
if(buffer[i] != '\0') printf("%c",buffer[i]);
else
{
printf("\n");
while((i + 1) < len && buffer[i + 1] == '\0')i++;
}
}
// Save new position of already consumed data
pos = scrBuffInfo.dwCursorPosition;
}
// no new output so sleep a bit before next check
else Sleep(100);
}
// 4) Cleanup and end
CloseHandle(scrBuff);
CloseHandle(procInfo.hProcess);
return 0;
}
这篇关于如何读取尚未刷新的进程输出?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!