我已经制作了一个程序,如果它从
cmd
运行,它会附加到控制台,创建一个子进程,并将子进程的输出重定向到控制台。
程序运行良好,但有一个烦人的问题:由于某种原因,当我从 Windows 终端运行我的程序时,显示当前目录的行显示在程序输出之前:
程序本身在输出后关闭(可执行文件在任务管理器中不可见)。
在我按下 Enter 后(其他键都不起作用,我什至可以输入批处理命令并且它们起作用,例如
echo hello
并且它会显示 hello
):
程序如下:
#include <future>
#include <Windows.h>
#include <fcntl.h>
#include <io.h>
#include <iostream>
// help binding IOs to the attached console
void bindCrtHandleToStdHandle()
{
FILE* dummyFile;
freopen_s(&dummyFile, "nul", "w", stdout);
// Redirect unbuffered stdout to the current standard output handle
HANDLE stdHandle = GetStdHandle(STD_OUTPUT_HANDLE);
if (stdHandle != INVALID_HANDLE_VALUE)
{
int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT);
if (fileDescriptor != -1)
{
FILE* file = _fdopen(fileDescriptor, "w");
if (file != NULL)
{
int dup2Result = _dup2(_fileno(file), _fileno(stdout));
if (dup2Result == 0)
{
setvbuf(stdout, NULL, _IONBF, 0);
}
}
}
}
std::cout.clear();
}
void readOutput(HANDLE hStdoutRead, std::atomic<bool>& exit) {
const int bufferSize = 4096;
char buffer[bufferSize];
DWORD bytesRead;
while (!exit) {
// Check if there is data available to read from the pipe
bool peekSuccess = PeekNamedPipe(hStdoutRead, NULL, 0, NULL, &bytesRead, NULL);
if (!peekSuccess || bytesRead == 0) {
// No data available, sleep for a short time before checking again
std::this_thread::sleep_for(std::chrono::milliseconds(10));
continue;
}
bool readSuccess = ReadFile(hStdoutRead, buffer, bufferSize, &bytesRead, NULL);
if (readSuccess && bytesRead > 0) {
std::cout.write(buffer, bytesRead);
}
}
}
int APIENTRY wWinMain(
_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nShowCmd
)
{
// Attach to the console is run from cmd to redirect stdout from the child process
bool runFromConsole = AttachConsole(ATTACH_PARENT_PROCESS);
if (runFromConsole) {
bindCrtHandleToStdHandle();
}
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
HANDLE hStdoutRead, hStdoutWrite;
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
if (!CreatePipe(&hStdoutRead, &hStdoutWrite, &saAttr, 0)) {
return EXIT_FAILURE;
}
// Auto close the handles
std::unique_ptr<void, decltype(&CloseHandle)> stdoutReadHandle(hStdoutRead, &CloseHandle);
std::unique_ptr<void, decltype(&CloseHandle)> stdoutWriteHandle(hStdoutWrite, &CloseHandle);
// Ensure the read handle to the pipe for stdout is not inherited.
if (!SetHandleInformation(hStdoutRead, HANDLE_FLAG_INHERIT, 0)) {
return EXIT_FAILURE;
}
si.hStdOutput = hStdoutWrite;
si.dwFlags |= STARTF_USESTDHANDLES;
std::wstring commandLine = L"hello.exe";
if (!CreateProcessW(
NULL,
&commandLine[0],
NULL,
NULL,
TRUE,
CREATE_UNICODE_ENVIRONMENT,
NULL,
NULL,
&si,
&pi)
)
{
return EXIT_FAILURE;
}
// Launch the threads to read from the pipes
std::atomic<bool> exit(false);
std::thread outputThread(readOutput, hStdoutRead, std::ref(exit));
// Wait for the process to finish
WaitForSingleObject(pi.hProcess, INFINITE);
exit = true;
// Wait for the threads to finish
outputThread.join();
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return EXIT_SUCCESS;
}
hello.exe
测试程序:
int main()
{
std::cout << "HELLO WORLD!" << std::endl;
return 0;
}
此外,我注意到如果我使用
program.exe > out.txt & pause
从批处理脚本调用我的程序,输出不会重定向到文件,而是在控制台中可见。但是,如果我在现有终端中执行program.exe > out.txt
,则文件或终端中没有输出。
对于某些上下文,我的程序的作用是修改环境变量并将它们传递给另一个程序,它可以是控制台应用程序或 GUI 应用程序。这就是为什么我需要
wWinMain()
并连接到控制台(如果有的话)的原因。
有什么想法吗?