|
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. CEventSink is the interface that is called to
handle those events. It hooks instances of "OfficeEquipment" being
created. This is the class defined/used by
OnAddEquipment.cpp.
Temporary events 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:
When using temporary events,
the CIMOM service calls back to the client application. The default security
authentication setting doesn't allow this call to get through. The client
application must lower its authentication setting for these callbacks to work.
This is complicated by the fact that Windows 95 doesn't come with DCOM which
contains the CoInitializeSecurity() routine (OLE32.dll is updated when you
install DCOM). The logic for dealing with this security issue is in
CAdvClientApp::InitSecurity() in AdvClient.cpp.
You also 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.