WMI SDK Sample - Client

Windows Management Instrumentation SDK Sample

Advanced Client
The Advanced Client sample demonstrates various ways to use WMI features. Where multiple ways exist to do the same things, an effort was made to show each way. Use the following table of user interface support functions to find the technique you want. The implementation of each 'button' is in a separate .CPP file to make it easier to deal with. Common helper routines are in the AdvClientDlg.cpp file itself. Class-wise, all WMI code is in the main dialog.

This sample is a dialog-based application created by AppWizard and uses the Microsoft Foundation Classes (MFC) for simplicity. The code is designed to be easy to follow and doesn't necessarily show a good practice for building 'real' WMI client apps. Concentrate on the steps and architect your application in a way that makes sense for you.

Building the Advanced Client Application

The application can be built from the command line using NMAKE, or it can be built using Microsoft Visual C++.

From the command line in the sample installation directory:

NMAKE /f "Makefile"

From Microsoft Visual C++:

1. Select File + Open Workspace

2. Select the AdvClient.dsp file

Summary of User Interface Functions

Button | File | Action
Connect | OnConnect.cpp | Connects to namespace.
Exit | WBEMSampDlg.cpp | Exits the application.
Enum Disks | OnEnumDisks.cpp | Lists the logical disks.
Get C: Disk Details | OnDiskDetails.cpp | Lists C: disk properties.
Enum Services | OnEnumSvcs.cpp | Lists the services.
Enum Services Async | OnAsync.cpp | Lists the services using the asynchronous functions.
Add Equipment | OnAddEquipment.cpp | Adds to a list of office equipment.
Register Perm | OnPerm.cpp | Registers and unregisters local-server event consumer PermConsumer.exe.
Register Temp | OnTemp.cpp | Registers and unregisters the in-proc event consumer CEventSink in OnTemp.*.
About Disk Properties | OnDiskPropsDescriptions.cpp | lists the description of the logical disk class, as well as descriptions of all its properties. Note that this information is localizable and will be displayed in the language that corresponds to the current user locale on the client machine, as long as the server has corresponding localized resources.

Common WMI Client Tasks Demonstrated by the Sample Functions

Task: Connecting to a namespace

Implementations:

OnConnect.cpp shows how to connect to a namespace. This will enable the rest of the buttons because they all require the client to be connected. \root\cimv2 is the most commonly used namespace since the Win32 schema classes are in it. \root\security is also built-in but it only contains security related classes. In this example, the '.' (period) can be replaced with a remote machine's name to connect remotely. Dot is used to refer to the local machine.

OnAddEquipment.cpp uses OpenNamespace() to connect to root\cimv2\office because its UNDER root\cimv2 and relative navigation is possible.

Task: Enumerating classes

Implementation:

OnEnumDisks.cpp creates an enumerator for all instances of disks then walks the result list using the 'classic' OLE enumerator scheme. Properties are extracted for display.

Task: Enumerating properties

Implementation:

OnDiskDetails.cpp enumerates the properties for your C: drive. It uses GetNames() to get a SAFEARRAY of property names which is then using to Get() property values directly.

Task: Retrieving (amended) qualifiers

Implementation:

OnDiskPropsDescriptions.cpp lists class description and property descriptions for Win32_LogicalDisk class. Note that description qualifiers can be quite lengthy and are normally not retrieved, unless WBEM_FLAG_USE_AMENDED_QUALIFIERS flag is specified in IWbemServices::GetObject().

Object qualifiers are retrieved by IWbemClassObject::GetQualifierSet(). Property qualifiers are retrieved by WbemClassObject::GetPropertyQualifierSet() - you need to supply property name as a parameter.

Get() method on the IWbemQualifierSet retrieves specific qualifier values - in this case, descriptions.

Amended qualifiers (such as descriptions) are localizable and will be displayed in the language that corresponds to the current user

locale on the client machine, as long as the server is able to provide appropriate localized resources.

Task: Using WQL for queries

Implementation:

OnEnumSvcs.cpp uses ExecQuery() to issue a WQL query to find all services running on the machine. It then uses the BeginEnumeration()/Next()/EndEnumeration() scheme to walk through the properties for each service-- looking for the properties of interest. This is a contrived example for demo purposes only. This scheme is normally used for displaying ALL properties rather than looking for particular ones.

