2

我有以下代码要写入 Windows 命令控制台:

use Win32::Console;
my $console = new Win32::Console(Win32::Console::STD_ERROR_HANDLE());
my $defaultAttribute = $console->Attr();
my $defaultFG = ($defaultAttribute & 0x0F);
my $defaultBG = ($defaultAttribute & 0xF0);
$console->Attr($defaultBG | $Win32::Console::FG_LIGHTGREEN);
$console->Write("blah blah");
$console->Attr($defaultAttribute);

如果用户在调用我的脚本时重定向 STDERR,此代码将失败:

perl myscript.pl 2> foo

如何在不参考标准句柄之一的情况下获得该进程所附加到的 Win32 控制台的句柄,以便用户进行何种重定向无关紧要?

我想要的效果是能够在正常程序输出之后立即在控制台上写一条消息,而不管以与 bash 内置time命令类似的方式进行的任何重定向。本质上,类似于/dev/tty在 Unix 中打开和写入。

我试图my $console = new Win32::Console()分配一个新的控制台,$console->Display()但这完全是错误的。

4

2 回答 2

3

在问了这个问题之后,我进行了更深入的研究,并且能够通过使用一个讨厌的 hack 来解决它:

use Win32API::File qw(createFile);
use Win32::Console;

my $handle = createFile('CONOUT$', 'rwke') or die "conout\$: $^E\n";
# my $console = new Win32::Console($handle) or die "new console: $^E\n";
my $console = bless {handle => $handle}, 'Win32::Console';

我查看了new()内部函数的代码Win32::Console,发现它只是创建了一个包含控制台句柄的哈希。如果参数指定stdin/stdout/stderr,它只检索关联的句柄,否则它会创建一个新的控制台屏幕缓冲区并为此使用句柄。

所以我只是手动创建了Win32::Console包含 CreateFile 返回的控制台句柄的对象。

所以现在perl myscript.pl > nul 2> nul < nulblah blah在命令行下方的屏幕上直接写入。

如果有人想出一个更好的答案,我会接受。

于 2013-02-12T22:16:07.023 回答
0

根据AllocConsole()文档(C++ 文档,但概念相同):

“一个进程只能与一个控制台关联,因此如果调用进程已经有一个控制台,则 AllocConsole 函数会失败。进程可以使用 FreeConsole 函数将自己与当前控制台分离,然后它可以调用 AllocConsole 创建一个新控制台或 AttachConsole 以附加到另一个控制台。”

由于您的控制台已被重定向,因此您似乎无能为力。即使您分离控制台并分配一个新控制台,新控制台也会继承重定向。在 C++ 中,您将使用SetStdHandle() API 来强制标准句柄指向不同的文件或设备,但我找不到任何与之等效的 Perl。

于 2013-02-12T21:55:30.063 回答