Windows GUI +控制台输出,Linux风格

我有一个GUI应用程序,我正在为Linux和Windows开发跨平台。 在Linux上,一切正常。 不过,我在Windows上遇到了麻烦。 我希望能够在Linux上使用GUI应用程序将某些消息logging到控制台

我的意思是Linux风格,如果从控制台打开程序,输出将进入控制台,但是如果程序是打开的,例如,通过开始菜单,用户将永远不会看到控制台输出。 显然,这比在Windows上听起来更难。

目前,我在main()中使用下面的技巧:

#if _WINDOWS /* Fix console output on Windows */ if (AttachConsole(ATTACH_PARENT_PROCESS)) { freopen("CONOUT$","wb",stdout); freopen("CONOUT$","wb",stderr); } #endif 

这允许我在程序实际打开一个窗口之前创build输出,例如从命令行响应“–help”。 但是,一旦窗口被我的程序实际初始化并打开,控制台就会返回。 我需要一个解决scheme,使我可以在程序的整个生命周期中继续访问控制台,而无需打开新的控制台(如果没有最初使用的话)。

Solutions Collecting From Web of "Windows GUI +控制台输出,Linux风格"

我们使用:: AllocConsole()而不是:: AttachConsole,它始终在整个应用程序中打开。 试试?

我记得有关这方面的东西,如果我没有记错的话,解决的办法是把一个GUI添加到一个控制台项目中,而不是把一个控制台添加到一个GUI项目中,因为后者只能通过打开一个新的控制台来完成。

我认为你应该创建一个控制台应用程序,然后检查谁初始化了这个过程(可能是cmd.exe),并根据这个可以隐藏控制台窗口。 然后,你在里面创建一个窗口…在这个donwside是,控制台窗口可能会打开一会儿,直到你把它藏起来,它会看起来非常丑陋,我想。 稍后打开控制台没有这个问题,但我不知道如果stdout重定向到它,因为它在控制台应用程序,或者如果你必须设置它,或者你可能必须重定向在每个电话…不, 一定会有更好的办法!

目前为止我找到的最好的解决方案是有两个可执行文件。

  • program.exe是GUI应用程序。
  • program.com是帮助程序生成program.exe并将标准I / O传递给它的命令行应用程序。 (它不是DOS下的COM可执行文件,它只是一个重命名的标准PE可执行文件;由于.comcmd.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开关。

 /** * \file * Command-line wrapper for Windows. * * Windows has two types of executables: GUI and console. * The GUI executables detach immediately when run from the command * prompt (cmd.exe), and whatever you write to standard output * disappears into a black hole. Console executables * do display standard output and take standard input from the console, * but when you run them from the GUI, an extra console window appears. * It's possible to hide it, but it still flashes for a fraction * of a second. * * To provide an Unix-like experience, where the application will behave * correctly in command line mode and at the same time won't create * the ugly console window when run from the GUI, we have to have two * executables. The first one, inkscape.exe, is the GUI application. * Its entry points are in main.cpp and winmain.cpp. The second one, * called inkscape.com, is a small helper application contained in * this file. It spawns the GUI application and redirects its output * to the console. * * Note that inkscape.com has nothing to do with "compact executables" * from DOS. It's a normal PE executable renamed to .com. The trick * is that cmd.exe picks .com over .exe when both are present in PATH, * so when you type "inkscape" into the command prompt, inkscape.com * gets run. The Windows program loader does not inspect the extension, * just like an Unix program loader; it determines the binary format * based on the contents of the file. * *//* * Authors: * Jos Hirth <jh@kaioa.com> * Krzysztof Kosinski <tweenk.pl@gmail.com> * * Copyright (C) 2008-2010 Authors * * Released under GNU GPL, read the file 'COPYING' for more information */ #ifdef WIN32 #undef DATADIR #include <windows.h> struct echo_thread_info { HANDLE echo_read; HANDLE echo_write; unsigned buffer_size; }; // thread function for echoing from one file handle to another 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() { // structs that will store information for our I/O threads echo_thread_info stdin = {NULL, NULL, 4096}; echo_thread_info stdout = {NULL, NULL, 4096}; echo_thread_info stderr = {NULL, NULL, 4096}; // handles we'll pass to inkscape.exe 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; // Determine the path to the Inkscape executable. // Do this by looking up the name of this one and redacting the extension to ".exe" const int pathbuf = 2048; WCHAR *inkscape = reinterpret_cast<WCHAR*>(LocalAlloc(LMEM_FIXED, pathbuf * sizeof(WCHAR))); GetmoduleeFileNameW(NULL, inkscape, pathbuf); WCHAR *dot_index = wcsrchr(inkscape, L'.'); wcsncpy(dot_index, L".exe", 4); // we simply reuse our own command line for inkscape.exe // it guarantees perfect behavior wrt quoting WCHAR *cmd = GetCommandLineW(); // set up the pipes and handles 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); // fill in standard IO handles to be used by the process 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; // spawn inkscape.exe CreateProcessW(inkscape, // path to inkscape.exe cmd, // command line as a single string NULL, // process security attributes - unused NULL, // thread security attributes - unused TRUE, // inherit handles 0, // flags NULL, // environment - NULL = inherit from us NULL, // working directory - NULL = inherit ours &si, // startup info - see above &pi); // information about the created process - unused // clean up a bit LocalFree(reinterpret_cast<HLOCAL>(inkscape)); CloseHandle(pi.hThread); CloseHandle(pi.hProcess); CloseHandle(inkscape_stdin); CloseHandle(inkscape_stdout); CloseHandle(inkscape_stderr); // create IO echo threads 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); // wait until the standard output thread terminates WaitForSingleObject(stdout_thread, INFINITE); return 0; } #endif