![](/img/trans.png)
[英]How do I write a very simple Visual Studio debugger visualizer?
[英]How to write a custom native visualizer DLL for Visual Studio 2012 debugger?
在C ++中为Visual Studio 2012调试器编写自定义本机可视化DLL需要什么? 我想显示一个只能根据需要从类/结构计算的值,因此需要一个本机可视化DLL。 Visual Studio 2012使用一种新方法来实现名为Natvis的本机可视化工具。 截至目前,关于Natvis的信息非常少,尤其是使用Natvis调用可视化DLL。 DLL将根据类/结构成员值计算显示字符串。
这是包含AddIn DLL的C ++代码。 我将文件命名为NatvisAddIn.cpp,项目创建了NatvisAddIn.dll。
#include "stdafx.h"
#include <iostream>
#include <windows.h>
#define ADDIN_API __declspec(dllexport)
typedef struct tagDEBUGHELPER
{
DWORD dwVersion;
HRESULT (WINAPI *ReadDebuggeeMemory)( struct tagDEBUGHELPER *pThis, DWORD dwAddr, DWORD nWant, VOID* pWhere, DWORD *nGot );
// from here only when dwVersion >= 0x20000
DWORDLONG (WINAPI *GetRealAddress)( struct tagDEBUGHELPER *pThis );
HRESULT (WINAPI *ReadDebuggeeMemoryEx)( struct tagDEBUGHELPER *pThis, DWORDLONG qwAddr, DWORD nWant, VOID* pWhere, DWORD *nGot );
int (WINAPI *GetProcessorType)( struct tagDEBUGHELPER *pThis );
} DEBUGHELPER;
typedef HRESULT (WINAPI *CUSTOMVIEWER)( DWORD dwAddress, DEBUGHELPER *pHelper, int nBase, BOOL bUniStrings, char *pResult, size_t max, DWORD reserved );
extern "C" ADDIN_API HRESULT MyClassFormatter( DWORD dwAddress, DEBUGHELPER *pHelper, int nBase, BOOL bUniStrings, char *pResult, size_t max, DWORD reserved );
extern "C" ADDIN_API HRESULT MyStructFormatter( DWORD dwAddress, DEBUGHELPER *pHelper, int nBase, BOOL bUniStrings, char *pResult, size_t max, DWORD reserved );
class MyClass
{
public:
int publicInt;
};
struct MyStruct { int i; };
ADDIN_API HRESULT MyClassFormatter( DWORD dwAddress, DEBUGHELPER *pHelper, int nBase, BOOL bUniStrings, char *pResult, size_t max, DWORD reserved )
{
MyClass c;
DWORD nGot;
pHelper->ReadDebuggeeMemory(pHelper,dwAddress,sizeof(MyClass),&c,&nGot);
sprintf_s(pResult,max,"Dll MyClass: max=%d nGot=%d MyClass=%x publicInt=%d",max,nGot,dwAddress,c.publicInt);
return S_OK;
}
ADDIN_API HRESULT MyStructFormatter( DWORD dwAddress, DEBUGHELPER *pHelper, int nBase, BOOL bUniStrings, char *pResult, size_t max, DWORD reserved )
{
MyStruct s;
DWORD nGot;
pHelper->ReadDebuggeeMemory(pHelper,dwAddress,sizeof(MyStruct),&s,&nGot);
sprintf_s(pResult,max,"Dll MyStruct: max=%d nGot=%d MyStruct=%x i=%d",max,nGot,dwAddress,s.i);
return S_OK;
}
以下是Visual Studio 2012调试器用于显示值的.natvis文件。 将其放在.natvis文件中。 我把它命名为NatvisAddIn.natvis。 该文件指示VS 2012调试器调用NatvisAddIn.dll。 该DLL包含两个可视化方法调用; MyClassFormatter格式化MyClass和MyStructFormatter以格式化MyStruct。 调试器将在Auto,Watch或工具提示显示中为指定类型的每个实例(MyClass,MyStruct)显示方法的格式化值。
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="MyClass">
<DisplayString LegacyAddin="NatvisAddIn.dll" Export="MyClassFormatter"></DisplayString>
</Type>
<Type Name="MyStruct">
<DisplayString LegacyAddin="NatvisAddIn.dll" Export="MyStructFormatter"></DisplayString>
</Type>
</AutoVisualizer>
将已编译的NatvisAddIn.dll文件和NatvisAddIn.natvis文件放入以下三个位置之一:
%VSINSTALLDIR%\Common7\Packages\Debugger\Visualizers (requires admin access)
%USERPROFILE%\My Documents\Visual Studio 2012\Visualizers\
VS extension folders
您需要确保存在以下注册表项且值为1:
[HKEY_CURRENT_USER \\软件\\微软\\ VisualStudio的\\ 11.0_Config \\调试]
“EnableNatvisDiagnostics”= DWORD:00000001
如果一切顺利,您将看到natvis消息出现在Visual Studio的调试器输出窗口中。 这些消息将显示Natvis是否能够解析.natvis文件。 解析每个.natvis文件的结果显示在输出窗口中。 如果出现问题,请使用命令“dumpbin / exports”仔细检查DLL方法的名称是否与.navis文件的Type =完全匹配。 还要确保已将当前的.dll和.natvis文件复制到相应的目录中。
Natvis: Parsing natvis xml file: C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\Packages\Debugger\Visualizers\atlmfc.natvis.
Natvis: Done parsing natvis xml file: C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\Packages\Debugger\Visualizers\atlmfc.natvis.
Natvis: Parsing natvis xml file: C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\Packages\Debugger\Visualizers\concurrency.natvis.
Natvis: Done parsing natvis xml file: C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\Packages\Debugger\Visualizers\concurrency.natvis.
Natvis: Parsing natvis xml file: C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\Packages\Debugger\Visualizers\NatvisAddIn.natvis.
Natvis: Done parsing natvis xml file: C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\Packages\Debugger\Visualizers\NatvisAddIn.natvis.
Natvis: Parsing natvis xml file: C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\Packages\Debugger\Visualizers\stl.natvis.
Natvis: Done parsing natvis xml file: C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\Packages\Debugger\Visualizers\stl.natvis.
Natvis: Parsing natvis xml file: C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\Packages\Debugger\Visualizers\windows.natvis.
Natvis: Done parsing natvis xml file: C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\Packages\Debugger\Visualizers\windows.natvis.
Natvis: Parsing natvis xml file: C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\Packages\Debugger\Visualizers\winrt.natvis.
Natvis: Done parsing natvis xml file: C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\Packages\Debugger\Visualizers\winrt.natvis.
测试程序:
#include "stdafx.h"
#include <iostream>
class MyClass
{
public:
int publicInt;
};
struct MyStruct { int i; };
int _tmain(int argc, _TCHAR* argv[])
{
struct MyStruct s = {1234};
std::cout << s.i << std::endl;
MyClass *c = new MyClass;
c->publicInt = 1234;
std::cout << c->publicInt << std::endl;
return 0;
}
信息资源:
\\ XML \\架构\\ natvis.xsd
http://code.msdn.microsoft.com/windowsdesktop/Writing-type-visualizers-2eae77a2
http://blogs.msdn.com/b/vcblog/archive/2012/07/12/10329460.aspx
对于64位版本调试,应使用以下行:
auto realAddress = pHelper->GetRealAddress(pHelper);
pHelper->ReadDebuggeeMemoryEx(pHelper, realAddress, sizeof(MyClass), &c, &nGot );
对于前面的示例,64位版本可能如下所示:
#include "stdafx.h"
#include <iostream>
#include <windows.h>
#define ADDIN_API __declspec(dllexport)
typedef struct tagDEBUGHELPER
{
DWORD dwVersion;
HRESULT (WINAPI *ReadDebuggeeMemory)( struct tagDEBUGHELPER *pThis, DWORD dwAddr, DWORD nWant, VOID* pWhere, DWORD *nGot );
// from here only when dwVersion >= 0x20000
DWORDLONG (WINAPI *GetRealAddress)( struct tagDEBUGHELPER *pThis );
HRESULT (WINAPI *ReadDebuggeeMemoryEx)( struct tagDEBUGHELPER *pThis, DWORDLONG qwAddr, DWORD nWant, VOID* pWhere, DWORD *nGot );
int (WINAPI *GetProcessorType)( struct tagDEBUGHELPER *pThis );
} DEBUGHELPER;
typedef HRESULT (WINAPI *CUSTOMVIEWER)( DWORD dwAddress, DEBUGHELPER *pHelper, int nBase, BOOL bUniStrings, char *pResult, size_t max, DWORD reserved );
extern "C" ADDIN_API HRESULT MyClassFormatter( DWORD dwAddress, DEBUGHELPER *pHelper, int nBase, BOOL bUniStrings, char *pResult, size_t max, DWORD reserved );
class MyClass
{
public:
int publicInt;
};
ADDIN_API HRESULT MyClassFormatter( DWORD dwAddress, DEBUGHELPER *pHelper, int nBase, BOOL bUniStrings, char *pResult, size_t max, DWORD reserved )
{
MyClass c;
DWORD nGot;
auto realAddress = pHelper->GetRealAddress(pHelper);
pHelper->ReadDebuggeeMemoryEx(pHelper, realAddress, sizeof(MyClass), &c, &nGot );
sprintf_s(pResult,max,"Dll MyClass: max=%d nGot=%d MyClass=%llx publicInt=%d",max, nGot, realAddress, c.publicInt);
return S_OK;
}
我需要一个与查找加载上述“NatvisAddIn.dll”的搜索路径相关的澄清。 让我试着通过扩展上面的例子来解释
为了可视化我的自定义C ++类对象(比如说MyCustomeType),我需要在“DisplayString”的“Export”属性中提到的函数(比如MyClassFormatter)的实现中调用一些额外的API(以计算MyCustomeType的显示字符串)。 XML。
调用这些额外的API会在上面提到的“NatvisAddIn.dll”上创建一个库/ dll依赖项。 如果这个额外的依赖只是一个或两个库,我可以将这些库放在NatvisAddIn.dll所在的相同位置。 但是,在我的情况下,依赖包含一长串库。
有人可以建议我一些优雅的方法来解决依赖关系而不将完整的库链拉入%USERPROFILE%\\ My Documents \\ Visual Studio 2012 \\ Visualizers文件夹?
为了演示我的用例,让我们考虑下面的依赖树:NatvisAddIn.dll a.dll
a1.dll a2.dll a3.dll
我在一个NatvisAddIn.dll的相同位置复制了a.dll。 它依赖的dll(a1,a2和a3)出现在添加到PATH变量的位置。 当我尝试在visual studio调试器中可视化MyCustomeType对象时,natvis diagonostic在输出窗口中给出以下错误
Natvis:C:\\ Users \\ myUser \\ Documents \\ Visual Studio 2017 \\ Visualizers \\ mydata.natvis(9,6):错误:无法从C:\\ Users \\ myuser \\ Documents \\ Visual Studio 2017 \\ Visualizers \\ NatvisAddIn加载插件。类型为MyCustomeType的dll ::找不到指定的模块。
我理解上面的错误,visual studio调试器无法解析a.dll(a1,a2和a3)的依赖关系,因此无法加载NatvisAddIn.dll
当我尝试在我的testApplication中使用a.dll并为MyCustomeType计算DisplayString时,依赖项得到解析,加载了a.dll并且我得到了预期的out字符串而没有复制a1.dll,a2.dll和a3.dll。 依赖的dll从窗口PATH变量中解析/拾取。 但是,在visual studio调试器的情况下,依赖DLL不会从PATH变量解析 。
正如依赖工具所确定的那样,调试器无法解析的一些dll是:
api-ms-win-core-errorhandling-l1-1-0.dll api-ms-win-crt-time-l1-1-0.dll api-ms-win-crt-heap-l1-1-0。 DLL
其中一些dll存在visual studio安装,其他存在于c:\\ windows \\ WinSxS中
我在visual studio 2012和visual studio 2017上尝试了我的用例。我在视觉工作室中遇到了同样的问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.