API函数AllocConsole和AttachConsole(-1)有什么区别?

9
< p>你能否解释一下,API 函数 AllocConsole AttachConsole(-1) 之间的区别?我指的是如果 AttachConsole 获取到 ATTACH_PARENT_PROCESS(DWORD)-1

4个回答

13

基本上,它们的区别在于:

  • AllocConsole()将创建一个新的控制台窗口(并附加到该窗口)
  • AttachConsole(ATTACH_PARENT_PROCESS /* -1 */)不会创建新的控制台窗口,而是会附加到父进程的现有控制台窗口。

在第一种情况下,您会得到一个全新的控制台窗口;在第二种情况下,您将使用现有的控制台窗口。

当然,如果您已经附加到控制台(即,从cmd.exe启动的控制台模式程序),那么两个API之间没有太大区别-您会收到错误。

还要注意,仅因为您从控制台分离出来并不意味着分离的控制台将很有用——例如,如果您是从cmd窗口启动的控制台进程,则该窗口实际上会阻塞,直到您的进程结束。

一些可供玩耍的代码:

int main( int argc, char* argv[])
{
    int ch;
    BOOL bResult;

    printf( "default console\n");
    ch = getchar();

    bResult = FreeConsole();
    bResult = AllocConsole();    
    printf( "AllocConsole()\n");
    ch = getchar();

    bResult = FreeConsole();
    bResult = AttachConsole( ATTACH_PARENT_PROCESS);    
    printf( "AttachConsole( ATTACH_PARENT_PROCESS)\n");
    ch = getchar();

    return 0;
}

1
这个能在C#.Net中用来创建一个控制台窗口,用于编译为Windows程序(而不是控制台程序)的可执行文件吗? - configurator
2
可以的。但是,更容易的方法是获取控制台,只需将项目的输出类型更改为控制台应用程序即可。 - Hans Passant
据我所知,您可以这样做,但我认为您将不得不使用p/invoke来调用Win32 API(对于这些API来说并不是什么大问题)- 我不确定是否有框架等效物。 - Michael Burr
@nobugz - 是的,你可以。更多信息请参见https://dev59.com/2XRB5IYBdhLWcg3w6LR2 - abatishchev
@abatishchev:好的链接变成了死链接……不好。 - 7vujy0f0hy
@7vujy0f0hy 噢,很抱歉8年前发布的链接现在对您不起作用了。这种情况以前从未发生过,请放心不会再次发生。请接受我真诚的道歉。附言:感谢您的编辑。干杯! - abatishchev

