EEAddIn 示例:调试表达式计算器外接程序

EEAddin 示例演示了如何使用“表达式计算器外接程序 API”来扩展本机调试器表达式计算器。

安全说明安全说明

此代码示例用于阐释一个概念,并且仅显示与此概念相关的代码。该代码可能不符合特定环境的安全要求,因此不应原样搬用所显示的代码。我们建议您添加安全性代码和错误处理代码,以使项目更加安全可靠。Microsoft“原样”提供此代码示例,不提供任何保证。

获取示例和安装示例的说明:

访问 Visual Studio 中的示例

  • 在**“帮助”菜单上,单击“示例”**。

    默认情况下,这些示例安装在 drive:\Program Files\Microsoft Visual Studio 10.0\Samples\ 中。

  • 有关此示例的最新版本以及其他示例的列表,请参见 MSDN 网站上的 Visual Studio 示例

EE 外接程序 API

表达式计算器是调试器的一部分,它解释(计算)表达式。 在调试器窗口中键入表达式或在表达式上设置断点时,表达式计算器即对输入进行解释。 有关详细信息,请参见调试器中的表达式。 使用“表达式计算器外接程序 API”,可以扩展表达式计算器以处理新类型。

若要为新类型扩展表达式计算器,需要在 Win32 DLL(与 autoexp.dat 在同一目录中)中编写一个函数,然后按名称将其导出。 还需要向 autoexp.dat 文件添加一行。 可以通过从 DLL 导出多个函数来为多种类型扩展表达式计算器。

生成并运行示例

生成并运行此示例所需的步骤分为三部分。

生成并运行此示例

  1. 生成表达式计算器外接程序 DLL (eeaddin.dll)。

  2. 编辑 autoexp.dat 以使用表达式计算器外接程序 DLL。

  3. 通过创建一个项目来测试外接程序,该项目使用由 autoexp.dat 计算的自定义数据类型。

以下过程对这些步骤进行了详细介绍。

生成表达式计算器外接程序 DLL

  1. 在 Visual Studio 中,打开解决方案 eeaddin.sln。

  2. 从**“生成”菜单中,单击“生成”**。

  3. 将生成的 eeaddin.dll 复制到 common7\ide 目录(包含 devenv.exe 的同一目录)。

  4. 从**“文件”菜单中,单击“关闭解决方案”**。

编辑 autoexp.dat

  1. 从**“文件”菜单中,指向“打开”,然后单击“文件”**。

  2. 在**“打开文件”对话框中,找到文件 autoexp.dat(在 common7\packages\debugger 目录中),然后单击“打开”**。

  3. 编辑 autoexp.dat 以添加下面的行:

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

    保存 autoexp.dat。

创建使用自定义数据类型的项目

  1. 从**“文件”菜单中,指向New,然后单击“项目”**。

  2. 在**“新建项目”对话框中,突出显示“Visual C++ 项目”,单击“MFC 应用程序”,输入项目的名称,然后单击“确定”**。

  3. 在**“MFC 应用程序向导”中,单击“完成”**。 项目必须是 MFC 应用程序,这是因为在下一步骤中,将要添加 MFC 函数。

  4. 在 MFC 应用程序中,添加一个 SYSTEMTIMEFILETIME 对象。

    SYSTEMTIME *s = new SYSTEMTIME();
    FILETIME *f = new FILETIME();
    GetSystemTime(s);
    SystemTimeToFileTime(s,f);
    
  5. 从**“生成”菜单中,单击“生成”**。

  6. 开始调试,并在监视窗口中检查 SYSTEMTIME 对象或 FILETIME 对象。

示例的工作机制

若要为自定义数据类型扩展表达式计算器,请在表达式计算器外接程序 DLL 中编写自定义查看器函数。 该函数使用一个指针,该指针指向正在调试的程序的内存空间(而非要扩展的表达式计算器的内存空间)中的一个对象。 不能对该指针应用普通强制转换。 必须使用回调函数读取它和它所指向的数据。 DEBUGHELPER* 类型的回调指针指向具有多个方法的对象。

语法如下所示:

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
)

该示例有这一类型的函数的两个实现:timeaddin.cpp 中的 AddIn_SystemTime 和 AddIn_FileTime。 DEBUGHELPER 结构(在 custview.h 中定义)包含有助于编写扩展的函数指针。 该指针传递给 CustomViewer 函数,您可以使用它来调用 Helper 函数。

可以使用 pHelper->GetProcessorType 来获取处理器类型。 有两种读取内存的方法:pHelper->ReadDebuggeeMemory 和 pHelper->ReadDebuggeeMemoryEx。 ReadDebuggeeMemoryEx 处理 64 位地址且受 Visual Studio .NET 调试器支持。 ReadDebuggeeMemory 不处理 64 位地址,Visual Studio .NET 和 Visual C++ 6.0 调试器都支持这种方法。 如果您的外接程序只能用于 Visual Studio .NET 调试器,则可以使用 ReadDebuggeeMemoryEx。 如果您的外接程序也需要与 Visual C++ 6.0 一起使用,则必须检查 dwVersion 字段,并对 Visual C++ 6.0 避免调用 ReadDebuggeeMemoryEx。

下面的代码可以与两种调试器一起使用,并从正在调试的程序中读取 localobject(其类型为 MyType)的内容:

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

编辑 autoexp.dat

在 autoexp.dat 的 [AutoExpand] 节中,将添加的行具有以下语法:

type=$ADDIN(dllname.dll,exportname)

例如:

_SYSTEMTIME=$ADDIN(eeaddin.dll,AddIn_SystemTime)

或:

_FILETIME=$ADDIN(eeaddin.dll,AddIn_FileTime)

如果 DLL 不在包含 devenv.exe 的目录中或 PATH 上,则必须使用 DLL 的完整路径名称。 exportname 参数区分大小写,必须与对 DLL 上运行 dumpbin –exports 时获得的导出名称完全匹配。

若要使用新外接程序测试调试器,请首先停止调试在安装新 DLL 时正在调试的所有程序,然后启动新的调试器会话。

注意 外接程序在调试器内运行,因此如果您代码出现故障,IDE 将崩溃。

请参见

其他资源

常规示例