Using a custom DirectShow filter (DLL, registered)

4.6k views Asked by At

I've written a small transform filter (derived from TransInPlaceFilter baseclass), and managed to make it work properly in a Directshow graph, entirely coded in C++. I basically followed the 5 first steps described on MSDN, and the last part of the 6th step (in order to use the filter directly within an application).

For a lot of reasons (including being able to use the filter in GraphEdit), I need to export that filter in a DLL and register it.

I've been trying to follow these steps : setting up the "CreateInstance" method, the CFactoryTemplate class, filter/pins description (AMOVIESETUP_FILTER, AMOVIESETUP_PIN, AMOVIESETUP_MEDIA), Registering/Unregistering functions, and finally DLLmain/entrypoint.

The code successfully compiles and provides a DLL, which seems to register without any problem using Regsvr32.

But then I'm unable to use the filter :

  • It appears in the list in GraphEdit, but fails when I try to insert it : 0x800401f9 ("Error in DLL").
  • When I try to create it in C++ (using pCustomFilter.CoCreateInstance(CLSID_Custom), after defining the correct GUID), I get error 0x80040154 (REGDB_E_CLASSNOTREG)

I'm quite confused here. Have I missed something in the DLL/registration code ? I'm posting most of the code I'm using, if it's of any use.

Any help would be greatly appreciated.

static const WCHAR g_wszName[] = L"Custom Filter";

AMOVIESETUP_MEDIATYPE sudMediaTypes[] = {
    { &MEDIATYPE_Video, &MEDIASUBTYPE_NULL },
    { &MEDIATYPE_Audio, &MEDIASUBTYPE_NULL },
};

AMOVIESETUP_PIN sudPins[2] = {
    {
        L"Input",            // Name
        FALSE,          // Is this pin rendered?
        FALSE,          // Is it an output pin?
        FALSE,          // Can the filter create zero instances?
        FALSE,          // Does the filter create multiple instances?
        &GUID_NULL,     // Obsolete.
        NULL,           // Obsolete.
        2,              // Number of media types.
        sudMediaTypes   // Pointer to media types.
    },
    {
        L"Output",            // Name
        FALSE,          // Is this pin rendered?
        TRUE,           // Is it an output pin?
        FALSE,          // Can the filter create zero instances?
        FALSE,          // Does the filter create multiple instances?
        &GUID_NULL,     // Obsolete.
        NULL,           // Obsolete.
        2,              // Number of media types.
        sudMediaTypes   // Pointer to media types.
    }
};

AMOVIESETUP_FILTER sudFilterReg = {
    &CLSID_Custom,          // Filter CLSID.
    g_wszName,              // Filter name.
    MERIT_DO_NOT_USE,       // Merit.
    2,                      // Number of pin types.
    sudPins             // Pointer to pin information.
};


CFactoryTemplate g_Templates[] = 
{
  { 
    g_wszName,
    &CLSID_Custom,
    CCustomFilter::CreateInstance,
    NULL,
    &sudFilterReg
  }
};

int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);

STDAPI DllRegisterServer()
{
    return AMovieDllRegisterServer2( TRUE );
}
STDAPI DllUnregisterServer()
{
    return AMovieDllRegisterServer2( FALSE );
}


extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID);

BOOL WINAPI DllMain(HANDLE hDllHandle, DWORD dwReason, LPVOID lpReserved)
{
    return DllEntryPoint(reinterpret_cast<HINSTANCE>(hDllHandle), dwReason, lpReserved);
}

// ---
// Meanwhile, in my filter class...
// ---
CUnknown * WINAPI CCustomFilter::CreateInstance(LPUNKNOWN pUnk, HRESULT *pHr)
{
    CCustomFilter *pFilter = new CCustomFilter();
    if (pFilter== NULL) 
    {
        *pHr = E_OUTOFMEMORY;
    }
    return pFilter;
}
3

There are 3 answers

2
Ralf On

One thing to check would be that the GUID is the same everywhere: AMOVIESETUP_FILTER, CFactoryTemplate, and passed to the CTransInPlaceFilter constructor.

Also, it is good practice to pass the pUnk, and pHr parameter of CCustomFilter::CreateInstance to your CCustomFilter constructor and from there to the CTransInPlaceFilter constructor, so that any errors can be propagated to the caller. In your case any such errors would be swallowed by your constructor. Example:

CUnknown* WINAPI FramerateDisplayFilter::CreateInstance(LPUNKNOWN pUnk, 
                                                        HRESULT *pHr )
{
  FramerateDisplayFilter *pFilter = new FramerateDisplayFilter(pUnk, pHr);
  if (pFilter== NULL) 
  {
    *pHr = E_OUTOFMEMORY;
  }
  return pFilter;
}


FramerateDisplayFilter::FramerateDisplayFilter(LPUNKNOWN pUnk, HRESULT *pHr)
: CTransInPlaceFilter(NAME("CSIR RTVC Framerate Estimator Filter"), pUnk,   
                      CLSID_RTVCFramerateEstimatorFilter, pHr, false),
  m_uiEstimatedFramerate(0),
  m_bSeenFirstFrame(false),
  m_previousTimestamp(0)
{;}
0
fulguroblastor On

Problem solved. It was actually two things :

  1. Directly debugging the DLL (somthing I hadn't thought of...), more specifically the CoCreateInstance function, helped me notice an issue with string macros. The constructor for TransInPlaceFilter was called with a bad argument, which caused the crash.

  2. Someone made me notice I wasn't exporting every required function (as described here on MSDN). My .def file was lacking DllMain, DllGetClassObject and DllCanUnloadNow. Those 2 last functions just need to appear in the def, as they are already defined in the baseclasses library.

`

LIBRARY "custom_filter"
EXPORTS
    DllMain                 PRIVATE /* missing */
    DllGetClassObject       PRIVATE /* missing */
    DllCanUnloadNow         PRIVATE /* missing */
    DllRegisterServer       PRIVATE
    DllUnregisterServer     PRIVATE

Thanks a lot for your help !

1
Mike W On

Did you verify that the correct registry entries were written?

The registry entry for CoCreateInstance is not the same as the entry for filter enumeration, so a filter could appear in GraphEdit's filter list but fail CoCreateInstance.

http://msdn.microsoft.com/en-us/library/dd390639(v=VS.85).aspx