Creating a DLL in DirectShow

This topic describes how to implement a component as a dynamic-link library (DLL) in DirectShow. This topic is a continuation from Implementing IUnknown in DirectShow, which describes how to implement the IUnknown interface by deriving your component from the CUnknown base class.

The following topics provide additional information:

  • Class Factories and Factory Templates
  • Factory Template Array
  • DLL Functions

Registering a DirectShow filter (as opposed to a generic COM object) requires some additional steps that are not covered in this topic. For information on registering filters, see Registering DirectShow Filters.

Class Factories and Factory Templates

Before a client creates an instance of a COM object, it creates an instance of the object's class factory, using a call to the CoGetClassObject function. The client then calls the class factory's IClassFactory::CreateInstance method. It is the class factory that actually creates the component and returns a pointer to the requested interface. (The CoCreateInstance function combines these steps, inside the function call.)

CoGetClassObject calls the DllGetClassObject function, which is defined in the DLL. This function creates the class factory and returns a pointer to an interface on the class factory. DirectShow implements DllGetClassObject for you, but the function relies on your code in a specific way. To understand how it works, you must understand how DirectShow implements class factories.

A class factory is a COM object dedicated to creating another COM object. Each class factory has one type of object that it creates. In DirectShow, every class factory is an instance of the same C++ class, CClassFactory. Class factories are specialized by means of another class, CFactoryTemplate, also called the factory template. Each class factory holds a pointer to a factory template. The factory template contains information about a specific component, such as the component's class identifier (CLSID), and a pointer to a function that creates the component.

In your DLL, declare a global array of factory templates, one for each component in the DLL. When DllGetClassObject makes a new class factory, it searches the array for a template with a matching CLSID. Assuming it finds one, it creates a class factory that holds a pointer to the matching template. When the client calls IClassFactory::CreateInstance, the class factory calls the instantiation function defined in the template.

The benefit of this architecture is that you can define just a few things that are specific to your component, such as the instantiation function, without implementing the entire class factory.

Factory Template Array

The factory template contains the following public member variables.

const WCHAR *              m_Name;                // Name
const CLSID *              m_ClsID;               // CLSID
LPFNNewCOMObject           m_lpfnNew;             // Function to create an instance
                                                  //   of the component
LPFNInitRoutine            m_lpfnInit;            // Initialization function (optional)
const AMOVIESETUP_FILTER * m_pAMovieSetup_Filter; // Set-up information (for filters)

The two function pointers, m_lpfnNew and m_lpfnInit, use the following type definitions.

typedef CUnknown *(CALLBACK *LPFNNewCOMObject)(LPUNKNOWN pUnkOuter, HRESULT *phr);
typedef void (CALLBACK *LPFNInitRoutine)(BOOL bLoading, const CLSID *rclsid);

The first is the instantiation function for the component. The second is an optional initialization function. If you provide an initialization function, it is called from inside the DLL entry-point function. The DLL entry-point function is discussed later in this topic.

Suppose you are creating a DLL that contains a component named CMyComponent, which inherits from CUnknown. You must provide the following items in your DLL.

  • The initialization function, a private method that returns a new instance of CMyComponent.
  • A global array of factory templates, named g_Templates. This array contains the factory template for CMyComponent.
  • A global variable named g_cTemplates that specifies the size of the array.

The following example shows how to declare these items.

// Private method that returns a new instance. 
CUnknown * WINAPI CMyComponent::CreateInstance(LPUNKNOWN pUnk, HRESULT *pHr) {
    CMyComponent *pNewObject = new CMyComponent(NAME("My Component"), pUnk, pHr );
    if (pNewObject == NULL) {
        *phr = E_OUTOFMEMORY;
    }
    return pNewObject;
} 

CFactoryTemplate g_Templates[1] = {
    { L"My Component",                // Name
      &CLSID_MyComponent,             // CLSID
      CMyComponent::CreateInstance,   // Method to create an instance of MyComponent
      NULL,                           // Initialization function
      NULL                            // Set-up information (for filters)
    }
};
int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);

The CreateInstance method calls the class constructor and returns a pointer to the new class instance. The parameter pUnk is a pointer to the aggregating IUnknown. You can simply pass this parameter to the class constructor. The parameter pHr is a pointer to an HRESULT value. The class constructor sets this to an appropriate value, but if the constructor fails, set the value to E_OUTOFMEMORY.

The NAME macro generates a string in debug builds but resolves to NULL in retail builds. It is used in this example to give the component a name that appears in debug logs but does not occupy memory in the final version.

CreateInstance is a private method, so it can have any name. The class factory receives a pointer to it in the factory template. However, g_Templates and g_cTemplates are global variables that the class factory expects to find, so they must have exactly those names.

DLL Functions

A DLL must implement the following functions so that it can be registered, unregistered, and loaded into memory.

  • DllMain: The DLL entry point. The name DllMain is a placeholder for the library-defined function name. The DirectShow implementation uses the name DllEntryPoint. For more information, see the Windows SDK.
  • DllGetClassObject: Creates a class factory instance. Described in the previous sections.
  • DllCanUnloadNow: Queries whether the DLL can safely be unloaded.
  • DllRegisterServer: Creates registry entries for the DLL.
  • DllUnregisterServer: Removes registry entries for the DLL.

Of these, the first three are implemented by DirectShow. If your factory template provides an initialization function in the m_lpfnInit member variable, that function is called from inside the DLL entry-point function. For more information on when the system calls the DLL entry-point function, see DllMain in the Windows SDK.

You must implement DllRegisterServer and DllUnregisterServer, but DirectShow provides a function named AMovieDLLRegisterServer that does the necessary work. Your component can simply wrap this function, as shown in the following example.

STDAPI DllRegisterServer()
{
    return AMovieDLLRegisterServer( TRUE );
}

STDAPI DllUnregisterServer()
{
    return AMovieDLLRegisterServer( FALSE );
}

However, within DllRegisterServer and DllUnregisterServer you can customize the registration process as needed.

In your module-definition (.def) file, export all the DLL functions except for the entry-point function. The following is an example .def file.

EXPORTS
    DllGetClassObject PRIVATE
    DllCanUnloadNow PRIVATE
    DllRegisterServer PRIVATE
    DllUnregisterServer PRIVATE

You can register the DLL using the Regsvr32.exe utility.

Last updated on Wednesday, April 13, 2005

© 2005 Microsoft Corporation. All rights reserved.