Important: |
---|
This is retired content. This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This content may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. |
This article describes how to implement a component as a dynamic-link library (DLL) in Microsoft DirectShow. This article is a continuation from How to Implement IUnknown, which describes how to implement the IUnknowninterface by deriving your component from the CUnknownbase class.
This article contains the following sections.
Registering a DirectShow filter (as opposed to a generic COM object) requires some additional steps that are not covered in this article. For information on registering filters, see How to Register DirectShow Filters.
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 CoGetClassObjectfunction. The client then calls the class factory's IClassFactory::CreateInstancemethod. It is the class factory that actually creates the component and returns a pointer to the requested interface. (The CoCreateInstancefunction combines these steps, inside the function call.)
CoGetClassObjectcalls the DllGetClassObjectfunction, 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 DllGetClassObjectfor 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 DllGetClassObjectmakes 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.
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_lpfnNewand 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 article.)
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 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 CreateInstancemethod calls the class constructor and returns a pointer to the new class instance. The parameter pUnkis a pointer to the aggregating IUnknown. You can simply pass this parameter to the class constructor. The parameter pHris a pointer to an HRESULTvalue. The class constructor sets this to an appropriate value, but if the constructor fails, set the value to E_OUTOFMEMORY.
The NAMEmacro generates a string in debug builds but resolves to NULL in retail builds. It is used 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_Templatesand g_cTemplatesare global variables that the class factory expects to find, so they must have exactly those names.
A DLL must implement the following functions so that it can be registered, unregistered, and loaded into memory.
Of these, the first three are implemented by DirectShow. If your factory template provides an initialization function in the m_lpfnInitmember 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 DllMainin the Platform SDK.
You must implement DllRegisterServerand DllUnregisterServer, but DirectShow provides a function named AMovieDLLRegisterServerthat 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 DllRegisterServerand DllUnregisterServeryou 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 Tuesday, May 18, 2004