Nov 30, 2013

HowTo: ActivePerl cmd.exe Monitor [UPDATED]

Purpose:
create a cmd.exe (command line) process that you can manipulate (write on its handle) and monitor (read on its handle) so that you can create programs that rely on the output of other programs without waiting for it to terminate. :)

How does it work:
1. open2 will create a cmd.exe process
2. Glib::Timeout will constantly calls the repeat_call sub
   - i did not use Gtk2::Helper because I am having problem with reading the output of the cmd.exe, I need to either move the mouse on the window for it to trigger the update, if anybody can help me, please comment.
3. pressing the button DIR will send a "dir" command to the cmd.exe process
4. pressing button TIME only prints "the current time"
5. the repeat_call sub will then read the unread output of the command line and push all the output to @queue array
6. action_call sub will then read 1 line from the @queue array and prints the result. action_call sub is called constantly even when the array is empty.
7. the @queue array makes sure that you will get the correct chronology of the output.

Below is the sample code I manage to whip up with lots and lots of help from the very nice and good MONKies of the www.perlmonks.org community! Check out our conversations at http://perlmonks.org/?node_id=1064595

Sample Code:
#!/usr/bin/perl -w

use strict;
use Gtk2 '-init';
use Gtk2::Helper;
use Data::Dumper;
use FileHandle;
use IPC::Open2;
use Win32API::File;
use Win32::API;

my @queue;

{
my $api;
die "PeekNamedPipe"
if ! ($api= Win32::API->Import("kernel32", "
BOOL PeekNamedPipe(
HANDLE hNamedPipe,
LPVOID lpBuffer,
DWORD nBufferSize,
LPDWORD lpBytesRead,
LPDWORD lpTotalBytesAvail,
LPDWORD lpBytesLeftThisMessage
);"));
}

my $wfh = FileHandle->new();
my $rfh = FileHandle->new();
open2($rfh,$wfh,"C:\\Windows\\System32\\cmd.exe");
my $hnd = Win32API::File::FdGetOsFHandle($rfh->fileno());
if($hnd == Win32API::File::INVALID_HANDLE_VALUE()){ die "bad hnd"; }


my $tag = Glib::Timeout->add(10,\&repeat_call);


my $window = Gtk2::Window->new();
$window->signal_connect("destroy",sub{Gtk2->main_quit();});

my $hbox = Gtk2::VBox->new();
$window->add($hbox);

my $button = Gtk2::Button->new("DIR");
$button->signal_connect('clicked'=>sub{
print $wfh "dir\n";
});
$hbox->pack_start($button,0,0,0);

$button = Gtk2::Button->new("TIME");
$button->signal_connect('clicked'=>sub{
print $wfh "time /t\n";
});
$hbox->pack_start($button,0,0,0);

$window->show_all();

Gtk2->main();

sub repeat_call {
my $bAvail = 0;
my $ret = 0;
my $buffer;
$bAvail = "\x00" x 4;
$ret = PeekNamedPipe($hnd,undef,0,undef,$bAvail,undef);
if(!$ret) {
my $err = Win32::GetLastError();
die "PNP failed $err $^E";
}
$bAvail = unpack('L', $bAvail) . "\n";
if($bAvail > 0) {
sysread($rfh,$buffer,$bAvail);
chomp($buffer);
my (@q) = split(/\n/,$buffer);
foreach my $qq (@q){
chomp($qq);
push @queue, $qq;
}
}

action_call();

while (Gtk2->events_pending()) {Gtk2->main_iteration();}
return 1;
}

sub action_call {
my $count = @queue;
if($count){
my $line = shift(@queue);
print $line . "\n";
}
}


Note:
If you have a better method please share, anybody can comment! :)

No comments:

Post a Comment