How to Implement the DLL Infrastructure


If you dump the named exports of a DLL that contains in-process COM objects, you usually see very few exported functions and none of the methods associated with the COM objects. The reason is that COM objects and their methods are not exported as they would be with a conventional DLL. One way of looking at COM, in fact, is that it is a different and more structured way to provide access to a DLL's methods.

To manage the process, the DLL must expose some basic infrastructure. The infrastructure has three basic components:

  • A DllMain function that provides an entry point for the DLL.

  • One or more class factories that external clients use to create instances of COM objects in the DLL.

  • A DllGetClassObject function, exported by name, that provides external clients with instances of the DLL's class factory objects.

With standard COM objects, you must also implement and export by name DllCanUnloadNow and, optionally, DllRegisterServer and DllUnregisterServer. UMDF drivers are not required to export these functions.

This section discusses how to implement the basic infrastructure that is required to support UMDF drivers.

A good starting point for most implementations is the Fx2_Driver or Skeleton sample and modify that code to suit your driver's requirements. That code should require at most only modest changes to adapt it to your driver's requirements. The relevant code is in the Comsup.cpp and Dllsup.cpp files.

DllMain

A DLL can contain any number of in-process COM objects, but it must have a single entry point named DllMain. Windows calls DllMain after the driver binary has been loaded into a host process and again before it is unloaded. The function is also called when threads are created or destroyed. The dwReason parameter indicates why the function was called:

  • When a UMDF driver's DllMain function is called for DLL loading or unloading, the function should perform only simple module-wide initialization and termination tasks.

    For example, it can initialize or free global variables and register or unregister WPP tracing. A UMDF driver's DllMain implementation should definitely not take actions such as calling LoadLibrary. For more information on what you should and should not do in DllMain, see the function's reference page in the Platform SDK.

  • When a UMDF driver's DllMain function is called for thread creation or destruction, it can ignore the call.

     Tip  See the DllMain reference page in the Platform SDK documentation-online at http://go.microsoft.com/fwlink/?LinkId=80069.

The example in Listing 18-7 is the DllMain function from the Fx2_Driver sample. It initializes and cleans up WPP tracing when the DLL is loaded or unloaded, respectively. It ignores calls for thread creation and destruction.

Listing 18-7: A typical DllMain implementation

