Search code examples
perlwinapiconsolewindows-consoleactivestate

Write to console when stdin/stderr/stdout redirected in Activestate Perl


I have the following code to write to the Windows command console:

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);

This code fails if the user redirects STDERR when invoking my script:

perl myscript.pl 2> foo

How can I obtain a handle to the Win32 console the process is attached to without reference to one of the standard handles so that it doesn't matter what redirections the user makes?

The effect I want is to be able to write a message on the console immediately following normal program output regardless of any redirection in a similar way to the bash builtin time command. Essentially, similar to opening and writing to /dev/tty in Unix.

I've tried my $console = new Win32::Console() to allocate a new console followed by $console->Display() but this does completely the wrong thing.


Solution

  • After asking this question, I delved a bit deeper and was able to solve it by using a nasty 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';
    

    I looked at the code for the new() function inside Win32::Console and saw that it just creates a hash containing the handle to a console. If the parameter specifies stdin/stdout/stderr, it just retrieves the associated handle otherwise it creates a new console screen buffer and uses the handle for that.

    So I just manually created the Win32::Console object containing a handle to the console returned by CreateFile.

    So now perl myscript.pl > nul 2> nul < nul will write blah blah on the screen immediately below the command line.

    I'll accept a better answer if someone comes up with one.