================================================================== WMI Client Sample ================================================================== This sample demonstrates various ways to do 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 contexts 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 SampDlg.cpp file itself. Class-wise, all WMI code is in the main dialog. This app is a dialog-based app created by AppWizard and using 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 app in a way that makes sense for you. ================================================================== Build Notes ================================================================== Things to remember when you're building your own WMI client app. 1. If you want your client to run on NT and non-DCOM versions of Win95, manually load the ole32.dll and see if CoInitializeSecurity() exists. This routine wont exist on win95 OS's that dont have DCOM installed separately. If this routine doesn't exist, the async routines in this sample wont work because of mismatched security level problems. The synchronous techniques will still work. 2. If you dont care about non-DCOM versions of win95, 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 Windows 9x OSs. 3. Either way, this 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 wmi\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's missing. 6. You'll need to link with oleaut32.lib and ole32.lib to get the COM stuff. 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, its VCVARS32.BAT. ================================================================== UI Summary ================================================================== Button File Action ------ ---- ------ Connect OnConnect.cpp Connects to specified namespace. Exit WBEMSampDlg.cpp Exits the app. 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. Add Equipment OnAddEquipment.cpp adds to a list of office equipment. Register Perm OnPerm.cpp registers/unregisters the local-server event consumer WbemPermConsumer.exe. Register Temp OnTemp.cpp registers/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. You can connect to remote machines by changing the namespace before connecting. 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 app that will start as needed. On Windows 95, no services will list because win95 doesn't have services. This is normal. ================================================================== 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 '.' (dot) can be replaced with a remote machine's name to connect remotely. Dot is used for 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 retieved by IWbemClassObject::GetQualifierSet(). Property qualifiers are retrieved by IWbemClassObject::GetPropertyQualifierSet() - you need to supply property name as a parameter. Get() method on the IWbemQualifierSet retrives 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 asychronously. ExecQueryAsync() is passed a CAsyncQuerySink COM object which implements an IWbemObjectSink. This object has it's Indicate() and SetStatus() called for the reesult 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 hooks the same events as OnTemp.cpp so that you can compare and contrast. The events are handled by WBEMPermEvents.exe; a separate project under wmi\samples\EventConsumer. Events are displayed in this separate app. The registry entries required to allow CIMOM to spawn a local server which displays to the user's desktop is documented in the RegisterServer() routine of that app. You must run 'mofcomp SampleViewer.mof' to register the Event Consumer before the Register Perm function will work. ================================================================== Task: Dealing With Security ================================================================== When using temporary events, the CIMOM service calls back to the client app. The default security authentication setting doesn't allow this call to get through. The client app 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 CWbemSampleApp::InitSecurity() in WBEMSamp.cpp. You also must call IClientSecurity::SetBlanket() for any IWbemServices or IEnumWbemClassObject. See CWBEMSampleDlg::SetBlanket() in OnConnect.cpp for this technique. END readme.txt