image from book
 BOOL WINAPI DllMain( HINSTANCE ModuleHandle,                      DWORD Reason,                      PVOID) {     UNREFERENCED_PARAMETER(ModuleHandle);     if (DLL_PROCESS_ATTACH == Reason) { // Initialize tracing.         WPP_INIT_TRACING(MYDRIVER_TRACING_ID);     }     else if (DLL_PROCESS_DETACH == Reason) { // Clean up tracing.         WPP_CLEANUP();     }     return TRUE; } 
image from book

DllGetClassObject

Because class factories are not exported by name, a client cannot directly access a class factory. Instead, the DLL exports the DllGetClassObject function by name, which allows the function to be called by any client that has loaded the DLL. For many COM DLLs, including the UMDF samples, DllGetClassObject is the only function that is listed in the project's .def file to be exported by name from the DLL.

When a client wants to create an instance of one of the COM objects in the DLL, the client passes the CLSID of the desired class factory object to DllGetClassObject along with the IID of the desired interface, usually IClassFactory. DllGetClassObject creates a new class factory object and returns a pointer to the requested interface. The client can then use the IClassFactory::CreateInstance method to create an instance of the associated COM object.

UMDF drivers receive pointers to UMDF objects directly from the UMDF runtime, so they usually are not required to call DllGetClassObject or to use a class factory. However, the UMDF runtime must use a class factory at the start of the loading process to create the driver's driver callback object. That means that all UMDF drivers must implement DllGetClassObject to provide a pointer to the class factory for the driver callback object.

The example in Listing 18-8 of a DllGetClassObject implementation is from the Fx2_Driver sample. The function receives a CLSID and an IID and performs the following tasks:

  1. Determines which class factory to create.

    UMDF drivers require only one COM object with a class factory-the driver callback object-so the implementation simply checks for the correct CLSID.

  2. Uses the C++ new operator to create an instance of the class factory for the driver callback object.

    The object's constructor sets the reference count to one.

  3. Calls QueryInterface on the new class factory object to request the interface that is specified by the InterfaceId parameter.

    With UMDF, the requested interface is always IClassFactory. This action increments the reference count to two.

  4. Releases the factory pointer.

    That pointer is only for temporary internal use and is not reused, so it is released as soon as it is no longer needed. The driver callback object now has a reference count of one, corresponding to the single active interface.

Listing 18-8: A typical DllGetClassObject implementation

image from book
 HRESULT STDAPICALLTYPE DllGetClassObject(__in REFCLSID ClassId,                                          __in REFIID InterfaceId,                                          __deref_out LPVOID *Interface) {     PCClassFactory factory;     HRESULT hr = S_OK;     *Interface = NULL;     if (IsEqualCLSID(ClassId, CLSID_MyDriverCoClass) == false) {         return CLASS_E_CLASSNOTAVAILABLE;     }     factory = new CClassFactory();     if (NULL == factory) {         hr = E_OUTOFMEMORY;     }     if (SUCCEEDED(hr)) {         hr = factory->QueryInterface(InterfaceId, Interface);         factory->Release();     }     return hr; } 
image from book

The UMDF runtime then uses the IClassFactory interface to create an instance of the driver callback object. Most UMDF drivers can use this code with little or no modification. An example implementation from Fx2_Driver is shown in Listing 18-8.

The Class Factory

Some COM objects must be directly created by external clients and must have a class factory. A class factory is a small specialized COM object whose sole purpose is to create a new instance of its associated COM object and return a pointer to a specified interface.

UMDF drivers have only one external client-the UMDF runtime, which uses a class factory to create a driver callback object. This object acts as an entry point for a UMDF driver in much the same way as the DriverEntry routine does for kernel-mode drivers. The other callback objects, such as the device or queue callback objects, do not require class factories and are discussed later in this chapter.

Because the UMDF runtime directly creates only one callback object, a UMDF driver requires only a single class factory. In addition to IUnknown, this class factory exposes one interface: IClassFactory. The IClassFactory interface has two members:

  • CreateInstance, which creates an instance of the object and returns the requested interface to the client.

  • LockServer, which can be used to keep the DLL in memory. UMDF drivers typically have only a token implementation because UMDF does not use LockServer.

How to Implement a Class Factory

A class factory is typically implemented as a single C++ class. The example in Listing 18-9 is an edited version of the declaration of CClassFactory, the Fx2_Driver sample's driver callback object class factory. The example omits some private members and the IUnknown interface, and it shows only the part of the declaration that is devoted to IClassFactory. For a complete declaration, see the Fx2_Driver sample.

Listing 18-9: The driver callback object's class factory declaration for the Fx2_Driver sample

image from book
 class CClassFactory : public CUnknown, public IClassFactory { public: . . .// Code omitted for brevity.      // IClassFactory methods.      virtual HRESULT STDMETHODCALLTYPE CreateInstance(          __in_opt IUnknown *OuterObject,          __in REFIID InterfaceId,          __out PVOID *Object          );      virtualHRESULT STDMETHODCALLTYPE LockServer(          __in BOOL Lock          ); }; 
image from book

CClassFactory inherits from IClassFactory. Like all COM objects, it also inherits from IUnknown, although in this case it does so indirectly through the CUnknown class. This implementation detail for the Fx2_Driver sample is discussed in "How to Implement IUnknown" later in this chapter.

The example in Listing 18-10 shows the Fx2_Driver sample's implementation of IClassFactory::CreateInstance. This is a typical implementation of the method, which most UMDF drivers can use without modification. When the UMDF runtime calls the method:

  • It ignores the first parameter. The purpose of this parameter is to support COM aggregation, which UMDF does not use.

  • It sets the InterfaceId parameter to the IID of IDriverEntry, which is IID_IDriverEntry.

Listing 18-10: The Fx2_Driver sample's 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

The IClassFactory::CreateInstance method:

  1. Creates a new driver callback object by calling the driver callback object's CreateInstance method.

    Objects are created with the C++ new operator. In this case, the driver callback object is implemented as a class named CMyDriver that includes a static object creation method: CMyDriver::CreateInstance. This method uses the new operator to create an instance of the class and returns an object pointer. The constructor for the class increments the object's reference count, so it is now set to one.

  2. Requests a pointer to the driver callback object's IDriverEntry interface by calling the object's QueryInterface method.

    If successful, QueryInterface sets the Object parameter to the driver callback object's IDriverEntry interface and calls the object's AddRef method to increment the reference count. The object's reference count now stands at two.

  3. Calls the driver callback object's Release method to release the object pointer, returning the reference count to one.

  4. Returns the result of the QueryInterface call to the UMDF runtime.

    Assuming success, hr is set to S_OK and the UMDF runtime receives a pointer to the driver callback object's IDriverEntry interface.

UMDF does not use the IClassFactory::LockServer method, but it must have at least a minimal implementation to satisfy the requirements of COM. The implementation of LockServer in the Fx2_Driver sample, as shown in Listing 18-11, simply tracks lock and unlock requests in a static variable:

Listing 18-11: Fx2_Driver sample's IClassFactory::LockServer implementation

image from book
 HRESULT STDMETHODCALLTYPE CClassFactory::LockServer(     __in BOOL Lock     ) {     if (Lock)     {         InterlockedIncrement(&s_LockCount);     }     else     {         InterlockedDecrement(&s_LockCount);     }     return S_OK; } 
image from book

Objects That Do Not Require a Class Factory

If an external client does not directly create a COM object, the object does not require a class factory. The way a client creates such objects is an implementation detail. For example, the UMDF runtime does not directly create a device callback object. It calls the driver callback object's IDriverEntry::OnDeviceAdd method, which creates the device callback object. The driver then passes the device callback object's IUnknown interface to the UMDF runtime when the driver calls IWDFDriver::CreateDevice.

For the Fx2_Driver sample, OnDeviceAdd creates a device callback object by calling the static CMyDevice::CreateInstance method. This method creates a device callback object and passes it to IWDFDriver::CreateDevice. The sample actually does this indirectly in CMyDevice:: Initialize, which is called by CreateInstance.




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

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