EEAddIn Sample: Debugging Expression Evaluator Add-In

The EEAddin sample shows how to extend the native debugger expression evaluator using the Expression Evaluator Add-In API.

Security noteSecurity Note

This sample code is intended to illustrate a concept, and it shows only the code that is relevant to that concept. It may not meet the security requirements for a specific environment, and it should not be used exactly as shown. We recommend that you add security and error-handling code to make your projects more secure and robust. Microsoft provides this sample code "AS IS" with no warranties.

To get samples and instructions for installing them:

To access samples from Visual Studio

  • On the Help menu, click Samples.

    By default, these samples are installed in drive:\Program Files\Microsoft Visual Studio 10.0\Samples\.

  • For the most recent version of this sample and a list of other samples, see Visual Studio Samples on the MSDN Web site.

EE Add-In API

The expression evaluator is the part of the debugger that interprets (evaluates) expressions. When you set a breakpoint on an expression or type an expression in a debugger window, the expression evaluator interprets the input. For details, see Expressions in the Debugger. With the Expression Evaluator Add-In API, you can extend the expression evaluator to handle new types.

To extend the expression evaluator for a new type, you need to write a function as part of a Win32 DLL (in the same directory as autoexp.dat) and export it by name. You also need to add a line to the autoexp.dat file. You can extend the expression evaluator for more than one type by exporting multiple functions from the DLL.

Building and Running the Sample

The steps required to build and run this sample fall into three parts.

To build and run the sample

  1. Build an expression evaluator Add-In DLL (eeaddin.dll).

  2. Edit autoexp.dat to use the expression evaluator Add-In DLL.

  3. Test the Add-In by creating a project that uses the custom data type evaluated by autoexp.dat.

The following procedures explain these steps in detail.

To build the expression evaluator Add-In DLL

  1. In Visual Studio, open the solution eeaddin.sln.

  2. From the Build menu, click Build.

  3. Copy the resulting eeaddin.dll to your common7\ide directory (the same directory that contains devenv.exe).

  4. From the File menu, click Close Solution.

To edit autoexp.dat

  1. From the File menu, point to Open and click File.

  2. In the Open File dialog box, find the file autoexp.dat (in the common7\packages\debugger directory) and click Open.

  3. Edit autoexp.dat to add the following lines:

    _SYSTEMTIME=$ADDIN(eeaddin.dll,AddIn_SystemTime@28)
    _FILETIME=$ADDIN(eeaddin.dll,AddIn_FileTime@28)
    

    Save autoexp.dat.

To create a project that uses the custom data types

  1. From the File menu, point to New and click Project.

  2. In the New Project dialog box, highlight Visual C++ Projects, click MFC Application, enter a name for the project, and click OK.

  3. In the MFC Application Wizard, click Finish. The project must be an MFC application because in the next step you will add MFC functions.

  4. In the MFC application, add a SYSTEMTIME or FILETIME object.

    SYSTEMTIME *s = new SYSTEMTIME();
    FILETIME *f = new FILETIME();
    GetSystemTime(s);
    SystemTimeToFileTime(s,f);
    
  5. From the Build menu, click Build.

  6. Start debugging and examine your SYSTEMTIME or FILETIME objects in the watch window.

How the Sample Works

To extend the expression evaluator for a custom data type, you write a custom viewer function in the expression evaluator Add-In DLL. The function uses a pointer to an object in the memory space of the program being debugged (not the memory space of the expression evaluator you are extending). You cannot use normal casts with this pointer. You must read it and the data it points to using a callback function. A callback pointer of type DEBUGHELPER* points to an object with various methods.

The syntax looks like this:

HRESULT WINAPI CustomViewer(
   DWORD dwAddress,       // low 32-bits of address
   DEBUGHELPER *pHelper,  // callback pointer to access helper functions
   int nBase,             // decimal or hex
   BOOL bIgnore,          // not used
   char *pResult,         // where the result needs to go
   size_t max,            // how large the above buffer is
   DWORD dwReserved       // always pass zero
)

The sample has two implementations of this type of function, AddIn_SystemTime and AddIn_FileTime in timeaddin.cpp. The DEBUGHELPER struct (defined in custview.h) consists of function pointers that can assist you in writing your extension. This pointer is passed to your CustomViewer function, and you can use it to call the helper functions.

You can get the processor type with pHelper->GetProcessorType. There are two methods for reading memory, pHelper->ReadDebuggeeMemory and pHelper->ReadDebuggeeMemoryEx. ReadDebuggeeMemoryEx handles 64-bit addresses and is supported by the Visual Studio .NET debugger. ReadDebuggeeMemory does not handle 64-bit addresses and is supported by the Visual Studio .NET and Visual C++ 6.0 debuggers. If your Add-In is designed for the Visual Studio .NET debugger only, you can use ReadDebuggeeMemoryEx. If your Add-In needs to work with Visual C++ 6.0 also, you must check the dwVersion field and avoid calling ReadDebuggeeMemoryEx for Visual C++ 6.0.

The following code works with both debuggers and reads the contents of a localobject (whose type is MyType) from the program being debugged:

DWORDLONG qwRealAddress;
DWORD dwGot;
MyType localobject;
if (pHelper->dwVersion<0x20000)
{
   // Visual C++ 6.0 version
   qwRealAddress = dwAddress;
   pHelper->ReadDebuggeeMemory( pHelper, dwAddress, 
      sizeof(localobject), &localobject, &dwGot );
}
else
{
   qwRealAddress = pHelper->GetRealAddress(pHelper);
   pHelper->ReadDebuggeeMemoryEx( pHelper, qwRealAddress, 
      sizeof(localobject), &localobject, &dwGot );
}
// TODO: display localobject here

Editing autoexp.dat

In the [AutoExpand] section of autoexp.dat, the lines you will add have the following syntax:

type=$ADDIN(dllname.dll,exportname)

For example:

_SYSTEMTIME=$ADDIN(eeaddin.dll,AddIn_SystemTime)

or:

_FILETIME=$ADDIN(eeaddin.dll,AddIn_FileTime)

If the DLL is not in the directory containing devenv.exe or on the PATH, you must use a full path name for the DLL. The exportname argument is case sensitive and must exactly match the exported name you got when you ran dumpbin –exports on your DLL.

To test the debugger with the new Add-In, first stop debugging any program you were debugging when you installed the new DLL, and then start a new debugger session.

Note The Add-In runs within the debugger, so if your code crashes, you will crash the IDE.

See Also

Other Resources

General Samples