wmi tutorial

27
WMI is a very powerfull tool, and once you know how to get what you need, it can be invaluable as a time saver. When developing Windows applications, developers often need information a system, either local or remote, that although commonplace, can be very tough to get. There is using the remote registry, but I myself do not allow remote registry access as do many network admins. WMI is usually wide open on networks, assuming you have the privelidges necessary to query it, just as it is with remote registry querying/editing. And although remote registry querying is very simple, WMI appeals to developers for yet another reason: WQL. 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: Select * from Win32_LogicalDisk 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: Select FreeSpace,Size,Name from Win32_LogicalDisk where DriveType=3 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: (all within System.Management) ConnectionOptions ManagementScope ObjectQuery ManagementObjectSearcher ManagementObjectCollection 1

Upload: kadiyamramana

Post on 07-Apr-2015

411 views

Category:

Documents


1 download

TRANSCRIPT

Page 1: WMI Tutorial

WMI is a very powerfull tool, and once you know how to get what you need, it can be invaluable as a time saver. When developing Windows applications, developers often need information a system, either local or remote, that although commonplace, can be very tough to get. There is using the remote registry, but I myself do not allow remote registry access as do many network admins. WMI is usually wide open on networks, assuming you have the privelidges necessary to query it, just as it is with remote registry querying/editing. And although remote registry querying is very simple, WMI appeals to developers for yet another reason: WQL.

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:

Select * from Win32_LogicalDisk

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:

Select FreeSpace,Size,Name from Win32_LogicalDisk where DriveType=3

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:

(all within System.Management)

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.

The following code shows use the query above on a remote system (MachineX) using user JohnDoe and password JohnsPass:

1

Page 2: WMI Tutorial

//Connection credentials to the remote computer – not needed if the logged in account has accessConnectionOptions oConn = new ConnectionOptions();oConn.Username = "JohnDoe";oConn.Password = "JohnsPass";

System.Management.ManagementScope oMs = new System.Management.ManagementScope("\\MachineX", oConn);    

//get Fixed disk statsSystem.Management.ObjectQuery oQuery = new System.Management.ObjectQuery("select FreeSpace,Size,Name from Win32_LogicalDisk where DriveType=3");

//Execute the query ManagementObjectSearcher oSearcher = new ManagementObjectSearcher(oMs,oQuery);

//Get the resultsManagementObjectCollection oReturnCollection = oSearcher.Get();            //loop through found drives and write out infoforeach( ManagementObject oReturn in oReturnCollection ) {    // Disk name    Console.WriteLine("Name : " + oReturn["Name"].ToString());    // Free Space in bytes    Console.WriteLine("FreeSpace: " + oReturn["FreeSpace"].ToString());    // Size in bytes    Console.WriteLine("Size: " + oReturn["Size"].ToString());}

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:

\\MachineX\root\cimv2 (the double \ is required for string literals in C#)

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 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

2

Page 3: WMI Tutorial

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).

WMI Method Invocation

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.

//Connection credentials to the remote computer – not needed if the logged in account has access ConnectionOptions oConn = new ConnectionOptions(); oConn.Username = "JohnDoe"; oConn.Password = "JohnsPass";

System.Management.ManagementScope oMs = new System.Management.ManagementScope("\\MachineX", oConn);    

//get Process objects System.Management.ObjectQuery oQuery = new System.Management.ObjectQuery("Select * from Win32_Process"); foreach( ManagementObject oReturn in oReturnCollection ) {     //Name of process     Console.WriteLine(oReturn["Name"].ToString().ToLower());     //arg to send with method invoke to return user and domain – below is link to SDK doc on it     string[] o = new String[2];                     //Invoke the method and populate the o var with the user name and domain     oReturn.InvokeMethod("GetOwner",(object[])o);     //write out user info that was returned     Console.WriteLine("User: " + o[1]+ "\" + o[0]);     Console.WriteLine("PID: " + oReturn["ProcessId"].ToString());     //get priority     if(oReturn["Priority"] != null)         Console.WriteLine("Priority: " + oReturn["Priority"].ToString());          //get creation date – need managed code function to convert date –

