c win32 api-GetStdHandle(STD_OUTPUT_HANDLE)无效,非常复杂

c win32 api-GetStdHandle(STD_OUTPUT_HANDLE)无效,非常复杂,c,winapi,C,Winapi,我的WinMain中有以下片段,我正在从控制台启动这个GUI应用程序。我想将输出重定向到启动我的应用程序的控制台。GetStdHandle()之后出现“句柄无效”错误 但是,如果我使用AllocConsole而不是AttachConsole,它可以正常工作。此外,如果我使用STD_ERROR_HANDLE而不是STD_outputhhandle,那么fprintf(stderr,“ERROR”)可以正常工作 我看到一篇博客文章也有同样的问题,但没有解决办法。我正在64位Windows7上使用VC

我的WinMain中有以下片段,我正在从控制台启动这个GUI应用程序。我想将输出重定向到启动我的应用程序的控制台。GetStdHandle()之后出现“句柄无效”错误

但是,如果我使用AllocConsole而不是AttachConsole,它可以正常工作。此外,如果我使用STD_ERROR_HANDLE而不是STD_outputhhandle,那么fprintf(stderr,“ERROR”)可以正常工作

我看到一篇博客文章也有同样的问题,但没有解决办法。我正在64位Windows7上使用VC2010编译器

谢谢

bConsole = AttachConsole(ATTACH_PARENT_PROCESS) != FALSE;

if (bConsole)
{
    int fd = 0;
    long lStdOut;
    lStdOut = (long)GetStdHandle(STD_OUTPUT_HANDLE);
    fd = _open_osfhandle(lStdOut, _O_TEXT);
    if (fd > 0)
    {
        *stdout = *_fdopen(fd, "w");
        setvbuf(stdout, NULL, _IONBF, 0 );
    }
}
printf("Test!!!!!!!!!!!!");

我认为你的问题在于:

long lStdOut;
lStdOut = (long)GetStdHandle(STD_OUTPUT_HANDLE);
fd = _open_osfhandle(lStdOut, _O_TEXT);
我不太擅长使用Win32 API,但我认为句柄有自己的
HANDLE
类型,我认为它们本质上是指针,在Win64上是64位的<由于某些原因,Win64中的code>long类型仍然是32位的

这是MSDN的声明:

HANDLE WINAPI GetStdHandle(
  __in  DWORD nStdHandle
);
开放式操作系统的声明:

int _open_osfhandle (
   intptr_t osfhandle,
   int flags 
);

我在默认VisualStudioC++的GUI项目中添加了以下代码,在 WiMeNe/COD>
if (AttachConsole(ATTACH_PARENT_PROCESS))
{
    if (GetStdHandle(STD_OUTPUT_HANDLE) == INVALID_HANDLE_VALUE)
        MessageBox(0, L"Invalid Handle", NULL, 0);
    else
        MessageBox(0, L"Valid Handle", NULL, 0);
}
从调试器或资源管理器运行GUI程序时,不会显示任何消息框。换句话说,我们不能附加控制台。当我从cmd运行时,我会看到“validhandle”消息


我的结论是,这种基本方法实际上没有问题,但您没有向我们展示的东西是导致问题的原因。

AttachConsole
确实将您的进程与控制台关联,但是
stdout
已经打开(并连接到旧句柄,不管它是什么)

直接覆盖stdout是个糟糕的主意。相反,您必须
freopen(“CONOUT$”,“w”,stdout)
获取
stdout
到控制台


但是还有很多其他的小细节。看一看我的问题,它涵盖了你在问题中的问题,然后问一个关于一些角落案例的问题。最后还有一个包含所有内容的代码示例。

Windows子系统进程(即带有
WinMain
的进程)将不会有STDOUT、STDERR或STDIN,除非在启动时特别提供了一个。假设是,由于它是一个windows程序,您正在通过windows与它进行交互

也就是说,GetStdHandle没有向STDOUT返回句柄,因为您没有STDOUT

要给它一个,就这样启动它:

winprog.exe > output.txt 2>&1
如果以这种方式启动,它将同时有一个STDOUT和STDERR,这两个都将进入命名文件

正如其他用户已经指出的,AttachConsole将为您提供一个控制台(最接近的unix/linux等价物是TTY),但它不会为您提供标准输出。如果您想要一个,您必须将其设置为单独的步骤。如果你想让它成为控制台,你也可以拥有它


另一方面,控制台子系统程序(带有
main的程序
)在默认情况下会将STDIN、STDOUT和STDERR全部设置到控制台。您可以从控制台分离进程,并根据需要关闭它们。

要将输出重定向到控制台,请使用以下代码:

AllocConsole();
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
然后,您可以使用WriteFile()写入控制台

    WriteFile(
        hConsole,                
        L"this is a debug line\n", 
        21, // string length 
        NULL,             // bytes written 
        NULL);

事实上,首先我用手柄而不是长柄试了一下。后来,我尝试了不同的东西,使这项工作,我把它改为长期。但是,如果我将STD_OUTPUT_HANDLE更改为STD_ERROR_HANDLE,同样的代码也可以工作,所以我认为这可能不是问题所在。这可能无关紧要的原因是因为我使用的是win32编译器。Maister提出了一个很好的观点。它不能解决您的问题,但您应该使用正确的类型。在调用
GetStdHandle
后,您需要检查错误。如果失败,它将返回无效的句柄值。如有必要,请检查此项和调用
GetLastError
。然后告诉我们什么是
GetLastError
报告。您应该将
lStdOut
定义为
intptr\t lStdOut
而不是使用
长lStdOut。请参见@Oleg Not true,它是一个
句柄
–请参见@David Heffernan:当然
GetStdHandle
返回一个
句柄
,但是
\u open\u osfhandle
使用
intptr\t
作为输入参数。我们必须进行类型转换,但在问题的代码中,我们使用
long
而不是
intptr\t
,但是
long
的大小可以不同于
句柄的大小
intptr\t
(与
句柄大小相同)。可能是@Ben的重复。他的错误似乎在他覆盖标准输出之前很久。他说GetStdHandle失败了。@David:请阅读我的问题,它有一个完整的、100%有效的代码示例,并且涵盖了许多常见的情况(例如shell重定向到文件)。@Ben我对此更感兴趣question@Ben所以我读了你链接到的问题,但它似乎不适用于这里。也许你应该重读这个问题。@David:这个问题说“我想将输出重定向到启动我的应用程序的控制台。”我的问题中提到了这一点。除了cygwin-bash之外,我自己也解决了其他组合的问题,所以我的问题是关于cygwin-bash的,但是我在那里发布的代码满足了帕格的要求,然后还有一些。我还将解决帕格所面临的具体问题的那一行复制到了这里的答案中。是的,
GetStdHandle
AttachConsole
之后返回垃圾,因此您不使用它。你使用了
freopen
,生活是美好的。对于未来的读者来说,他们可能还没有读过我答案下面的整个评论帖子,David代码片段中的测试问题在于
GetStdHandle
没有使用
无效句柄值的神奇数字,而是返回
NULL
。和
NULL!=无效的\u HANDLE\u值
,因此它错误地通过了测试。@Ben Well它可以根据文档返回无效的\u HANDLE\u值或NULL。但是,这是一个很好的例子