Saturday, May 01, 2004

This is about a small tool I have been using for a while now to let me talk to the WMI (Windows Management Instrumentation) object model exposed by the Windows operating system. WMI is used for various management related tasks and is a very powerful API.

 

I wanted to write the script at one sitting (and not a long one) and so a scripting language was an obvious choice. This could have been written in C++ or C# or any COM interface aware language also. I started of with a Ruby version, but decided to quit on that because the Ruby libraries for interfacing with OLE were not very stable when it came to WMI programming. I believe that is being fixed now. Perl was good choice because it was sufficiently dynamic and more importantly it had stable libraries. The source here can be readily translated to VBScript or JScript versions also. If you do so, drop me a mail.

 

I had mailed about this script to the user groups in the past, but I got a feeling that I had done no justice to what this script and WMI could actually achieve. Hence this article.

 

Download and Docs

You can download the script here, or simply copy paste it and save it from this article. To make this work, you need to have Perl on your machine. Perl can be downloaded for free from here.

 

If you are new to WMI and what to have a good understanding of what is available I suggest you take a look at:

·         WMI Scripting Primer Part 1 Part 2 Part 3

·         The MSDN site on scripting is a good resource: http://msdn.microsoft.com/scripting

·         Scriptomatic: playing around with this HTA application for auto-generating WMI scripts is also lots of fun

·         My previous blog entry Bangalore User Group – WSH, WMI and System.Management has some beginner level scripts that I had demoed at one the UG meetings.

 

What can you do with wmi.pl ?

 

The following is a brief description of what you can do with the wmi.pl.

 

Listing Instances

> wmi.pl

Lists all the running processes on your computer. The out looks a little like this:

D:\scr>wmi.pl

1       System Idle Process

2       System

3       SMSS.EXE

4       CSRSS.EXE

5       WINLOGON.EXE

6       SERVICES.EXE

 

Specifying properties to display

> wmi.pl -executablepath

Lists all the processes as well as their physical paths on disk.

D:\scr>wmi.pl -executablepath

1       System Idle Process

2       System

3       SMSS.EXE        C:\WINNT\System32\smss.exe

4       CSRSS.EXE

5       WINLOGON.EXE    C:\WINNT\system32\winlogon.exe

6       SERVICES.EXE    C:\WINNT\system32\services.exe

7       LSASS.EXE       C:\WINNT\system32\lsass.exe

8       svchost.exe     C:\WINNT\system32\svchost.exe

 

> wmi.pl -< property name >

Lists all the processes with the specified property of the process displayed. This requires some explanation. The processes I refer to here are instance of the WMI class called Win32_Process. You can look at the documentation for the class to see what properties this class supports. Or you can you some of the reflection capabilities of WMI which I have discussed in the next usage sample. The ‘executablepath’ that was shown earlier was one such property. There are several others that the Win32_Process class supports.

This shows the process id of each process as well as how many threads each process currently has. Powerful?

D:\scr>wmi.pl -processid -threadcount

1       System Idle Process     0       1

2       System  8       46

3       SMSS.EXE        152     6

4       CSRSS.EXE       176     12

5       WINLOGON.EXE    172     19

6       SERVICES.EXE    224     37

7       LSASS.EXE       236     18

8       svchost.exe     420     12

9       spoolsv.exe     452     11

 

Examining Classes for Properties and Methods

> wmi.pl ?

Lists all the properties and methods available for the process class. This uses the reflection capabilities of WMI. It examines the Win32_Process class and tells you what properties and methods the class provides. So you can use the information retrieved to know what property name parameters you can use. The output here is edit, the Win32_Process class support many more properties than shown here.

D:\scr>wmi.pl ?

Win32_Process Properties : -----------------------------

1       Caption

2       CreationClassName

3       CreationDate

4       CSCreationClassName

5       CSName

6       Description

7       ExecutablePath

8       ExecutionState

44      WriteTransferCount

Win32_Process Methods : -----------------------------

1       Create

2       Terminate

3       GetOwner

