Using a custom DirectShow filter (DLL, registered)
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 DllUnregiste开发者_运维百科rServer()
{
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;
}
Problem solved. It was actually two things :
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.
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 !
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)
{;}
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
精彩评论