3

Page 4: WMI Tutorial

    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().ToString());     }          //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.

How To: Connect to a Remote Computer

Remote connections for WMI in .NET Framework are accomplished through the

ManagementScope object.

WMI is intended to monitor the hardware and software on remote computers. Remote connections for WMI in .NET Framework are accomplished through the ManagementScope object.

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

4

Page 5: WMI Tutorial

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(); // and then set the options.Username and // options.Password properties to the correct values // and also set // options.Authority = "ntdlmdomain:DOMAIN"; // and replace DOMAIN with the remote computer's // domain. You can also use kerberose instead // of ntdlmdomain. */

// Make a connection to a remote computer. // Replace the "FullComputerName" section of the // string "\\\\FullComputerName\\root\\cimv2" with

5

Page 6: WMI Tutorial

// the full computer name or IP address of the // remote computer. ManagementScope scope = new ManagementScope( "\\\\FullComputerName\\root\\cimv2"); scope.Connect();

// Use this code if you are connecting with a // different user name and password: // // ManagementScope scope = // new ManagementScope( // "\\\\FullComputerName\\root\\cimv2", options); // scope.Connect();

//Query system for Operating System information ObjectQuery query = new ObjectQuery( "SELECT * FROM Win32_OperatingSystem"); ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope,query);

ManagementObjectCollection queryCollection = searcher.Get(); foreach ( ManagementObject m in queryCollection) { // Display the remote computer information Console.WriteLine("Computer Name : {0}", m["csname"]); Console.WriteLine("Windows Directory : {0}", m["WindowsDirectory"]); Console.WriteLine("Operating System: {0}", m["Caption"]); Console.WriteLine("Version: {0}", m["Version"]); Console.WriteLine("Manufacturer : {0}", m["Manufacturer"]); } }}

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.

6

Page 7: WMI Tutorial

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.

The following code example connects to a remote computer and displays information about the operating system on the remote computer. The code also creates a form to gather the user name and password information for the connection.

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; private System.Windows.Forms.Button cancelButton; private System.ComponentModel.Container components = null;

public MyQuerySample() { InitializeComponent(); }

protected override void Dispose( bool disposing ) { if( disposing ) { if (components != null) {

7

Page 8: WMI Tutorial

components.Dispose(); } } base.Dispose( disposing ); }

private void InitializeComponent() { this.userNameLabel = new System.Windows.Forms.Label(); this.userNameBox = new System.Windows.Forms.TextBox(); this.passwordBox = new System.Windows.Forms.TextBox(); this.passwordLabel = new System.Windows.Forms.Label(); this.OKButton = new System.Windows.Forms.Button(); this.cancelButton = new System.Windows.Forms.Button(); this.SuspendLayout(); // // userNameLabel // this.userNameLabel.Location = new System.Drawing.Point(16, 8); this.userNameLabel.Name = "userNameLabel"; this.userNameLabel.Size = new System.Drawing.Size(160, 32); this.userNameLabel.TabIndex = 0; this.userNameLabel.Text = "Enter the user name for the remote computer:"; // // userNameBox // this.userNameBox.Location = new System.Drawing.Point(160, 16); this.userNameBox.Name = "userNameBox"; this.userNameBox.Size = new System.Drawing.Size(192, 20); this.userNameBox.TabIndex = 1; this.userNameBox.Text = ""; // // 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:";

8

Page 9: WMI Tutorial

// // 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); 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()); }

9

Page 10: WMI Tutorial

private void OKButton_Click(object sender, System.EventArgs e) { try { ConnectionOptions connection = new ConnectionOptions(); connection.Username = userNameBox.Text; connection.Password = passwordBox.Text; connection.Authority = "ntlmdomain:DOMAIN";

ManagementScope scope = new ManagementScope( "\\\\FullComputerName\\root\\CIMV2", connection); scope.Connect();

ObjectQuery query= new ObjectQuery( "SELECT * FROM Win32_Service");

ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);

foreach (ManagementObject queryObj in searcher.Get()) { Console.WriteLine("-----------------------------------"); Console.WriteLine("Win32_Service instance"); Console.WriteLine("-----------------------------------"); Console.WriteLine("Caption: {0}", queryObj["Caption"]); Console.WriteLine("Description: {0}", queryObj["Description"]); Console.WriteLine("Name: {0}", queryObj["Name"]); Console.WriteLine("PathName: {0}", queryObj["PathName"]); Console.WriteLine("State: {0}", queryObj["State"]); 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); } }

private void cancelButton_Click(object sender, System.EventArgs e) { Close(); }

10

Page 11: WMI Tutorial

}}

