简体   繁体   English

如何为Visual Studio 2012调试器编写自定义本机可视化DLL?

[英]How to write a custom native visualizer DLL for Visual Studio 2012 debugger?

What is needed to write a custom native visualizer DLL in C++ for Visual Studio 2012 debugger? 在C ++中为Visual Studio 2012调试器编写自定义本机可视化DLL需要什么? I want to display a value that can only be calculated from a class/struct on-demand hence a native visualizer DLL is required. 我想显示一个只能根据需要从类/结构计算的值,因此需要一个本机可视化DLL。 Visual Studio 2012 uses a new method for implementing native visualizers called Natvis. Visual Studio 2012使用一种新方法来实现名为Natvis的本机可视化工具。 As of this time, there is very little correct information on Natvis and especially on using Natvis to call a visualizer DLL. 截至目前,关于Natvis的信息非常少,尤其是使用Natvis调用可视化DLL。 The DLL will calculate a display string based on class/struct member values. DLL将根据类/结构成员值计算显示字符串。

Here's the C++ code that comprises the AddIn DLL. 这是包含AddIn DLL的C ++代码。 I named the file NatvisAddIn.cpp and the project created NatvisAddIn.dll. 我将文件命名为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;
}

Here is the .natvis file which Visual Studio 2012 debugger uses to display the value. 以下是Visual Studio 2012调试器用于显示值的.natvis文件。 Place it in a .natvis file. 将其放在.natvis文件中。 I named it NatvisAddIn.natvis. 我把它命名为NatvisAddIn.natvis。 The file instructs VS 2012 debugger to call NatvisAddIn.dll. 该文件指示VS 2012调试器调用NatvisAddIn.dll。 The dll contains two visualizer method calls; 该DLL包含两个可视化方法调用; MyClassFormatter to format MyClass and MyStructFormatter to format MyStruct. MyClassFormatter格式化MyClass和MyStructFormatter以格式化MyStruct。 The debugger will show the method's formatted value in the Auto, Watch or tooltip display for each instance of the specified type (MyClass, 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>

Place both the compiled NatvisAddIn.dll file and the NatvisAddIn.natvis files into one of the following three locations: 将已编译的NatvisAddIn.dll文件和NatvisAddIn.natvis文件放入以下三个位置之一:

%VSINSTALLDIR%\Common7\Packages\Debugger\Visualizers (requires admin access)

%USERPROFILE%\My Documents\Visual Studio 2012\Visualizers\

VS extension folders

You will need to make sure the following registry key exists and the value is 1: 您需要确保存在以下注册表项且值为1:

[HKEY_CURRENT_USER\\Software\\Microsoft\\VisualStudio\\11.0_Config\\Debugger] [HKEY_CURRENT_USER \\软件\\微软\\ VisualStudio的\\ 11.0_Config \\调试]

"EnableNatvisDiagnostics"=dword:00000001 “EnableNatvisDiagnostics”= DWORD:00000001

If all goes well, you will see natvis messages appear in Visual Studio's debugger Output window. 如果一切顺利,您将看到natvis消息出现在Visual Studio的调试器输出窗口中。 The messages will show whether Natvis was able to parse the .natvis files. 这些消息将显示Natvis是否能够解析.natvis文件。 Results of parsing every .natvis file is shown in the output window. 解析每个.natvis文件的结果显示在输出窗口中。 If something is wrong, use the command "dumpbin/exports " to double check that the DLL methods' names are exactly matching the .navis file's Type=. 如果出现问题,请使用命令“dumpbin / exports”仔细检查DLL方法的名称是否与.navis文件的Type =完全匹配。 Also make sure the current .dll and .natvis files have been copied to the appropriate directory. 还要确保已将当前的.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.

Test program: 测试程序:

#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;
}

Information resources: 信息资源:

\\Xml\\Schemas\\natvis.xsd \\ XML \\架构\\ natvis.xsd

http://code.msdn.microsoft.com/windowsdesktop/Writing-type-visualizers-2eae77a2 http://code.msdn.microsoft.com/windowsdesktop/Writing-type-visualizers-2eae77a2

http://blogs.msdn.com/b/mgoldin/archive/2012/06/06/visual-studio-2012-and-debugger-natvis-files-what-can-i-do-with-them.aspx http://blogs.msdn.com/b/mgoldin/archive/2012/06/06/visual-studio-2012-and-debugger-natvis-files-what-c​​an-i-do-with-them.aspx

http://blogs.msdn.com/b/vcblog/archive/2012/07/12/10329460.aspx http://blogs.msdn.com/b/vcblog/archive/2012/07/12/10329460.aspx

For 64 bit version debugging, following lines should be used: 对于64位版本调试,应使用以下行:

auto realAddress = pHelper->GetRealAddress(pHelper);
pHelper->ReadDebuggeeMemoryEx(pHelper, realAddress, sizeof(MyClass), &c, &nGot );

