Developing Drivers with the Windows Driver Foundation (Pro Developer)

COM programming has two aspects: implementing clients that use existing COM objects and implementing your own COM objects. These two aspects are not mutually exclusive because COM objects are often clients of other COM objects. Both UMDF drivers and the UMDF runtime function as COM clients:

This section provides a brief overview of how a COM client uses existing COM objects, which is the simplest type of COM programming and is good preparation for the discussion of object implementation later in this chapter.

How to Start Using a COM Object

To use a COM object, you must obtain a pointer to at least one of the object's interfaces. You can then call any of the methods on the interface by using the same syntax that you would for a pointer to a C++ method. In the example in Listing 18-2, pWdfRequest is a pointer to an IWDFIoRequest interface.

Listing 18-2: How to invoke a method on an interface

HRESULT hr; . . . // Code omitted for brevity. hr = pWdfRequest->Send( m_pIUsbTargetDevice, WDF_REQUEST_SEND_OPTION_SYNCHRONOUS, 0);

The interface on a UMDF object can be obtained in one of the following three ways:

Receive an Interface through a Callback Method

This way is the simplest. For example, when the UMDF runtime calls a driver's IDriverEntry::OnDeviceAdd method, the runtime passes a pointer to the device object's IWDFDriver interface as an IN parameter.

Listing 18-3: How to receive an interface pointer through a callback method

HRESULT CMyDriver::OnDeviceAdd( __in IWDFDriver *FxWdfDriver, __in IWDFDeviceInitialize *FxDeviceInit) { //Install the driver in the device stack }

The OnDeviceAdd implementation then uses FxWdfDriver to access the methods on the driver object's IWDFDriver interface. Because the IWDFDriver pointer is passed as an IN parameter, the driver should not release it. The caller ensures that the associated object remains valid during the scope of the method call and releases the interface pointer when it is no longer necessary.

Call a UMDF Object Creation Method

Sometimes a driver must explicitly create a UMDF object by calling the appropriate object creation method. As shown in Listing 18-4, to create an I/O request object, a driver calls the UMDF device object's IWDFDevice::CreateRequest method.

Listing 18-4: Declaration of the IWDFDevice::CreateRequest method

HRESULT CreateRequest( __in IUnknown* pCallbackInterface, __in IWDFObject* pParentObject, __out IWDFIoRequest** ppRequest);

ppRequest is an OUT parameter that provides an address at which the CreateRequest method can store a pointer to the newly created request object's IWDFObject interface. The following procedure and sample show how the Fx2_Driver sample calls CreateRequest.

To handle parameters when calling CreateRequest:

  1. Declare a pWdfRequest variable to hold a pointer to IWDFIoRequest.

  2. Pass a pointer to pWdfRequest to CreateRequest. When CreateRequest returns, pWdfRequest holds a pointer to an IWDFIoRequest interface.

  3. Use pWdfRequest to call the interface's methods.

When the driver is finished with the interface pointer, the driver should release the interface pointer by calling IUnknown::Release. Listing 18-5 shows the use of an object creation method.

Listing 18-5: How to use an object creation method-from Fx2_Driver

IWDFIoRequest *pWdfRequest = NULL; . . . // Code omitted for brevity. hr = m_FxDevice->CreateRequest(NULL, NULL, &pWdfRequest); . . .// Code omitted for brevity. hr = pWdfRequest->Send(m_pIUsbTargetDevice, WDF_REQUEST_SEND_OPTION_SYNCHRONOUS, 0); //Timeout

Call QueryInterface to Request a New Interface

UMDF objects can expose more than one interface. If you have a pointer to one interface and need a pointer to another interface on the same object, you can call IUnknown::QueryInterface to request the desired pointer. Pass QueryInterface the IID of the desired interface and the address of the memory allocated to hold the requested interface pointer; QueryInterface returns the requested pointer. When you are finished with the interface pointer, release it.

In the example from Fx2_Driver in Listing 18-6, the driver requests an IWDFIoTargetStateManagement interface pointer from the UMDF I/O target object. This example uses the IID_PPV_ARGS macro-declared in Objbase.h-which takes an interface pointer and produces the correct arguments for QueryInterface.

Listing 18-6: How to request a new interface on an existing object

VOID CMyDevice::StartTarget(IWDFIoTarget * pTarget) { IWDFIoTargetStateManagement * pStateMgmt = NULL; HRESULT hrQI = pTarget->QueryInterface(IID_PPV_ARGS(&pStateMgmt)); . . .// Code omitted for brevity. }

Tip 

QueryInterface is a method on the IUnknown interface. However, as shown in this example, you are not required to have an explicit pointer to an object's IUnknown interface to call QueryInterface. All interfaces inherit from IUnknown, so you can use any interface to call QueryInterface.

How to Manage a COM Object's Lifetime

One of the big differences between COM and C++ is how a client manages the lifetime of the objects that it uses. In C++, clients manage object lifetimes directly by creating objects with the new operator and destroying them with the delete operator. In contrast, clients manage a COM object's lifetime indirectly, through its reference count.

An object's reference count is usually the number of interface pointers that have been successfully requested minus the number that have been released. When the reference count reaches zero-indicating that there are no active interfaces-the object destroys itself. Clients generally do not know the reference count on an object, nor do they usually need to. The client's task is to use AddRef and Release correctly and let the object take care of itself. For details on how to use AddRef and Release correctly, see "Guidelines for AddRef and Release" earlier in this chapter.

Tip 

Both AddRef and Release return the current value of the object's reference count. Do not trust that value, however, it's provided mainly for debugging purposes. The actual reference count might have changed by the time you try to use the returned value.

Категории