PHP and WMI – Dig deep into Windows with PHP

Taylor Ren

There are many devices (servers, desktops, laptops, tablets, phones, etc) running a Windows operating system. Many of us who live in the nix based world have to work in this OS, or if we don't, we will, sooner or later. Besides the regular tools we can expect from a *nix system (say Apache, PHP, MySQL, C/C++ compilers, etc), Windows offers a set of unique features not present in any other OS, and WMI is one of them.

In this article, we will address the questions like: What is WMI? How to use WMI with PHP? We will have some minimal sample codes to go through the basic programming techniques.

What is WMI and why we need to deal with it

The MSDN site has its official definition of WMI in this article, out of which a few lines are extracted below:

Windows Management Instrumentation (WMI) is the Microsoft implementation of Web-Based Enterprise Management (WBEM), which is an industry initiative to develop a standard technology for accessing management information in an enterprise environment.

Three keywords here are: Web-Based, management information, enterprise environment. So if our managed IT environment is a large scale, Windows-based architecture and we want to retrieve management information of each individual node and present it in a web fashion, we will be required to interact with WMI. WMI is also capable of doing other things like spawning a process in a remote PC but that will be beyond the scope of this article.

WMI provides the comprehensive knowledge of a machine, both hardware and software. It has the so called CIM (Common Information Model) to encapsulate the information in an object-oriented manner. It also provides several programming interfaces to retrieve said information. In a pure Windows environment, these will be PowerShell, VB Script and .NET languages. But in our case, it will be PHP.

One of the fundamental questions when programming with WMI is: which "information" is available? In other words, which objects/classes are available? Luckily, Microsoft provides a full list of what WMI offers in terms of classes and their properties. Please visit here for a complete reference. In WMI programming, most of the time we are referring to Win32 Classes.

Pre-requisites

On a host Windows machine, WMI must be installed to provide the CIM. By default, any Windows system newer than Windows XP should have WMI installed and enabled.

We can verify that that is the case through the following two steps:

  1. Launch "Computer Management" in your Windows machine and see if the service called "Windows Management Instrumentation" is running. If not, start that service.
  2. Launch "wbemtest" in your Command Prompt window. A dialog titled "Windows Management Instrumentation Test" will appear. A lot of the buttons in that dialog are currently disabled but we can click the "Connect..." button to invoke a new dialog similar to the one shown below:

Normally, we don't need to change a thing. root\cimv2 is the system built-in namespace for our WMI interface. Just click the "Connect" button in this dialog. It will bring us back to the previous window with all the buttons enabled.

Being able to connect to a machine's WMI interface is just one of the pre-requisites. We also need to make sure the Windows Firewall will allow WMI calls to pass through.

In Windows Firewall, choose "Advanced Settings" then enable both inbound and outbound access rules for WMI related entries. Please see the screenshots below.

After we enable the WMI firewall rules in a remote machine, we can test the connection as illustrated in Step 2 above. To connect to a remote machine, we need to prefix the default namespace ("root\cimv2") with the IP or name of the PC we need to connect ("\\192.168.1.2\root\cimv2 for example") and provide the user name and password for that remote machine.

PHP extension for WMI (.NET)

To use WMI (or more precisely, .NET functionality) in PHP, we will need to enable php_com_dotnet.dll. Add one line to the php.ini like this:

extension=php_com_dotnet.dll

and restart the web server.

Note: php_com_dotnet.dll is a Windows only extension. This means that we will have to run the WMI-calling PHP file in a WAMP-like environment, and not in a *nix environment. And, of course, the machines that we will manage via WMI all need to be Windows based.

A further look of what WMI provides

Having done all the necessary preparation, and before we start programming WMI with PHP, we really need to get back to the fundamental question we raised earlier: What "information" is available?

We can expect the WMI to provide information regarding the BIOS, CPU, disks, memory usage, etc. But how is this information presented?

Besides digging into the official documents provided, let's bring up the wbemtest dialog again and connect to our local machine. In the WMI Tester dialog, click the Enum Classes... button and bring up the below dialog:

In this dialog, don't enter anything in the text box, choose Recursive and click OK. It should bring up another dialog like this:

This is a very long list (1,110 objects in my Windows 8.1 PC). Your PC may give out a different list but should be more or less the same as this one. Please take some time to scroll through it and look at the names of the classes that WMI provides. For example, in the above image, we have highlighted a class Win32_LogicalDisk. This contains all the information related to the machine's logical disks. To get a deeper insight of what this class offers, please double click on that class and another Object editor dialog will appear:

Take a closer look at the Properties panel. All the properties listed here are those we can retrieve. For example, VolumeName will be the name we assigned for a logical disk.

