Multimedia dedicated weblog.

DirectShow BaseClasses and MFC

January 10th, 2008 Posted in DirectShow

Ever since the DirectShow framework has appeared the filter writers have used the BaseClasses library (BC) to build their own filters on. Some time ago it has been moved from the DirectX SDK to Platform SDK and has been extended vastly but it still contains several important issues that made me use patched builds.

To find out how to modify and build the library for use with MFC or ActiveX objects read the rest of this article.

MFC Interoperability

If you try to use MFC framework classes (CWinAapp-, CWnd- derived) in your AX filters you end up with tons of error messages from the not-initialized MFC framework. To fix this several changes need to be done in the initialization functions (dllentry.cpp).

DirectShow filter library, being a standard COM library, needs to provide 4 functions:

  • DllRegisterServer
  • DllUnregisterServer
  • DllCanUnloadNow
  • DllGetClassObject

The first two functions are usually handled easily using the provided function that takes care of the registry - AMovieDllRegisterServer2.

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

The next two (DllCanUnloadNow, DllGetClassObject) are handled inside the BC library and utilize the 2 mandatory symbols - g_Templates & g_cTemplates. These records hold the information on the total number of objects provided by our library and their initialization methods. In most cases these could be just static constants but in some cases it may be necessary that the DLL entry point function calls their initializers first.

This brings us to the root of all evil - DllEntryPoint. If you have written at least one filter I’m sure you’ve noticed that DirectShow filter libraries do not use the default entry point and use the DllEntryPoint instead. This function initializes and unitializes all filter internals properly but does not give the chance for MFC or other frameworks to initialize.

Solution is quite simple. We define a new entry point function that will call the default entry point first and then carry on with the BC entry point.

extern "C" BOOL WINAPI _DllMainCRTStartup(HINSTANCE, ULONG, LPVOID);
BOOL APIENTRY FilterDllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
{
_DllMainCRTStartup((HINSTANCE)hModule, dwReason, lpReserved);
return DllEntryPoint((HINSTANCE)hModule, dwReason, lpReserved);
}

The next step is to modify the DllGetClassObject and DllCanUnloadNow functions according to the MFC rules by adding these lines at their beginning. It is also recommended to add them in DllRegisterServer and DllUnregisterServer but if you don’t do anything else other than just registering/unregistering the filter it will work even without the modification.

#ifdef _AFXDLL
AFX_MANAGE_STATE(AfxGetStaticModuleState());
#endif

Issues with built-in filters

When you are developing applications with built-in filters you’re gonna have to link the application executable directly against the static (/or dynamic) BC library. This means that the final binary will contain symbols obtained in the BC. This is completely fine when building executables, but if you’re developing a DLL (or ActiveX control) you may need to remove or comment the DllCanUnloadNow and DllGetClassObject so they would not interfere with those provided by MFC framework.

One can assume that you will not use one ActiveX DLL as a control DLL and a DirectShow filter at the same time so you don’t need to worry that you’ll mess something up by disabling the DllEntry*** functionality of BC.

MFC-Shared vs. MFC-Static

If you are using MS Visual Studio 2005 as IDE you may need to make several builds of the BC library. Applications and DLL libraries produced by VS2005 and compiled with shared MFC libraries require a special DLL package in order to work properly. It can be downloaded from the Microsoft website but your filters will fail to register on systems without them. If you want to avoid such problems you may need to compile BC and all your filters as MFC-Static.

So much for today. I hope that this clears a few things and could help you get rid of a few problems. If you have anything to add or correct my mistake drop me a line.

