Introduction
Recently while doing the inventory of several servers I realized that it was cumbersome to have to logon to each one and access the default Windows System Information utility. After doing some reading I found the WMI classes in the MSDN. While these classes allow for access to just about anything on a Windows operating system, they require a lot of typing and calls to multiple WMI classes. There is not just one place to access everything. This class solves that.
Background
This class is a wrapper for some basic WMI interfaces that provide detailed information about a system. The class allows the use of the current security context or a specified username and password to access either a remote or a local machine. Once connected the class is then populated with the values from the machine. This gives us an easy way of accessing the information without having to worry about the calls to each of the WMI interfaces or how to store all the information. I'm not going to spend a lot of time explaining the WMI or what it is other than that it is an interface for accessing the management information on a computer. If you would like to have more reading material on the subject then check out the following link Windows Management Instrumentation at Microsoft.
Using the code
This wrapper class is designed to allow for easy access of information related to a system whether local or remote. Once the class is initialized with the data for a system there is no further need to connect to the machine. If done separately a user would need to make multiple calls to each WMI interface and then store the information. The class does all of this.
To begin using the code start by adding a reference to the DLL. To populate the class with the information from the local machine pass in the loopback address as shown below:
SystemInformation sysinfo = new SystemInformation();
sysinfo.stderr = Console.OpenStandardOutput();
if (sysinfo.Get("127.0.0.1") != 0)
{
MessageBox.Show("Error getting system information.", "System Information");
}
else
{
MessageBox.Show("The current OS version is " + sysinfo.OSVersion.description,
"System Information");
}
...
In the above example the stderr
property is also set. This property allows the internal errors that occur in the wrapper class to be piped to an external stream. In this example, the standard output stream for the Console
class is used. This little trick allows for internal debugging information to be controlled by the calling application so that the SystemInformation
class doesn't have to worry about logging.
To access the information from a remote machine you can simply change the address given above to that of an external machine. When connecting to a remote or local machine the current user context is used to authenticate the user. This presents a problem if the current context does not have access to the machine. An example of this would be if this class was used in a web application which was running as the anonymous user account. To solve this problem you may pass user credentials to the Get
method of the class as shown below:
SystemInformation sysinfo = new SystemInformation();
sysinfo.stderr = Console.OpenStandardOutput();
if (sysinfo.Get("127.0.0.1", "johndoe", "password") != 0)
{
MessageBox.Show("Error getting system information.", "System Information");
}
else
{
MessageBox.Show("The current OS version is " + sysinfo.OSVersion.description,
"System Information");
}
...
When this is done the user specified is impersonated when connecting to the remote machine. The username can also contain a domain if necessary. Note that if the username or password are not valid on the machine you're connecting to the method will fail and return an error code.
The included demo project connects to the local machine and displays all the information in a similar fashion as seen by the System Information utility included with Windows. It is not a fully complete application but only meant to demonstrate the capabilities of the class.
Points of interest
The above code has only been tested against Windows 2000/2003/XP. It may work against other systems but I have not tested it. Also in order for the class to connect to a computer the RPC service must be available. Most firewalls block this sort of traffic.
In the class you may notice that there are two methods used to query the interfaces. The first method uses the ManagementObjectSearcher
class. The other uses the ManagementClass
class. While both of these perform similar tasks and return a collection of data the latter does not allow the specification of which attributes to return or the use of a filter by means of the WHERE
clause.
One problem I saw when querying the data was that the time zone was returned as an integer representing the offset from GMT time in minutes. While this is nice if you need to do computations, it is difficult to read. While it would have been possible to build a static list of all the time zones out there and map the value to this I chose to do it in a more dynamic way. Windows stores all the information on the time zones in the registry. By querying these keys you can determine what time zone the minutes map to. The structure populates with the time zone information, therefore contains both the offset in minutes and the readable version of the time zone.
History
- v1.0
- Initial build. Nothing special.
- v1.1
- Added the ability to get the standard time zone name from the registry to make it more readable.
- v1.2
- The class now returns proper error codes and has a new method to allow access to the extended error code if applicable. Also modified the sample to allow connecting to multiple hosts.