3

我正在构建一个 Win32 GUI 应用程序。在该应用程序中,我使用了一个打算在命令行应用程序中使用的 DLL。

假设 Foo.exe 是我的 GUI 应用程序,bar() 是 DLL 中的一个函数,它将“hello”打印到标准输出。Foo.exe 调用 bar()。

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

但是,如果我在没有重定向的情况下运行 Foo.exe (从 cmd.exe 或通过在 Windows 资源管理器中双击),它会在调用 bar() 时崩溃。

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

我需要一些东西来“初始化”标准吗?有没有办法可以在应用程序启动中设置此重定向?(重定向到一个文件将是理想的。但只需丢弃 DLL 打印的数据也可以。)

最后,我怀疑 DLL 是通过 CRT POSIX-like API 写入标准输出的,因为它是一个跨平台的 DLL。我不知道这是否重要。

我尝试使用 CreateFile 创建文件并调用 SetStdHandle,但这似乎不起作用。但是,我可能错误地创建了文件。请参阅下面的代码。

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 的问题。事实上,由于其他复杂性,无法纠正此问题,因此有必要使用流 I/O 而不是低- 级 I/O。”

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

我正在研究一种检查这种情况的解决方案,并使用 CreateProcess 适当地重新启动 exe。完成后我会在这里发布。

4

3 回答 3

1

我找到的解决方案如下:

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

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

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

无论如何,下面的文章很好地解释了文件句柄、描述符和字段流的概念:

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

于 2012-09-07T17:12:38.580 回答
0

你能告诉我你使用哪个库吗?这个问题有很好的解决方案。编写带有您的图标和所有快捷方式启动的小型存根启动程序 EXE(在 GUI 模式下但没有窗口!)。使这个存根 EXE“CreateProcess”成为真正的 EXE,并将输出重定向到“NUL”或“CON”,或者,它挂起的 CreateProcess(),取它的 STDOUT,什么都不做。这样,您的原始 EXE 应该在没有可见控制台的情况下工作,但实际上会有写入位置 - 在父不可见存根 EXE 所采用的句柄 0,1 和 2 中。请注意,杀死父 EXE 可能会使子进程丢失其句柄 - 并且 - 崩溃。

您最终可能会在任务管理器中使用两个进程。所以你可以试着让这两个过程像谷歌浏览器一样工作。

关于您的问题,我需要一些东西来“初始化”标准吗?- 只有您的父母/启动器可以“正确”地为句柄 0,1 和 2 预初始化您的 STDOUT。

于 2013-02-14T06:21:43.853 回答
0

您必须将 foo.exe 构建为带有/SUBSYSTEM开关的控制台应用程序。Windows 将自动为您的应用程序分配一个控制台(标准输出),它可以是:

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

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

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

于 2012-09-04T04:21:08.177 回答