Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options

Problem enumerating a foreach loop using Win32::OLE->GetObject

by t_rex_joe (Sexton)
on Apr 12, 2018 at 14:31 UTC ( #1212736=perlquestion: print w/replies, xml ) Need Help??
t_rex_joe has asked for the wisdom of the Perl Monks concerning the following question:

Perl Monks,
I seek your knowledge and wisdom again.

This time pertaining to enumerating WIN32::OLE response.

The issue is finding a timeout/hung session when retrieving
the win32 information (wmic) from a remote machine.

The code below is step by step, the failure does not occur until near the
bottom. I included this code for FYI.
eval { local $SIG{ALRM} = sub { warn "ALARM FOR GET WMIC L: \"" . __LINE_ +_ . "\" F: \"" . __FILE__ . "\"\n" }; # NB: \n required alarm 10; $wmicon = Win32::OLE->GetObject('winmgmts:' . '{impersonationLevel +=impersonate, authenticationLevel=pkt}!\\\\' . $host . '\\root\\CIMV2 +\sms') or $ok = 0; alarm 0; }; if($@){ $err = 1; $ret = "timeout while accessing wmi on machine $h +ost"; return($err, $ret); } else { } # didn't

The code above works as desired when a machine is offline,
the alarm feature is not the issue
$col = undef; eval { local $SIG{ALRM} = sub { warn "ALARM FOR GET EXEC QUERY: \"$quer +y\" L: \"" . __LINE__ . "\" F: \"" . __FILE__ . "\"\n" }; # NB: \n re +quired alarm 5; $col = $wmicon->ExecQuery($query, "WQL", wbemFlagReturnImmediate +ly | wbemFlagForwardOnly | WbemAuthenticationLevelPkt) or $ok = 0; alarm 0; }; if($@){ $err = 1; $ret = "timeout while QUERY: \"$query\" wmi on +machine $host"; return($err, $ret); } else { } # didn't if($ok == 0) { $err = 1; $ret = clean(Win32::OLE->LastError); retu +rn($err, $ret); }
The code above works as expected
$objct = 0; $enabled = 0; $obj = undef; foreach $obj (in $col) { my $delete = 0; if($debug == 1) { print "\tOBJ: \"$obj\" L: \"" . __LINE__ . "\" +\n"; } }

This code is where the problem occurs.

I dont understand what the "foreach $obj (in $col)"
actually does/executes on the back end.
Why not use "foreach $obj (keys % { $col })".
This does not work.

I need to trap/eval the "foreach $obj (in $col)",
to test for failure then exit/return. currently
when this hangs, the script stops until i kill it manually.

"$col" is a hash, however when I move this
scalar to a hash with
my %colhash = % { $col }; if($debug == 1) { print "DUMP COLHASH: \"" . Dumper(%colhash) . "\" +L: \"" . __LINE__ . "\"\n"; }

The actual value is not the same as using the "foreach $obj (in $col)".
What is the difference in the "foreach" styles?



P.S. I will sweeten the pot by honoring a single item
from a amazon wishlist (NOT GT $30USD).

Replies are listed 'Best First'.
Re: Problem enumerating a foreach loop using Win32::OLE->GetObject
by hexcoder (Chaplain) on Apr 12, 2018 at 16:15 UTC
    Hello t_rex_joe,

    I have not used module Win32::OLE myself, but from reading the documentation in seems to be a function from the module, that delivers a list of from a given collection object.

    From the module documentation:


    If COLLECTION is an OLE collection object then in $COLLECTION returns a list of all members of the collection. This is a shortcut for Win32::OLE::Enum->All($COLLECTION). It is most commonly used in a foreach loop:

    foreach my $value (in $collection) { # do something with $value here }

    So, there is no new foreach loop syntax, just an imported function in. Hope this helps a bit, hexcoder

      Thanks for the info.

      The hang is caused by the reading of the collection
      $val = Win32::OLE::Enum->All($col) or $ok = 0;

      Here is where the script locks/hangs.

      I have tried to eval and alarm. Nothing is breaking
      out if the commands hangs.
      eval { local $SIG{ALRM} = sub { warn "ALARM FOR GET EXEC QUERY: \"$quer +y\" L: \"" . __LINE__ . "\" F: \"" . __FILE__ . "\"\n" }; # NB: \n re +quired alarm 5; $val = Win32::OLE::Enum->All($col) or $ok = 0; alarm 0; }; if($@) { $ret = $@; print "ERR: \"$ret\"\n"; $err = 1; $ret = "timeout while QUERY: \"$query\" wmi on + machine $host"; return($err, $ret); }

      any suggestions on howto eval/jail this line, then
      breakout/throw error in case the time/condition is met?

      Only certain machines are causing the hang/lock. Most
      of the machines breeze past within 5 seconds.

      So iterating 5000 plus machines then hanging on a random
      is a PITA.

      TIA, Joe
        Hello again,

        you must try to isolate your problem. I admit that, for me, reading the expose of your question result to be a bit difficult. Also the code you show is not so perlish and lacks of use strict; use warnings; Anyway, wat I found is:

        > Only certain machines are causing the hang/lock.

        You have not to eval/jail (as you say) the function: you are outside perl domain. Infact you are dealing with something similar to a complex system call: microsoft stuff are well known to return (or not return) weird behaviours. You know if you are used to work with these things.

        If a remote call fails or hangs is not useful to wrap in eval and wait 300 seconds to have back a 255 error or something else, equally unuseful. Better try to whatch $^E that is last operating system error or even better check first if the machine is reachable. Programatically or manually check the Event Viewer in the remote machine to spot some (rare) useful entries.

        Other sparse hints: the problem maight be at RPC level (i'm in guess mode now..) try some command line version using wmic to confirm both versions hangs; wmi connection can be set to use a timeout, like in $os.options.Timeout=[timespan]::FromSeconds(30) or similar. There are here around WMI Diagnosis Utility and WMIdiag. Build a list of hanging machines and look at them carefully (what if remote service is stopped? ram cpu?)


        There are no rules, there are no thumbs..
        Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.
Re: Problem enumerating a foreach loop using Win32::OLE->GetObject
by Discipulus (Monsignor) on Apr 12, 2018 at 16:21 UTC
    Hello t_rex_joe

    the foreach $obj (in $col syntax is not a perl syntax; it's a WIN32::OLE one.

    Due to the weirdness of the matter (win32) and also a bit aged perl syntax used building the interface of the modules in the win32 namespace (I thanks many time their authors for having wrote them..) some misunderstang can occour.

    In this case you have a container $col that is not a simple perl datastructure but an abstraction made by the module. Or at least i understand it this way.

    See here to have in explained.


    There are no rules, there are no thumbs..
    Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.
Re: Problem enumerating a foreach loop using Win32::OLE->GetObject
by afoken (Abbot) on Apr 12, 2018 at 20:20 UTC

    alarm is a Unix thing. Ir requires signals, also a Unix thing. Windows has neither. Perl has an emulation, but it is far from being perfect. When the perl interpreter is blocked, it can not emulate signals and so it can not emulate alarm to interrupt a blocking system call.

    See also Re: Handling killing the perl process, Re: Signals in Strawberry Perl: Name or number?, $SIG{ALRM} and windows vista?, and several other threads.

    Update: See also "alarm" in perlport:

    (Win32) Emulated using timers that must be explicitly polled whenever Perl wants to dispatch "safe signals" and therefore cannot interrupt blocking system calls.


    Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1212736]
Front-paged by Corion
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others lurking in the Monastery: (3)
As of 2018-07-22 09:03 GMT
Find Nodes?
    Voting Booth?
    It has been suggested to rename Perl 6 in order to boost its marketing potential. Which name would you prefer?

    Results (453 votes). Check out past polls.