Task: Using WQL for asynchronous queries

Implementation:

OnAsync.cpp does exactly the same thing as OnEnumSvcs.cpp except it does it asynchronously. ExecQueryAsync() is passed a CAsyncQuerySink COM object which implements an IWbemObjectSink. This object has it's Indicate() and SetStatus() called for the result of the query instead of creating an enumerator.

Task: Creating user-defined classes

Implementation:

OnAddEquipment.cpp shows how to create classes and instances. After prompting for items in your office, the OfficeEquipment class is created if it already doesn't exist then a new instance of the class is created for the item you typed into the dialog box. Once the first equipment is added, the special namespace will exist and the "Register" buttons will enable since they get events from this namespace. The namespace must exist before you can register for its events.

Task: Creating instances

Implementations:

OnAddEquipment.cpp creates instances of the user-defined classes.

Task: Creating new namespaces

Implementation:

OnAddEquipment.cpp creates a namespace of root\cimv2\office to store the OfficeEquipment class and instances.

Task: Temporary Event Consumers

Implementation:

OnTemp.cpp registers and unregisters temporary events. The sample demonstrates semi-synchronous event polling technique. It polls for instance creation events of "OfficeEquipment". This is the class defined/used by OnAddEquipment.cpp. Event traces are displayed in the lower listbox.

Task: Permanent Event Consumers

Implementation:

OnPerm.cpp registers and unregisters Permanent events. It consumes the same events as OnTemp.cpp so that you can compare and contrast. The events are also handled by the SDK EventConsumer sample. Events are displayed in this separate application. The registry entries required for CIMOM to spawn a local server, which displays to the user's desktop, are documented in the RegisterServer() routine of that application.

NOTE: In order for the “Register Perm” function to work, the SampleViewer MOF in the SDK EventConsumer sample directory needs to be compiled into the CIMOM repository. From the command line type the following:

MOFCOMP SampleViewer.mof

This registers the Client sample as a consumer of the events generated by adding instances of OfficeEquipment within the Advanced Client sample.

Task: Dealing With Security

Implementation:

You must call IClientSecurity::SetBlanket() for any IWbemServices or IEnumWbemClassObject. See CAdvClientDlg::SetBlanket() in OnConnect.cpp for this technique.

User Interface Notes

1. You can connect to remote machines by changing the namespace before connecting.

2. The results of actions show up in the upper listbox. Event related messages go in the lower listbox. The Permanent Event Consumer is a separate application that will start as needed.

3. On Windows 95, no services will list because win95 doesn't have services. This is normal.

General Notes

Things to remember when you're building your own WMI client application:

1. If you want your client to run on NT and non-DCOM versions of Windows 95, manually load the ole32.dll and see if CoInitializeSecurity() exists. This routine won’t exist on Windows 95 installations that don’t have DCOM installed separately. If this routine doesn't exist, the asynchronous routines in this sample won’t work because of mismatched security level problems. The synchronous techniques will still work.

2. If you don’t care about non-DCOM versions of Windows 95, you can define _WIN32_DCOM so that CoInitializeSecurity() is available for implicit linking. Don't use _WIN32_WINNT to get this prototype since it won't compile under the Windows 95/98 operating systems.

3. In any case, the CoInitializeSecurity() call (in InitInstance()) is required to work around a security problem when WMI trying to call a Sink object but won't identify itself. The CoInitializeSecurity() call turns off the authentication requirement.

4. WMI interfaces are defined in wbemcli.h and wbemprov.h found in the wbem\include directory. You may #include both these files by including just wbemidl.h located in the same directory.

5. WMI interface CLSIDs are defined in wbemuuid.lib. If you get unresolved externals in interfaces and CLSIDs, this is what is missing.

6. You'll need to link with oleaut32.lib and ole32.lib to get the needed COM support.

7. In the Link|Output settings, specify 'wWinMainCRTStartup' as the entry point. This is per the Unicode programming instructions.

8. If you're using the makefiles, don't forget to set the VC vars. In VC++ 5.0, it is VCVARS32.BAT.

9. If you are running Microsoft Visual Studio .NET, you should modify the sample’s MAKEFILE (and/or project file) to reference MFC70 libraries instead of MFC42 libraries which shipped with earlier versions of Visual C++.