Microsoft DirectX 9.0

Debugging in DirectShow

This article provides some tips for debugging Microsoft® DirectShow® applications and filters. Most of the debugging facilities described in this article require the DirectShow bass class library. See DirectShow Base Classes for more information.

Assertion Checking

Use assertion checking liberally. Assertions can verify assumptions that you make in your code about preconditions and postconditions. DirectShow provides several assertion macros. For more information, see Assert and Breakpoint Macros.

Object Names

In debug builds, there is a global list of objects that derive from the CBaseObject class. As objects are created, they are added to the list. When they are destroyed, they are removed from the list. To display a list of those objects, call the DbgDumpObjectRegister function.

The constructor method for the CBaseObject base class—and most classes derived from it—includes a parameter for the name of the object. Give your objects unique names to identify them. Use the NAME macro when you declare the name, so that the name is allocated only in debug builds. In retail builds, the name becomes NULL.

Debug Logging

The DbgLog function displays debugging messages as your program executes. Use this function to trace the execution of your application or filter. You can log return codes, the values of variables, and any other relevant information.

Every debug message has a type, such as LOG_TRACE or LOG_ERROR, and a debug level, which indicates the importance of the message. Messages with lower debug levels are more important than those with higher levels.

In the following example, a hypothetical filter disconnects a pin from an array of pins. If the disconnect attempt succeeds, the filter displays a LOG_TRACE message. If the attempt fails, it displays a LOG_ERROR message:

hr = m_PinArray[iPin]->Disconnect();
if (FAILED(hr))
    DbgLog((LOG_ERROR, 1, TEXT("Could not disconnect pin %d. HRESULT = %#x", iPin, hr));
   // Error handling code (not shown).
DbgLog((LOG_TRACE, 3, TEXT("Disconnected pin %d"), iPin));

When you are debugging, you can set the debug level for each message type. Also, each module (DLL or executable) maintains its own debugging levels. If you are testing a filter, raise the debug levels for the DLL that contains the filter.

For more information, see Debug Output Functions.

Critical Sections

To make deadlocks easier to track, include assertions that determine whether the calling code owns a given critical section. The CritCheckIn and CritCheckOut functions indicate whether the calling thread owns a critical section. Typically, you would call these functions from inside an assert macro.

You can also use the DbgLockTrace function to trace when critical sections are held or released.

Pointer Validation

Consider validating pointers in debug builds. For example, the ValidateReadPtr macro checks whether a specified pointer points to readable memory. Currently, the DirectShow pointer validation macros use Microsoft® Win32® pointer validation functions, such as IsBadReadPtr. On some systems, these functions swap in every page in the specified range, which can greatly reduce performance. For more information, see Pointer Validation Macros.

Troubleshooting Tips

This following tips will help you to avoid deadlocks or crashes in your application.

Global Objects

A global C++ object should not create DirectShow objects in its constructor method or release them in its destructor method. Doing so can cause the application to block indefinitely, for the following reason:

Threads cannot exit while inside a DLL's entry-point function. Kernel 32 holds a global process lock during the entry-point function, and the lock prevents the thread from exiting. Because some DirectShow objects own threads, they can block if released from inside a DLL entry-point function. If an application has a global C++ object, the C runtime DLL calls the object's destructor when the DLL is unloaded. If the destructor releases DirectShow objects, it can block as a result.

For similar reasons, a DLL should not create or release DirectShow objects in its entry point routine.

Releasing Interfaces

You should release all DirectShow interface pointers while your application is still processing messages, before it exits the message loop. Otherwise, you might see various asserts, because some DirectShow objects send messages during their clean-up routines.

(As a corollary, if you are using the ATL CWindowImpl class, do not wait until OnFinalMessage to free the interfaces. Instead, free them when you handle the WM_CLOSE message.)

Reference Counts

When the debug version of Quartz.dll unloads, it checks whether any DirectShow objects have reference counts that were not released. If so, it throws an assertion:

g_cFGObjects == 0 

When this assertion fails, it means your application has leaked a reference count. Review your code and make sure that you release all interface pointers.

See Also