Igor

  1. 11 Responses to “DirectShow BaseClasses and MFC”

  2. By pan One on Mar 18, 2008

    And if we do have MFC environment in a filter, would it be possible to have property pages derived from CDialog? Could be a nice follow up to this post ;)

  3. By Igor Janos on Mar 18, 2008

    Yeah sure. I might write an article about that.
    For now you can try to read the following source code from GraphStudio. There is a class that supports MFC CDialog derived property pages.

    CustomPage.h
    CustomPage.cpp

  4. By pan One on Mar 19, 2008

    Thanks! This is exactly what I was going to try. So now I know that this is the way :)

  5. By pan One on Mar 20, 2008

    I know it starts to sound like whining but I have nobody else to complain to. I cannot get it working with shared MFC — it crashes deep inside IPropertyPage::Activate() handling while trying to get some resources. Here is the call stack (cleaned-up version):

    mfc80ud.dll!AfxGetResourceHandle() <– KA-BOOM
    mfc80ud.dll!AfxFindResourceHandle()
    mfc80ud.dll!CWnd::ExecuteDlgInit()
    mfc80ud.dll!CDialog::OnInitDialog()
    mfc80ud.dll!AfxDlgProc()
    user32.dll!7e418734()

    user32.dll!7e41c665()
    mfc80ud.dll!CWnd::DefWindowProcW()
    mfc80ud.dll!CWnd::Default()
    mfc80ud.dll!CDialog::HandleInitDialog()
    mfc80ud.dll!CWnd::OnWndMsg()
    mfc80ud.dll!CWnd::WindowProc()
    mfc80ud.dll!AfxCallWndProc()
    mfc80ud.dll!AfxWndProc()
    mfc80ud.dll!AfxWndProcBase() <– Oops!
    user32.dll!7e418734()

    user32.dll!7e43f002()
    mfc80ud.dll!CWnd::CreateDlgIndirect()
    mfc80ud.dll!CDialog::CreateIndirect()
    mfc80ud.dll!CDialog::Create()
    mfc_filter.ax!FilterPropertyPage::Activate()

    While I restore module state with AFX_MANAGE_STATE(AfxGetStaticModuleState()) when entering Activate(), by the time it reaches AfxWndProcBase() MFC decides that I need some _afxBaseModuleState goodness, which is fool of zeros and null pointers. So when it gets to resources access the module handle is null and it doesn’t look pretty :(

    Any ideas?

  6. By Igor Janos on Mar 20, 2008

    Hm. Perhaps you could try to put something like this:

    // The one and only application object
    #ifndef MFCSTATIC
    CWinApp theApp;
    #endif

    Somewhere in the code.

  7. By pan One on Mar 21, 2008

    Yes, I do. By now I managed to trace it to MFC DllMain(), which does something along the lines:

    if (dwReason == DLL_PROCESS_ATTACH) {
    // shared initialization
    AFX_MODULE_STATE* pModuleState = _AfxGetOleModuleState();
    pModuleState->m_hCurrentResourceHandle = hInstance; m_pPrevModuleState); <– null

    // wire up this DLL into the resource chain
    CDynLinkLibrary* pDLL = new CDynLinkLibrary(coreDLL, TRUE); <– create bms
    }

    The module state in shared initialization gets proper handle to the instance. But then the module state in current thread state, which is the same as _AfxGetOleModuleState(), is replaced (for some reason) by previous module state, which is null.

    From that point any attempt to get module state with AfxGetModuleState() leads to _afxBaseModuleState (bms) because module state in the thread state is null. The first such attempt happens during construction of CDynLinkLibrary, which creates bms (it didn’t exist until that moment).

    I talked with some guy here and he suggested that the problem stems from the fact that GraphEdit uses different MFC dll. The idea is that when MFC rolls back to previous state it expects to end up withthe state of the application, but that state is stored in mfc42.dll instance and we are in mfc80ud.dll.

    Does it make any sense?

  8. By pan One on Mar 21, 2008

    Oops! the code snipped got screwed up. Repost:

    if (dwReason == DLL_PROCESS_ATTACH) {
    // shared initialization
    AFX_MODULE_STATE* pModuleState = _AfxGetOleModuleState();
    pModuleState->m_hCurrentResourceHandle = hInstance; m_pPrevModuleState); <- null

    // wire up this DLL into the resource chain
    CDynLinkLibrary* pDLL = new CDynLinkLibrary(coreDLL, TRUE); <- create bms
    }

  9. By pan One on Mar 21, 2008

    Damn! I just can’t get it right. Last attempt:

    if (dwReason == DLL_PROCESS_ATTACH) {
    // shared initialization
    AFX_MODULE_STATE* pModuleState = _AfxGetOleModuleState();
    pModuleState->m_hCurrentResourceHandle = hInstance; // the good stuff

    // restore previously-saved module state
    AfxSetModuleState(AfxGetThreadState()->m_pPrevModuleState); // null

    // wire up this DLL into the resource chain
    CDynLinkLibrary* pDLL = new CDynLinkLibrary(coreDLL, TRUE); // create bms
    }

  10. By mansi on Apr 2, 2009

    Hi..I often visit this blog for directshow knowledge. I have a problem and am stuck up..I have a client and a server. Client implements asfwriter filter and streams live data to a port on Client System. The client also sends its IP to server…On the server, I have WMAsfReader with IFileSourceFilter which loads URL of client which server recieves from client.The server reads data from Port of client to port of server..This stuff fails for PCs Behind NAT….Pl suggest me some options…Thanks a lot.

  11. By Surabhi on Sep 2, 2009

    I want to use a direct show filter in my MFC application. I am inserting a filter using graph edit. After inserting this filter when I right click on this filter, I get a filter properties dialog box. I want to show this dialog box through my MFC application and access its fields. Can u plz help me to implement this functionality..

  1. 1 Trackback(s)

  2. Jun 5, 2008: RadScorpion’s blog » Blog Archive » Beginner’s guide: How to make a DirectShow filter VC++ project

Post a Comment