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