Подтвердить что ты не робот

Как написать настраиваемую DLL визуализатора Visual Studio 2012 для отладчика?

Что необходимо для написания настраиваемой DLL визуализатора визуализации в С++ для отладчика Visual Studio 2012? Я хочу отобразить значение, которое может быть вычислено только из класса/структуры по запросу, поэтому требуется встроенная DLL визуализатора. Visual Studio 2012 использует новый метод для реализации собственных визуализаторов Natvis. На данный момент очень мало правильной информации о Natvis и особенно о том, как использовать Natvis для вызова DLL визуализатора. DLL вычислит строку отображения на основе значений класса/структуры.

4b9b3361

Ответ 1

Здесь код С++, который содержит AddIn 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;
}

Вот файл .natvis, который использует отладчик Visual Studio 2012 для отображения значения. Поместите его в файл .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\Software\Microsoft\VisualStudio\11.0_Config\Debugger]

"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\Schemas\natvis.xsd

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/vcblog/archive/2012/07/12/10329460.aspx

Ответ 2

Для отладки 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;
}

Ответ 3

Мне нужно одно разъяснение, связанное с путями поиска, которые ищутся для загрузки вышеупомянутого "NatvisAddIn.dll". Позвольте мне попытаться объяснить, расширив приведенный выше пример

Чтобы визуализировать мой собственный объект класса C++ (скажем, MyCustomeType), мне нужно вызвать несколько дополнительных API (чтобы вычислить отображаемую строку для MyCustomeType) в реализации функции (скажем, MyClassFormatter), которая упоминается в атрибуте "Export" объекта "" DisplayString "XML.

Вызов этих дополнительных API создает зависимость библиотеки /DLL от вышеупомянутого "NatvisAddIn.dll". Если эта дополнительная зависимость - только одна или две библиотеки, я могу поместить эту библиотеку в то же место, где присутствует NatvisAddIn.dll. Однако в моем случае зависимость содержит длинную цепочку библиотек.

Кто-нибудь может мне предложить какой-нибудь элегантный способ разрешения зависимости без перетаскивания полной цепочки библиотек в папку% USERPROFILE%\My Documents\Visual Studio 2012\Visualizers?

Для демонстрации моего варианта использования, давайте рассмотрим следующее дерево зависимостей: NatvisAddIn.dll a.dll
a1.dll a2.dll a3.dll

Я скопировал a.dll в то же место, где NatvisAddIn.dll. Это зависимые dll (a1, a2 и a3) присутствуют в местоположении, которые добавляются к переменной PATH. Когда я пытаюсь визуализировать объект MyCustomeType в отладчике Visual Studio, диагональная диаграмма natvis выдает следующую ошибку в окне вывода.

Natvis: C:\Users\myUser\Documents\Visual Studio 2017\Visualizers\mydata.natvis(9,6): Ошибка: не удалось загрузить надстройку из C:\Users\myuser\Documents\Visual Studio 2017\Visualizers\NatvisAddIn. dll для типа MyCustomeType: указанный модуль не найден.

В моем понимании вышеуказанной ошибки визуальный отладчик студии не смог разрешить зависимость a.dll(a1, a2 и a3) и, следовательно, не смог загрузить NatvisAddIn.dll.

Когда я пытался использовать a.dll в своем тестовом приложении и вычислять DisplayString для MyCustomeType, разрешается получение зависимости, загружается a.dll, и я получаю ожидаемую строку без копирования a1.dll, a2.dll и a3.dll. зависимые dll разрешаются/выбираются из переменной PATH окна. Но, в случае визуального студийного отладчика, зависимые 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 присутствуют в Visual Studio, другие находятся в c:\windows\WinSxS

Я попробовал свой сценарий использования на Visual Studio 2012 и Visual Studio 2017. Я столкнулся с одной и той же проблемой с обеими визуальными студиями.

Ответ 4

Вы решаете эту проблему? У меня такая же проблема, как и у вас.