4       GetOwnerSid

So it is easy to see that ‘wmi.pl – creationdate’ is a valid query. Exploring this API can give you lots of useful information. I shall shortly show you what we can do with the methods.

 

Displaying all the Properties of a Class

> wmi.pl -*

This will list all available information about every process on the system. Information such as memory usage, kernel usage, thread count etc are available. In case you want to see a lot of information about every process, the tabular view (generated by specifying property names in the command line) maybe inadequate. This is again highly truncated output that is shown.

51

 Caption = perl.exe

 CreationClassName = Win32_Process

 CreationDate = 20040430233435.908750+330

 CSCreationClassName = Win32_ComputerSystem

 CSName = RMZ10F01

 Description = perl.exe

 ExecutablePath = D:\RoshanJ\Progs\perl\bin\perl.exe

 ExecutionState =

 Handle = 3368

 HandleCount = 106

PeakWorkingSetSize = 5586944

 Priority = 8

 PrivatePageCount = 2265088

 ProcessId = 3368

 QuotaNonPagedPoolUsage = 4780

 QuotaPagedPoolUsage = 20416

 QuotaPeakNonPagedPoolUsage = 5176

 QuotaPeakPagedPoolUsage = 20420

 ReadOperationCount = 54

 

 

Specifying classes to use

> wmi.pl < class name>

So far we have seen that wmi.pl retrieves information about the Win32_Process class. What about all the other information that WMI? The Perl script basically has Win32_Process hard coded as the default class to use. To provide all the behavior that you saw above, taking place for some other class (other than Win32_Process), simply specify the name of that class at the command line.

 

Doing so will cause the rest of the parameters to act for the given class name. For example Win32_Share is the class name for shares of your computer. So ‘wmi.pl win32_share’ will list all the shares and ‘wmi.pl win32_share –path’ will list the share names and their physical paths on your computer.

D:\scr>wmi.pl win32_share

2       System.DDL

4       ut

7       dotnet-talk

8       install

10      ftproot

and (notice we pass –path here, so ‘path’ must be a property of win32_share)

D:\scr>wmi.pl win32_share -path

2       System.DDL      D:\RoshanJ\Homepage\work\System.DDL

4       ut      D:\ut

7       dotnet-talk     D:\dotnet-talk

8       install D:\RoshanJ\install

10      ftproot C:\Inetpub\ftproot

To take a look at what Win23_Share provides

D:\scr>wmi.pl win32_share ?

win32_share Properties : -----------------------------

1       AccessMask

2       AllowMaximum

3       Caption

4       Description

5       InstallDate

6       MaximumAllowed

7       Name

8       Path

9       Status

10      Type

win32_share Methods : -----------------------------

1       Create

2       SetShareInfo

3       Delete

 

Win32_Service represents services on your computer. Win32_LogicalDisk represent drives on your system and so on. All available properties about a win32_LogicalDisk maybe queried by typing ‘wmi.pl win32_logicaldisk ?’, similar to the above sample with Win32_Share. Simple?

 

Now you may ask, how you can know what classes are available. Read on.

 

Examining Namespaces for Classes and Namespaces

> wmi.pl dir

In WMI classes are organized into namespaces. The script wmi.pl is configured to use the namespace ‘root/cimv2’ by default. The above line will list all the classes and nexted namespaces in a namespace. This is a rather huge list so you may want to save this into a text file. Once you find a class that you think is useful, you can query information about the classes listed here by specifying the class name and using the ‘?’ parameter.

D:\scr>wmi.pl dir

root/cimv2 Classes : -----------------------------

441     \\RMZ10F01\ROOT\CIMV2:Win32_DeviceBus

442     \\RMZ10F01\ROOT\CIMV2:Win32_CIMLogicalDeviceCIMDataFile

443     \\RMZ10F01\ROOT\CIMV2:Win32_ShareToDirectory

444     \\RMZ10F01\ROOT\CIMV2:Win32_NetworkAdapterConfiguration

