到目前为止,我找到的最佳解决方案是有两个可执行文件。
program.exe
是GUI应用程序。
program.com
是一个辅助命令行应用程序,它会生成 program.exe
并将标准I / O传递给它。(它不是来自DOS的COM可执行文件,它只是一个重命名的标准PE可执行文件; 由于 .com
在 cmd.exe
的默认优先级顺序中排在 .exe
之前,如果这两者都在路径中,您可以键入 program
,它将自动调用 program.com
而不是 program.exe
。)
通过这种设置,您可以在 Windows 命令提示符下键入 program
,在 program.exe
中写入标准输出,它将正确地出现在控制台上;当您从 GUI 打开 program.exe
时,不会产生控制台窗口。
以下是一个辅助程序的示例实现,取自 Inkscape:http://bazaar.launchpad.net/~inkscape.dev/inkscape/trunk/view/head:/src/winconsole.cpp
该辅助程序创建三个管道,并使用 CreateProcess
生成GUI程序,将其适当端的管道给它。然后它创建三个线程,在无限循环中在管道和辅助程序的标准I / O之间复制数据。该辅助程序编译为控制台应用程序(重要)- 在MinGW中使用 -mconsole
开关。
#ifdef WIN32
#undef DATADIR
#include <windows.h>
struct echo_thread_info {
HANDLE echo_read;
HANDLE echo_write;
unsigned buffer_size;
};
DWORD WINAPI echo_thread(void *info_void)
{
echo_thread_info *info = static_cast<echo_thread_info*>(info_void);
char *buffer = reinterpret_cast<char *>(LocalAlloc(LMEM_FIXED, info->buffer_size));
DWORD bytes_read, bytes_written;
while(true){
if (!ReadFile(info->echo_read, buffer, info->buffer_size, &bytes_read, NULL) || bytes_read == 0)
if (GetLastError() == ERROR_BROKEN_PIPE)
break;
if (!WriteFile(info->echo_write, buffer, bytes_read, &bytes_written, NULL)) {
if (GetLastError() == ERROR_NO_DATA)
break;
}
}
LocalFree(reinterpret_cast<HLOCAL>(buffer));
CloseHandle(info->echo_read);
CloseHandle(info->echo_write);
return 1;
}
int main()
{
echo_thread_info stdin = {NULL, NULL, 4096};
echo_thread_info stdout = {NULL, NULL, 4096};
echo_thread_info stderr = {NULL, NULL, 4096};
HANDLE inkscape_stdin, inkscape_stdout, inkscape_stderr;
HANDLE stdin_thread, stdout_thread, stderr_thread;
SECURITY_ATTRIBUTES sa;
sa.nLength=sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor=NULL;
sa.bInheritHandle=TRUE;
const int pathbuf = 2048;
WCHAR *inkscape = reinterpret_cast<WCHAR*>(LocalAlloc(LMEM_FIXED, pathbuf * sizeof(WCHAR)));
GetModuleFileNameW(NULL, inkscape, pathbuf);
WCHAR *dot_index = wcsrchr(inkscape, L'.');
wcsncpy(dot_index, L".exe", 4);
WCHAR *cmd = GetCommandLineW();
stdin.echo_read = GetStdHandle(STD_INPUT_HANDLE);
stdout.echo_write = GetStdHandle(STD_OUTPUT_HANDLE);
stderr.echo_write = GetStdHandle(STD_ERROR_HANDLE);
CreatePipe(&inkscape_stdin, &stdin.echo_write, &sa, 0);
CreatePipe(&stdout.echo_read, &inkscape_stdout, &sa, 0);
CreatePipe(&stderr.echo_read, &inkscape_stderr, &sa, 0);
PROCESS_INFORMATION pi;
STARTUPINFOW si;
ZeroMemory(&si,sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = inkscape_stdin;
si.hStdOutput = inkscape_stdout;
si.hStdError = inkscape_stderr;
CreateProcessW(inkscape,
cmd,
NULL,
NULL,
TRUE,
0,
NULL,
NULL,
&si,
&pi);
LocalFree(reinterpret_cast<HLOCAL>(inkscape));
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
CloseHandle(inkscape_stdin);
CloseHandle(inkscape_stdout);
CloseHandle(inkscape_stderr);
DWORD unused;
stdin_thread = CreateThread(NULL, 0, echo_thread, (void*) &stdin, 0, &unused);
stdout_thread = CreateThread(NULL, 0, echo_thread, (void*) &stdout, 0, &unused);
stderr_thread = CreateThread(NULL, 0, echo_thread, (void*) &stderr, 0, &unused);
WaitForSingleObject(stdout_thread, INFINITE);
return 0;
}
#endif