Select From: Win32 - Logicaldisk
Select From: Win32 - Logicaldisk
Select From: Win32 - Logicaldisk
WQL allows us to query WMI providers using a SQL-like query language. If you
know the provider classes and the fields available, then you can get the info very
easily. For instance, if you wanted to get a list of logical drives from a system you
would use the following query:
You can, however, refine the search by using where clauses and getting specific
"fields" in the query. The following query gets the amount of freespace, the size,
and the name of all fixed disk drives:
As you can see, constructing a simple WMI query is quite easy. To get results, you
need and interface, and in .Net it is provided by the System.Management
namespace. To make it work all you need is a query, and a little bit of code, just as
if you were querying a database.
You need a few different objects to perform WMI queries in .Net. They include the
following:
ConnectionOptions
ManagementScope
ObjectQuery
ManagementObjectSearcher
ManagementObjectCollection
ManagementObject
Though this may seem like a lot of objects to perform a simple query, it is quite
easy in practice. I will not go into great detail on the objects (you can review each
object in the .Net documentation as they are documented very thoroughly). I will
attempt to show a very easy way of utilizing these objects to query WMI providers
as well as perform intristic methods available on some of the objects.
1
The following code shows use the query above on a remote system (MachineX)
using user JohnDoe and password JohnsPass:
As you can see, the code is not that difficult. This, although a simple query, would
save a lot of time compared to other methods, especially when querying a remote
machine. Please note that usually the ManagementScope would require a WMI
namespace in addition to the machine name, but .Net kindly defaults to the root
namespace. If you wish to use it anyway you would use the following scope:
One of the problems I had in using WMI with .Net was not knowing what "fields"
were available for a given object. I found the class reference on MSDN and all the
problems went away, at least most of them. Data type conversions can be a
problem, especially with datetime structures. Datetime data types from WMI
2
providers are not compatible with .Net DateTime variables. You must use a
managed function that you can get from my sample code or by using the
mgmtclassgen utility that comes with the .Net SDK (Thanks to Chetan Parmar for
this info and the code). Also, some objects will return null in some fields, so make
sure to check for it (see sample code i.e WMITest.zip).
Another interesting feature of WMI is the methods that are available with certain
objects. For instance, with the Win32_Process object you can use the GetOwner
method to return the name and domain of the user under whom the process is
running. You must use the Invoke method of the ManagementObject object and
send it the proper parameters. In this case you are only required to send the name
of the method as a string ("GetUser") and a 2 element string array for the return.
Don't be fooled. Even though the array would seem to be used as a ref variable,
you do not have to declare that way when calling the Invoke method.
Below is a sample of getting all processes along with the name, user and domain,
memory used, priority, and process id for each process. This information is similar
to what you see in the task manager. If you want CPU usage you have to use the
Win32_PerfFormattedData_PerfProc_Process class which is actually a WMI
interface for the perfomance counters. I do not use this class because the
GetOwner method is not available with it.
3
//get priority
if(oReturn["Priority"] != null)
Console.WriteLine("Priority: " + oReturn["Priority"].ToString());
//get creation date – need managed code function to convert date –
if(oReturn["CreationDate"] != null)
{
//get datetime string and convert
string s = oReturn["CreationDate"].ToString();
//see ToDateTime function in sample code
DateTime dc = ToDateTime(s);
//write out creation date
Console.WriteLine("CreationDate: " + dc.AddTicks(-
TimeZone.CurrentTimeZone.GetUtcOffset(DateTime.Now).Ticks).ToLocalTime().ToS
tring());
}
//this is the amount of memory used
if(oReturn["WorkingSetSize"] != null)
{
ass="keyword">long mem =
Convert.ToInt64(oReturn["WorkingSetSize"].ToString()) / 1024;
Console.WriteLine("Mem Usage: {0:#,###.##}Kb",mem);
}
}
There is a wealth of information waiting to be gleaned from WMI and it is far easier
than using several API calls or remote registry calls. WMI simplifies things by
making all common information handy in one place. You can get all system info,
partition info, processor stats, profile settings, and much more using WMI. WMI
can replace performance counters as well. Once you get used to it, browse through
the MSDN WMI class reference and you are sure to find what you are looking for in
most cases.
4
Connecting remotely using classes in the System.Management namespace uses
DCOM as the underlying remote mechanism. WMI remote connections must comply
with DCOM security requirements for impersonation and authentication.
Connections between different operating systems, for example, a connection
initiated from a Windows 2000 computer to a Windows Server 2003 computer, may
require a different impersonation and authentication level than a connection
between two Windows 2000 computers. For more information, see "Connecting to
WMI on a Remote Computer" in the Windows Management Instrumentation
documentation in the MSDN Library at http://msdn.microsoft.com/library.
By default, a scope is bound to the local computer and the root\cimv2 system
namespace. However, you can either change the namespace in the string that
specifies the constructor path or use a ManagementPath object. The string used for
the path follows the same rules as other WMI paths and backslashes (\) must be
escaped. For more information, see "Describing the Location of a WMI Object" in
the Windows Management Instrumentation documentation in the MSDN Library at
http://msdn.microsoft.com/library.
In addition, when connecting remotely, you can specify credentials for a user other
than the currently logged-on user, and the operations on that remote machine will
be performed in the context of the specified user. This can be done using a
ConnectionOptions object.
Example
The following code example connects to a remote computer in the same domain as
the user and displays information about the operating system on the remote
computer. The user must be an administrator on the remote computer for the
connection to be made.
using System;
using System.Management;
public class RemoteConnect
{
public static void Main()
{
/*// Build an options object for the remote connection
// if you plan to connect to the remote
// computer with a different user name
// and password than the one you are currently using
ConnectionOptions options =
new ConnectionOptions();
5
// options.Authority = "ntdlmdomain:DOMAIN";
// and replace DOMAIN with the remote computer's
// domain. You can also use kerberose instead
// of ntdlmdomain.
*/
6
The ConnectionOptions object also controls the impersonation and authentication
levels used by WMI in the remote DCOM operations. The default settings for these
parameters are Impersonate and Unchanged respectively.
The Unchanged value means that the client defaults to the server's requirements
for authentication, using the standard DCOM negotiation process. On Windows
2000, Windows NT 4.0, and Windows 98, the WMI service will request Connect level
authentication, while on Windows XP Home Edition, Windows XP Professional,
Windows Server 2003 and Windows Server 2003 it will request Packet level
authentication. If the client requires a specific authentication setting, the
Authentication property on the ConnectionOptions object can be used to control
the authentication level on this particular connection.
The Impersonate value means that the client allows the WMI data provider to
impersonate its identity when gathering the requested information. This default
setting is advantageous when the provider is a trusted application or service
because it eliminates the need for the provider to perform explicit identity and
access checks when retrieving information for this client. However, if the addressed
provider or instrumented application cannot be trusted for some reason, allowing it
to impersonate the client may constitute a security threat. In such cases, we
recommend that the impersonation level be changed by the client application to a
lower value, such as Identify. Note that this may lead to the inability to access
information from certain providers, in cases where the provider does not perform
access checks or does not have sufficient permissions in its own running context to
retrieve the requested information.
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Management;
namespace WMISample
{
public class MyQuerySample : System.Windows.Forms.Form
{
private System.Windows.Forms.Label userNameLabel;
private System.Windows.Forms.TextBox userNameBox;
private System.Windows.Forms.TextBox passwordBox;
private System.Windows.Forms.Label passwordLabel;
private System.Windows.Forms.Button OKButton;
7
private System.Windows.Forms.Button cancelButton;
public MyQuerySample()
{
InitializeComponent();
}
8
// passwordBox
//
this.passwordBox.Location = new System.Drawing.Point(160, 48);
this.passwordBox.Name = "passwordBox";
this.passwordBox.PasswordChar = '*';
this.passwordBox.Size = new System.Drawing.Size(192, 20);
this.passwordBox.TabIndex = 3;
this.passwordBox.Text = "";
//
// passwordLabel
//
this.passwordLabel.Location = new System.Drawing.Point(16, 48);
this.passwordLabel.Name = "passwordLabel";
this.passwordLabel.Size = new System.Drawing.Size(160, 32);
this.passwordLabel.TabIndex = 2;
this.passwordLabel.Text =
"Enter the password for the remote computer:";
//
// OKButton
//
this.OKButton.Location = new System.Drawing.Point(40, 88);
this.OKButton.Name = "OKButton";
this.OKButton.Size = new System.Drawing.Size(128, 23);
this.OKButton.TabIndex = 4;
this.OKButton.Text = "OK";
this.OKButton.Click +=
new System.EventHandler(this.OKButton_Click);
//
// cancelButton
//
this.cancelButton.DialogResult =
System.Windows.Forms.DialogResult.Cancel;
this.cancelButton.Location = new System.Drawing.Point(200, 88);
this.cancelButton.Name = "cancelButton";
this.cancelButton.Size = new System.Drawing.Size(128, 23);
this.cancelButton.TabIndex = 5;
this.cancelButton.Text = "Cancel";
this.cancelButton.Click +=
new System.EventHandler(this.cancelButton_Click);
//
// MyQuerySample
//
this.AcceptButton = this.OKButton;
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.CancelButton = this.cancelButton;
this.ClientSize = new System.Drawing.Size(368, 130);
this.ControlBox = false;
this.Controls.Add(this.cancelButton);
9
this.Controls.Add(this.OKButton);
this.Controls.Add(this.passwordBox);
this.Controls.Add(this.passwordLabel);
this.Controls.Add(this.userNameBox);
this.Controls.Add(this.userNameLabel);
this.Name = "MyQuerySample";
this.StartPosition =
System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Remote Connection";
this.ResumeLayout(false);
[STAThread]
static void Main()
{
Application.Run(new MyQuerySample());
}
ManagementObjectSearcher searcher =
new ManagementObjectSearcher(scope, query);
10
Console.WriteLine("Status: {0}", queryObj["Status"]);
}
Close();
}
catch(ManagementException err)
{
MessageBox.Show("An error occured while querying for WMI data: "
+ err.Message);
}
catch(System.UnauthorizedAccessException unauthorizedErr)
{
MessageBox.Show("Connection error " +
"(user name or password might be incorrect): " +
unauthorizedErr.Message);
}
}
You can asynchronously call a method using WMI in .NET Framework by passing in
an instance of the ManagementOperationObserver class into the InvokeMethod
method. By calling a method asynchronously, you can complete other tasks while
the method is being called and executed. If you call the method semisynchronously,
you must wait for the method to finish executing before you start any other tasks.
For information on calling a method semisynchronously, see How To: Execute a
Method.
Example
using System;
using System;
using System.Management;
11
public class InvokeMethodAsync
{
public InvokeMethodAsync()
{
12
public static void Main()
{
InvokeMethodAsync wmiMethod = new InvokeMethodAsync();
}
1. Create an event query specifying the events using either the EventQuery
class or the WqlEventQuery class.
3. Set up a listener for events using the EventArrived and the Stopped events of
the ManagementEventWatcher class.
6. Start listening for the events by calling the Start method of the
ManagementEventWatcher class.
13
8. Call the Stop method of the ManagementEventWatcher class to stop
listening.
Example
The following asynchronous code example sets up a WMI timer to raise an event
every second, and removes it when it is no longer needed. The
ManagementEventWatcher object defines several .NET Framework events, which
are raised when WMI events are delivered. Delegates are attached to these events
for handling the incoming data.
using System;
using System.Management;
14
// Start listening
watcher.Start();
// Stop listening
watcher.Stop();
Example
using System;
15
using System.Management;
Reset();
}
16
obj.NewObject["State"]);
}
Example
The following code example grants all rights to the Power Users group for a given
namespace. To change the namespace name, change the value of the
namespaceName variable in the Sample class.
using System;
using System.Management;
using System.ComponentModel;
using System.Runtime.InteropServices;
namespace ManagementSample
{
17
class Sample
{
private readonly string namespaceName =
"TestNamespaceSecurity";
try
{
// Create a test namespace
this.CreateTestNamespace();
// Retreive SD of a namespace
ManagementClass systemSecurity =
new ManagementClass("root/" +
namespaceName + ":__SystemSecurity");
ManagementBaseObject outParams =
systemSecurity.InvokeMethod("GetSD",
null, null);
if ((uint)outParams["ReturnValue"] != 0)
{
Console.WriteLine("GetSD returns an error: " +
outParams["ReturnValue"]);
return;
}
// Convert SD to string SD
this.ConvertSDtoStringSD((byte[])outParams["SD"],
out stringSecurityDescriptorPtr,
out stringSecurityDescriptorSize);
string stringSecurityDescriptor =
Marshal.PtrToStringAuto(
stringSecurityDescriptorPtr);
Console.WriteLine("Original string security " +
"descriptor of the {0} namespace:",
namespaceName);
Console.WriteLine(stringSecurityDescriptor);
//Convert string SD to SD
18
Console.WriteLine(
"\nNew String Security Descriptor:");
Console.WriteLine(stringSecurityDescriptor);
this.ConvertStringSDtoSD(stringSecurityDescriptor,
out securityDescriptorPtr,
out securityDescriptorSize);
byte[] securityDescriptor =
new byte[securityDescriptorSize];
Marshal.Copy(securityDescriptorPtr,
securityDescriptor, 0, securityDescriptorSize);
19
bool result =
ConvertSecurityDescriptorToStringSecurityDescriptor(
securityDescriptor,
1,
SecurityInformation.DACL_SECURITY_INFORMATION |
SecurityInformation.GROUP_SECURITY_INFORMATION |
SecurityInformation.OWNER_SECURITY_INFORMATION |
SecurityInformation.SACL_SECURITY_INFORMATION,
out stringSecurityDescriptorPtr,
out stringSecurityDescriptorSize);
if (!result)
{
Console.WriteLine( "Fail to convert" +
" SD to string SD:" );
throw new Win32Exception(
Marshal.GetLastWin32Error());
}
}
20
UNPROTECTED_SACL_SECURITY_INFORMATION= 0x10000000,
};
[DllImport("Advapi32.dll", CharSet=CharSet.Auto,
SetLastError=true, ExactSpelling=false)]
private static extern bool
ConvertSecurityDescriptorToStringSecurityDescriptor(
[In] byte[] SecurityDescriptor,
[In] int RequestedStringSDRevision,
[In] SecurityInformation SecurityInformation,
[Out] out IntPtr StringSecurityDescriptor,
[Out] out int StringSecurityDescriptorLen
);
[DllImport("Advapi32.dll", CharSet=CharSet.Auto,
SetLastError=true, ExactSpelling=false)]
private static extern bool
ConvertStringSecurityDescriptorToSecurityDescriptor(
[In] string StringSecurityDescriptor,
[In] uint StringSDRevision,
[Out] out IntPtr SecurityDescriptor,
[Out] out int SecurityDescriptorSize
);
21
return;
}
}
[STAThread]
static void Main(string[] args)
{
try
{
new Sample().Run();
}
catch (Win32Exception e)
{
Console.WriteLine(e);
}
}
}
}
To serve customer extensibility needs, WMI objects are typically late-bound, which
does not force strong typing. In the .NET Framework environment, WMI provides
the ability to automatically generate early-bound wrappers for WMI objects.
22
Programmatically through the GetStronglyTypedClassCode methods.
The wrappers are implemented as managed code classes, and as such provide
multilanguage support so that they can be used with any programming language.
Example
The following code example includes the strongly-typed Service class, which is a
wrapper for the Win32_Service class. Before running this example, you must
generate a specific class using the following command in the Visual Studio 2005
command prompt (change the value of the code language depending on whether
you want to generate a C# or Visual Basic .NET code file):
The output of the generator tool will be the service.cs code file, which you should
then add to your project along with the code below. Notice the usage of the
strongly typed "Service" class in the foreach statement instead of the generic
ManagementObject class, and the simplified standard dot notation access to the
properties of the returned objects.
using System;
using ROOT.CIMV2.Win32;
// Contains the strongly typed generated class "Service"
// in ROOT.CIMV2.Win32 namespace. This namespace was
// generated using the MgmtClassGen tool for
// the Win32_Service class
class Sample
{
// Enumerate instances of Win32_Service class
void EnumerateServices()
{
Console.WriteLine("List services and their state");
foreach(Service ser in Service.GetInstances())
Console.WriteLine(
"Service: "+ ser.Name + " is " + ser.State);
}
23
24