How To: Call a Method Asynchronously

A client application may call a method asynchronously and carry on other

operations while the method is executing.

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

The following code calls a method asynchronously. The Win32_Process.Create method is called to create a new process for Calc.exe.

using System;using System;using System.Management;

public class InvokeMethodAsync {

public InvokeMethodAsync() {

// Get the object on which the method // will be invoked ManagementClass processClass = new ManagementClass("Win32_Process");

// Create a results and completion handler ManagementOperationObserver handler = new ManagementOperationObserver(); handler.Completed += new CompletedEventHandler(Completed);

// Invoke method asynchronously ManagementBaseObject inParams = processClass.GetMethodParameters("Create"); inParams["CommandLine"] = "calc.exe"; processClass.InvokeMethod(

11

Page 12: WMI Tutorial

handler, "Create", inParams, null); // Do something while method is executing while(!isComplete) { System.Threading.Thread.Sleep(1000); } }

private bool isComplete = false; private ManagementBaseObject returnObject;

// Delegate called when the method completes // and results are available private void NewObject(object sender, ObjectReadyEventArgs e) { Console.WriteLine("New Object arrived!"); returnObject = e.NewObject; }

private void Completed(object sender, CompletedEventArgs e) { isComplete = true; }

public static void Main() { InvokeMethodAsync wmiMethod = new InvokeMethodAsync(); } }

How To: Receive Management Events Without Waiting

A client application may set up an asynchronous subscription and carry on other operations while waiting for events.

To subscribe to events asynchronously

1. Create an event query specifying the events using either the EventQuery class or the WqlEventQuery class.

2. Create a new ManagementEventWatcher instance to subscribe to events matching the query.

3. Set up a listener for events using the EventArrived and the Stopped events of the ManagementEventWatcher class.

12

Page 13: WMI Tutorial

4. Create an event handler class with a method that receives the EventArrivedEventArgs class.

5. Create an instance of the EventArrivedEventHandler delegate that uses the event handler method.

6. Start listening for the events by calling the Start method of the ManagementEventWatcher class.

This is the same operation as calling IWbemServices::ExecNotificationQueryAsync or SWbemServices::ExecNotificationQueryAsync in unmanaged WMI. For more information see, "IWbemServices::ExecNotificationQueryAsync" and "SWbemServices::ExecNotificationQueryAsync" in the Windows Management Instrumentation documentation in the MSDN Library at http://msdn.microsoft.com/library.

7. Perform other operations.8. Call the Stop method of the ManagementEventWatcher class to stop

listening.

This call terminates the subscription.

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;

// This example shows asynchronous consumption of events.// In this example you are listening for timer events.// The first part of the example sets up the timer.public class EventWatcherAsync { public EventWatcherAsync() { // Set up a timer to raise events every 1 second //============================================= ManagementClass timerClass = new ManagementClass("__IntervalTimerInstruction"); ManagementObject timer = timerClass.CreateInstance(); timer["TimerId"] = "Timer1"; timer["IntervalBetweenEvents"] = 1000; timer.Put();

// Set up the event consumer //==========================

13

Page 14: WMI Tutorial

// Create event query to receive timer events WqlEventQuery query = new WqlEventQuery("__TimerEvent", "TimerId=\"Timer1\"");

