在Win32 GUI应用程序中使用stdout:如果我没有在参数中redirect到文件,则崩溃

我正在构build一个Win32 GUI应用程序。 在这个应用程序中,我使用了一个旨在用于命令行应用程序的DLL

假设Foo.exe是我的GUI应用程序,而bar()是在DLL中输出“hello”到stdout的函数。 Foo.exe调用bar()。

如果我使用redirect(>)(即Foo.exe > out.txt )从命令行运行Foo.exe,它将“hello”写入out.txt并正常退出(如预期的那样)。

但是,如果我运行没有redirect的Foo.exe(无论是从cmd.exe还是通过在Windows资源pipe理器中双击),它会在调用bar()时崩溃。

如果我在debugging器中使用命令行中的redirect(通过VS的项目属性设置)运行Foo.exe并调用“GetStdHandle(STD_OUTPUT_HANDLE)”,我得到一个合理的句柄地址。 如果我没有在命令行中redirect,我得到0。

我需要什么来“初始化”标准吗? 有没有办法,我可以在应用程序启动设置此redirect? (redirect到一个文件将是理想的,但只是抛出由DLL打印的数据也可以。)

最后,我怀疑DLL是通过类似于POSIX的API写入stdout的,因为它是一个跨平台的DLL。 我不知道这是否重要。

我已经尝试使用CreateFile创build一个文件,并调用SetStdHandle,但似乎没有工作。 但是,我可能会错误地创build文件。 看下面的代码。

 HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); // hStdOut is zero HANDLE hFile; hFile = CreateFile(TEXT("something.txt"), // name of the write GENERIC_WRITE, // open for writing 0, // do not share NULL, // default security CREATE_NEW, // create new file only FILE_ATTRIBUTE_NORMAL, // normal file NULL); // no attr. template BOOL r = SetStdHandle(STD_OUTPUT_HANDLE, hFile) ; hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); // hStdOut is now equal to hFile, and r is 1 bar(); // crashes if there isn't a redirect in the program arguments 

更新:我刚刚发现这篇文章: http : //support.microsoft.com/kb/105305 。 它声明“请注意,这段代码不能纠正句柄0,1和2的问题。实际上,由于其他复杂性,不可能纠正这个问题,因此有必要使用streamI / O而不是低级别的I / O“。

我的DLL肯定使用文件句柄0,1和2.所以,这个问题可能没有很好的解决scheme。

我正在开发一个解决scheme来检查这种情况,并使用CreateProcess正确地重新启动exe。 我完成后会在这里发帖。

我找到的解决方案如下:

  • 以某种方式获取有效的文件句柄以指导标准输出。 让我们调用文件句柄“fh”。 (请注意,在Windows上,文件句柄与文件描述符不同)
  • 使用_open_osfhandle将文件描述符关联到文件句柄(有关详细信息,请参阅http://msdn.microsoft.com/zh-cn/library/kdfaxaay.aspx )允许调用“fd”新文件描述符(一个int值)。
  • 调用dup2将STDOUT_FILENO关联到给定的文件描述符:dup2(fd,STDOUT_FILENO)
  • 创建与stdout文件描述符FILE * f = _fdopen(STDOUT_FILENO,“w”)关联的文件strem;
  • memset stdout的内容为f:* stdout = * f
  • 在给定的文件句柄上调用SetStdHandle:SetStdHandle(STD_OUTPUT_HANDLE,ofh);

请注意,我没有测试完全这个序列,但稍有不同。

我不知道是否有些步骤是多余的。

在任何情况下,下面的文章都很好地解释了文件句柄,描述符字符流的概念:

http://dslweb.nwnexus.com/~ast/dload/guicon.htm

您必须使用/SUBSYSTEM开关将foo.exe构建为控制台应用程序。 Windows将自动为您的应用程序分配一个控制台(stdout),这可以是:

  • 当前的控制台
  • 重定向到一个文件
  • 一个管道到另一个程序的STDIN

如果您将foo.exe作为GUI应用程序构建,则控制台不会默认分配,这将解释崩溃

如果您必须使用GUI子系统,则仍然可以使用AllocConsole完成。 这个旧的WDJ文章有示例代码来帮助你。

你能告诉我你使用哪个图书馆吗? 这个问题有很好的解决办法 写小的存根发射EXE(在GUI模式,但没有窗户!)有你的图标,所有的捷径启动。 将这个存根EXE“CreateProcess”作为重定向输出的实际EXE为“NUL”或“CON”,或者它的CreateProcess()被挂起,取其STDOUT,不做任何事情。 这样,你原来的EXE应该没有可见的控制台工作,但实际上会有写入的地方 – 在父级不可见存根EXE所取的句柄0,1和2中。 请注意,杀死父EXE可能会使孩子失去了手柄和崩溃。

您可能最终在任务管理器中有两个进程。 所以你可以尝试让这两个过程像Google Chrome那样工作。

在你的问题上我需要一些“初始化”标准吗? – 只有你的父母/发射器可以预先初始化您的标准输出为手柄0,1和2“正常”。