Getting Notifications

The following code shows how to obtain and report verbose status information from the symbol handler about searching for and loading of modules and the corresponding symbol files.

Many familiar with the WinDbg debugger may remember a command called "!sym noisy". This command is used by the user to determine why WinDbg is or is not able to load symbol files. It shows a verbose listing of everything the symbol handler tries.

This same listing is also available to anyone developing a client to the DbgHelp symbol handler.

First, call SymSetOptions with SYMOPT_DEBUG. This causes DbgHelp to turn on debug notifications.

After calling SymInitialize, use SymRegisterCallback64 to register a callback function that DbgHelp will call whenever an interesting event occurs. In this example, the callback function is called SymRegisterCallbackProc64. Symbol callback functions are passed an assortment of action codes that they can handle according to type. In this example, we are handling only the CBA_EVENT action code. This function passes a string containing verbose information about an event that occurred in the process of loading a symbol. This event could be anything from an attempt to read the data within an executable image to the successful location of a symbol file. SymRegisterCallbackProc64 displays that string and returns TRUE.


Make sure you return FALSE to every action code that you do not handle, otherwise you may experience undefined behavior. Refer to SymRegisterCallbackProc64 for a list of all the action codes and their implications.


Now that the callback is registered, it is time to load the module specified on the command line by calling SymLoadModuleEx.

Lastly, call SymCleanup before exiting.

#include <windows.h>
#include <stdio.h>
#include <tchar.h>

#ifdef UNICODE
#include <dbghelp.h>

// Here is an implementation of a Symbol Callback function.

    __in HANDLE hProcess,
    __in ULONG ActionCode,
    __in_opt ULONG64 CallbackData,
    __in_opt ULONG64 UserContext

    // If SYMOPT_DEBUG is set, then the symbol handler will pass
    // verbose information on its attempt to load symbols.
    // This information be delivered as text strings.
    switch (ActionCode)
    case CBA_EVENT:
        evt = (PIMAGEHLP_CBA_EVENT)CallbackData;
        _tprintf(_T("%s"), (PTSTR)evt->desc);

    // CBA_DEBUG_INFO is the old ActionCode for symbol spew.
    // It still works, but we use CBA_EVENT in this example.
#if 0
    case CBA_DEBUG_INFO:
        _tprintf(_T("%s"), (PTSTR)CallbackData);

        // Return false to any ActionCode we don't handle
        // or we could generate some undesirable behavior.
        return FALSE;

    return TRUE;

// Main code.

int __cdecl
#ifdef UNICODE
    __in int argc,
    __in_ecount(argc) PCTSTR argv[]
    BOOL status;
    int rc = -1;
    HANDLE hProcess;
    DWORD64 module;
    if (argc < 2)
        _tprintf(_T("You must specify an executable image to load.\n"));
        return rc;

    // If we want to se debug spew, we need to set this option.
    // We are not debugging an actual process, so lets use a placeholder
    // value of 1 for hProcess just to ID these calls from any other
    // series we may want to load.  For this simple case, anything will do.
    hProcess = (HANDLE)1;
    // Initialize the symbol handler.  No symbol path.  
    // Just let dbghelp use _NT_SYMBOL_PATH
    status = SymInitialize(hProcess, NULL, false);
    if (!status)
        _tprintf(_T("Error 0x%x calling SymInitialize.\n"), GetLastError());
        return rc;
    // Now register our callback.
    status = SymRegisterCallback64(hProcess, SymRegisterCallbackProc64, NULL);
    if (!status)
        _tprintf(_T("Error 0x%x calling SymRegisterCallback64.\n"), GetLastError());
        goto cleanup;
    // Go ahead and load a module for testing.
    module = SymLoadModuleEx(hProcess,  // our unique id
                             NULL,      // no open file handle to image
                             argv[1],   // name of image to load
                             NULL,      // no module name - dbghelp will get it
                             0,         // no base address - dbghelp will get it
                             0,         // no module size - dbghelp will get it
                             NULL,      // no special MODLOAD_DATA structure
                             0);        // flags
    if (!module)
        _tprintf(_T("Error 0x%x calling SymLoadModuleEx.\n"), GetLastError());
        goto cleanup;
    rc = 0;
    return rc;    

Specifying NULL as the second parameter of SymInitialize indicates that the symbol handler should use the default search path to locate symbol files. For detailed information on how the symbol handler locates symbol files or how an application can specify a symbol search path, see Symbol Paths.

Running this program shows how the symbol path is processed. As DbgHelp looks through the symbol path to find the symbol file, it repeatedly calls SymRegisterCallbackProc64 which, in turn, displays the following strings being passed by DbgHelp.

d:\load.exe c:\home\dbghelp.dll
DBGHELP: No header for c:\home\dbghelp.dll.  Searching for image on disk
DBGHELP: c:\home\dbghelp.dll - OK
DBGHELP: .\dbghelp.pdb - file not found
DBGHELP: .\dll\dbghelp.pdb - file not found
DBGHELP: .\symbols\dll\dbghelp.pdb - file not found
DBGHELP: .\symbols\dll\dbghelp.pdb - file not found
DBGHELP: cache*c:\symbols\dbghelp.pdb - file not found
DBGHELP: cache*c:\symbols\dll\dbghelp.pdb - file not found
DBGHELP: cache*c:\symbols\symbols\dll\dbghelp.pdb - file not found
DBGHELP: d:\nt.binaries.amd64chk\symbols.pri\dbg\dbghelp.pdb - file not found
DBGHELP: dbghelp - private symbols & lines