// Initialize an event watcher and // subscribe to timer events ManagementEventWatcher watcher = new ManagementEventWatcher(query);

// Set up a listener for events watcher.EventArrived += new EventArrivedEventHandler(HandleEvent);

// Start listening watcher.Start();

// Do something in the meantime System.Threading.Thread.Sleep(10000); // Stop listening watcher.Stop(); }

private void HandleEvent(object sender, EventArrivedEventArgs e) { Console.WriteLine("Event arrived !"); }

public static void Main() { EventWatcherAsync eventWather = new EventWatcherAsync(); }}

How To: Receive Management Information Without Waiting

Management operations can be performed asynchronously.

Access to management information often occurs in distributed environments, and might involve large amounts of data. To support this, management operations can also be performed asynchronously.

The method used to invoke an operation asynchronously is an overload of the synchronous method, with an additional parameter of type ManagementOperationObserver for handling the callbacks for results. This object

14

Page 15: WMI Tutorial

defines events for notification when results arrive and on completion. You can create handlers and include them in the subscription that execute when these events are generated.

Example

The following code example demonstrates how to query for management information asynchronously. The query is for instances of the Win32_Service class (services running on the local computer), and the name and state of instances of the Win32_Service class are displayed.

using System;using System.Management;

// This example demonstrates how// to perform an asynchronous instance enumeration.

public class EnumerateInstancesAsync { public EnumerateInstancesAsync() { // Enumerate asynchronously using Object Searcher // ===============================================

// Instantiate an object searcher with the query ManagementObjectSearcher searcher = new ManagementObjectSearcher(new SelectQuery("Win32_Service"));

// Create a results watcher object ManagementOperationObserver results = new ManagementOperationObserver();

// Attach handler to events for results and completion results.ObjectReady += new ObjectReadyEventHandler(NewObject); results.Completed += new CompletedEventHandler(Done);

// Call the asynchronous overload of Get() // to start the enumeration searcher.Get(results); // Do something else while results // arrive asynchronously while (!isCompleted) { System.Threading.Thread.Sleep (1000); }

15

Page 16: WMI Tutorial

Reset(); }

private bool isCompleted = false;

private void NewObject(object sender, ObjectReadyEventArgs obj) { Console.WriteLine("Service : {0}, State = {1}", obj.NewObject["Name"], obj.NewObject["State"]); } private void Reset() { isCompleted = false; }

private void Done(object sender, CompletedEventArgs obj) { isCompleted = true; }

public static void Main() { EnumerateInstancesAsync asyncQuery = new EnumerateInstancesAsync(); } }

How To: Update a Security Descriptor of a WMI Namespace

This example updates the security descriptor information for a namespace so that

certain users can access and modify the namespace.

A security descriptor identifies a securable object's owner and primary group. A functional security descriptor is in binary format, but security descriptors can also be in string format to store and transmit security descriptor information. By changing a security descriptor, you can grant a group certain rights to access a securable object. This example updates the security descriptor information for a namespace so that certain users can access and modify the namespace.

Example

16

Page 17: WMI Tutorial

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{ class Sample { private readonly string namespaceName = "TestNamespaceSecurity";

public void Run() { IntPtr stringSecurityDescriptorPtr = IntPtr.Zero; IntPtr securityDescriptorPtr = IntPtr.Zero; int stringSecurityDescriptorSize = 0; int securityDescriptorSize = 0;

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 " +

17

Page 18: WMI Tutorial

"descriptor of the {0} namespace:", namespaceName); Console.WriteLine(stringSecurityDescriptor); // Grant all permissions to Power Users Group stringSecurityDescriptor += "(A;;CCDCLCSWRPWPRCWD;;;PU)";

//Convert string SD to SD 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);

//Set the new SD for the namespace ManagementBaseObject inParams = systemSecurity.GetMethodParameters("SetSD"); inParams["SD"] = securityDescriptor; outParams = systemSecurity.InvokeMethod("SetSD", inParams, null); if ((uint)outParams["ReturnValue"] != 0) { Console.WriteLine("SetSD returns error: " + outParams["ReturnValue"]); return; } Console.WriteLine("\nNew string security descriptor" + " is set. Press Enter to exit."); Console.ReadLine(); } finally { // Free unmanaged memory if (securityDescriptorPtr != IntPtr.Zero) { Marshal.FreeHGlobal(securityDescriptorPtr); securityDescriptorPtr = IntPtr.Zero; } if (stringSecurityDescriptorPtr != IntPtr.Zero) { Marshal.FreeHGlobal(stringSecurityDescriptorPtr); stringSecurityDescriptorPtr = IntPtr.Zero; } this.DeleteTestNamespace();

18

Page 19: WMI Tutorial

} }

