## Debugging with DirectShow

This article discusses debugging practices in Microsoft® DirectShow™ for the C and C++ languages. Many of these practices apply both to writing filters and to writing applications that use the DirectShow run time. The article provides some tips on writing code that can be easily debugged and some general debugging topics. This article also provides some hints about detecting memory leaks.

Contents of this article:

- Writing Code You Can Test and Debug
- Using Different Kinds of Builds
- Debugging New Filters
- Detecting Leaks

Writing Code You Can Test and Debug

Debugging code in the DirectShow environment can be easier if it's written to be easily tested and debugged in the first place. Some techniques that DirectShow supports include the following, which are discussed in this section.

- Assertion Checking
- Pass Debugging Names
- Debug Logging
- IOStream Sample Code
- Critical Section Usage
- Pointer Validation
- DLL Base Address Conflicts

Assertion Checking

Use assertion checking liberally. If you're not familiar with asserts, they're a popular way to isolate potential programming errors. DirectShow provides a number of assertion macros and functions, including ASSERT. The Microsoft® Foundation Classes (MFC) have an equivalent ASSERT macro. For example the following displays a message box if the value of First does not equal NULL:

        ASSERT( First != NULL );

For more information about assertion, see Assert Macros and Functions.

Pass Debugging Names

Pass the debugging name to the constructors that support it. Tracking object creation and destruction is provided in debugging builds for the CBaseObject class and classes derived from it. The object register is the list of objects that have been created but not yet destroyed in those classes. The debugging name that is passed to the constructors of those classes is stored in the object register. For more information about debugging object registers and the DbgDumpObjectRegister function, see Object Register Debugging.

Debug Logging

Use the DirectShow DbgLog function to display debugging messages on a debugger as your program executes. Here's an example from the bouncing ball source filter:

        DbgLog(( LOG_TRACE, 1, TEXT("New time: %d, Proportion: %d"),
            m_iRepeatTime, q.Proportion));

See the Debug Logging by Module Level for more information on the following topics:

- The macros and functions you can call to do debugging logging from code you write.
- How to enable and disable debugging logging by module level at run time.
- How to indicate the destination of the output of the debugging log.

IOStream Sample Code

The C and C++ helpers provided in the IOStream helper library, SampIOS.lib, provide text output of the IBaseFilter interface and other DirectShow objects. The output from these helpers might be useful during debugging, to help understand the details of a given pin or filter. You can use these helpers in your DirectShow filters and applications. For more information about this library, see SampIOS Sample (IOStream Helper Library).

Critical Section Usage

To make deadlocks easier to track, insert assertions in the code that determine whether a critical section is owned by the calling code. The CritCheckIn and CritCheckOut functions indicate whether the calling thread owns the given critical sections, and are generally called in ASSERT macros. For more information about these functions, see CCritSec Debug Functions.

For debug logging of each lock and unlock of a given critical section, you might want to use the DirectShow DbgLockTrace function.

Note Logging can affect performance.

Pointer Validation

Consider using the pointer validation macros. For example, you can call ValidateReadPtr to ensure that the given pointer actually points to readable memory. Note the performance cost of each of these calls. Currently, the DirectShow pointer validation macros are built on top of the Win32 pointer validation functions such as IsBadReadPtr. On some systems, the Win32 pointer validation functions swap in every page in the range specified. For more information about validation macros, see Pointer Validation Macros.

DLL Base Address Conflicts

If you copy any sample makefile to create any new DLL, including filters and plug-in distributors (PIDs), ensure you change the base address to avoid collisions with other DLLs. A collision of DLL load address results in one of the DLLs having to be relocated during the time of loading. This increases the load time for that DLL.

In the sample makefiles, the DLL base address is set in DLL_BASE, which is used in ActiveX.mak. Do not let ActiveX.mak use the default value for DLL_BASE, because this will cause collisions.

Using Different Kinds of Builds