5
在Windows 7上,当您执行cmd.exe时,CreateProcess将具有CREATE_NEW_CONSOLE标志,这将分配一个新的控制台,而不是附加到父控制台(当PE头包含Subsystem = 3IMAGE_SUBSYSTEM_WINDOWS_CUI表示它是控制台应用程序时,默认行为)。这意味着在当前进程地址空间的.exe映像入口点之前将调用AllocConsoleAllocConsole创建一个新的conhost.exe实例,该实例绘制GUI窗口,处理鼠标和键盘事件,并维护并写入输入缓冲区以及维护并从屏幕缓冲区读取,当有键盘事件时,它会更新输入缓冲区。 AllocConsole还将cmd.exe进程PEB中的ParameterBlock中的stdin句柄设置为控制台输入缓冲区,stdoutstderr设置为控制台伪句柄,并将ParameterBlock中的ConsoleHandle设置为所连接的conhost.exe实例的PID。 cmd.exe是使用C编写的控制台应用程序,例如diskpart.exesetx.exe,它将命令提示符显示到屏幕缓冲区(cmd.exestdout),并从stdin读取一个命令,并解释按键事件以显示在stdout中,并确定要调用哪个命令以及要显示结果的内容(可能是PEB中的文件句柄而未发送到conhost)。一个命令本身有一个stdin,可以是没有任何内容的匿名管道的读取端,一个文件或者是con文件。 cmd.exe调用像WriteFile这样的函数,如果它正在写入标准句柄,则将调用WriteConsole(它是WriteConsoleA的别名)。否则,它将调用NtWriteFile。这会启动一个ALPC调用到在ParameterBlock->ConsoleHandle中的conhost.exe PID,传递控制台伪句柄和要写入结果的缓冲区(或要读取的缓冲区)。
当您在cmd.exe中执行cmd / c时,它会创建一个子cmd.exe,但不提供CREATE_NEW_CONSOLE,这会导致子cmd.exe附加到相同的可见控制台即conhost.exe实例,并且在入口点之前调用AttachConsole。如果在非提升的cmd.exe中使用admin / ccmd.exe的管理员版本)创建子级,则情况不同,因为它具有不同的特权,因此需要一个新的conhost.exe实例。同样,当启动程序时,start会提供CREATE_NEW_CONSOLE,这会为其子进程打开一个新的conhost.exe,但是对于使用call并将程序文件名+扩展名指定为原始命令的情况,则不会打开另一个控制台窗口,而是附加到父级。由cmd / c diskpart创建的cmd.exe子级会附加到父级所附加的控制台,然后该子级创建其自己的diskpart.exe子级,该子级附加到父级所附加的控制台。
当您执行GUI应用程序时,它不会分配或附加到控制台。如果在应用程序中使用AllocConsole,它将创建一个新的conhost.exe控制台窗口(可以通过应用程序隐藏,即blender具有window > toggle system console,它通过获取conhost.exe控制台窗口的句柄,并在其上使用ShowWindowSW_HIDE来实现),但不会创建子进程,因为它是当前进程的控制台窗口,所以现在会有2个窗口。您还可以选择连接到属于pid进程的控制台(使用AttachConsole)。AttachConsole(-1)会连接到父pid。这将使用该进程所附加的conhost
如果已经附加到控制台,则不能AttachConsole,除非使用FreeConsole,并且如果使用DETACHED_PROCESS创建进程,则无法AttachConsole连接到父进程的控制台。CREATE_NO_WINDOW的效果与DETACHED_PROCESS相同,即不会为控制台应用程序附加或分配控制台,但允许其附加到父控制台。

如果您使用DETACHED_PROCESS创建进程,则无法使用AttachConsole连接到父进程的控制台。您确定吗?我编写了一个小测试程序尝试了一下,似乎对我有效。 - Joseph Sible-Reinstate Monica
我在那里读到了这篇文章,但从未测试过。也许你使用的是不同版本的Windows。我看到了你的评论,需要进一步调查。 - Lewis Kelsey

5
我认为并没有名为CreateConsole的函数,但有一个名为AllocConsole的函数。
假设这就是你所想要的,我认为它们之间的区别在于,如果父进程没有控制台,AttachConsole(ATTACH_PARENT_PROCESS)可能会返回ERROR_INVALID_HANDLE
请尝试从命令提示符和“开始”菜单的“运行”中运行此代码。
#include <windows.h>
#pragma comment ( lib, "user32.lib" )

int main()
{
    BOOL b;
    char msg[1024];

    b = FreeConsole();
    sprintf(msg, "%d", b);
    MessageBox(NULL, msg, "FreeConsole", 0);

    b = AttachConsole(ATTACH_PARENT_PROCESS);
    sprintf(msg, "%d", b);
    MessageBox(NULL, msg, "AttachConsole", 0);

    return 0;
}

当从命令提示符运行时,将显示两个包含 1 的消息框,表示两个调用都成功了。当从“开始” -> “运行”运行时,第一个框包含 1 ,第二个框包含0,这意味着只有第一个调用成功了。第二个调用失败是因为 explorer.exe(从“开始” -> “运行”启动的进程的父级)没有控制台。

0

我已经有一段时间没有使用winapi了,但是我查阅了MSDN文档,并没有找到CreateConsole API函数。因此,我的猜测是CreateConsole是遗留的东西,已经被AttachConsole取代。所以它们可能没有区别,但是CreateConsole可能已经被弃用了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接