#  NT_ProcessList.pl
#  Example 7.15:
#  ----------------------------------------
#  From "Win32 Perl Scripting: Administrators Handbook" by Dave Roth
#  Published by New Riders Publishing.
#  ISBN # 1-57870-215-1
#
#  This script demonstrates how the NT process list can be procured
#  using the Win32::PerfLib extension.
#
print "From the book 'Win32 Perl Scripting: The Administrator's Handbook' by Dave Roth\n\n";


use Win32::PerfLib;
my $Server;
my $ProcessIndex;
my $ProcessObject;

$Server = Win32::NodeName unless( $Server = $ARGV[0] );
print "Collecting process info for $Server...\n";
Win32::PerfLib::GetCounterNames( $Server, \%StringIndex );
map { $String{$StringIndex{$_}} = $_; } ( keys( %StringIndex ) );
$ProcessIndex = $String{Process};

# Connect to the server's performance data
$Perf = new Win32::PerfLib( $Server );
if( ! $Perf )
{
        print "Could not obtain the process list.\n";
        exit();
}
$ProcessList = {};

# get the performance data for the process object
$Perf->GetObjectList( $ProcessObject, $ProcessList );
$Perf->Close();
$InstanceHash = $ProcessList->{Objects}->{$ProcessIndex}->{Instances};
foreach my $ProcessObject ( sort( keys( %{$InstanceHash} ) ) )
{
    my $ProcessCounter = $InstanceHash->{$ProcessObject}->{Counters};
    my %ThisProcess;
    $ThisProcess{Name} = $InstanceHash->{$ProcessObject}->{Name};
    foreach my $Attrib ( keys( %$ProcessCounter ) )
    {
        my $AttribName = $StringIndex{$ProcessCounter->{$Attrib}->{CounterNameTitleIndex}} || '';
        $ThisProcess{$AttribName} = Format( $ProcessCounter->{$Attrib}, $ProcessList->{Objects}->{$ProcessIndex} );
    }
    $ProcessList{$ThisProcess{'ID Process'}} = \%ThisProcess;
}

$~ = PROCESS_HEADER;
write;
$~ = PROCESS_INFO;
foreach $Process ( sort( keys( %ProcessList ) ) )
{
    # Don't make $Proc lexicaly scoped since it is used in a formatted
    # write.
    $Proc = $ProcessList{$Process};
    write;
}

sub Format
{
    my( $Proc, $ObjectList ) = @_;
    my $Value = $Proc->{Counter};
    my $Type = $Proc->{CounterType};
    my $TB = $ObjectList->{PerfFreq};
    my $Y = $ObjectList->{PerfTime};

    if( PERF_100NSEC_TIMER == $Type )
    {
        $Value = 100 * ( $Value / 1000000 ) ;
    }
    elsif( PERF_ELAPSED_TIME == $Type )
    {
        my( $Hour, $Min, $Sec );
        # Convert the value into seconds...
        $Value = ( $Y - $Value ) / $TB;

        $Hour = $Value / ( 60 * 60 );
        $Min = ( $Hour - int( $Hour ) ) * 60;
        $Sec = ( $Min - int( $Min ) ) * 60;
        $Value = sprintf( "%d:%02d:%02d", int( $Hour ), int( $Min ), int( $Sec ) );
    }
    return( $Value );
}

sub FormatNumber
{
    my( $Number ) = @_;
    my( $Suffix ) = "";
    my $K = 1024;
    if( $K <= $Number )
    {
        $Suffix = " K";
        $Number /= $K;
    }
    $Number =~ s/(\.\d{0,2})\d*$/$1/;
    while ($Number =~ s/^(-?\d+)(\d{3})/$1,$2/){};
    return( $Number . $Suffix );
}

format PROCESS_HEADER =
@||| @||||| @|||||||||||||||| @|||||| @|||||||||||| @|||||||||||| @||||||
PID, Parent, "Process Name", "Threads", "Memory", "Memory Peak", "Handles"
---- ------ ----------------- ------- ------------- ------------- -------
.

format PROCESS_INFO =
@||| @||||| @<<<<<<<<<<<<<<<< @>>>>>> @>>>>>>>>>>>> @>>>>>>>>>>>> @>>>>>>
$Proc->{'ID Process'}, $Proc->{'Creating Process ID'} || "---", $Proc->{Name}, $Proc->{'Thread Count'}, FormatNumber( $Proc->{'Working Set'} ), FormatNumber( $Proc->{'Working Set Peak'} ), $Proc->{'Handle Count'}
.        