DirectShow can be built for three kinds of builds: retail, debug, and performance. See Reserved Identifiers for information on the kinds of builds. Debugging has varying degrees of difficulty for the three kinds of builds, depending on the situation. For instance, the debug build can provide much more information, but it can run so slowly as to make real-time debugging impossible.

The binaries you create must match the kind of build you're using. The makefiles provided for each sample use ActiveX.mak, which comes with the DirectShow SDK. Comments at the head of ActiveX.mak explain the various nmake command-line parameters to use to obtain binaries compatible with the different DirectShow builds. Some of these parameters define identifiers like DEBUG and PERF when compiling the C or C++ code.

If you must have build-dependent code, you can conditionally compile with the same identifiers that the DirectShow headers use for that purpose. See Reserved Identifiers for a list of the identifiers reserved for that purpose.

For instance, in C or C++, you can conditionally compile code like this:

        ...   /* normal processing */
    #ifdef DEBUG
        ...   /* debug only code */
    #endif
        ...   /* resume normal processing */

Debugging New Filters

This section discusses the following points of which you should be aware when debugging new filters:

- Avoid GUID Conflicts
- Test With the Filter Graph Editor and Other Sample Filters
- Add the Filter as an Additional DLL in Developer Studio

Avoid GUID Conflicts

DirectShow uses globally unique identifiers (GUIDs) to find each filter, pin, and property page. Avoid reusing any of the same GUIDs when copying from the DirectShow sample code. The Guidgen.exe and Uuidgen.exe utilities generate unique GUIDs.

Test With the Filter Graph Editor and Other Sample Filters

Register your new filter. See Register DirectShow Objects and AMovieDllRegisterServer2 for information about registering a filter.

After you have registered your filter, you can use a tool called the Filter Graph Editor (also called GraphEdit, or Graphedt.exe) to insert your filter into a filter graph and connect it to other filters. You can access GraphEdit from the DXMedia SDK program group. Run GraphEdit and choose Insert Filters from the Graph menu to insert your filter.

If you are debugging an audio filter, there are two sample filters you might consider connecting to your filter to make sure it behaves as expected. You can also look at the source code for those samples to see how they implement methods and member functions. For overviews of those code samples, see Synth Sample (Audio Synthesizer Filter) and Scope Sample (Oscilloscope Filter).

After you have the Filter Graph Editor successfully loading your new filter, you can use the File Dump Filter (Dump.ax) as a useful debugging tool. For instance, it can be used to verify, bit by bit, the results of a transform filter. Build a graph manually using the Filter Graph Editor and hook the File Dump Filter onto the output of a transform or any other pin. You can also hook up the Inftee Sample (Infinite-Pin Tee Filter) (InfTee.ax), and put the File Dump Filter on one leg of the tee and the "normal" output on another to monitor what happens in the real-time case. For more information, see Dump Sample (Dump Filter).

Add the Filter as an Additional DLL in Developer Studio

If you're going to debug your filter with Microsoft Developer Studio version 5.0, you must tell the debugger about your filter. Here are the steps you should follow in Developer Studio to identify your filter as being a debuggable DLL:

1. From the Project menu, choose Settings....
2. Select the Debug tab.
3. Choose "Additional DLLs" from the Category drop-down list.
4. Add "myfilter.ax" to the list, where "myfilter" is the name of your filter.

Detecting Leaks

Detecting and fixing memory leaks is another important debugging topic.

Visual C++ has an optional debug heap, which can be useful in tracking down memory leaks. (See the "Using the Debug Heap" section of the Visual C++ documentation for more information.) For example, the Visual C++ _CrtSetDbgFlag function enables you to turn on the memory-leak-checking flag bit.

Other providers of memory leak tools can be found in the Microsoft Enterprise Development Partners directory.

Another kind of leak is of COM object references. You can track down object reference leaks by performing the following steps.

1. Put a break point on the NonDelegatingAddRef and NonDelegatingRelease methods of that object.
2. Use Developer Studio (or another debugger) and step through every reference count change, trying to pair them up.
3. Look at the call stack for each change.

© 1997 Microsoft Corporation. All rights reserved. Terms of Use.