How to Customize the Skeleton Sample Source Files


The Skeleton sample source files serve as a useful starting point for most UMDF drivers. Many of them require little or no modification.

DLL Infrastructure

The DLL infrastructure for a UMDF driver typically consists of two functions that are exported by name: DllMain and DllGetClassObject. Both functions are implemented in Dllsup.cpp. The header content for this file is in Internal.h.

DllMain

DllMain is the DLL's entry point. Windows calls DllMain after the driver binary has been loaded into a host process and again before it is unloaded. There are several restrictions on what can be done in DllMain, so the implementation is usually fairly limited.

 Tip  See the DllMain reference on MSDN-online at http://go.microsoft.com/fwlink/?LinkId=80069.

The Skeleton sample implementation of DllMain simply registers and unregisters WPP tracing, as shown in Listing 13-1. If your driver uses WPP tracing, you can use the Skeleton sample code, although you should replace the definition of MYDRIVER_TRACING_ID in Internal.h with an ID that is unique to your driver. If appropriate for your driver, you can also add code to DllMain for such purposes as initializing or freeing global variables.

Listing 13-1: The Skeleton sample DllMain implementation

image from book
 BOOL WINAPI DllMain(     HINSTANCE ModuleHandle,     DWORD Reason,     PVOID) {     if (DLL_PROCESS_ATTACH == Reason) {         WPP_INIT_TRACING(MYDRIVER_TRACING_ID);         //TODO: Initialize global variables     }     else if (DLL_PROCESS_DETACH == Reason) {         WPP_CLEANUP();         //TODO: Free global variables     }     return TRUE; } 
image from book

If your driver implements WPP tracing, you must also modify Internal.h, which is shown in Listing 13-2. The numbered comments are explained following this example.

Listing 13-2: WPP-related code from the Skeleton sample Internal.h file

image from book
 //[1] #define WPP_CONTROL_GUIDS                       \     WPP_DEFINE_CONTROL_GUID(                    \         MyDriverTraceControl,                   \         (e7541cdd,30e8,4b50,aeb0,51927330ae64), \         WPP_DEFINE_BIT(MYDRIVER_ALL_INFO)) //[2] #define WPP_FLAG_LEVEL_LOGGER(flag, level)      \     WPP_LEVEL_LOGGER(flag) #define WPP_FLAG_LEVEL_ENABLED(flag, level)     \     (WPP_LEVEL_ENABLED(flag) &&                 \      WPP_CONTROL(WPP_BIT_ ## flag).Level >= level) // begin_wpp config // FUNC Trace{FLAG=MYDRIVER_ALL_INFO}(LEVEL, MSG, ...); // end_wpp //[3] #define MYDRIVER_TRACING_ID L"Microsoft\\UMDF\\Skeleton" 
image from book

Notes on Internal.h in Listing 13-2:

  1. Replace the values in the WPP_CONTROL_GUIDS macro with appropriate values for your driver.

  2. The Skeleton sample implements one custom trace message function-Trace.

    If you want to use a different trace message function, define it here.

  3. Replace "Microsoft\\UMDF\\Skeleton" with a string that is appropriate to your driver.

Chapter 11, "Driver Tracing and Diagnosability," provides a more thorough discussion of WPP tracing.

DllGetClassObject

After the framework loads the DLL, it can call DllGetClassObject to obtain a pointer to any of the class factories in the DLL. The client can then use that class factory to create an instance of the associated COM object. UMDF DLLs typically have only one class factory-for the driver callback object-so the DllGetClassObject implementation is usually short. Most UMDF drivers can use the Skeleton sample implementation of DllGetClassObject without modification if they also implement the driver callback object's class factory as the CClassFactory class. Otherwise, replace CClassFactory with the appropriate class name. Listing 13-3 shows the Skeleton sample implementation of DllGetClassObject.

Listing 13-3: The Skeleton sample DllGetClassObject implementation

image from book
 __control_entrypoint(DllExport) HRESULT STDAPICALLTYPE DllGetClassObject(     __in REFCLSID ClassId,     __in REFIID InterfaceId,     __deref_out LPVOID *Interface) { PCClassFactory factory;     HRESULT hr = S_OK;     factory = new CClassFactory();     ...//Code omitted     return hr; } 
image from book

Basic COM Support

Basic COM support in the Skeleton sample consists of two classes: CUnknown, a base implementation of the IUnknown interface, and CClassFactory, which implements the class factory for the driver callback object. The code for these two classes is located in Comsup.cpp and Comsup.h.

CUnknown

All of the Skeleton sample COM objects implement IUnknown by inheriting from CUnknown and then implementing their own interface-specific versions of the three IUnknown methods. As long as you follow this model, you can use the Skeleton sample implementation of CUnknown without modification.

CClassFactory

The purpose of this class is to create a new instance of the driver callback object. CClassFactory exposes one interface: IClassFactory, which supports the CreateInstance and Lock-Server methods.

You can use the Skeleton sample implementation of CreateInstance without modification, as long as the following conditions exist:

  • The name you choose for the class that implements the driver callback object is CMyDriver.

  • You follow the Skeleton sample implementation of CMyDriver by implementing a public utility method-CreateInstance-that creates an instance of the driver callback object.

Otherwise, you must modify the driver callback object's class name and the code that handles the creation of an instance of the object. Listing 13-4 shows the Skeleton sample implementation of CreateInstance.

Listing 13-4: The Skeleton sample IClassFactory::CreateInstance implementation

image from book
 HRESULT STDMETHODCALLTYPE CClassFactory::CreateInstance(     __in_opt IUnknown * /* OuterObject */,     __in REFIID InterfaceId,     __out PVOID *Object) {     HRESULT hr; PCMyDriver driver;     *Object = NULL; hr = CMyDriver::CreateInstance(&driver);     if (SUCCEEDED(hr))     {         hr = driver->QueryInterface(InterfaceId, Object);         driver->Release();     }     return hr; } 
image from book

UMDF does not call the class factory's LockServer method, so the method usually requires only a token implementation to satisfy the COM requirement that all methods on an interface must be implemented. Most UMDF drivers can just use the Skeleton sample implementation without modification.

Skeleton Sample Driver Callback Object

The Skeleton sample driver callback object is implemented in a class named CMyDriver, which is located in Driver.cpp and Driver.h. The class has the following three basic components:

  • A public helper function named CreateInstance that creates an instance of the driver callback object.

  • An interface-specific implementation of IUnknown.

  • An implementation of the IDriverEntry interface.

CreateInstance

This CreateInstance implementation is not part of an IClassFactory interface, but it serves the same purpose by providing a convenient mechanism that other classes in the DLL can use to create an instance of CMyDriver. In this case, the driver callback object's class factory calls CreateInstance. Other Skeleton sample classes implement a similar method.

You should be able to use the object creation code from CMyDriver::CreateInstance without modification, but you will probably need to add code to initialize the object appropriately. The Skeleton sample puts the initialization code in a private utility method-CMyDriver::Initialize-which is called by CreateInstance. As shown in Listing 13-5, the Skeleton sample implementation of Initialize is a placeholder that simply returns S_OK, so just add any necessary initialization code to that method.

Listing 13-5: The Skeleton sample CMyDriver::CreateInstance implementation

image from book
 HRESULT CMyDriver::CreateInstance(     __out PCMyDriver *Driver) {   PCMyDriver driver;   HRESULT hr;   driver = new CMyDriver();   ...//Omitted code hr = driver->Initialize();   if (SUCCEEDED(hr))   {     *Driver = driver;   }   ...//Omitted code   return hr; } HRESULT CMyDriver::Initialize(     VOID) {   //TODO: Add initialization code   return S_OK; } 
image from book

IUnknown

CMyDriver inherits from CUnknown-a base implementation of the IUnknown interface-as discussed in "Basic COM Support" earlier in this chapter. This chapter assumes that you will use this model for implementing IUnknown. If so, you can use the Skeleton sample implementation of the IUnknown methods for the driver callback object without modification.

IDriverEntry

The primary functionality of CMyDriver is implemented in the IDriverEntry interface, which supports the OnInitialize, OnDeinitialize, and OnDeviceAdd methods:

  • OnInitialize is called during driver loading to initialize the driver, and OnDeinitialize is called before the driver is unloaded to allow the driver to do any necessary cleanup.

    The Skeleton sample does not do any initialization or cleanup, so it has only token implementations of OnInitialize and OnDeinitialize. If your driver must do initialization or cleanup, add code to the Skeleton sample methods, as appropriate.

  • OnDeviceAdd is the key method on the interface. Its primary purpose is to create a device object and add it to the stack.

    OnDeviceAdd is also used to initialize the device and change any device-specific settings. Most drivers require several additions or modifications to the Skeleton sample implementation.

Listing 13-6 describes IDriverEntry initialization in the Skeleton sample. The numbered comments are explained in notes following the listing.

Listing 13-6: The Skeleton sample IDriverEntry::OnDeviceAdd implementation

image from book
 HRESULT CMyDriver::OnDeviceAdd(     __in IWDFDriver *FxWdfDriver,     __in IWDFDeviceInitialize *FxDeviceInit) {   HRESULT hr;   PCMyDevice device = NULL; // [1] TODO: Do any per-device initialization // [2] Create the device object hr = CMyDevice::CreateInstance(FxWdfDriver, FxDeviceInit, &device); // [3] TODO: Change any per-device settings // [4] Call the device callback object's configure method if (SUCCEEDED(hr)) {       hr = device->Configure();   }   ... //Omitted code } 
image from book

Notes on the OnDeviceAdd implementation in Listing 13-6:

  1. Do any per-device initialization, such as reading registry settings, before you create the device object.

  2. You can use the Skeleton sample code without modification to create the device object as long as you follow the Skeleton sample implementation for the device callback object:

    • The class that implements the device callback object is named CMyDevice.

    • The class has a public CreateInstance method that creates a device callback object and an instance of the device object.

    Both these methods are discussed in more detail later in the chapter.

  3. Change any per-device settings that the object exposes before completing the initialization by configuring the device.

  4. Configure the device by calling the public CMyDevice::Configure method.

    This method completes the initialization process by performing such tasks as creating queue objects to manage I/O requests. Another common task performed by this method is to create and enable the driver interface.

    If your device callback object uses a different approach for configuration, you must modify this code. The Configure method is discussed later in the chapter.

    The remaining code from OnDeviceAdd is omitted in this listing because it simply performs some cleanup before returning, so most drivers can use it without modification. See the sample for details.

Optional Interfaces

The driver callback object exposes only IUnknown and IDriverEntry, so there are no optional interfaces to be implemented for this object.

The Skeleton Sample Device Callback Object

The Skeleton sample device callback object usually requires substantial modification. The Skeleton sample does not support an actual device, so the sample implements very few of the features that device drivers require. The device callback object is implemented in a class named CMyDevice, which is located in Device.cpp and Device.h.

Utility Methods for the Device Callback Object

The device callback object implements the CreateInstance, Initialize, and Configure helper methods.

CreateInstance

CreateInstance is a public helper method that creates an instance of CMyDevice and then calls Initialize, which creates the UMDF device object. Most drivers can use the Skeleton sample code without modification.

Initialize

Initialize is a public helper method that performs several important tasks. Most drivers must modify this method significantly, as explained in the notes following Listing 13-7.

Listing 13-7: The Skeleton sample CMyDevice::Initialize implementation

image from book
 HRESULT CMyDevice::Initialize(     __in IWDFDriver           * FxDriver,     __in IWDFDeviceInitialize * FxDeviceInit) {   IWDFDevice *fxDevice;   HRESULT hr;   // [1] TODO: Set the locking constraint   FxDeviceInit->SetLockingConstraint(WdfDeviceLevel);   // [2] TODO: Filter driver must indicate that here.   // FxDeviceInit->SetFilter();   // [3] TODO: Any per-device initialization   ...Code omitted.   // [4] Create the device object. Code omitted. } 
image from book

Notes on the Initialize implementation in Listing 13-7:

  1. Set the locking constraint for your driver by calling IWDFDeviceInitialize::SetLockingConstraint.

    The Skeleton sample uses WdfDeviceLevel as a locking constraint, so you must change that value if your driver uses a different constraint.

  2. If you are implementing a filter driver, you must call IWDFDeviceInitialize::SetFilter. You can do so by simply uncommenting the Skeleton sample code.

  3. Add code here for any necessary per-device initialization.

  4. Most drivers can use the Skeleton sample code without modification to create the UMDF device object, so it is omitted from Listing 13-7.

Configure

As discussed earlier, the driver callback object calls the device callback object's Configure method after the UMDF device object has been created. The primary purpose of Configure is to:

  • Create and enable the device interface.

  • Create and configure I/O queue objects to manage incoming I/O requests.

Because the Skeleton sample does not handle I/O requests, Configure has a token implementation that returns only S_OK. Add code to this method as appropriate for your driver. See the Fx2_Driver sample for an example of how to implement Configure.

IUnknown

If you follow the Skeleton sample model for implementing IUnknown, you can use the Skeleton sample code without modification to implement the interface-specific IUnknown methods.

Optional Interfaces

The device callback object has no required interfaces. However, most devices support Plug and Play, so they must expose the IPnpCallback interface and possibly the IPnpCallbackHardware and IPnpCallbackSelfManagedIo interfaces. Because the Skeleton sample does not manage a device, it does not implement any of these interfaces. For drivers that support Plug and Play, the simplest approach is to add the necessary code for the required interfaces to CMyDevice.

See the Fx2_Driver sample for an example of how to do this.

image from book
What to Do Next

Any further modifications to the Skeleton sample depend on the requirements of your driver. The most common modification is to implement one or more queue callback objects to receive I/O requests from the associated I/O queues. A common practice is to implement each queue callback object as a separate class in a separate file.

A good starting point is the Echo sample, which implements a single I/O queue callback object to handle read, write, and IOCTL requests. The queue callback object is implemented as a single class and is located in Queue.cpp.

The Fx2_Driver sample uses a more sophisticated approach, with one queue callback object to handle read and write requests and another queue callback object to handle IOCTL requests. Both of the classes that implement these two objects inherit from a parent class that implements the common functionality that all queue callback objects require.

image from book




Developing Drivers with the Microsoft Windows Driver Foundation
Developing Drivers with the Windows Driver Foundation (Pro Developer)
ISBN: 0735623740
EAN: 2147483647
Year: 2007
Pages: 224

Similar book on Amazon
Windows Internals, Part 1: Covering Windows Server 2008 R2 and Windows 7
Windows Internals, Part 1: Covering Windows Server 2008 R2 and Windows 7
Windowsu00ae Internals: Including Windows Server 2008 and Windows Vista, Fifth Edition (Pro Developer)
Windowsu00ae Internals: Including Windows Server 2008 and Windows Vista, Fifth Edition (Pro Developer)
Advanced Windows Debugging
Advanced Windows Debugging
The Windows 2000 Device Driver Book: A Guide for Programmers (2nd Edition)
The Windows 2000 Device Driver Book: A Guide for Programmers (2nd Edition)

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