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.
This article contains the following sections.
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.
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 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.
Use the DirectShow DbgLog function to display debugging messages on a debugger as your program executes. The following example is from the bouncing ball source filter.
DbgLog(( LOG_TRACE, 1, TEXT("New time: %d, Proportion: %d"), m_iRepeatTime, q.Proportion));
For more information on the following topics, see Debug Logging by Module Level.
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).
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.
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.
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.
DirectShow can be built for three kinds of builds: retail, debug, and performance. For information on the kinds of builds, see Reserved Identifiers. 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 Microsoft® DirectX® Media 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. For a list of the identifiers reserved for that purpose, see Reserved Identifiers.
For instance, in C or C++, you can conditionally compile code like the following:
... /* normal processing */ #ifdef DEBUG ... /* debug only code */ #endif ... /* resume normal processing */
This section contains the following topics that discuss points you should be aware of when debugging new filters.
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.
Register your new filter. For information about registering a filter, see Register DirectShow Objects and AMovieDllRegisterServer2.
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 DirectX Media 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).
If you're going to debug your filter with Microsoft Developer Studio version 5.0, you must tell the debugger about your filter. You should perform the following steps in Developer Studio to identify your filter as a debuggable DLL.
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.
Top of Page
© 2000 Microsoft and/or its suppliers. All rights reserved. Terms of Use.