Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot

Odd WMI situation

by Anonymous Monk
on Jul 08, 2019 at 02:36 UTC ( #11102523=perlquestion: print w/replies, xml ) Need Help??

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

This is a bit out there, but here it goes. I have been using ActiveState perl to query the WMI interface to gather windows hardware info for some time. I am in the process of expanding my reach to linux and mac hardware, but running into an issue. I have been able to steer my code into OS specific sections with if ($^O =~ /MSWin32/i).

I am trying to get the win32 modules pulled into that section and have been successful with most of the modules (Win32::TieRegistry, Win32::NetAdmin, Win32), but Win32::OLE is resisting my efforts.

If I place use Win32::OLE qw(in) before the steering code (available to all OS's), everything works well under windows, but when I run the script on linux where I get can't locate Win32/ (as expected). If I place require Win32::OLE::->import (qw(in)) within the Win32 section and comment it out of the main branch, I get the error:

Win32::OLE(0.1712): GetOleEnumObject() Not a Win32::OLE::Enum object at C:/Perl64/lib/Win32/OLD/ line 167.

When I look at the documentation, I get a bit confused. Evidently "in" is a function that is exported by Win32::OLE to deal with collections of objects, but when I use require, it isn't being exported correctly. I can include some actual code that works under windows but throws the module missing error under linux...

Thanks for any help!

#!/usr/bin/perl use strict; use warnings; use Win32::OLE qw(in); ### Need to move to Win32 specific section... our ($Registry,$WMI); if ($^O =~ /MSWin32/i) { require Win32; Win32::->import(); require Win32::TieRegistry; Win32::TieRegistry::->import(Delimiter = +>'/'); # require Win32::OLE; Win32::OLE::->import(qw(in)); ### Doesn't work! $WMI = Win32::OLE->GetObject('winmgmts:{impersonationLevel=impersona +te}!\\\\.\\root\\CimV2'); unless (defined $WMI) {Death("Can't connect to WMI interface!");} $WMI->Security_->Privileges->AddAsString('SeLoadDriverPrivilege', 1) +; } elsif ($^O =~ /Linux/i) { # linux things } else {die("Unknown Operating System: <$^O>");} my $ram; my $i = 1; my %inv; foreach my $item (in $WMI->ExecQuery('Select * from Win32_PhysicalMemo +ry')) { my $bank = int($item->Capacity/1024/1024); my $spd = $item->Speed; next unless $spd && $bank; $inv{"Dimm_$i"} = "$bank Mb $spd MHz"; $ram += $bank; $i++; } $inv{"Ram_Installed"} = "$ram Mb" if $ram; foreach my $key (sort keys %inv) {print "$key: \t$inv{$key}\n";} exit;

Replies are listed 'Best First'.
Re: Odd WMI situation
by Corion (Pope) on Jul 08, 2019 at 07:53 UTC

    The in() function has a prototype and that influences how Perl parses the rest.

    You could either predeclare the in function so that Perl knows how to parse it:

    sub in(@);

    ... or alternatively, you can tell Perl that in will become a function by explicitly using parentheses at every invocation (which I prefer):

    foreach my $item (in($WMI->ExecQuery('Select * from Win32_PhysicalMemo +ry'))) {

    Personally, I would try to split up the functionality into two (or three) modules, one Win32-specific module, one POSIX-specific module and one module that loads either the Windows or the other module, depending on the OS.

Re: Odd WMI situation
by swl (Chaplain) on Jul 08, 2019 at 10:11 UTC

    Corion's comment about using multiple modules is a sensible one, as that will avoid inadvertently calling windows code on linux and so forth.

    My only real comment is that the calling module can use the if pragma to control which is called. For example:

    use if ($^O =~ /MSWin32/i), 'Some::Windows::Module';
Re: Odd WMI situation
by dwreese (Initiate) on Jul 08, 2019 at 02:38 UTC
    Sorry, for some reason I got logged out before this posted, it was my post!
Re: Odd WMI situation
by Anonymous Monk on Jul 08, 2019 at 16:55 UTC

    In order to be effective, the require() has to be executed before the rest of the code is compiled. You can make this happen by putting it (and the conditional that causes it to execute) inside a BEGIN block:

    BEGIN { if ( $^O eq =~ m/MSWin32/i ) { ... require Win32::OLE; Win32::OLE->import( qw{ in } ); } }

    The BEGIN is unconditionally executed as soon as it finishes compiling, and before anything below it compiles. Note that there is no colon after the BEGIN. If you insert the colon it becomes a labelled block; this is syntactically valid so you don't get an error, but it will not do what you want.

    On the other hand, there's always use if ....

      You might need to add

      else { *in = \&in; }

      to prevent compilation errors depending on the rest of the code (i.e. whether it's called using or without using parens).

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://11102523]
Approved by Athanasius
Front-paged by haukex
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others studying the Monastery: (9)
As of 2019-07-19 14:55 GMT
Find Nodes?
    Voting Booth?

    No recent polls found