public void ConvertSDtoStringSD(byte[] securityDescriptor, out IntPtr stringSecurityDescriptorPtr, out int stringSecurityDescriptorSize) { 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()); } }

public void ConvertStringSDtoSD( string stringSecurityDescriptor, out IntPtr securityDescriptorPtr, out int securityDescriptorSize) { bool result = ConvertStringSecurityDescriptorToSecurityDescriptor( stringSecurityDescriptor, 1, out securityDescriptorPtr, out securityDescriptorSize); if (!result) { Console.WriteLine( "Fail to convert string SD to SD:" ); throw new Win32Exception( Marshal.GetLastWin32Error()); } }

private enum SecurityInformation : uint { OWNER_SECURITY_INFORMATION= 0x00000001, GROUP_SECURITY_INFORMATION= 0x00000002,

19

Page 20: WMI Tutorial

DACL_SECURITY_INFORMATION= 0x00000004, SACL_SECURITY_INFORMATION= 0x00000008, PROTECTED_DACL_SECURITY_INFORMATION= 0x80000000, PROTECTED_SACL_SECURITY_INFORMATION= 0x40000000, UNPROTECTED_DACL_SECURITY_INFORMATION= 0x20000000, 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 );

private void CreateTestNamespace() { ManagementClass rootNamespace = new ManagementClass("root:__namespace"); ManagementObject testNamespace = rootNamespace.CreateInstance(); testNamespace["Name"] = namespaceName; testNamespace.Put(); }

private void DeleteTestNamespace() { ManagementObject testNamespace = new ManagementObject("root:__namespace.Name='" + namespaceName + "'"); try { testNamespace.Get(); testNamespace.Delete(); }

20

Page 21: WMI Tutorial

catch (ManagementException e) { if (e.ErrorCode == ManagementStatus.NotFound) return; } }

[STAThread] static void Main(string[] args) { try { new Sample().Run(); } catch (Win32Exception e) { Console.WriteLine(e); } } }}

How To: Use Strongly-Typed Objects

WMI provides the ability to automatically generate early-bound wrappers for WMI

objects.

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.

Strongly-typed wrappers allow applications to take advantage of all the available Microsoft Visual Studio assistance features, such as Intellisense and object browsing, for strongly-typed objects. For example, you can generate an early-bound wrapper for the Win32_Service class so that you can use Intellisense to quickly find the class members in the Win32_Service class. The following list contains the tools and methods that generate wrappers for WMI objects.

The MgmtClassGen.exe tool in the .NET Framework SDK.For more information, see Management Strongly Typed Class Generator (Mgmtclassgen.exe). For more information about the code generated by the MgmtClassGen.exe tool, see Code Generated from MgmtClassGen.exe.

The Server Explorer Management extension in Visual Studio .NET, which automatically generates the wrapper when dragging a management class into your designer. To download the Server Explorer Management extension for

21

Page 22: WMI Tutorial

Visual Studio, go to http://www.microsoft.com/downloads/details.aspx?familyid=62d91a63-1253-4ea6-8599-68fb3ef77de1

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):

C:\> MgmtClassGen Win32_Service /L CS /N root\cimv2 /P C:\temp\service.cs

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); } public static void Main(string[] args) { Sample test = new Sample(); test.EnumerateServices(); return; }}

22

Page 23: WMI Tutorial

23