WMI's Win32 Classes have a lot of entries to look through. Some of the most frequently used are:

  • Computer System Hardward Classes, including Cooling Device, Input Device (Keyboard, Mouse, etc), Mass Storage, Motherboard, Networking Device, Printing, Video & Monitor, etc.
  • Installed Application Classes, including Font, etc.
  • Operating System Classes, including Drivers, Memory, Processes, Registry, Users, etc.
  • Performance Counter Classes, including all performance related classes.
  • etc, etc.

We now have a much clearer picture of the structure of the WMI classes and their associated properties.

Programming WMI in PHP

The code snippet below shows some basic information about the logical disks of a remote machine on the IP 192.168.1.4:

<?php
    $pc = "192.168.1.4"; //IP of the PC to manage

    $WbemLocator = new COM ("WbemScripting.SWbemLocator");
    $WbemServices = $WbemLocator->ConnectServer($pc, 'root\\cimv2', 'your account', 'your password');
    $WbemServices->Security_->ImpersonationLevel = 3;

    $disks =    $WbemServices->ExecQuery("Select * from Win32_LogicalDisk");

    foreach ($disks as $d)
    {
        $str=sprintf("%s (%s) %s bytes, %4.1f%% free\n", $d->Name,$d->VolumeName,number_format($d->Size,0,'.',','), $d->FreeSpace/$d->Size*100.0);

        echo $str;
    }

On my system, the above will print out something like:

C: (System) 104,864,059,392 bytes, 60.4% free
D: (Data) 209,719,963,648 bytes, 84.3% free
E: (Misc) 185,521,188,864 bytes, 95.3% free

This is a very simple example but it lays down the fundamental structure and flow of a PHP WMI program.

Firstly, a COM object instance of type WbemScripting.SWbemLocator is created.

Then the connection to the PC will be established via the ConnectServer method. The four parameters for this method call are self explanatory. Finally, we need to set the security impersonation to a proper level. Level 3 is the recommended level for WMI scripts. A detailed explanation of the level is documented here. Level 3 means "Impersonation", which means and we quote:

The server process can impersonate the client's security context on its local system. The server cannot impersonate the client on remote systems.

In short, our script (and the service instance we created) are "impersonating" the user with the account/password provided. Perfect for what we need here.

Please note the code above is the way to create a remote COM connection to manage a remote PC. To manage a local PC, the syntax will be slightly different but not much:

<?php
    $pc = "."; 
    $obj = new COM ("winmgmts:\\\\".$pc."\\root\\cimv2");

    $disks =  $obj->ExecQuery("Select * from Win32_LogicalDisk");
    // Rest of the code is the same as previous remote connection sample

It is somewhat simpler as we don't need to provide a credential and impersonate but this is based on the assumption that the user running this snippet has the Administrator privilege.

To get the classes and their associated data, we have used a WQL (WMI Query Language) statement. It is very similar to SQL statements we issue to a MySQL server but in this case, we are retrieving data from WMI. Win32_LogicalDisk is one "table" in WMI that stores all information related to logical disks. To access data from other tables, please use the name listed in the Query Result dialog as shown above. This also allows us to filter the results. For example, Select * from Win32_LogicalDisk where size > 150000000000 will only return those logical devices with size over 150G (roughly).

The ExecQuery statement, if successful, will return a variant typed object. One downside is that if we try to var_dump that object, PHP will simply print something like object (variant) #3.... Same thing happens when we try to var_dump the $d variable. There is actually nothing useful for further programming in the output.

In actuality, we just need to know that the object is iterable. In this case, when we use a foreach loop, every $d instance will hold an object reference to a logical disk. Then we can access the properties in that logical disk instance with the familiar -> notation. The properties list can be found in the Object editor dialog for that particular class as shown above.

Be sure to spell the class name (Win32_LogicalDisk) and property names (like Size, Name) correctly. Windows is not case sensitive but if we provide the wrong name, an error will be thrown and returned.

As we mentioned earlier, WMI programming can be done with other languages as well – languages like C#, VB Script, etc. However, the WMI COM interface is such a dynamic interface that we can't count on any of these languages to provide a code completion hint to have easy access to all the properties. We have to rely on the dialogues shown above.

One solution to help the programmers is to further encapsulate each WMI class into a PHP class with necessary methods. This should be a very straightforward task and I will leave it to those interested to play around with it.

Conclusion

WMI is a powerful tool holding some of the most hidden secrets kept by the Windows operating system. In a large scale network with homogeneous Windows based machines, we can rely on WMI to retrieve this vital information and help the system admins better manage all the machines.

In this article, we only cover the very basics of WMI and PHP WMI programming but have laid down the fundamentals for further work.

Please leave your comments below if you'd like to see a more detailed WMI tutorial!

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • http://wikichua.gopagoda.com/ Wiki Chua

    great article