< p>你能否解释一下,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;
}
cmd.exe
时,CreateProcess
将具有CREATE_NEW_CONSOLE
标志,这将分配一个新的控制台,而不是附加到父控制台(当PE头包含Subsystem = 3
即IMAGE_SUBSYSTEM_WINDOWS_CUI
表示它是控制台应用程序时,默认行为)。这意味着在当前进程地址空间的.exe
映像入口点之前将调用AllocConsole
。
AllocConsole
创建一个新的conhost.exe
实例,该实例绘制GUI窗口,处理鼠标和键盘事件,并维护并写入输入缓冲区以及维护并从屏幕缓冲区读取,当有键盘事件时,它会更新输入缓冲区。 AllocConsole
还将cmd.exe
进程PEB中的ParameterBlock
中的stdin
句柄设置为控制台输入缓冲区,stdout
和stderr
设置为控制台伪句柄,并将ParameterBlock
中的ConsoleHandle
设置为所连接的conhost.exe
实例的PID。
cmd.exe
是使用C编写的控制台应用程序,例如diskpart.exe
或setx.exe
,它将命令提示符显示到屏幕缓冲区(cmd.exe
的stdout
),并从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 / c
(cmd.exe
的管理员版本)创建子级,则情况不同,因为它具有不同的特权,因此需要一个新的conhost.exe
实例。同样,当启动程序时,start
会提供CREATE_NEW_CONSOLE
,这会为其子进程打开一个新的conhost.exe
,但是对于使用call
并将程序文件名+扩展名指定为原始命令的情况,则不会打开另一个控制台窗口,而是附加到父级。由cmd / c diskpart
创建的cmd.exe
子级会附加到父级所附加的控制台,然后该子级创建其自己的diskpart.exe
子级,该子级附加到父级所附加的控制台。AllocConsole
,它将创建一个新的conhost.exe
控制台窗口(可以通过应用程序隐藏,即blender具有window > toggle system console
,它通过获取conhost.exe
控制台窗口的句柄,并在其上使用ShowWindow
和SW_HIDE
来实现),但不会创建子进程,因为它是当前进程的控制台窗口,所以现在会有2个窗口。您还可以选择连接到属于pid
进程的控制台(使用AttachConsole
)。AttachConsole(-1)
会连接到父pid
。这将使用该进程所附加的conhost
。AttachConsole
,除非使用FreeConsole
,并且如果使用DETACHED_PROCESS
创建进程,则无法AttachConsole
连接到父进程的控制台。CREATE_NO_WINDOW
的效果与DETACHED_PROCESS
相同,即不会为控制台应用程序附加或分配控制台,但允许其附加到父控制台。DETACHED_PROCESS
创建进程,则无法使用AttachConsole
连接到父进程的控制台。您确定吗?我编写了一个小测试程序尝试了一下,似乎对我有效。 - Joseph Sible-Reinstate MonicaCreateConsole
的函数,但有一个名为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(从“开始” -> “运行”启动的进程的父级)没有控制台。我已经有一段时间没有使用winapi了,但是我查阅了MSDN文档,并没有找到CreateConsole API函数。因此,我的猜测是CreateConsole是遗留的东西,已经被AttachConsole取代。所以它们可能没有区别,但是CreateConsole可能已经被弃用了。