445     \\RMZ10F01\ROOT\CIMV2:Win32_NetworkAdapterSetting

446     \\RMZ10F01\ROOT\CIMV2:Win32_PortableBattery

447     \\RMZ10F01\ROOT\CIMV2:Win32_SystemSlot

448     \\RMZ10F01\ROOT\CIMV2:Win32_PortConnector

449     \\RMZ10F01\ROOT\CIMV2:Win32_PhysicalMemory

450     \\RMZ10F01\ROOT\CIMV2:Win32_SystemEnclosure

451     \\RMZ10F01\ROOT\CIMV2:Win32_BaseBoard

Win32_Process Namespaces : -----------------------------

1       Applications

2       ms_409

To redirect content to text file

D:\scr>wmi.pl dir > filename.txt

 

Specifying other Namespaces

> wmi.pl /ns:< namespace> < other parameters>

            You can change the namespace where the script should find the WMI class you mention by using the /ns: specification. This might be a little abstract to understand but consider this. The previous option ‘dir’ showed you only contents of the ‘root/cimv2’ namespace, if you wanted to see the contents of the ‘root’ namespace the you could use this feature.

D:\scr>wmi.pl /ns:root dir

root Classes : -----------------------------

1       \\RMZ10F01\ROOT:__SystemClass

2       \\RMZ10F01\ROOT:__NAMESPACE

3       \\RMZ10F01\ROOT:__Provider

4       \\RMZ10F01\ROOT:__Win32Provider

5       \\RMZ10F01\ROOT:__ProviderRegistration

6       \\RMZ10F01\ROOT:__ObjectProviderRegistration

7       \\RMZ10F01\ROOT:__ClassProviderRegistration

8       \\RMZ10F01\ROOT:__InstanceProviderRegistration

9       \\RMZ10F01\ROOT:__PropertyProviderRegistration

Win32_Process Namespaces : -----------------------------

1       DEFAULT

2       SECURITY

3       CIMV2

4       WMI

5       directory

6       MSAPPS10

7       NetFrameworkv1

8       MSAPPS

9       MSAPPS11

As you can see, the root namespace contains a namespace called the ‘cimv2’.

 

Here is an example that lists instances of the Win32_OleDbProvider which is part of the ‘root/msapps’ namespace.

D:\scr>wmi.pl /ns:root/msapps win32_oledbprovider

1       VSEE Versioning Enlistment Manager Proxy Data Source

2       MediaCatalogDB OLE DB Provider

3       Microsoft OLE DB Provider for SQL Server

4       Microsoft OLE DB Provider for DTS Packages

5       SQL Server Replication OLE DB Provider for DTS

6       MediaCatalogMergedDB OLE DB Provider

7       Microsoft ISAM 1.1 OLE DB Provider

8       Microsoft OLE DB Provider For Data Mining Services

9       MSDataShape

10      VSEE Versioning Enlistment Manager Proxy Data Source

 

Collecting data from remote computers

> wmi.pl @< computer name > [some other parameters]

All the commands that you saw so far were getting information about your computer. If you are on a network, WMI can let you gather such information about a remote computer as well.

Specifying a computer name or IP address preceded by an @ will cause the whole command to run on a remote system. To execute this successfully your current logged on user id should have admin rights on the remote system. Such a security check is essential because WMI can do potentially powerful and dangerous things such as stopping and starting of services, processes, access to disk partitions, bios etc.

The following command lists shares on a remote computer and shows you the physical paths of the share on that computer.

D:\scr>wmi.pl @machine01 win32_share -path

22      v0.2_dist       D:\v0.2_dist

23      ecma    D:\rotor_text\ecma

25      rotor_text      D:\rotor_text

26      ruby1.8 D:\RoshanJ\Progs\ruby1.8

27      1.0.0001.0020   D:\blog\blogx\1.0.0001.0020

 

Calling Methods on WMI classes

wmi.pl –terminate()

Will terminate every process on your system (that you have rights to terminate). I am not running this as a demo right now, just take my word for it.

If you remember the output of the ‘?’ parameter you might recollect that it displays properties and methods that are available for a class. Thus terminate() was a method of the Win32_Process class. Calling terminate on a process, as expected, would try to terminate it.

 

wmi.pl  -< property name >=< value > < method name>()

Often we do not want to want to call a method on every instance of a class. Like, would would not want to terminate() all the process, but say only a set of them. This is when the above syntax comes in useful. This will call the method specified on every instance of the specified class where the given property has the given value.

So if you want to terminate all the instances of Internet explorer that are running on your system you would say:

D:\scr>wmi.pl -name=iexplore.exe terminate()

Query = select * from Win32_Process where   name='iexplore.exe'

0 IEXPLORE.EXE->terminate

1 IEXPLORE.EXE->terminate

2 IEXPLORE.EXE->terminate

 

Similarly if you want to terminate all instances of IE on a remote system ‘machine01’, you could say:

D:\scr>wmi.pl @machine01 -name=iexplore.exe terminate()

Query = select * from Win32_Process where   name='iexplore.exe'

0 IEXPLORE.EXE->terminate

1 IEXPLORE.EXE->terminate

2 IEXPLORE.EXE->terminate

 

Here is another example.

wmi.pl @machine01 win32_operatingsystem reboot()

This will reboot the remote machine. Of course, your current logged on user needs admin permissions on the remote system for this to happen.

 

 

That’s it for now.

I haven’t finished shaking my head at the power of this API that has been ignored to obscurity, and my neck’s beginning to hurt. Try your own stuff and in the meanwhile I will be adding to the script.

 

There are some things I want to add to script, but haven’t done out of sheer laziness and the thought of getting back to Perl’s rather arcane syntax.

·         Support for impersonation

·         Support for regular expressions

·         Support for method calls with parameters

·         Support for display the CIM class definitions

 

Here is the Perl source (Those of you who have used WMI in some way will notice the consistency when you use the API across languages). How large did you expect the source to be? Since I am a Perl newbie, this code maybe substantially clunkier than what could be written by experienced hands.

 

use Win32;

use Win32::OLE qw (in);

 

$system = ".";

$classname = "Win32_Process";

@props = ("name");

%prop_value=();

$namespace = "root/cimv2";

$call = "instances";

$serial_no = 1;

 

sub list_instances {

       $serv = Win32::OLE->GetObject("winmgmts://$system/$namespace");

       $objs = $serv->InstancesOf("$classname");

       $i = 1;

       foreach $obj (in($objs)) {

              if($serial_no == 1){

                     $str = "$i ";

              }

              else {

                     $str = "";

              }

              if ($all_props) {

                     foreach $prop (in($obj->{Properties_})) {

                           $str = "$str\n $prop->{name} = $obj->{$prop->{name}}";

                     }

                     $str = "$str\n"

              }

              else{

                     foreach $prop (in(@props)) {

                           $str = "$str\t$obj->{$prop}";

                     }

              }

              $str = "$str\n";

              print $str;

              $i = $i + 1;

       }

}

 

sub list_classinfo {

       $obj = Win32::OLE->GetObject("winmgmts://$system/$namespace:$classname");

       print "$classname Properties : -----------------------------\n";

       $i = 1;

       foreach $prop (in($obj->{Properties_})) {

              print "$i\t$prop->{name}\n";

              $i = $i + 1;

       }

       print "$classname Methods : -----------------------------\n";

       $i = 1;

       foreach $m (in($obj->{Methods_})) {

              print "$i\t$m->{name}\n";

              $i = $i + 1;

       }

}

 

sub list_namespaceinfo {

       $serv = Win32::OLE->GetObject("winmgmts://$system/$namespace");

       print "$namespace Classes : -----------------------------\n";

       $i = 1;

       foreach $class (in($serv->SubClassesOf())) {

              $path = $class->{Path_}->{Path};

              print "$i\t$path\n";

              $i = $i + 1;

       }

       print "$classname Namespaces : -----------------------------\n";

       $i = 1;

       foreach $ns (in($serv->InstancesOf("__NAMESPACE"))) {

              print "$i\t$ns->{name}\n";

              $i = $i + 1;

       }

}

 

 

sub call_method {

       $serv = Win32::OLE->GetObject("winmgmts://$system/$namespace");

      

       if ((scalar keys (%prop_value)) == 0){

              $query="select * from $classname";

       } else{

              $query="select * from $classname where ";

              $and = "";

              for $key (keys %prop_value) {

                     $value = %prop_value->{$key};

                     $query = "$query $and $key=\'$value\'";

                     $and = "and";

              }

       }

      

       print "Query = $query\n";

       $objs = $serv->ExecQuery($query);

       $i = 0;

       foreach $obj (in ($objs)) {

              print "$i $obj->{name}->$methodname\n";

              $obj->{$methodname};

              $i = $i + 1

       }

}

 

 

 

foreach $arg (in(@ARGV)) {

       if ($arg =~ m#/sys:(.*)#) {

              $system = $1;

       } elsif ($arg =~ m#@(.*)#) {

              $system = $1;

       } elsif ($arg =~ m#\/class\:(.*)#) {

              $classname = $1;

       } elsif ($arg =~ m#\-\*#) {

              $all_props = 1;

       } elsif ($arg =~ m#\-(.*)=(.*)#) {

              %prop_value->{$1}=$2;

       } elsif ($arg =~ m#\-(.*)#) {

              @props[$#props+1]=$1;

       } elsif ($arg =~ m#^\?$#) {

              $call = "classinfo";

       } elsif ($arg =~ m#dir#) {

              $call = "dir";

       } elsif ($arg =~ m#(.*)\(.*\)#) {

              $methodname = $1;

              $call = "method";

       } elsif ($arg =~ m#\/ns\:(.*)#) {

              $namespace = $1;

       } elsif ($arg eq "/no_serial") {

              $serial_no = 0;

       } elsif (($arg eq "help") || ($arg eq '/?')) {

              print "WMI command line Tool (c)Roshan James, 2004\n\n";

              print "Help is available at:\n";

              print "http://www.thinkingms.com/pensieve/CommentView,guid,64df1ee9-a582-474c-960a-0063cd848609.aspx\n";

              print "Or mail spark\@mvps.org\n\n";

              exit 0

       } else {

              $classname = $arg;

       }

}

 

if ($call eq "instances") {

       list_instances();

} elsif ($call eq "dir") {

       list_namespaceinfo();

} elsif ($call eq "classinfo") {

       list_classinfo();

} elsif ($call eq "method") {

       call_method();

}

 

Saturday, May 01, 2004 1:55:11 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0]  | 
 Friday, April 30, 2004

Marcello Tosatti

The current maintainer of the Linux kernel.

http://kerneltrap.org/node/view/1880/5842

Marcelo Tosatti became the maintainer of the 2.4 stable kernel when he was 18 years old in November of 2001. His first kernel release was 2.4.16 on November 26'th which very quickly followed the earlier 2.4.15 to address an issue with filesystem corruption. Two years later, he has recently released 2.4.23 and plans to soon put the 2.4 stable kernel into maintenance mode, only addressing bugs and security issues.

Well, if you can handle a wife at age of 20, the kernel is nothing ;)

 

Yukihiro Matsumoto (Matz)

The author of the Ruby programming language.

http://www.artima.com/intv/craftP.html

Yukihiro Matsumoto, the creator of the Ruby language, talks with Bill Venners about becoming a better programmer through reading code, learning languages, focusing on fundamentals, being lazy, and considering interfaces.

Bill Venners: You also mentioned in your ten top tips: "Be lazy. Machines should serve human beings. Often programmers serve machines unconsciously. Let machines serve you. Do everything you can to allow yourself to be lazy." Why should I try to be lazy?

Yukihiro Matsumoto: You want to be lazy. You want to do anything to reduce your work. I work hard to reduce my work, to be lazy.

 

