【发布时间】:2010-09-30 18:37:31
【问题描述】:
您能否解释一下,API 函数 AllocConsole 和 AttachConsole(-1) 有什么区别?我的意思是如果AttachConsole 得到ATTACH_PARENT_PROCESS(DWORD)-1。
【问题讨论】:
您能否解释一下,API 函数 AllocConsole 和 AttachConsole(-1) 有什么区别?我的意思是如果AttachConsole 得到ATTACH_PARENT_PROCESS(DWORD)-1。
【问题讨论】:
嗯,根本的区别是:
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;
}
【讨论】:
我认为没有名为CreateConsole 的函数,但有AllocConsole。
假设这就是你的意思,我认为区别在于如果父进程没有控制台,AttachConsole(ATTACH_PARENT_PROCESS) 可以return 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 的消息框,这意味着两个调用都成功了。从 Start -> Run 运行时,第一个框包含1,第二个框包含0,这意味着只有第一个调用成功。第二个失败是因为 explorer.exe(它是从 Start -> Run 启动的进程的父进程)没有控制台。
【讨论】:
在 Windows 7 上,当您执行 cmd.exe 时,CreateProcess 将具有 CREATE_NEW_CONSOLE 标志,它将分配一个新控制台而不是附加到父控制台(这是 PE 标头包含 @ 时的默认行为987654324@ 即IMAGE_SUBSYSTEM_WINDOWS_CUI 表示它是一个控制台应用程序)。这意味着AllocConsole将在当前进程地址空间中的.exe图像入口点之前被调用。
AllocConsole 创建一个新的conhost.exe 实例,它绘制GUI 窗口,处理鼠标和键盘事件,维护和写入输入缓冲区,维护和读取屏幕缓冲区,当有键盘时事件,它更新输入缓冲区。 AllocConsole 还将cmd.exe 进程PEB 中ParameterBlock 中的stdin 句柄设置为控制台输入缓冲区,并将stdout 和stderr 设置为控制台伪句柄,并将ConsoleHandle 设置为ParameterBlockto the PID of theconhost.exe` 附加到的实例。
cmd.exe 是用 C 编写的控制台应用程序,如 diskpart.exe 或 setx.exe,它将命令提示符显示到屏幕缓冲区(cmd.exe 的 stdout)并从 stdin 读取命令cmd.exe 并解释要显示给 stdout 的按键事件,并确定调用什么命令以及显示给 stdout 的结果(可能是在 PEB 中处理,而不是发送到conhost)。 command 本身有一个 stdin 或者什么都没有、匿名管道的读取端、一个文件或 con 文件。
cmd.exe 调用类似WriteFile 的函数,如果它正在写入标准句柄(否则它调用NtWriteFile),它将调用WriteConsole(这是WriteConsoleA 的别名)。这将在ParameterBlock->ConsoleHandle 中启动对conhost.exe PID 的ALPC 调用,传递控制台伪句柄和缓冲区以将结果写入(或从缓冲区读取)。
当您从cmd.exe 内部执行cmd /c 时,它会创建一个子cmd.exe,但不提供CREATE_NEW_CONSOLE,这会导致子cmd.exe 附加到同一个可见控制台,即conhost.exe 实例并且在入口点之前调用AttachConsole。如果在非提升的cmd.exe 中完成使用admin /c(cmd.exe 的管理员版本)创建的孩子则不是这种情况,因为它具有不同的权限,所以需要一个新的conhost.exe 实例。类似地,当启动一个程序时,start 提供CREATE_NEW_CONSOLE,这会为其子进程打开一个新的conhost.exe,但call 并将程序文件名+扩展名指定为原始命令不会打开另一个控制台窗口,而是附加到父级。 cmd /c diskpart 创建一个 cmd.exe 子级,该子级附加到父级附加到的控制台,然后该子级创建自己的子级diskpart.exe,它附加到父级附加到的控制台。
当您执行 GUI 应用程序时,它不会分配或附加到控制台。如果您在应用程序中使用AllocConsole,它将创建一个新的conhost.exe 控制台窗口(它可以被应用程序隐藏,即搅拌机有window > toggle system console,它通过获取conhost.exe 控制台的句柄来实现这一点窗口,然后使用ShowWindow和SW_HIDE就可以了),但不会创建子进程,因为它是当前进程的控制台窗口,所以现在会有2个窗口。您也可以附加到属于pid 进程的控制台(使用AttachConsole)。 AttachConsole(-1) 附加到父 pid。这将使用该进程所附加到的conhost。
如果您已经连接到控制台而不使用FreeConsole,则不能AttachConsole,如果您使用DETACHED_PROCESS 创建进程,则不能AttachConsole 连接到父控制台。 CREATE_NO_WINDOW 与DETACHED_PROCESS 具有相同的效果,因为它不附加到控制台应用程序或为控制台应用程序分配控制台,但允许它附加到父控制台。
【讨论】:
使用winapi已经有一段时间了,但是我查了the MSDN documentation并没有找到CreateConsole API函数。所以我的猜测是 CreateConsole 是遗留的东西,已经被 AttachConsole 取代了。所以可能没有区别,但 CreateConsole 可能已被弃用。
【讨论】: