The TimeServiceProvider Sample WMI Provider

[Previous] [Next]

The TimeServiceProvider sample WMI provider ("08 TimeServiceProvider.dll"), shown in Listing 8-1, demonstrates how to create a WMI provider DLL for the TimeService service presented in Chapter 3. TimeServiceProvider exposes a property of the TimeService service to WMI. By using WMI, a client could retrieve this property. Unlike the other samples in this book, the WMI SDK tools create the source code for this sample. In this section, you'll learn how to use these tools to create a dynamic extension to WMI's Win32_Service class. The source code files for the provider are in the 08-TimeServiceProvider directory on the companion CD. Now I will step through the creation of a simple dynamic provider.

Selecting Information to Provide

The first step in creating a provider is deciding what information about the service to make available. The TimeService service uses a named pipe for the client/server connections—the name of this named pipe is one piece of information you can make available. In the TimeService.cpp file (from Chapter 3), we see that the pipe name is "\\.\pipe\TimeService". We'll create a dynamic provider that allows an administrator or client to query the name of the named pipe by using WMI. You might consider revising the WMI provider so that it also allows an administrator to change the name of the named pipe by using WMI.

Deriving a Class Using MOF

The next step is to derive a class by using Win32_Service as the base class and add a named pipe property to the derived class. The following code shows the MOF code used to create this class and is in the TimeServiceStart.mof file on the companion CD:

 #pragma namespace("\\\\.\\root\\cimv2") class Richter_TimeServiceProvider : Win32_Service {    string PipeName; }; 

Using the MOF Compiler

Once you have created the .mof file, you need to compile it using the MOF compiler (MOFComp.exe), which is a part of WMI. The MOF compiler will add the new Richter_TimeServiceProvider class to the system. The following command shows how you can compile:

 C:\WINNT\system32\wbem\mofcomp.exe _class:forceupdate     "<filepath>\TimeServiceStart.mof" 

Notice the use of the "-class:forceupdate" switch and the use of quotation marks around the MOF file path. The "-class:forceupdate" switch forces the update of classes when there might be conflicting classes, and the quotes allow you to use names containing spaces. For more information on the switches available for the MOF compiler, see the "MOF Compiler" topic in the WMI SDK and Platform SDK documentation.

Integrating the MOF Compiler in Visual Studio

If you use the MOF compiler frequently, you'll find it convenient to add it to the Microsoft Visual Studio Tools menu. To do this, open Visual Studio and select Customize from the Tools menu. In the Customize dialog box, click on the Tools tab, scroll to the bottom of the list, and add a new tool named MOF Compiler, as shown in Figure 8-6.

Because you've established the MOF compiler as a Visual Studio tool, you can open any MOF file and compile it. Since the Use Output Window check box is checked, any compilation errors will appear as errors in the Output window. You can double-click the errors in the Output window and have Visual Studio automatically position itself on the offending line in the source file.

click to view at full size.

Figure 8-6. The Tools tab of the Customize dialog box, where you can add the MOF Compiler to the Visual Studio Tools menu

After you compile the TimeServiceStart.mof file, run CIM Studio and open the CIMV2 namespace. You should see the new class under the following node. If CIM Studio is already open you will need to refresh the display.

 CIM_ManagedSystemElement\CIM_LogicalElement\CIM_Service\    Win32_BaseService\Win32_Service\Richter_TimeServiceProvider 

Figure 8-7 shows what CIM Studio should look like. The new PipeName property is highlighted so that you can easily see that it is part of this class instance.

To populate the new Richter_TimeServiceProvider class, a provider must be defined and associated with the class definition. This is how the Windows Management Service (WinMgmt.exe) knows to call the provider to get the instance's data. The easiest way to create the provider is to have the CIM Studio tool generate the DLL's source code for you.

Using the WMI Provider Code Generator Wizard

To generate a provider DLL for the Richter_TimeServiceProvider class, you must first select the class by checking the box to the left of the class's name in the left pane (as shown in Figure 8-7). Then double-click the Provider Code Generator button at the top of CIM Studio to invoke the WMI Provider Code Generator Wizard.

click to view at full size.

Figure 8-7. The Richter_TimeServiceProvider class in CIM Studio, after compiling TimeServiceStart.mof

In the welcome dialog box, click Next to begin the wizard. In the next dialog box, add the appropriate text in the Description text box. Then check the Override Inherited Properties check box, and check the Name property in the list of properties that the provider is to override from the base class. You must override the Name property because it is used by the object manager to determine whether the instance properties you supply correspond to the instance properties supplied by the derived-class provider. The dialog box should look like Figure 8-8. (Note that you can't see that I checked the Name property.)

click to view at full size.

Figure 8-8. The WMI Provider Code Generator Wizard dialog box used to specify names and properties

Click the Next button to display the dialog box shown in Figure 8-9. You use this dialog box to give the provider DLL a filename and description, and to specify the directory where you'd like the generated code to be output.

Click the Finish button to generate the provider source code. The wizard will generate six files: MAINDLL.cpp, Richter_TimeServiceProvider.cpp, Richter_TimeServiceProvider.h, TimeServiceProvider.def, TimeServiceProvider.mak, and TimeServiceProvider.mof.

Modifying the Wizard-Generated Code

You still need to make a few changes to the generated code before it will work correctly. By default, the TimeServiceProvider class will be placed in the root\default namespace. To make the class part of the root\CIMV2 namespace, you must add the following pragma to the top of TimeServiceProvider.mof:

 #pragma namespace("\\\\.\\root\\cimv2") 

click to view at full size.

Figure 8-9. The WMI Provider Code Generator Wizard dialog box used to specify the provider's filename, description, and output directory

Deploying a provider is more convenient if the contents of the TimeServiceProvider.mof and TimeServiceStart.mof files are combined. I appended the contents of TimeServiceStart.mof to the bottom of the TimeServiceProvider.mof file produced by the wizard. I also added the following line so that the WinMgmt service is aware that the values of this class's properties are generated dynamically. Listing 8-2 shows the TimeServiceProvider.mof file after the modifications have been made.

 [provider("TimeServiceProvider"), dynamic]  

Now let's focus on the Richter_TimeServiceProvider.cpp file, which requires a few modifications before it is useful. First, find this line:

 CRichter_TimeServiceProvider MyRichter_TimeServiceProviderSet    (PROVIDER_NAME_RICHTER_TIMESERVICEPROVIDER, L"NameSpace") ; 

Replace NameSpace with the appropriate namespace:

 CRichter_TimeServiceProvider MyRichter_TimeServiceProviderSet    (PROVIDER_NAME_RICHTER_TIMESERVICEPROVIDER, L"root\\cimv2") ; 

Second, modify the EnumerateInstances function so that it returns the correct property values. For the TimeService service, this is very easy because there is only one property, the name of the named pipe. The following code shows how the EnumerateInstances function should look after its modifications.

 HRESULT CRichter_TimeServiceProvider::EnumerateInstances (    MethodContext* pMethodContext, long lFlags ) {    HRESULT hRes = WBEM_S_NO_ERROR;    // Create a new instance based on the passed-in MethodContext.    // Note that CreateNewInstance may throw but will never return NULL.    CInstance* pInstance = CreateNewInstance(pMethodContext);    // Class name must match programmatic name of service    pInstance->SetCHString(pName,       "Programming Server-Side Applications Time");    pInstance->SetCHString(pPipeName, "TimeService");    hRes = pInstance->Commit();    pInstance->Release();    return(hRes); } 

Third, you must modify the GetObject function so that it returns the data for a given instance. For a service, modifying the function is very simple because there is only one instance of a service on a machine at any given time. The following code shows how the GetObject function should look after its modifications. Listing 8-1 shows the resulting Richter_TimeServiceProvider.cpp file.

 HRESULT CRichter_TimeServiceProvider::GetObject (CInstance* pInstance,     long lFlags)  {    HRESULT hr = WBEM_E_NOT_FOUND;    // Class name must match programmatic name of service    pInstance->SetCHString(pName,       "Programming Server-Side Applications Time");    pInstance->SetCHString(pPipeName, "TimeService");    hr = WBEM_S_NO_ERROR;     return(hr); } 

Building the TimeServiceProvider Sample

To build the provider DLL, I created an empty Win32 project using Visual Studio. Then I added the MAINDLL.cpp, Richter_TimeServiceProvider.cpp, Richter_TimeServiceProvider.h, and TimeServiceProvider.def files to the project. You must also remember to link with the FrameDyn.lib library (supplied with the WMI SDK). I ensure this by placing the following line in the Richter_ TimeServiceProvider.cpp file:

 #pragma comment(lib, "FrameDyn.lib") 

Finally, I added the USE_POLARITY symbol for both the Debug and Release builds of the project. I did this on the C/C++ tab of the Project Settings dialog box, shown in Figure 8-10. You'll want to compile the files using warning level 3, because the framework header files generate many warnings when compiled at warning level 4.

click to view at full size.

Figure 8-10. The C/C++ tab, where you add symbols and set warning levels for your files

Deploying the TimeServiceProvider Sample

To deploy the provider, you must run the MOF compiler, passing it the TimeServiceProvider.mof file. This makes the WinMgmt service aware of the WMI class provider information. Next you must run RegSvr32, passing it the "08 TimeServiceProvider.dll" file. Finally, you must make sure that the TimeService service (which has a display name of "Programming Server-Side Applications Time") is installed on the machine. Note that the service does not have to be running, but it must be installed. You can install the service by using the install switch ("03 TimeService.exe" _install).

To see the results of all this hard work, open CIM Studio and navigate to the following node:

 CIM_ManagedSystemElement\CIM_LogicalElement\CIM_Service\    Win32_BaseService\Win32_Service\Richter_TimeServiceProvider 

Click the Instances button above the right pane. You should see information being returned from the TimeServiceProvider DLL, specifically the normal properties of the Win32_Service class and the new PipeName property, as shown in Figure 8-11.

click to view at full size.

Figure 8-11. The TimeServiceProvider DLL exposing the PipeName property of the TimeService service in CIM Studio

NOTE
The provider DLL is loaded into WinMgmt.exe's address space when it is needed. If you want to make changes to your provider's source code and rebuild it, you must stop the WinMgmt service first and then restart it when you're ready to have it load your newly built DLL.

If your provider does not work, take a look at the C:\WINNT\System32\WBEM\Logs directory, which contains a number of text log files. These files track various errors such as the failure to locate or load a provider when needed. Also, to help you debug a provider, you can run WinMgmt.exe as a normal process instead of a service. To do this, you must first stop the WinMgmt service and then run WinMgmt.exe from the command line using the /exe switch.

Listing 8-1. The TimeServiceProvider sample

 

Richter_TimeServiceProvider.cpp

/****************************************************************** Richter_TimeServiceProvider.CPP -- WMI provider class implementation Generated by Microsoft WMI Code Generation Engine TO DO: - See individual function headers - When linking, make sure you link to framedyd.lib & msvcrtd.lib (debug) or framedyn.lib & msvcrt.lib (retail). Description: Programming Server-Side Applications for Microsoft Windows TimeService WMI Provider ******************************************************************/ #pragma message("This project requires that the Platform SDK's WMI components") #pragma message("be installed. You may install these componenets from the ") #pragma message("book's CD-ROM or from msdn.microsoft.com") // identifier was truncated to `255' characters in the debug information #pragma warning(disable: 4786) //*************************************************************************** #pragma warning(push, 3) #include <fwcommon.h> // This must be the first include. #pragma comment(lib, "FrameDyn.lib") #include "Richter_TimeServiceProvider.h" // TO DO: Replace "NameSpace" with the appropriate namespace for your // provider instance. For instance: "root\\default or "root\\cimv2". //=================================================================== CRichter_TimeServiceProvider MyRichter_TimeServiceProviderSet ( PROVIDER_NAME_RICHTER_TIMESERVICEPROVIDER, L"root\\cimv2") ; // Property names //=============== const static WCHAR* pName = L"Name" ; const static WCHAR* pPipeName = L"PipeName" ; /***************************************************************************** * * FUNCTION : CRichter_TimeServiceProvider::CRichter_TimeServiceProvider * * DESCRIPTION : Constructor * * INPUTS : none * * RETURNS : nothing * * COMMENTS : Calls the Provider constructor. * *****************************************************************************/ CRichter_TimeServiceProvider::CRichter_TimeServiceProvider (LPCWSTR lpwszName, LPCWSTR lpwszNameSpace ) : Provider(lpwszName, lpwszNameSpace) { } /***************************************************************************** * * FUNCTION : CRichter_TimeServiceProvider::~CRichter_TimeServiceProvider * * DESCRIPTION : Destructor * * INPUTS : none * * RETURNS : nothing * * COMMENTS : * *****************************************************************************/ CRichter_TimeServiceProvider::~CRichter_TimeServiceProvider () { } /***************************************************************************** * * FUNCTION : CRichter_TimeServiceProvider::EnumerateInstances * * DESCRIPTION : Returns all the instances of this class. * * INPUTS : A pointer to MethodContext for communication with WinMgmt. * A long that contains the flags described in * IWbemServices::CreateInstanceEnumAsync. Note that the following * flags are handled by (and filtered out by) WinMgmt: * WBEM_FLAG_DEEP * WBEM_FLAG_SHALLOW * WBEM_FLAG_RETURN_IMMEDIATELY * WBEM_FLAG_FORWARD_ONLY * WBEM_FLAG_BIDIRECTIONAL * * RETURNS : WBEM_S_NO_ERROR if successful * * COMMENTS : TO DO: All instances on machine should be returned here and * all properties this class knows how to populate must * be filled in. If there are no instances, return * WBEM_S_NO_ERROR. Not an error to have no instances. * If you are implementing a `method only' provider, you * should remove this method. * *****************************************************************************/ HRESULT CRichter_TimeServiceProvider::EnumerateInstances ( MethodContext*pMethodContext, long lFlags) { HRESULT hRes = WBEM_S_NO_ERROR; // TO DO: The following commented lines contain the `set' methods for the // properties entered for this class. They are commented because they // will NOT compile in current form. Each <Property Value> should be // replaced with the appropriate value. Also, consider creating a new // method and moving these set statements and the ones from GetObject // into that routine. See the framework sample (ReindeerProv.cpp) for // an example of how this might be done. // // If the expectation is there is more than one instance on the machine // EnumerateInstances should loop through instances & fill accordingly. // // Note that you must ALWAYS set ALL the key properties. See docs for // further details. /////////////////////////////////////////////////////////////////////////////// // Create a new instance based on the passed-in MethodContext. // Note that CreateNewInstance may throw, but will never return NULL. CInstance* pInstance = CreateNewInstance(pMethodContext); // Class name must match programmatic name of service pInstance->SetCHString(pName, "Programming Server-Side Applications Time"); pInstance->SetCHString(pPipeName, "TimeService"); hRes = pInstance->Commit(); pInstance->Release(); return(hRes); } /***************************************************************************** * * FUNCTION : CRichter_TimeServiceProvider::GetObject * * DESCRIPTION : Find a single instance based on the key properties for the * class. * * INPUTS : A pointer to a CInstance object containing key properties. * A long that contains the flags described in * IWbemServices::GetObjectAsync. * * RETURNS : WBEM_S_NO_ERROR if the instance can be found * WBEM_E_NOT_FOUND if the instance described by the key * properties could not be found * WBEM_E_FAILED if the instance could be found but another * error occurred. * * COMMENTS : If you are implementing a `method only' provider, you * should remove this method. * *****************************************************************************/ HRESULT CRichter_TimeServiceProvider::GetObject (CInstance* pInstance, long lFlags) { // TO DO: The GetObject function is used to search for an instance of this // class on the machine based on the key properties. Unlike // EnumerateInstances which finds all instances on the machine, // GetObject uses the key properties to find the matching single // instance and returns that instance. // // Use the CInstance Get functions (for example, call // GetCHString(L"Name", sTemp)) against pInstance to see the key // values the client requested. HRESULT hr = WBEM_E_NOT_FOUND; // Class name must match programmatic name of service pInstance->SetCHString(pName, "Programming Server-Side Applications Time"); pInstance->SetCHString(pPipeName, "TimeService"); hr = WBEM_S_NO_ERROR; return(hr); } /***************************************************************************** * * FUNCTION : CRichter_TimeServiceProvider::ExecQuery * * DESCRIPTION : You are passed a method context to use in the creation of * instances that satisfy the query, and a CFrameworkQuery * which describes the query. Create and populate all * instances which satisfy the query. You may return more * instances or more properties than are requested and * WinMgmt will post filter out any that do not apply. * * INPUTS : A pointer to MethodContext for communication with WinMgmt. * A query object describing the query to satisfy. * A long that contains the flags described in * IWbemServices::CreateInstanceEnumAsync. Note the fol- * lowing flags are handled by (and filtered out by) WinMgmt: * WBEM_FLAG_FORWARD_ONLY * WBEM_FLAG_BIDIRECTIONAL * WBEM_FLAG_ENSURE_LOCATABLE * * RETURNS : WBEM_E_PROVIDER_NOT_CAPABLE if queries not supported for * this class or if query is too complex for this class * to interpret. The framework will call the * EnumerateInstances function instead and let Winmgmt post filter. * WBEM_E_FAILED if the query failed * WBEM_S_NO_ERROR if query was successful * * COMMENTS : TO DO: Most providers will not need to implement this method. If you don't, WinMgmt will call your enumerate * function to get all the instances and perform the * filtering for you. Unless you expect SIGNIFICANT * savings from implementing queries, you should remove * this method. You should also remove this method * if you are implementing a `method only' provider. * *****************************************************************************/ HRESULT CRichter_TimeServiceProvider::ExecQuery (MethodContext *pMethodContext, CFrameworkQuery& Query, long lFlags) { return (WBEM_E_PROVIDER_NOT_CAPABLE); } /***************************************************************************** * * FUNCTION : CRichter_TimeServiceProvider::PutInstance * * DESCRIPTION : PutInstance should be used in provider classes that can * write instance information back to the hardware or * software. For example: Win32_Environment will allow a * PutInstance to create or update an environment variable. * However, a class like MotherboardDevice will not allow * editing of the number of slots, since it is difficult for * a provider to affect that number. * * INPUTS : A pointer to a CInstance object containing key properties. * A long that contains the flags described in * IWbemServices::PutInstanceAsync. * * RETURNS : WBEM_E_PROVIDER_NOT_CAPABLE if PutInstance is not * available * WBEM_E_FAILED if there is an error delivering the instance * WBEM_E_INVALID_PARAMETER if any of the instance properties * are incorrect * WBEM_S_NO_ERROR if instance is properly delivered * * COMMENTS : TO DO: If you don't intend to support writing to your * provider, or are creating a `method only' provider, * remove this method. * *****************************************************************************/ HRESULT CRichter_TimeServiceProvider::PutInstance ( const CInstance &Instance, long lFlags) { // Use the CInstance Get functions (for example, call // GetCHString(L"Name", sTemp)) against Instance to see the key values // the client requested. return (WBEM_E_PROVIDER_NOT_CAPABLE); } /***************************************************************************** * * FUNCTION : CRichter_TimeServiceProvider::DeleteInstance * * DESCRIPTION : DeleteInstance, like PutInstance, actually writes * information to the software or hardware. * For most hardware devices, DeleteInstance should not be * implemented, but for software configuration, * DeleteInstance implementation is plausible. * * INPUTS : A pointer to a CInstance object containing key properties. * A long that contains the flags described in * IWbemServices::DeleteInstanceAsync. * * RETURNS : WBEM_E_PROVIDER_NOT_CAPABLE if DeleteInstance is not * available. * WBEM_E_FAILED if there is an error deleting the instance. * WBEM_E_INVALID_PARAMETER if any of the instance properties * are incorrect. * WBEM_S_NO_ERROR if instance is properly deleted. * * COMMENTS : TO DO: If don't intend to support deleting instances or are * creating a `method only' provider, remove this method. * *****************************************************************************/ HRESULT CRichter_TimeServiceProvider::DeleteInstance ( const CInstance &Instance, long lFlags ) { // Use the CInstance Get functions (for example, call // GetCHString(L"Name", sTemp)) against Instance to see the key values // the client requested. return (WBEM_E_PROVIDER_NOT_CAPABLE); } /***************************************************************************** * * FUNCTION : CRichter_TimeServiceProvider::ExecMethod * * DESCRIPTION : Override this function to provide support for methods. * A method is an entry point for the user of your provider * to request your class perform some function above and * beyond a change of state. (A change of state should be * handled by PutInstance() ) * * INPUTS : A pointer to a CInstance containing the instance the * method was executed against. * A string containing the method name * A pointer to CInstance which contains the IN parameters. * A pointer to CInstance to contain the OUT parameters. * A set of specialized method flags * * RETURNS : WBEM_E_PROVIDER_NOT_CAPABLE if not implemented for this * class * WBEM_S_NO_ERROR if method executes successfully * WBEM_E_FAILED if error occurs executing method * * COMMENTS : TO DO: If you don't intend to support Methods, remove this * method. * *****************************************************************************/ HRESULT CRichter_TimeServiceProvider::ExecMethod ( const CInstance& Instance, const BSTR bstrMethodName, CInstance *pInParams, CInstance *pOutParams, long lFlags) { // For non-static methods, use the CInstance Get functions (for example, // call GetCHString(L"Name", sTemp)) against Instance to see the key // values the client requested. return (WBEM_E_PROVIDER_NOT_CAPABLE); }

Listing 8-2. The MOF file for the TimeServiceProvider sample WMI provider

 

TimeServiceProvider.mof

// TimeServiceProvider.MOF // // Generated by Microsoft WBEM Code Generation Engine // // TO DO: If this class is intended to be created in a namespace // other than the default (root\default), you should add // the #pragma namespace command here. If these classes // are going into your own namespace, consider creating // the namespace here as well. See CIMWIN32.MOF for an // example of how to create a namespace. Also, consider // combining this mof with the mof the defines the class // that this provider provides. // //=================================================================== // NOTE: The line below was added manually: #pragma namespace("\\\\.\\root\\cimv2") //************************************************************* //*** Registers Framework Provider *** //************************************************************* instance of __Win32Provider as $P { Name = "TimeServiceProvider"; ClsId = "{ccb338cc-f3af-4d80-8bba-d8ba0f8c325d}"; }; instance of __InstanceProviderRegistration { Provider = $P; SupportsGet = TRUE; SupportsPut = TRUE; SupportsDelete = TRUE; SupportsEnumeration = TRUE; QuerySupportLevels = {"WQL:UnarySelect"}; }; instance of __MethodProviderRegistration { Provider = $P; }; // NOTE: The following lines (from TimeServiceStart.mof) were added manually: [provider("TimeServiceProvider"), dynamic] class Richter_TimeServiceProvider : Win32_Service { string PipeName; };



Programming Server-Side Applications for Microsoft Windows 2000
Programming Server-Side Applications for Microsoft Windows 2000 (Microsoft Programming)
ISBN: 0735607532
EAN: 2147483647
Year: 2000
Pages: 126

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net