For previous example, 64bit version could look like this: 对于前面的示例,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;
}

I need one clarification related to search path(s) that are looked up for loading the above mentioned "NatvisAddIn.dll". 我需要一个与查找加载上述“NatvisAddIn.dll”的搜索路径相关的澄清。 Let me try to explain by extending the above example 让我试着通过扩展上面的例子来解释

To visualize my custom C++ class object(let's say MyCustomeType), I need to call some additional APIs (to compute the display string for MyCustomeType) in the implementation of function(say MyClassFormatter) that is mentioned in "Export" attribute of "DisplayString" XML. 为了可视化我的自定义C ++类对象(比如说MyCustomeType),我需要在“DisplayString”的“Export”属性中提到的函数(比如MyClassFormatter)的实现中调用一些额外的API(以计算MyCustomeType的显示字符串)。 XML。

Calling these additional APIs create a library/dll dependency on the above mentioned "NatvisAddIn.dll". 调用这些额外的API会在上面提到的“NatvisAddIn.dll”上创建一个库/ dll依赖项。 If this additional dependency is just one or two library, I can put those library at same location where NatvisAddIn.dll is present. 如果这个额外的依赖只是一个或两个库,我可以将这些库放在NatvisAddIn.dll所在的相同位置。 However, in my case the dependency contains a long chain of libraries. 但是,在我的情况下,依赖包含一长串库。

Can some one suggest me some elegant way to resolve the dependency without pulling the complete chain of libraries into %USERPROFILE%\\My Documents\\Visual Studio 2012\\Visualizers folder? 有人可以建议我一些优雅的方法来解决依赖关系而不将完整的库链拉入%USERPROFILE%\\ My Documents \\ Visual Studio 2012 \\ Visualizers文件夹?

For demonstration of my use case, let's consider the below dependency tree: NatvisAddIn.dll a.dll 为了演示我的用例,让我们考虑下面的依赖树:NatvisAddIn.dll a.dll
a1.dll a2.dll a3.dll a1.dll a2.dll a3.dll

I copied the a.dll at same location where NatvisAddIn.dll. 我在一个NatvisAddIn.dll的相同位置复制了a.dll。 It's dependent dlls (a1, a2 and a3) are present at location that are added to PATH variable. 它依赖的dll(a1,a2和a3)出现在添加到PATH变量的位置。 When I try to visualize MyCustomeType object in visual studio debugger, the natvis diagonostic give the below error in output window 当我尝试在visual studio调试器中可视化MyCustomeType对象时,natvis diagonostic在输出窗口中给出以下错误

Natvis: C:\\Users\\myUser\\Documents\\Visual Studio 2017\\Visualizers\\mydata.natvis(9,6): Error: Failed to load addin from C:\\Users\\myuser\\Documents\\Visual Studio 2017\\Visualizers\\NatvisAddIn.dll for type MyCustomeType: : The specified module could not be found. Natvis:C:\\ Users \\ myUser \\ Documents \\ Visual Studio 2017 \\ Visualizers \\ mydata.natvis(9,6):错误:无法从C:\\ Users \\ myuser \\ Documents \\ Visual Studio 2017 \\ Visualizers \\ NatvisAddIn加载插件。类型为MyCustomeType的dll ::找不到指定的模块。

My understanding of above error, the visual studio debugger was not able to resolve dependency of a.dll (a1, a2 and a3) and hence it failed to load NatvisAddIn.dll 我理解上面的错误,visual studio调试器无法解析a.dll(a1,a2和a3)的依赖关系,因此无法加载NatvisAddIn.dll

When I tried to use a.dll in my testApplication and compute the DisplayString for MyCustomeType, dependency get resolve, a.dll is loaded and I get the expected out string without copying a1.dll, a2.dll and a3.dll. 当我尝试在我的testApplication中使用a.dll并为MyCustomeType计算DisplayString时,依赖项得到解析,加载了a.dll并且我得到了预期的out字符串而没有复制a1.dll,a2.dll和a3.dll。 the dependent dll are resolved/picked-up from window PATH variable. 依赖的dll从窗口PATH变量中解析/拾取。 But, In case of visual studio debugger, dependent DLL are NOT resolved from PATH variable . 但是,在visual studio调试器的情况下,依赖DLL不会从PATH变量解析

As identified by depends tool, some dlls which are not resolved by debugger are: 正如依赖工具所确定的那样,调试器无法解析的一些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 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

Some of these dll are present visual studio installation, others are present in c:\\windows\\WinSxS 其中一些dll存在visual studio安装,其他存在于c:\\ windows \\ WinSxS中

I've tried my use case on visual studio 2012 and visual studio 2017. I landed up in same issue with both the visual studios. 我在visual studio 2012和visual studio 2017上尝试了我的用例。我在视觉工作室中遇到了同样的问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM