2024年3月27日发(作者:)
XDE(Xanavi Development Enveriment)的在PC机上运行的时候,除了模拟导航仪屏
幕的对话框窗口外,还有一个用来协助调试的控制台(console)窗口。这个console窗口可
以用来输入调试命令,也可以进行打屏输出以观察程序的运行状态,非常方便。这样做有两
个好处:一、console窗口的输入输出速度比一般的windows窗口要快;二、console窗口的
输入输出不会对消息流产生影响(你知道,就像dos窗口不知道消息的存在一样,console
窗口也具有这样的特性),而在一般的windows窗口上进行输入输出时,会打乱原有的消息
流(因为会有一大堆的像WM_PAINT、WM_GETFOCUS这样与鼠标、焦点、键盘、刷新
相关的消息产生)。对于调试与消息相关的程序来说(比如像XDE这样拥有自己消息控制
的程序),console这样的特性是非常重要的。
在windows系统下使用C/C++编程的时候,我们一般会有两种选择,一个是做成类似
DOS字符界面样式的程序(这就是console程序),这种程序的入口函数是main(对于
UNICODE则是wmain),另一种是做成win32窗口程序,这样的程序会产生标准的windows
窗口,它的入口函数是WinMain 。一般来讲,一个win32的窗口程序在默认状态下是不会
产生console窗口的。
但是,对于一个win32窗口程序,我们可以在运行期间给这个程序添加一个控制台窗口。
有几个win32的API是用来实现这个功能的:AllocConsole,FreeConsole以及与Console相
关的Get和Set系列函数。AllocConsole用来产生一个窗口,FreeConsole用来销毁它。下面
我们来具体地看看每一个函数的作用。
与Console窗口相关的API函数介绍:
BOOL AllocConsole(void)
这个函数为调用它的进程产生一个console窗口,如果成功,就返回非0值;否则,返
回0。要注意的是,每个进程最多只有拥有一个console的窗口(但多个进程可能同时拥有
一个console窗口,比如子进程可以共享父进程的console窗口),如果这个进程已经有一个
console窗口了,那么再调用AllocConsole的时候它会返回0。
BOOL FreeConsole(void)
销毁进程所拥有的console窗口(如果这个console是被多个进程共同拥有,那么它不
会被Free掉,但是调用此函数的进程将无法再访问到这个console了)。成功返回非0值,
否则返回0 。
HANDLE GetStdHandle(
DWORD nStdHandle
);
nStdHandle:取 STD_INPUT_HANDLE、STD_OUTPUT_HANDLE、STD_ERROR_HANDLE
三个值中的一个。
STD_INPUT_HANDLE:标准输入设备句柄
STD_OUTPUT_HANDLE:标准输出设备句柄
STD_ERROR_HANDLE:标准错误输出设备句柄。
这个函数用来获取标准输入、输出、错误输出设备的句柄。这些句柄是某些console相关的
函数上会用到,比如下面几个函数。
BOOL WriteConsole(
HANDLE hConsoleOutput, // 控制台窗口输出句柄
const VOID* lpBuffer, // 需要输出的字符所在的buffer address
DWORD nNumberOfCharsToWrite, // 需要输出的字符个数
LPDWORD lpNumberOfCharsWritten, // 实际被输出的字符个数
LPVOID lpReserved // 保留用,必须为NULL
);
这个函数用来向console窗口的输入缓冲区中写入字符串。
BOOL WriteConsoleInput(
HANDLE hConsoleInput, // 控制台窗口输出句柄
const INPUT_RECORD* lpBuffer, // 需要输出的字符所在的buffer address
DWORD nLength, // 需要输出的字符个数
LPDWORD lpNumberOfEventsWritten // 实际被输出的字符个数
);
功能与上一个函数类似,只是它不写入缓冲区而是直接写到输出设备上。
BOOL ReadConsole(
HANDLE hConsoleInput, // 控制台窗口输入句柄
LPVOID lpBuffer, // 需要输入的字符存放的buffer address
DWORD nNumberOfCharsToRead, // 需要输入的字符个数
LPDWORD lpNumberOfCharsRead, // 实际被输入的字符个数
LPVOID lpReserved // 保留用,必须为NULL
);
这个函数用来从console窗口的输入字符。
BOOL ReadConsoleInput(
HANDLE hConsoleInput, // 控制台窗口输入句柄
PINPUT_RECORD lpBuffer, // 需要输入的字符存放的buffer address
DWORD nLength, // 需要输入的字符个数
LPDWORD lpNumberOfEventsRead // 实际被输入的字符个数
);
功能与上一个函数类似,如果输入函数中包含除键盘之外的输入事件(如窗口的大小变化、
鼠标事件)时,用ReadConsole 无法读入,只能使用ReadConsoleInput 读入。
虽然使用这几个API可以为win32产生一个控制台窗口,并在这个窗口中进行输入和
输出。但是仅仅这样做的话,便不能使用标准C函数库的输入输出系列的函数(如printf、
scanf、gets等),而只能使用WriteConsole这类win32函数向这个窗口进行输出,使用
ReadConsole这类win32函数来从控制台窗口得到输入。但是这样会有一些问题:最重要的
是WriteConsole、ReadConsole等几个函数没有printf系列的函数那样方便灵活,因为它们
不能像printf系列函数那样对字符串进行格式化,而且WriteConsole、ReadConsole等几个
函数的输入参数使用起来也不那么方便,比如要得到控制台窗口的句柄等等。另外,对于构
建在系统的上层的部分(如HMI)来说,使用标准C是一个很重要的选择――这样HMI层
(包括它的调试代码部分)才能与平台无关。所以我们需要将标准C的输入输出流与生成
的控制台窗口绑定到一起去,或者说,把标准C的输入输出流重定向到用AllocConsole生
成的控制台窗口中去。
标准C的输入输出在内部是使用stdin、stdout、stderr来进行的。这三者实际上是三个
FILE *类型的指针。这三个指针所指向的三个FILE类型的数据存储着标准输入输出流的对


发布评论