Miguel De Icaza

Coauthor of Mono, Gnome, MC.

http://msdn.microsoft.com/netframework/using/understanding/cli/default.aspx?pull=/library/en-us/dndotnet/html/deicazainterview.asp

Using the ECMA Standards: An Interview with Miguel de Icaza

In this interview, Miguel de Icaza, the founder of GNOME and Ximian, talks about UNIX components, Bonobo, Mono, and Microsoft .NET

 

Charles Simonyi

Microsoft Distinguished Engineer – the WYSIWYG, Hungarian Notation, Intentional Programming

http://www.edge.org/digerati/simonyi/simonyi_p2.html

"Intentional Programming"

A Talk With Charles Simonyi

("The WYSIWYG")

 

Chris Pratley’s blog

Now here is man.

Chris Pratley is Group Program Manager with Microsoft’s Authoring Services – Word, Publisher OneNote.. Recently he has had a sent of blog articles about Word that you simply got to read. Absolutely got to read them because he says it – and he says it so in-your-face.

Let's talk about Word (168 comments) - look at the number of comments on this – this is at the time when I am writing this

Word Myths and Feedback (38 comments)

More Word Feedback (25 comments)

More responses to comments on Word posts (18 comments)

(Thanks Sajith for pointing me at this)

 

Friday, April 30, 2004 11:49:22 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0]  | 
 Thursday, April 29, 2004

I thought of putting together some of my old mails and user group posting as blog entries, with some patches so that they are’ blogopatible’. They would have ended up on my blog, if I had a blog when I made these posts. I feel some of these are of lasting importance, at least with respect to the impressions they had on me.

 

All of these are personal opinions, probably more relevant in the context that they were originally written.

 

-----Original Message-----
From: James, Roshan
Sent:
Tuesday, December 09, 2003
11:57 PM
 Subject: XAML and markups: Introducing WFML - WinForms Markup Language

Hi Folk,

 

Ever since Linux Bangalore I have been thinking about something that Miguel de Icaza had demoed on stage. He was showing Mono’s UI, Glade, and showed how Glade generates UI from XML markup. They (Miguel and Nat) were also joking as to how XAML is an idea that they had thought of 6 years back – the idea of using XML markup for representing UI and separating 'business logic' from UI.

 

            He also went on to show how Glade is used to generate GTK# UI in linux. A '.glade' file is simply an xml file that contains tags that correspond to the properties of the controls (or widgets as they call them).

 

(I pulled glade file off the net to give you an idea of what it looks like

 

< widget class="GtkWindow" id="window2">

  < property name="visible">True< /property>

  < property name="title" translatable="yes">window2< /property>

  < property name="type">GTK_WINDOW_TOPLEVEL< /property>

  < property name="window_position">GTK_WIN_POS_NONE< /property>

  < property name="modal">False< /property>

  < property name="resizable">True< /property>

  < property name="destroy_with_parent">False< /property>

 

  < child>

    < widget class="GtkButton" id="button1">

      < property name="border_width">10< /property>

      < property name="visible">True< /property>

      < property name="can_focus">True< /property>

      < property name="label" translatable="yes">button1< /property>

      < property name="use_underline">True

      < property name="relief">GTK_RELIEF_NORMAL< /property>

    < /widget>

  < /child>

< /widget>

 

)

 

The strange thing was how they used glade files - in XAML you compile the xml file to generate a class (or what MS calls a partial class, one that can be extended elsewhere). In glade they did no such thing, they simple wrote a cs file and called one Glade API function passing it the name of '.glade' file. That returned some kind of object and presto they had their UI. Now the claim was that this really separates the UI from the code and as a matter of fact the XML can be chnaged after the exe file has been compiled. Changes to the XML will reflect in the UI without recompilation of anything. They also mentioned that Microsoft hasn’t figured out how to do this yet.

 

(Here is fragment of c# code that uses the .glade

 

                using Gtk;

                using Gnome;

                using Glade;

                using GtkSharp;

 

        public class GladeTest

                {

&