Lotus1 has asked for the wisdom of the Perl Monks concerning the following question:

After seeing this node I started trying to understand why Win32::Process::Memory wouldn't work. The node author warned that Win32 modules have issues. I found this module has major problems but I haven't had issues with other Win32 modules. If anyone is looking for Win32 modules to take over this one seems to need some work.

I ran the test program from the other node but saw different output:

use strict ; use warnings ; use Win32::Process::Memory ; my $proc = Win32::Process::Memory->new({ name => 'notepad.exe' }); printf "Commited Memory = %X Bytes\n", $proc->get_memtotal ; __END__ Win32::Process::Info is required to get process by name at C:/Strawber +ry/perl/site/lib/Win32/Process/Memory.pm line 35.

I installed Win32::Process::Info but it should have been a prerequisite of installing Win32::Process::Memory. After installing it and running the test program again I saw the same output as was reported in the other thread.

Use of uninitialized value in lc at C:/Perl64/site/lib/Win32/Process/M +emory.pm line 38.

Line 38 looks like this:

foreach ( Win32::Process::Info->new( '', 'NT' )->GetProcInfo ) + { if ( lc( $_->{Name} ) eq $pargs->{name} ) { ######## Line +38 $pargs->{pid} = $_->{ProcessId};

Since $pargs->{name} is the name that was passed to the function the problem seemed to be with $_->{Name}. I wrote this script to see what ->GetProcInfo is returning.

use strict ; use warnings ; use Win32::Process::Info ; use Data::Dumper; foreach ( (Win32::Process::Info->new( '', 'NT' )->GetProcInfo ) ) { print Dumper($_); print '*'x75, "\n"; }

The output looked like this (these are the last few):

[...] $VAR1 = { '_status' => 127, 'ProcessId' => 10524 }; ********************************************************************** +***** $VAR1 = { '_status' => 127, 'ProcessId' => 7768 }; ********************************************************************** +***** $VAR1 = { 'WriteOperationCount' => 0, 'WriteTransferCount' => 0, '_status' => 127, 'MaximumWorkingSetSize' => 1413120, 'MinimumWorkingSetSize' => 204800, 'OtherOperationCount' => 1104, 'OtherTransferCount' => 9046, 'CreationDate' => 1530745952, 'KernelModeTime' => '0.03125', 'UserModeTime' => '0.15625', 'ProcessId' => 484, 'ReadTransferCount' => 398115, 'ReadOperationCount' => 77 }; ********************************************************************** +*****

None of the hashes that were output contained a key called 'Name'. I found if I removed the arguments to Win32::Process::Info->new( '', 'NT' ) that the returned hashes do contain a key called 'Name'.

use strict ; use warnings ; use Win32::Process::Info ; use Data::Dumper; foreach ( (Win32::Process::Info->new( )->GetProcInfo )[-1]) { print Dumper($_); print '*'x75, "\n"; } __END__ $VAR1 = { 'QuotaPagedPoolUsage' => 123, 'ParentProcessId' => 7984, 'ProcessId' => 5568, 'Caption' => 'perl.exe', 'OwnerSid' => 'S-1-5-21-3545305261-4160602198-2318032092-100 +1', 'ExecutablePath' => 'C:\\Strawberry\\perl\\bin\\perl.exe', 'Status' => undef, 'Owner' => 'QUEST\\Brent', 'WindowsVersion' => '6.3.9600', 'ThreadCount' => 6, 'WriteTransferCount' => '0', 'HandleCount' => 151, 'TerminationDate' => undef, 'VirtualSize' => '81350656', 'SessionId' => 2, 'UserModeTime' => '0.21875', 'OtherTransferCount' => '136620', 'OtherOperationCount' => '4717', 'KernelModeTime' => '0.203125', 'InstallDate' => undef, 'PageFaults' => 4153, 'PeakPageFileUsage' => 8484, 'Name' => 'perl.exe', 'PageFileUsage' => 8484, 'OSCreationClassName' => 'Win32_OperatingSystem', 'PrivatePageCount' => '8687616', 'PeakVirtualSize' => '81350656', 'QuotaPeakNonPagedPoolUsage' => 12, 'PeakWorkingSetSize' => 15336, 'OSName' => 'Microsoft Windows 8.1|C:\\Windows|\\Device\\Har +ddisk0\\Partition4', 'Priority' => 8, 'QuotaNonPagedPoolUsage' => 12, 'MinimumWorkingSetSize' => 200, 'MaximumWorkingSetSize' => 1380, 'CSCreationClassName' => 'Win32_ComputerSystem', 'CSName' => 'QUEST', 'QuotaPeakPagedPoolUsage' => 123, 'ReadOperationCount' => '86', 'Description' => 'perl.exe', 'Handle' => '5568', 'WriteOperationCount' => '0', 'CreationDate' => 1530731971, 'ExecutionState' => undef, 'CommandLine' => '"C:\\Strawberry\\perl\\bin\\perl.exe" "C:\ +\usr\\pm\\Win32\\win32processinfo_noargs.pl" ', 'ReadTransferCount' => '375821', 'CreationClassName' => 'Win32_Process', 'WorkingSetSize' => '15704064' }; ********************************************************************** +*****

I found a description of the arguments to new in the documentation that explains what happens if no arguments are passed to new.

$pi = Win32::Process::Info->new ([machine], [variant], [hash]) This method instantiates a process information object, connected to th +e given machine, and using the given variant. The following variants are currently supported: NT - Uses the NT-native mechanism. Good on any NT, including Windows 2 +000. This variant does not support connecting to another machine, so +the 'machine' argument must be an empty string (or undef, if you pref +er). PT - Uses Dan Urist's Proc::ProcessTable, making it possible (paradoxi +cally) to use this module on other operating systems than Windows. On +ly those Proc::ProcessTable::Process fields which seem to correspond +to WMI items are returned. Caveat: the PT variant is to be considered + experimental, and may be changed or retracted in future releases. WMI - Uses the Windows Management Implementation. Good on Win2K, ME, a +nd possibly others, depending on their vintage and whether WMI has be +en retrofitted. The initial default variant comes from environment variable PERL_WIN32 +_PROCESS_INFO_VARIANT. If this is not found, it will be 'WMI,NT,PT', +which means to try WMI first, NT if WMI fails, and PT as a last resor +t. This can be changed using Win32::Process::Info->Set (variant => wh +atever).

The WMI variant seems to be providing more information that includes the 'Name' key. I then modified Memory.pm on my machine so that new is called with no arguments. At that point the following test script will just hang.

use strict ; use warnings ; use Win32::Process::Memory ; my $proc = Win32::Process::Memory->new({ name => 'cmd.exe' }); #my $proc = Win32::Process::Memory->new({ name => 'notepad.exe' }); print "ok1\n"; printf "Commited Memory = %X Bytes\n", $proc->get_memtotal ; print "ok2\n"; __END__ pargs->pid = 7984 hi 1 hi 2 ok1 hi 3 .....(hangs here)

Here is the part of memory.pm that I added some print statements to for debugging.

# get process handle by command line name if ( defined( $pargs->{name} ) ) { eval 'use Win32::Process::Info;'; die "Win32::Process::Info is required to get process by name" +if $@; $pargs->{name} = lc( $pargs->{name} ); #foreach ( Win32::Process::Info->new( '', 'NT' )->GetProcInfo +) { foreach ( Win32::Process::Info->new( )->GetProcInfo ) { if ( lc( $_->{Name} ) eq $pargs->{name} ) { $pargs->{pid} = $_->{ProcessId}; print "pargs->pid = $pargs->{pid}\n"; last; } } } # get process handle by pid if ( defined( $pargs->{pid} ) ) { my $hProcess = _OpenByPid( $pargs->{pid}, $access ); print "hi 1\n"; $this->{hProcess} = $hProcess if $hProcess; print "hi 2\n"; } return $this; } sub DESTROY { my $this = shift; _CloseProcess( $this->{hProcess} ) if defined $this->{hProcess}; } sub get_memlist { _GetMemoryList( $_[0]->{hProcess} ); } sub get_memtotal { my $this = shift; print "hi 3\n"; my %memlist = $this->get_memlist; print "hi 4\n"; my $sum = 0; $sum += $_ foreach values %memlist; return $sum; }

It stops at the call to get_memlist and that is just a wrapper for _GetMemoryList. This is the point where I'm stuck since this is an xs function that I don't know how to troubleshoot. This module has no significant tests defined and doesn't seem to have been touched in a long time. Maybe someone who knows XS could fix it up. It isn't something I really need. I was just curious to see how far I could get with troubleshooting it.

Edit: Fixed link.

Replies are listed 'Best First'.
Re: Partial troubleshooting of Win32::Process::Memory and request for fixes
by vr (Curate) on Jul 04, 2018 at 23:56 UTC
    It stops at the call to get_memlist and that is just a wrapper for _GetMemoryList.

    Perl process continues to run loading one core. Looks like endless loop starting at this line. I changed 4 instances of unsigned to ULONGLONG, then

    perl Makefile.PL gmake

    and

    perl -Mblib=blib -MWin32::Process::Memory -E "say Win32::Process::Memo +ry->new({ pid=>6496 })->get_memtotal" 127885312

    (6496 being Notepad's PID). Is it reasonable number? And solution? If so, it was just intuition (inspired by this), I'm not any good at XS to write good formal universal solution.

      I changed 4 instances of unsigned to ULONGLONG

      Well spotted vr - but I'm guessing that change will pose problems on perls whose IV/UV is not "long long".

      I think (untested) it would be better to replace the 4 occurrences of "unsigned" with "UV" as that should be portable across all builds of Windows perl.

      Update: Bad guess. Either ULONGLONG or UV is fine with vr's one-liner if ptrsize and ivsize are both 8. Else the one-liner hangs irrespective.
      Portability will require a little more investigation.

      Cheers,
      Rob
Re: Partial troubleshooting of Win32::Process::Memory and request for fixes
by Veltro (Hermit) on Jul 04, 2018 at 23:05 UTC

    Hello Lotus1

    I wrote that concern recently. Please note though that I don't know how well these module are maintained exactly. The only thing that I said in my post that some things that I have tried didn't work, so I just expressed my concerns.

    Now back to why this may not work...

    I think that the problem is in the NT.pm module:

    if ($EnumProcessModules->Call ($prchdl, $modhdl, length $modhdl, $ +modgot)) { $modhdl = unpack ('L', $modhdl); my $mfn = ' ' x MAX_PATH; if ($GetModuleFileNameEx->Call ($prchdl, $modhdl, $mfn, length $mf +n)) { $mfn =~ s/\0.*//; $mfn =~ s/^\\(\w+)/$ENV{$1} ? $ENV{$1} : "\\$1"/ex; $mfn =~ s/^\\\?\?\\//; $self->_build_hash ($dat, ExecutablePath => $mfn); my $base = basename ($mfn); $self->_build_hash ($dat, Name => $base) if $base; } }

    I think EnumProcessModules is not returning anything in $modhdl. One thing that I don't exactly understand about this code that EnumProcessModules returns an array (according to Microsoft docs). And then there is an unpack statement and passed on to GetModuleFileNameEx which takes a module handle as second argument.

    Other things that can be said about EnumProcessModules is that there is also an EnumProcessModulesEx which can be used to get 32-bit processes and 64-bit processes, so there may be the problem as well, that EnumProcessModules is not working on 64-bit

    See also this discussion here

    So to fix these kind of problems I think that someone may need to have a look at things.

Re: Partial troubleshooting of Win32::Process::Memory and request for fixes
by Anonymous Monk on Jul 05, 2018 at 20:39 UTC
    A lot has changed in the Windows world since that module apparently was first produced, particularly with regard to security. Does this module continue to work?