Understanding Your DirectShow Filter Graph

Many questions in DirectShow development are caused by lack of developer’s understanding what topology his code effectively built. Intelligent Connect and RenderXxx methods help adding and connecting filters and in the end a developer does not have a faintest idea what the pipeline looks like.

DirectShow API provides methods to enumerate filters, pins, connection and obtained detailed information about the filter graph. The API is well-documented. Then Windows SDK is shipped with GraphEdit which helps building graphs interactively. Ability to publish a graph on ROT and review it from GraphEdit is nothing but powerful. And then we have GraphStudioNext which makes everything even more convenient.

This does not seem sufficient and clear as many new questions and misunderstanding show that developers have false assumptions on graphs their applications use.

DirectShowSpy goes one step further with debugging options. With DirectShowSpy one can embed reviewing UI right into the developed application and either generate detailed textual description of filters, connections, media types as well as pass filter graph to GraphEdit/GraphStudioNext for interactive review with visualized topology. No excuses left any longer for misunderstanding built topologies.

Steps below explain in detail how to visualize your application DirectShow filter graph and generate a textual report on graph details.

1. For starters, one needs to intall DirectShowSpy in target system. Standard installation is mentioned in original post.

  • It is necessary that DirectShowSpy of correct/matching bitness is installed. 32-bit applications use 32-bit DirectShowSpy and 64-bit applications – 64-bit DirectShowSpy. .NET applications built as “Any CPU” are effectively either 32 or 64 bit processes and respectively need a matching spy as well.
  • To cut long story short, simply download DirectShow*.* from Toolbox and use DirectShowSpy-Win32-reg-ui.bat or DirectShowSpy-x64-reg-ui.bat to pop up registration UI. You need local administrator privileges for the registration step (or spy is usable through COM otherwise but it’s beyond scope of this post).

2. DirectShowSpy’s FilterGraphHelper object (already mentioned earlier) offers DoPropertyFrameModal method to pop up diagnostic UI. The helper needs prior initialization with either graph, filter or pin interface. C++ code snippet:

#import "libid:B9EC374B-834B-4DA9-BFB5-C1872CE736FF" raw_interfaces_only // AlaxInfoDirectShowSpy
// ...
CComPtr<IFilterGraph2> pFilterGraph;
// ...
CComPtr<AlaxInfoDirectShowSpy::IFilterGraphHelper> pFilterGraphHelper;
ATLENSURE_SUCCEEDED(pFilterGraphHelper.CoCreateInstance(__uuidof(AlaxInfoDirectShowSpy::FilterGraphHelper)));
ATLENSURE_SUCCEEDED(pFilterGraphHelper->put_FilterGraph(pFilterGraph));
ATLENSURE_SUCCEEDED(pFilterGraphHelper->DoPropertyFrameModal(NULL));

C# code snippet:

IFilterGraph2 graph = new FilterGraph() as IFilterGraph2;
// ...
FilterGraphHelper helper = new FilterGraphHelper();
helper.FilterGraph = graph;
helper.DoPropertyFrameModal(0);

