|
. NET System Management Services Authors: Golomshtok A. Published year: 2005 Pages: 14/79 |
From the day WMI was introduced, the developers that wished to access Windows management resources and services had a choice. They could either use the native WMI COM interfaces or they could use its scripting API. Unfortunately, neither of these two options was completely problem-free. Though the COM API offered virtually unlimited power, high performance, and access to even the most obscure features of WMI, it was, after all, just another COM API. As such, it remained completely inaccessible to millions of those poor developers and system administrators who never managed to overcome the COM's steep learning curve.
The scripting API partly solved this problem bringing the joy of WMI to an audience that was much wider than just a bunch of skilled C++ programmers. However, even the scripting, despite its simplicity and adequate power, did not appear to be a complete solution to the problem. First, it was not fast enough. This was because the dispatch interfaces that were necessary for scripting clients did not offer the same speed as the native COM API. Second, the scripting API lacked power and covered only a limited subset of WMI functionality. In fact, certain things, such as provider programming, remained outside the realm of script developers and still required the use of native COM interfaces.
The rollout of the Microsoft .NET Platform took the world of software development by storm . It is rapidly (and hopefully forever) changing the Windows programming paradigm. Programmatic access to WMI was one of the million things that has been radically affected by .NET. Using .NET you can completely replace both the native COM and the scripting APIs. In the true spirit of .NET, the new WMI interface combines the best features of the older APIs; it merges the performance and unlimited power of the native COM API with accessibility and simplicity of the scripting interface.
There are two parts to the .NET Platform: the .NET Framework and the Framework Class Library (FCL). While the Framework, which supports such modern programming concepts as automatic garbage collection, seamless language interoperability, and much more, is definitely the enabling technology behind .NET, it remains fairly transparent to a casual developer.
The FCL, on the other hand, is something that a programmer will immediately appreciate; it exposes thousands of classes or types that address nearly every aspect of Windows programming. There are types that deal with GUI and Web Interface development, types that are designed to make working with structured and unstructured data easier, and, most importantly, there are types that are dedicated solely to interfacing with WMI. The entire FCL is structured so that functionally related types are organized into a hierarchy of namespaces. For instance, types that are used to build Windows GUI applications are grouped into the System.Windows.Forms namespace. The namespace that holds all the types that you need to interact with WMI is called System.Management . The most important types, contained in the System.Management namespace, are the following:
ManagementObject : This is a fundamental abstraction that is used to represent a single instance of a managed element in the enterprise.
ManagementClass : This is a type that corresponds to a class of a managed object as defined by WMI.
ManagementObjectSearcher : This type is used to retrieve collections of ManagementObject or ManagementClass objects that satisfy particular criteria specified by a WQL query or enumeration.
ManagementQuery : This type is used as a basis for building queries, used to retrieve collections of ManagementObject or ManagementClass objects.
ManagementEventWatcher : This is a type that allows you to subscribe for WMI event notifications.
Nested inside the System.Management namespace, there is a System.Management.Instrumentation namespace that contains types that are used primarily when you instrument .NET applications. These types allow application developers to use WMI to expose the management data relevant to their applications as well as their application-originated events; this process makes these events and data accessible to a wide variety of management clients.
Because the .NET model used to expose the management applications is mainly declarative, you will not need to use much coding. The System.Management.Instrumentation namespace includes a number of attribute types; developers can use these to describe their managed objects and classes to WMI using simple declarations. In addition to these attributes, this namespace defines a number of schema types that are designed to serve as base types for custom managed classes. These schema types already include all necessary attribution so that WMI immediately recognizes any custom type that uses a schema type as its parent.
As the next few chapters will show, the .NET System.Management types offer enough building blocks to solve nearly any system management problem, and backed by the power and flexibility of the .NET Framework, these types are likely to become the ultimate platform that will be used to develop future management applications. While the Framework addresses such general programming issues as automatic memory management, language interoperability, security, and distribution and maintenance ease, the System.Management namespace of the FCL brings the following benefits to the developers of management applications:
Consistent object-oriented programming model: As is most of the FCL, the System.Management namespace is organized in a very consistent fashion and exposes a well designed object model, which is both natural and easy to understand. The types are well thought-out logical abstractions that are not affected by the peculiarities of the WMI inner workings; as a result they are fairly self-describing and comprehensible. The overall programming paradigm is consistent with the rest of the .NET programming model. This consistency makes it so that .NET developers do not need to learn new skills in order to start programming management applications.
Relative simplicity: Unlike COM programming, in .NET, developers no longer have to take care of the low-level plumbing, such as memory management via reference counting. Instead, they can concentrate on the problem domain. The .NET programming model not only greatly minimizes the amount of boilerplate code for which the developers are responsible, but it also reduces the level of complexity to match that of the legacy WMI scripting API.
Uncompromised performance: When compared to the WMI scripting interface, the .NET System.Management types offer a significant performance enhancement. This is because you no longer a need to use the inefficient dispatch interfaces in order to access the WMI functionality. However, this does not mean that the System.Management types completely solve the performance problems associated with WMI. Unfortunately, some of these performance problems have little to do with the API used by the client applications. The dynamic class providers, for example, are inherently slow and no matter how fast a client application may be able to process the data, the overall efficiency is still hampered by the necessity to generate the class definitions on the fly. Nevertheless, .NET System.Management types offer a significant performance improvement over the less efficient scripting clients.
Tight integration with .NET: The types of System.Management namespace are an integral part of FCL and as a result, they comply with all the .NET programming principles and interoperate seamlessly with the types in other namespaces. Typically, even the simplest management application still has to possess some basic user interface capabilities and may request the operating system or I/O subsystem services. Thus, the FCL offers a complete end-to-end solution to the entire universe of programming problems by making thousands of uniformly designed types available. These types adhere to consistent naming conventions, all use the same error handling protocol, feature the same event dispatching and notification mechanism, and most importantly, are designed to work together.
So with a complete arsenal of powerful tools at their disposal, programmers can build and deploy large-scale enterprise management systems.
As you can see, the .NET System.Management types represent an important step toward turning Microsoft Windows into the number one enterprise-computing platform. However, so far I have still yet to answer a couple of questions: How exactly do these types interact with WMI? Also, did Microsoft intend to replace some or all of the WMI infrastructure with .NET-compliant implementation, or was the System.Management namespace designed to work in concert with the existing WMI components ? The best way to answer these questions is by taking a closer look at how the System.Management types are implemented.
As I already mentioned, the main COM interface that the client and provider applications use to access WMI is IWbemServices . This interface exposes a slew of methods that let you issue queries so that you can retrieve collections of classes and instances, create and delete instances, update instance properties, and execute methods . Thus, perhaps the first thing you would want to do with any WMI application is make sure that it gets hold of an object that implements the IWbemServices interface. Listing 1-5 shows a common coding pattern that you can use to retrieve an IWbemServices interface pointer. Note that for the sake of simplicity, this example does not implement the proper error checking and does not include the code necessary to initialize the variables used in the method calls.
Listing 1-5: Retrieving an IWbemServices Interface Pointer
|
|
IWbemLocator *pIWbemLocator = NULL; IWbemServices *pIWbemServices = NULL; CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &pIWbemLocator); ... pIWbemLocator->ConnectServer(pNamespace, NULL, NULL, 0L, 0L, NULL, NULL, &pIWbemServices);
|
|
Here the client application first creates an instance of WbemLocator class, which you then use to retrieve the IWbemServices interface pointer. The WbemLocator class implements the IWbemLocator interface, which has a single method, ConnectServer . The purpose of this method is to establish a connection to WMI services on a particular host. This method takes several parameters, namely the full path of the namespace to which the client application wishes to connect ( pNamespace ), the address of the memory location that will hold the IWbemServices interface pointer ( pIWbemServices ), and a few security-related arguments, which our example ignores completely. When the ConnectServer method finishes successfully, the pIWbemServices variable will contain a valid IWbemServices interface pointer that you can use as a main point of access to WMI services and resources.
Now let's see how a typical .NET application accomplishes the same task of connecting to WMI.The type in the System.Management namespace that is responsible for connecting to WMI is ManagementScope , and Listing 1-6 shows the disassembly of the class definition and one of its methods, called InitializeGuts . The disassembly listing, which contains Microsoft Intermediate Language (MSIL) instructions, was produced using ILDASM.EXE utility, distributed as part of the .NET Framework SDK.. The MSIL instruction sequences may look quite intimidating at first; however, given the limited amount of documentation that comes with .NET, disassembling various parts of FCL is an excellent source of in-depth information, pertinent to the implementation of certain .NET features. Throughout the course of this book, I will occasionally resort to using such disassembly listings to help you better understand the underpinnings of the System.Management types. The disassembly listing, presented here, is not a complete implementation of the ManagementScope type or its InitializeGuts method—for the sake of simplicity, only the code fragments relevant to the WMI connection establishment process are shown.
Listing 1-6: Disassembly of the InitializeGuts Method of the ManagementScope Type
|
|
.class public auto ansi beforefieldinit ManagementScope extends [mscorlib]System.Object implements [mscorlib]System.ICloneable { ... .field private class System.Management.IWbemServices wbemServices ... .method private hidebysig instance void InitializeGuts() cil managed { // Code
size
268 (0x10c) .maxstack 9 .locals init ( class System.Management.IWbemLocator V_0, string V_1, bool V_2, class System.Management.SecurityHandler V_3, int32 V_4, class [mscorlib]System.Exception V_5) newobj instance void System.Management.WbemLocator::.ctor() stloc.0 ... ldflda class System.Management.IWbemServices System.Management.ManagementScope::wbemServices callvirt instance int32 System.Management.IWbemLocator::ConnectServer_( string, string, string, string, int32, string, class System.Management.IWbemContext, class System.Management.IWbemServices&) ... } ... }
|
|
The first important thing you should notice about Listing 1-6 is that there is a private member variable wbemServices of type System.Management.IWbem Services declared using the .field directive at the beginning of the class definition. This variable has been designed to hold what appears to be a reference to an instance of IWbemServices type. Also, at the very beginning of the InitializeGuts method, notice that a local variable of type System.Management.IwbemLocator is declared. This declaration marks a storage location that will hold a reference to the IWbemLocator instance. The first executable instruction of the InitializeGuts method, newobj , creates an instance of the System.Management.IWbemLocator type and invokes its constructor. When this operation finishes, the operands stack will hold a reference to a newly created instance of the IWbemLocator type. The method will now execute its next instruction, stloc.0 . This pops the reference off the stack and stores it at the location that is designated by the first local variable declaration— V_0 of type System.Management.IWbemLocator . Then the code loads the wbemServices member variable's address on the stack using the ldflda instruction and calls the ConnectServer method of the IWbemServices type, which passes this address as one of the parameters. When the ConnectServer method returns, the wbemServices variable contains a valid reference to IWbemServices type.
The code described here is very similar to the native COM API code, shown previously in Listing 1-5; in fact, the interface usage pattern is the same! Now, the only thing that remains unclear is how the IWbemLocator and IWbemServices types are implemented in the System.Management namespace. Listing 1-7 should clear this up by showing the complete class declarations for System.Management.IWbemLocator and System.Maagement.IWbemServices types.
Listing 1-7: IWbemLocator and IWbemServices Class Declarations
|
|
.class interface private abstract auto ansi import IWbemLocator { .custom instance void [mscorlib] System.Runtime.InteropServices.TypeLibTypeAttribute::.ctor(int16) = ( 01 00 00 02 00 00 ) .custom instance void [mscorlib] System.Runtime.InteropServices.GuidAttribute::.ctor(string) = ( 01 00 24 44 43 31 32 41 36 38 37 2D 37 33 37 46 // ..$DC12A687-737F 2D 31 31 43 46 2D 38 38 34 44 2D 30 30 41 41 30 // -11CF-884D-00AA0 30 34 42 32 45 32 34 00 00 ) // 04B2E24.. .custom instance void [mscorlib] System.Runtime.InteropServices.InterfaceTypeAttribute::.ctor(int16) = ( 01 00 01 00 00 00 ) } .class interface private abstract auto ansi import IWbemServices { .custom instance void [mscorlib] System.Runtime.InteropServices.InterfaceTypeAttribute::.ctor(int16) = ( 01 00 01 00 00 00 ) .custom instance void [mscorlib] System.Runtime.InteropServices.TypeLibTypeAttribute::.ctor(int16) = ( 01 00 00 02 00 00 ) .custom instance void [mscorlib] System.Runtime.InteropServices.GuidAttribute::.ctor(string) = ( 01 00 24 39 35 35 36 44 43 39 39 2D 38 32 38 43 // ..56DC99-828C 2D 31 31 43 46 2D 41 33 37 45 2D 30 30 41 41 30 // -11CF-A37E-00AA0 30 33 32 34 30 43 37 00 00 ) // 03240C7.. }
|
|
Interestingly, the declarations of both types happen to carry the import flag, which indicates that these types are not implemented in managed code. When the .NET runtime encounters a type marked with the import flag, it identifies a type as COM server and invokes its COM interoperability mechanism. Obviously, COM objects are quite different from the types implemented in managed code—they are allocated from the unmanaged heap and require explicit memory management. Thus, to accommodate a COM server, .NET runtime creates Runtime Callable Wrappers (RCW) for each instance of a COM object to be consumed by the managed code. An RCW is a managed object, allocated from the garbage-collected heap, that caches the actual reference-counted COM interface pointer so that .NET code treats it just like any other managed type. To the COM server, however, RCW looks like a conventional well-behaved COM client that adheres to all COM rules and restrictions.
In short, each time an instance of IWbemLocator or IWbemServices types is requested , the runtime silently creates an underlying COM object, allocates the corresponding RCW, and returns the reference to this RCW back to the requestor .
The last question to answer is how the runtime knows which COM object to allocate when the managed code requests an instance of IWbemLocator or IWbemServices type. As you may have noticed, the declarations of both of these classes are decorated with several attributes— System.Runtime.InteropServices.GuidAttribute is one of these. You can then see that the constructor for this attribute takes a string parameter, which specifies the GUID of the COM server to be created. To no surprise, the inspection of the Windows registry shows that the COM objects, used here, are the same COM objects that are utilized when programming native COM API WMI applications.
As it turns out, the types of the System.Management namespace are by no means a complete reimplementation of the WMI access API. Instead, the entire .NET system management class library is just a clean, managed, object-oriented wrapper that is implemented on top of the existing COM WMI-access API.
|
. NET System Management Services Authors: Golomshtok A. Published year: 2005 Pages: 14/79 |