Downloadable sample projects (FilterGraphHelperDialog for C# and FilterGraphHelperDialog2 for C++) are available in Subversion repository or Trac.

3. DoPropertyFrameModal methods opens a window (it’s argument is parent window handle, optional) with details about the graph, including copyable diagnostic text, filters and their property pages all gathered in single window.

FilterGraphHelper.DoPropertyFrameModal UI

NOTE: With root tree element “Filters” selected, the right-side pane contains the text that provides filter graph description (see image above)!

4. Additionally, it is possible to launch GraphEdit/GraphStudioNext with a hotkey and open – through ROT – the graph visually.

FilterGraphHelper.DoPropertyFrameModal UI (Actions)

Remote Graph in GraphStudioNext

This requires that Windows SDK proppage.dll is available. It is normally registered with Windows SDK, and otherwise can be copied from SDK into target system and COM-registered using regsvr32. Or copied into the folder of DirectShowSpy in which case DirectShowSpy-Win32-reg-ui.bat (see item 1 above) file will see it and offer additional property page for registration.

5. When no longer needed, DirectShowSpy can be removed from system using the batch file mentioned above in item 1.

Whatever debugging you do with DirectShow filter graph, you need a complete understanding what filter graph you deal with. If you want to provide additional information to certain DirectShow related question, a copy/pasted diagnostic information needs to be attached to such question so that others understand what you are dealing with exactly.

DirectShowSpy: Restore default system behavior

There was a problem reported for registered and relocated DirectShowSpy, which might be causing issues: Deleting faulty DirectShowSpy registry key.

Some users that use a 3rd party tool called DirectShowSpy may encounter errors when logging in to XSplit.

This can be caused by a fault registry key that is introduced when DirectShowSpy is registered to intercept Filter Graph initialization — Filter Graph is used by XSplit. The faulty DirectShowSpy registry key is usually caused by DirectShowSpy program begin relocated after registration.

To workaround this situation, XSplit1 detects the presence of HKEYCLASSESROOT\CLSID{E436EBB3-524F-11CE-9F53-0020AF0BA770}\TreatAs registry key2 when it fails to initialize Filter Graph and exits when it is found. In this case, user must manually correct the DirectShowSpy registration or delete3 the registry key. Only after either is done can XSplit be restarted.

The description of the problem is good, solution is good but incomplete.

DirectShowSpy intercepts a few COM classes, not just one, and removing single registry value is only a partial fix.

DirectShowSpy.dll exports UnregisterTreatAsClasses function to accurately restore operation of system classes. It does registry permission magic and updates all COM classes involved. Default unregistration (DllUnregisterServer, regsvr32 /u) behavior is to restore original classes only in case they are currently overridden by DirectShowSpy. That is, if the DLL is moved (deleted) the broken registrations are retained in the registry during unregistration process.

UnregisterTreatAsClasses resolved this problem by forcing recovery of original classes no matter who is overriding them at the moment.

C:\>rundll32 DirectShowSpy-Win32.dll,UnregisterTreatAsClasses
C:\>rundll32 DirectShowSpy-x64.dll,UnregisterTreatAsClasses

DirectShowSpy: REGDB_E_CLASSNOTREG with IMMDevice::Activate

A DirectShow developer complained on sudden failure of Core Audio IMMDevice::Activate call supposed to instantiate a DirectShow filter for a given device.

The problem appeared to be related to installed DirectShowSpy and its interference with the API calls. The symptom was of the following kind: when Activate was called for different types of objects, the calls all succeeded except interoperation with DirectShow (activation for IBaseFilter), e.g. EnumerateAudioDevices output:

    IAudioClient            0x00000000
    IAudioEndpointVolume    0x00000000
    IAudioMeterInformation  0x00000000
    IAudioSessionManager    0x00000000
    IAudioSessionManager2   0x00000000
    IBaseFilter             REGDB_E_CLASSNOTREG
    IDeviceTopology         0x00000000
    IMFTrustedOutput        0x00000000

When Core Audio is requested to do DirectShow activation, the API creates and instance of System Device Enumerator, which is forwarded the activation call to. DirectShowSpy intercepts these calls, however what it did not do was support for unknown COM interfaces, and support for undocumented IMMDeviceActivator interface which is used internally by the APIs to forward the activation call.

So, System Device Enumerator implements documented ICreateDevEnum and then it also implements undocumented internal IMMDeviceActivator. The entire sequence call is as follows:

// Top level code:

CComPtr<IMMDevice> pDevice = ...; // Audio endpoint interface
pDevice->Activate(..., __uuidof(IBaseFilter), ...)

// API:

STDMETHOD(Activate)(...)
{
    // ...
    if(requested is IBaseFilter)
    {
        CComPtr<IMMDeviceActivator> pDeviceActivator;
        pDeviceActivator.CoCreateInstace(CLSID_SystemDeviceEnum);
        return pDeviceActivator->Activate(pDevice, ...)
    }

DirectShowSpy’s failure to provide IMMDeviceActivator resulted in symptom in question and is fixed with version 1.0.0.2106 and on. The failure code is not so much descriptive, but of course the APIs did not expect external hook and failure is not actually a supposed possible behavior there.

System Device Enumerator matches the known devices to the provided Core Audio device and creates an instance of respective filter – this is how APIs work together. DirectShowSpy prints these calls out to its log.

roatlbase.h(1582): TraceModuleVersion: "D:\...\DirectShowSpy-Win32.dll" version is 1.0.0.2107, running in "D:\...\EnumerateAudioDevices-Win32.exe" at 0x63210000
dllmain.h(36): CDirectShowSpyModule::CDirectShowSpyModule: this 0x633963A4
SystemDeviceEnumeratorSpy.h(669): CSystemDeviceEnumeratorSpyT<...>::CSystemDeviceEnumeratorSpyT: this 0x02F1DA68
SystemDeviceEnumeratorSpy.h(681): CSystemDeviceEnumeratorSpyT<...>::FinalConstruct: pszPath "D:\...\EnumerateAudioDevices-Win32.exe", this 0x02F1DA68, m_dwRef 1
SystemDeviceEnumeratorSpy.h(49): CSystemDeviceEnumeratorSpyT<...>::InternalQueryInterface: 0x02F1DA68, Interface {3B0D0EA4-D0A9-4B0E-935B-09516746FAC0}, Result 0x00000000
SystemDeviceEnumeratorSpy.h(49): CSystemDeviceEnumeratorSpyT<...>::InternalQueryInterface: 0x02F1DA68, Interface {3B0D0EA4-D0A9-4B0E-935B-09516746FAC0}, Result 0x00000000
SystemDeviceEnumeratorSpy.h(808): CSystemDeviceEnumeratorSpyT<...>::Activate: this 0x02F1DA68, InterfaceIdentifier {56A86895-0AD4-11CE-B03A-0020AF0BA770}, pMmDevice 0x0054E7F8
SystemDeviceEnumeratorSpy.h(815): CSystemDeviceEnumeratorSpyT<...>::Activate: nActivateResult 0x00000000 
SystemDeviceEnumeratorSpy.h(673): CSystemDeviceEnumeratorSpyT<...>::~CSystemDeviceEnumeratorSpyT: this 0x02F1DA68

Download links

DirectShowSpy: Automated Media Sample Traces, Load Trace from File

Some time ago DirectShowSpy received a capability to record media sample traces (in specific points of the pipeline). DirectShowSpy UI visualized the chronology of the streaming which facilitated detection of various problems, especially out of order media samples, lost or late delivered samples, thread racing conditions.

A developer can easily integrate his filters with DirectShowSpy media sample tracing, and example of this is a fork of MP4 filters where this powerful option is integrated.

This time DirectShowSpy is improved to offer automated traces. Typical use scenario is automatic creation of media sample trace file so that it could be logged/attached to specific export process and be available for further review. More specifically, an application could run a transcoding session and generate a detailed media sample trace alogn with the output in order to troubleshoot specific issues (dropped frame etc).

DirectShowSpy’s IFilterGraphHelper (implemented by FilterGraphHelper class) object received new additional methods:

interface IFilterGraphHelper : IDispatch
{
    [...]
    [id(7)] HRESULT ResetMediaSampleTrace([in] VARIANT vProcessIdentifier);
    [id(8)] HRESULT LockMediaSampleTrace([out, retval] IUnknown** ppLockUnknown);
    [id(9)] HRESULT GetMediaSampleTrace([in] VARIANT vProcessIdentifier, [out, retval] BSTR* psText);

ResetMediaSampleTrace clears currently recorded in-memory trace. The method is similar to pressing Backspace key in media sample trace UI. Optional argument allows cleanup for specific process (not implemented, everything is reset).

LockMediaSampleTrace creates a COM object which internally references newly created traces so that they don’t get freed with the respective filter graph release. Traces are owned by respective filter graphs and even though are accessible across process boundaries they are disposed with the release of graphs-creators. Unless anyone else accesses and references the traces, in which case they are available for review even is originating graph is already gone. This is the behavior of media sample trace UI, which is automatically referencing new traces as long as UI is visible. Developer can review traces for graphs already terminated (even crashed!). LockMediaSampleTrace method returns an interface pointer which owns an internals thread monitoring and referencing new traces. Once the interface pointer is releases, all referenced traces are released as well.

GetMediaSampleTrace obtains a copy of media sample trace for all or for specific process, in multi-line TSV format similar to text created by user interactively when data is copied to clipboard or saved into file with the help of UI.

All together, a controlling application can create a trace automatically using the following code:

#import "libid:B9EC374B-834B-4DA9-BFB5-C1872CE736FF" raw_interfaces_only // AlaxInfoDirectShowSpy

// ...

CComPtr<AlaxInfoDirectShowSpy::IFilterGraphHelper> pFilterGraphHelper;
pFilterGraphHelper.CoCreateInstance(__uuidof(AlaxInfoDirectShowSpy::FilterGraphHelper));
ULONGLONG nAlaxInfoDirectShowSpyFileVersion = 0;
CComPtr<IUnknown> pMediaSampleTraceLock;
if(pFilterGraphHelper)
{
    const CComQIPtr<AlaxInfoDirectShowSpy::IModuleVersionInformation> pModuleVersionInformation = pFilterGraphHelper;
    if(pModuleVersionInformation)
        _V(pModuleVersionInformation->get_FileVersion((LONGLONG*) &nAlaxInfoDirectShowSpyFileVersion));
    if(nAlaxInfoDirectShowSpyFileVersion >= _VersionInfoHelper::MakeVersion(1, 0, 0, 2060)) // Available in 1.0.0.2060+
    {
        _V(pFilterGraphHelper->ResetMediaSampleTrace(CComVariant((LONG) GetCurrentProcessId())));
        _V(pFilterGraphHelper->LockMediaSampleTrace(&pMediaSampleTraceLock));
    }
}

// DirectShow filter graph runs go here

if(pFilterGraphHelper && nAlaxInfoDirectShowSpyFileVersion >= _VersionInfoHelper::MakeVersion(1, 0, 0, 2060))
    _ATLTRY
    {
        CComBSTR sText;
        __C(pFilterGraphHelper->GetMediaSampleTrace(CComVariant((LONG) GetCurrentProcessId()), &sText));
        if(sText && *sText)
        {
            CPath sPath = Combine(m_sOutputDirectory, _T("MediaSampleTrace.tsv"));
            CAtlFile File;
            __C(File.Create(sPath, GENERIC_WRITE, FILE_SHARE_READ, CREATE_ALWAYS));
            // SUGG: UTF-8 BOM
            CStringA sTextA = Utf8StringFromString(CStringW(sText));
            __C(File.Write(sTextA, (DWORD) sTextA.GetLength()));
        }
    }
    _ATLCATCHALL()
    {
        _Z_EXCEPTION();
    }

Additionally, interactive UI for reviewing media sampla traces (DoMediaSampleTracePropertySheetModal DLL export, see respective BAT file) reviewed a new command to load external TSV file (such as generated with the help of code above!) for review:

DirectShowSpy Media Sample Trace

Once data loaded from file, disabled Refresh link indicates use of non-live data. Context menu command offers option to switch back to live data.

Download links

DirectShowSpy: Media Sample Traces

Overview

DirectShow filters pass media samples (portions of data) through graphs and details of how the streaming happens exactly is important for debugging and troubleshooting DirectShow graphs and development. A developer needs clear understanding of parts of streaming process, importance of which increases with multiple streams, threads, parallelization, cut-off times, multiple graphs working simultaneously.

Details of streaming is typically hidden from top level control over the graph where the application is controlling state of the process overall, and then filters are on their own sending data through.

DirectShowSpy provides API to filters to register media samples as well as other details of streaming process, including comments and application defined emphasis (highlighting), it stores the traces and provides UI to review and export the traces for analysis and troubleshooting.

A similar tracing is implemented by GraphStudioNext Analyzer Filter.

05

DirectShowSpy trace is different in several ways:

  1. DirectShowSpy is a drop-in module and adds troubleshooting capabilities to already built and existing application, including that it is suitable for temporary troubleshooting in production environment
    • DirectShowSpy offers tracing for filter which are private and not registered globally
    • DirectShowSpy tracing better reproduces production application environment
  2. DirectShowSpy allows supplementary application defined comments, which are registered chronologically along with media samples tracing
    • it is possible to trace not only at filter boundaries/granularity, but also internal events and steps
  3. DirectShowSpy combines tracing from multiple graphs, multiple processes and presents them in a single log

DirectShowSpy media sample trace is a sort of log capability, which is implemented with small overhead. The traces reside in RAM, backed by paging file and are automatically released with release and destruction of filter graph. The important exception is however the media sample tracing UI of DirectShowSpy. When UI is active, each (a) manual refresh of the view, and (b) each destruction of filter graph in analyzed process makes UI add a reference to trace data and data lifetime is extended up to closing of DirectShowSpy UI.

The important feature of behavior mentioned above is that media tracing data outlives, or might outlive the processes that host filter graphs. As long as UI is active, processes including terminated expose media sample trace for interactive review.

Basically the feature is to review a streaming session details obtained from the filters which registered the respective events. For example, this filter graph

01

has two filters, MPEG-4 demultiplexer and multiplexer, which register streaming events. Because the trace is chronological, it in particular allows seeings how “Stuff” filter is doing the processing: threads, timings. If “Stuff” filter registers its own events, the picture becomes more complete.

02

Using

To leverage media sample traces, a filter developer obtains ISpy interface from filter graph (which succeeds when DirectShowSpy is registered and hooks between application and DirectShow API) and creates a IMediaSampleTrace interface using ISpy::CreateMediaSampleTrace call. An example of such integration is shows in a fork of GDCL MPEG-4 filters here, in DemuxOutputPin::Active method.

It does not matter whether filter and pins are sharing IMediaSampleTrace pointers. Each CreateMediaSampleTrace creates a new trace object, which is thread safe on its own, and data is anyway combined on UI from all sources of tracing.

With no DirectShowSpy registered, QueryInterface for ISpy fails and this is the only expense/overhead of integration of media sample tracing in production code.

A developer is typically interested in registering the following events:

  • Segments starts, media sample receives and deliveries, end of stream events; DirectShowSpy introduces respective methods in IMediaSampleTrace interface: RegisterNewSegment, RegisterMediaSample, RegisterEndOfStream
  • Application defined comments, using RegisterComment method

All methods automatically track process and thread information associated with the source of the event. Other parameters include:

  • filter interface
  • abstract stream name, which is a string value and can be anything; typically it makes sense to use pin name/type or it can be pin name with appended stage of processing if developer wants to track processing steps as they happen in the filter; UI offers filtering capability for stream values, and separate column in exported text so that filter could be applied in spreadsheet software such as Excel when reviewing the log
  • user defined comment and highlighting option

RegisterMediaSample methods can be used with anything associated with a media sample, not exactly one event per processing call. The method logs media sample data (it takes AM_SAMPLE2_PROPERTIES pointer as byte array pointer) and makes it available for review with its flags and other data.

Comments can be anything and are to hold supplementary information for events happening in certain relation to streaming:

03

An application can automatically highlight the log entries to draw attention to certain events. For example, if data is streamed out of order and the filter registers the event with highlighting, the entry immediately drawing attention on UI review. Then interactive user can change the highlighting interactively as well:

04

The media trace data can be conveniently filtered right in DirectShowSpy UI, which is invoked by DoMediaSampleTracePropertySheetModal exported function, or copied to clipboard or saved as file in tab-separated values format. The file can be opened with Microsoft Excel for further review.

Limitations

  • there is a global limit on in-memory trace storage; there is no specific size in samples (it’s 8 MB for global registry of data here) and the storage is large enough to hold streaming of a movie with multiple tracks, however once in a while it is possible to hit the limit and there is no automatic recycling of data: the data is released with last copy of UI closed and graphs released in context of their respective processes
  • traces are visible from the same session only, in particular processes with elevated privileges are “visible” by similarly started DirectShowSpy UI and vice versa
  • 32-bit process traces are visible from 32-bit DirectShowSpy UI, and the same applies to 64 bits; technically it is possible to share that but it is not implemented

Download links

Additional stuff

A fork of GDCL MPEG-4 filters is uploaded to GitHub, which in particular has integration with media sample tracing and includes pre-built binaries, 32- and 64-bit versions.

DirectShow Spy: Integration with GraphStudioNext

DirectShow Spy is introducing integration with GraphStudioNext (and GraphEdit too) to let a developer quickly open a filter graph through Running Object table with external inspection tool.

Note that you need a revision 301 GraphStudioNext or later, prebuilt versions available for download here: graphstudionext.exe (Win32)graphstudionext64.exe (x64).

DirectShow Filter Graph list window offers context menu items and hotkeys to launch GraphStudioNext with command line parameters to open specific filter graph.

GraphStudioNext Integration in Filter Graph List

DirectShow Filter Graph Property Frame dialog’s actions view has a button and a hotkey to open current filter graph in GraphStudioNext:

GraphStudioNext Integration in Filter Graph Actions

When invoked from DirectShowSpy UI, GraphStudioNext opens immediately at view of interest:

A Filter Graph in Graph Studio Next

Spy is looking for GraphStudioNext in last used location (stored in registry), in current spy’s directory, in GraphStudioNext registry subkey and if none of the mentioned works then prompts user to locate the binary.

Similar functionality is also available programmatically using one of the following ways:

  • ISpy::OpenGraphStudioNext([in] LONG nParentWindowHandle, [out, retval] VARIANT_BOOL* pbResult);
  • IFilterGraphHelper::OpenGraphStudioNext([in] LONG nParentWindowHandle, [in] BSTR sMonikerDisplayName, [out, retval] VARIANT_BOOL* pbResult);

Download links

DirectShow Spy: Filter Graph Data and Properties UI

Summary

DirectShow Spy receives a set of new powerful updates helping to develop and troubleshoot DirectShow applications:

  • UI combining Markdown formatted filter graph details as well as property pages for all participating filters
    • available interactively via filter graph list
    • available via helper COM class letting DirectShow application integrate incredible troubleshooting capabilities
  • Option to email filter graph details

On of the next posts should be summarizing all DirectShowSpy capabilities currently split across posts.

Filter Graph Details UI

Filter graph list window received a new button Properties with the associated action duplicated on double click handler for the list item. Once clicked, a new UI pops up to bring information about the entire graph.

Properties Button in the Filter Graph List

The window contains a tree control on the left, items of which show the following elements:

  1. Top “Filters” item shows Markdown formatted text, mentioned in an earlier post
  2. Second level items are one per each filter participating in the filter graph
    • a double click of such item brings up standard OleCreatePropertyFrame property sheet with filter properties
    • an edit control to the right lists filter specific information, with connections and media types
  3. Third level items are property pages of the filter, the property pages are integrated into the window; all together, the window provides access to all property pages of all filters in the graph
  4. Bottom first level “Email” items provides a convenient way to send the information via email

The picture below shows a property page integrated into the UI:

Integrated Filter Property Page

Email interface adds basic information about the system, and also provides space to enter a note about the graph. Email information (From, To etc) is put on registry for further reuse.

Email Filter Graph

Programmatic Interface

Additionally to interactive access from filter graph list, the new UI is also accessible viaFilterGraphHelper COM class. This lets applications integrate new capability right into the application as a troubleshooting option.

While accessing the graph through ROT using GraphStudioNext/GraphEdit application remains the most convenient interactive way to work with the graph, the helper’s advantage is access to the graph without crossing process boundaries: this, in particular, provides smooth access to property pages not implemented well for marshaling, such as, for example, Audio Renderer related property pages.

FilterGraphHelper‘s FilterGraph property accepts filter graph interface pointer (from now on, it is also possible to supply any filter or pin interface, and helper will walk up to the graph automatically).

IFilterGraphHelper::DoPropertyFrameModal method shows the UI modal window introduced above.

Two samples are demonstrating programmatic access to the new helper method.

  1. Sample\FilterGraphHelperDialog (SVN) C# application (code snippet) shows it straightforward way: to build a graph, to initialize helper, to show modal UI:
    IFilterGraph2 graph = new FilterGraph() as IFilterGraph2;
    graph.RenderFile(@"E:\Media\GoPro 2010 Highlights - You in HD - 1920x1080.mp4", "");
    FilterGraphHelper helper = new FilterGraphHelper();
    helper.FilterGraph = graph;
    helper.DoPropertyFrameModal(0);
    
  2. Sample\RegistrationFreeFilterGraphHelper (SVN) C++ application features an advanced technique to leverage new capability in application, without need to COM-register the Spy.

Registration of the DirectShowSpy requires, generally speaking, administrative privileges. Then in addition spy hooks the system which might be not desirable.

The application however load the Spy directly using LoadLibrary API and instantiates the helper directly bypassing COM registration. This is sufficient to start the new UI feature and access the graph interactively.

    CComPtr<IFilterGraphHelper> pFilterGraphHelper;
    #if TRUE
        const HMODULE hModule = LoadLibrary(_T("DirectShowSpy.dll"));
        [...]
        ATLENSURE_SUCCEEDED(pClassFactory->CreateInstance(NULL, __uuidof(IFilterGraphHelper), (VOID**) &pFilterGraphHelper));
        // TODO: FreeLibrary against hModule
    #else
        ATLENSURE_SUCCEEDED(pFilterGraphHelper.CoCreateInstance(__uuidof(FilterGraphHelper)));
    #endif
    ATLENSURE_SUCCEEDED(pFilterGraphHelper->put_FilterGraph(pFilterGraph));
    ATLENSURE_SUCCEEDED(pFilterGraphHelper->DoPropertyFrameModal(0));

The alternate #if code path shows the COM registration equivalent for the code.

Download links